TowardsDataScience-博客中文翻译-2019-五十-
TowardsDataScience 博客中文翻译 2019(五十)
【反欺骗】零拍人脸反欺骗的深度树学习

Photo by Joel Burgess on Unsplash
解决的问题
通常,人们会创建一种算法(或构建神经网络)来缓解特定的已知攻击。然而,在缓解技术和新攻击类型的创造之间有一场持续不断的竞赛。该论文认为,对于现实世界的应用程序来说,这是一个问题,并渴望建立一种方法来检测目前未知的欺骗攻击。
这里未知的欺骗攻击意味着算法设计者没有意识到的攻击,并且一个有经验的模型从未见过这种攻击的例子。由于已学习的模型从未见过这些当前未知的欺骗攻击的例子,因此本文将相应的检测&缓解技术归类为零镜头人脸反欺骗。
现有技术及其局限性
在全球范围内,该方法一直将此类未知欺骗攻击视为异常检测问题。
其他方法有 3 个主要缺点:
- 创建算法时考虑了有限的欺骗种类,更具体地说,只有打印&重放攻击
- 使用手工制作的特征来检测活体与非活体人脸
- 在构建新的算法和网络时,没有利用已知的攻击类型。论文强烈认为这是一个被忽视的有价值的信息。
主要贡献
本文有三个主要贡献:
- 综合研究 13 种不同类型的欺骗攻击

Extracted from the paper
- 提出一种称为深度树网络(DTN) 的新网络架构,用于检测未知的欺骗攻击
- 创建新的数据集来训练和评估零镜头人脸反欺骗
关键见解
正如在现有技术及其局限性部分中提到的,作者假设应该利用现有欺骗类型的知识,因为新的欺骗类型可以与它们共享一些(如果不是全部)属性。这些共有的属性就是他们所谓的同质特征。
该论文认为,这些同质特征可以用于将未知的欺骗类型投射到一组已知的攻击类型中。
传统上,人们会尝试建立一个分类器来检测活体人脸和某一欺骗类型的欺骗人脸。这将是一个二元分类或多元分类的问题。这给使用监督学习机制设计网络带来了压力,因此需要标签。因为这里的目标是找到设计者目前未知的欺骗,所以他们将此作为无监督学习问题(在监督块的帮助下,如后所述)。
基于树的机器学习算法专门以分层的方式分离特征。他们设想了一种神经网络,可以使用树状结构的属性来帮助以无监督的方式分离特征(即不需要标签)。
它是如何工作的?
第一个重要的目标是建立一个树状结构。作者从 ICCV 2015 年题为 用于形态感知人脸识别的条件卷积神经网络 的论文中获得灵感,以创建所需的树架构&借用形态感知投影树的概念(从精神上来说),然而他们发现了该论文提出的路由函数的缺点,因此提出了一个新的路由函数(如下所述)。

Extracted from the paper
深度树网络(如上所示)由 3 种类型的单元组成:
- 卷积残差单元。这里没什么特别的,3 个有身份连接的卷积层和一个最大池层。
- 树状路由单元(TRU) 。网络中最有趣和最重要的部分,因为它是将欺骗样本投射(模态感知投射)到子类别之一的部分。
- 监督特征学习(SFL) 。该单元或块由二进制分类器(实况对欺骗)和使用逐像素掩码回归的回归器组成。
使用多个损失项对网络进行端到端训练,以帮助学习上述各种单元的参数。
总体而言,有两个部分— 非监督损失&监督损失,如下式所示

Extracted from the paper
路由(由 TRU)确实是最有趣的方面。在这里,他们定义了一个新的路由函数,使用最大方差的概念(类似于 PCA)来帮助分离样本。这最终转化为寻找最大的特征向量。对应于上述损失函数中所示的 L(路线)。
L(唯一)术语负责平衡每个叶片的子组。报纸解释说,我在这里逐字引述
为了使每个叶子有一个平衡的子组,我们将实时数据的响应抑制为零,以便所有实时数据可以均匀地划分到子节点。同时,我们还抑制了没有访问该节点的欺骗数据的响应,使得每个节点都模拟了唯一的欺骗子集的分布
损失函数的监督部分很容易理解,在上面我描述了 3 种类型的单元。
假设是如何被证明的
假设在欺骗类型中存在同质特征,并且使用模态感知投影技术,他们能够将欺骗样本分类到子类别之一。
他们通过使用留一测试协议来证明这一点。本质上,他们会使用 12 种(共 13 种)欺骗攻击来训练他们的深度树网络(DTN ),然后使用剩下的一种来测试它。简单地说,剩下的一种类型从未被他们的网络看到,因此被认为是一种未知的恶搞类型!
杂项说明和文件的非目标
解决方案是而不是试图确定“恶搞类型”。最后,目标本质上是二元的,也就是说,它是一张恶搞的或真实的脸。
DTN 图像中所示的叶节点并不对应于欺骗类型,而是样本将根据与欺骗类型的相似性而被投射到的潜在类别。
各种链接和详细信息
论文有开源实现吗?
是的。请看这里—https://github . com/Yaojie Liu/cvpr 2019-deeptreelingforzeroshotfacespoofing
他们用的是 Tensorflow 2.0。考虑到多个损失函数和各种单元的交替优化步骤,因此使用定制的训练循环,训练有点棘手。
但是,如果没有签署所需的协议,数据集就不可用。
这篇论文是在一次会议上发表的吗?
是的。这篇论文在 CVPR2019 上被接受,是最佳论文奖的决赛选手之一。
https://arxiv.org/abs/1904.02860
有解释论文的视频吗?
这篇论文也参加了 CVPR 2019 年的口试。下面是作者解释这个概念的视频链接。
https://youtu.be/j7A83F6PRAE?t=3976
注意——CVPR 口述的时间非常有限,但作者在阐述要点方面做得很好。我喜欢!
我的观点、问题和要点
- 我真诚地感谢对新攻击媒介和缓解技术之间持续战争的认可。当然,还有解决问题的尝试。
- 起初,我对他们的假设感到困惑,即各种类型的电子欺骗有一些相同的特征。老实说,我仍然怀疑,即使他们确实用留一法从经验上证明了他们的假设。
- 我真的很喜欢参考论文( 用于模态感知人脸识别的条件卷积神经网络 )解释的模态感知投影的整个概念,以及本文对它的巧妙利用和提炼。可能在不久的将来会写一篇评论。
- 如果叶节点对应于已知的欺骗类型(即这里的监督方法)会怎样?了解欺骗类型也有助于了解给定部署中的攻击面。
- GAN 构建的数字面孔似乎没有被考虑在内。在我看来,他们应该被认为是假的,可能是第 14 种欺骗类型。
- 最后,该论文非常全面,可读性强,并对许多消融研究进行了尽职调查。
希望你喜欢这个摘要,我可能误解/曲解了论文的某些部分,因此,如果有的话,错误是我的,而不是原论文作者的。
论文综述:dense net-密集连接的卷积网络
CVPR 2017,最佳论文奖获得者
在我们进入 DenseNets 之前,这里有一本免费的实用自然语言处理书籍,你可能会喜欢
More information in video description.

Dense connections
“简单的模型和大量的数据胜过基于更少数据的更复杂的模型。”彼得·诺维格
关于报纸
密集连接的卷积网络在 IEEE 计算机视觉和模式识别大会上获得最佳论文奖(CVPR)2017。论文可以在这里阅读。
第一作者, 黄高 曾在康乃尔大学做博士后,目前在清华大学做助理教授。他的研究重点是计算机视觉的深度学习。
我是如何看到这张纸的?
我在研究专注于通过重建提高图像质量(在分辨率或动态范围方面)的神经网络实现时,看到了这篇论文。虽然这篇论文展示了该体系结构在图像分类方面的能力,但密集连接的思想已经激发了许多其他深度学习领域的优化,如图像超分辨率、图像分割、医疗诊断等。
DenseNet 架构的主要贡献
- 缓解消失渐变问题
- 更强的特征传播
- 特征重用
- 减少参数计数
阅读前:
理解这篇帖子需要对深度学习概念有一个基本的了解。
论文评论
本文首先讨论消失梯度问题,即随着网络越来越深,梯度如何不能充分地反向传播到网络的初始层。随着梯度向后移动到网络中,梯度变得越来越小,结果,初始层失去了学习基本低级特征的能力。
已经开发了几种架构来解决这个问题。这些包括—资源网、高速公路网、分形网、随机深度网。

不管这些网络的架构设计如何,它们都试图为信息在初始层和最终层之间的流动创建通道。出于同样的目的,DenseNets 在网络的各层之间创建路径。
相关作品
- 高速公路网络(使更深层次模型的训练变得容易的最初尝试之一)
- ResNet(通过使用标识映射求和来绕过连接)
- 随机深度(在训练期间随机丢弃层)
- GoogLeNet(初始模块——增加网络宽度)
- 分形网络
- 网络中的网络
- 深度监督网络
- 梯形网络
- 深度融合网络
密集连接
根据网络的前馈特性,密集块中的每一层都从所有前面的层接收要素地图,并将其输出传递给所有后面的层。从其他层接收的特征地图通过连接进行融合,而不是通过求和(如在 ResNets 中)。

Concatenation of feature maps
这些连接形成了密集的路径回路,让有更好的梯度流动。

Dense connections
每一层都可以直接访问损失函数的梯度和原始输入信号。
由于这些密集的连接,该模型需要更少的层,因为不需要学习冗余的特征地图,允许重复使用集体知识(由网络集体学习的特征)。所提出的架构具有窄层,其为低至 12 个通道特征图提供了最先进的结果。更少更窄的层意味着模型需要学习的参数更少,更容易训练。作者还谈到了作为串联特征图结果的层输入变化的重要性,这防止了模型过度拟合训练数据。

DenseNet 模型的许多变体已经在论文中提出。我选择用他们的标准网络(DenseNet-121)来解释这些概念。

Some of the variants of the DenseNet architecture
复合函数

Composite function
*论文(和博客)中网络表述的每个 CONV 块对应于一个操作—
BatchNorm→ReLU→Conv*
密集块
密集连接的概念已经在密集块中描述。一个密集块包括 n 个密集层。这些密集层使用密集电路连接,使得每个密集层接收来自所有先前层的特征地图,并将其特征地图传递给所有后续层。特征的尺寸(宽度、高度)在密集块中保持不变。

Dense block (DB) with six Dense Layers (DL)
致密层
每个密集层由两个卷积运算组成
- 1×1 CONV(用于提取特征的常规 conv 操作)
- 3 X 3 CONV (降低特征深度/通道数)

Dense layer of DB-1
DenseNet-121 在致密块中包含 6 个这样的致密层。每个致密层的输出深度等于致密块体的生长速度。
增长率(k)
这是一个你会在报纸上经常遇到的术语。基本上就是一个密集层输出的通道数( 1x1 conv → 3x3 conv )。作者在实验中使用了一个值 k = 32 。这意味着密集层( l )从前一密集层( l-1 )接收的特征数量是 32。这被称为增长率,因为在每一层之后,32 个通道特征被连接并作为输入馈送到下一层。

Dense block with channel count (C) of features entering and exiting the layers
转变层
在每个密集块的末尾,特征图的数量累积到一个值— 输入特征+(密集层数 x 增长率)。因此,对于进入增长率为 32 的 6 个密集层的密集块的 64 个通道特征,在块的末端累积的通道数将是—
64+(6×32)= 256。为了减少这个通道数,在两个密集块之间增加了一个过渡层(或块)。过渡层包括-

Transition layer/block
- 1 X 1 CONV 操作
- 2×2 AVG 池操作
1 X 1 CONV 操作将通道数减半。
2 X 2 AVG 池图层负责根据宽度和高度对特征进行下采样。
全网络
从下图中可以看出,作者为三个密集区块中的每一个选择了不同数量的密集层。

Full DenseNet architecture
与 DenseNet 的比较

ResNet DenseNet comparison
我们可以看到,即使参数数量减少,DenseNet 模型的验证误差也比参数数量相同的 ResNet 模型低得多。这些实验是在具有更适合 ResNet 的超参数的两个模型上进行的。作者声称,DenseNet 在广泛的超参数搜索后会表现得更好。
具有 20M 参数的 DenseNet-201 模型与具有超过 40M 参数的 101 层 ResNet 产生类似的验证误差。
检查代码
我相信仔细阅读代码会更容易理解这种架构的实现。研究论文(在深度学习的背景下)可能很难理解,因为它们更多地是关于驱动神经网络设计决策的因素。检查代码(通常是网络/模型代码)可以降低这种复杂性,因为有时我们感兴趣的只是实现。有些人喜欢先看到实现,然后试图找出网络设计决策背后的原因。不管怎样,在之前或之后阅读代码总是有帮助的。
DenseNet 实现的代码可以在这里找到。由于我对 PyTorch 更熟悉,我将试着解释这个模型的 PyTorch 实现,它可以在这里找到。最重要的文件是 models/densenet.py ,它保存了 densenet 的网络架构。
代码被分成这些类,其中每种类型的块由一个类表示。

Class hierarchy in code
致密层
_DenseLayer 类可用于初始化密集层的组成操作——
巴特诺姆→雷卢→ Conv (1X1) →巴特诺姆→雷卢→ Conv (3X3)
_bn_function_factory() 函数负责将前几层的输出连接到当前层。
DenseBlock
_ DenseBlock 类包含一定数量的_ dense layer(num _ layers)。
该类由 DenseNet 类初始化,取决于网络中使用的密集块的数量。
过渡块
DenseNet
由于这部分代码有点太大,不适合写在这篇博客里,所以我将只使用一部分代码,这将有助于理解网络的要点。
我在网上找到了这张图片,它帮助我更好地理解了网络。

受本文启发的其他作品
- 图像超分辨率残差密集网络 (2018)

- 用于图形到序列学习的密集连接图形卷积网络 (2019)

结论
DenseNet 是一个网络,它描述了在使用密集块的网络中拥有密集连接的重要性。这有助于特征重用、更好的梯度流、减少的参数计数和跨网络更好的特征传输。这样的实现可以帮助使用更少的计算资源和更好的结果来训练更深的网络。
为了了解更多关于机器学习和数据科学领域的信息,请查看并订阅深度神经笔记本播客,在这里,我采访了专家和从业者,了解他们的专业状态、他们迄今为止的旅程以及未来的道路。
也可以在所有主要的播客流媒体平台上使用,包括 苹果播客 、 Spotify 和 谷歌播客 。
论文综述:数据感知集群调度中选择的力量
在本帖中,我们将介绍一个名为 KMN 的调度器,该调度器旨在解决 Spark 或 MapReduce 等分布式计算框架中 I/O 密集型任务的调度问题。这个调度器不同于我们之前讨论的调度器,因为它强调数据感知调度,我们将在本文中讨论。
背景
在当今的批处理计算框架中,如 Hadoop 和 Spark,它们为每个构建成 DAG(有向无环图)依赖图的作业运行许多阶段和任务。如果我们假设这些任务中有很大一部分是 I/O 密集型的,那么调度器的任务就是尽量减少任务读取数据的时间。然而,在大型多租户集群中,具有数据局部性的完美节点可能经常不可用。
今天的数据应用程序和算法也可以选择只选择源数据的子集来逼近答案,而不是需要完整的数据集。
Spark & MapReduce 框架通常有读取源数据的输入任务和将数据从输入任务转发到进一步处理的中间任务。对于任务调度器,它可以为输入任务优化的是尝试将任务放置在更靠近源数据的地方(位置)。对于中间任务,调度程序将进行优化,以最小化来自输入任务的网络传输。集群内网络带宽的一个主要瓶颈是跨机架链接饱和。作者模拟了使用过去的脸书跟踪实现网络争用和数据局部性的情况,并估计性能提高了 87.6%。
KMN 调度
KMN 调度器在 Spark 中实现,它提供了一个应用程序接口,允许用户选择查询将选择的输入数据比率(1–100%)。

KMN 调度程序将基于所有可用的 N 个输入和局部性选择,选择在具有内存局部性的 K 个可用块的随机样本上启动输入任务(一对一传输)。

对于进行多对一传输的中间任务,作者发现的主要见解是,避免跨机架网络带宽偏斜的关键是允许启动 K 个以上的输入任务(M 个任务),因为这允许从下游任务传输数据的更多选择,从而可以避免偏斜。虽然找到任务的最佳机架位置是一个 NP-hard 问题,但作者建议,在他们的设置中,要么使用最适合小型任务的贪婪搜索,要么使用大型任务的循环调度的变体。

这里一个重要的决定当然是我们应该启动多少额外的任务。过多的任务将导致更长的作业等待时间(也考虑到掉队者),但是过少的额外任务可能会导致网络不平衡问题。找到平衡点可以让你最大化两者之间的平衡。这里的一个策略是,调度程序可以决定在启动下游任务之前,等待上游任务启动和完成的时间,因此当您遇到掉队的任务时,您不必等待样本中的所有任务都完成。
思绪
当我与几家运营大型本地集群的公司聊天时,跨机架网络拥塞仍然是一个现实问题。虽然随着时间的推移,数据位置的重要性正在降低,这使得云中的速度更快,但我认为跨 AZ 和网络拥塞仍然是我看到的公司在云中经常遇到的问题。
当然,我们可以看到,在制定任务和分布决策时,所有分布式数据框架都开始意识到集群资源瓶颈。
【反欺骗】关于检测数字人脸操纵

Photo by Claudia Ramírez on Unsplash
解决的问题
使人脸识别成为现实的技术(即机器学习)也使攻击者有可能欺骗它。使用先进的合成人脸生成和处理方法,更复杂类型的攻击正在被创造出来。
大致来说,假面攻击分为三类:
- 物理欺骗
- 对抗性的面部攻击
- 数字操纵攻击
本文仅针对第三类攻击,即 数字操纵攻击 。这种攻击类型被进一步分为四个子类别,如下图所示。

Extracted from the paper and annotated by me for clarification of types
这里的目的是不仅要确定输入图像是真实的还是伪造的,而且要定位图像被操纵的部分。
定位图像的虚假部分的好处是有助于算法的可解释性,从而(潜在地)理解操纵的类型、幅度和意图。
现有技术及其局限性
到目前为止,数字图像操作的检测已经使用两种方法完成— 图像分割&重复应用利用滑动窗口的二进制分类器。这两种类型都主要依赖于需要额外监督的多任务学习。
该论文认为,上述两种方法都没有“直接”提高最终的图像分类性能。
关键见解
论文为利用注意力图解决检测(分类)&定位的任务提供了坚实的案例。
那么注意力地图是什么意思呢?将“注意力”的概念理解为“焦点”。当我们人类看一幅画或一个场景时,我们通过观察某些特定的线索来形成对它的看法。这些线索,无论多么微小,常常告诉我们整个故事,更重要的是,通过忽略不相关的方面,帮助我们快速做出决定。

请看这张狗的图片。这里的线索显示在边界框中。如果我们没有这些,那么就不可能确定它是狗还是人。
阅读本文https://lilian Weng . github . io/lil-log/2018/06/24/attention-attention . html对神经网络中所有“注意”的事物进行深入的处理。
该论文认为,图像处理通常只在某些部分进行,如果我们能够只关注(注意)这些部分,我们最终可能会提高最终的分类性能。
他们假设,与人们可以从数字图像的高频信息中留下的“指纹”来确定相机型号的事实非常相似,对部分图像的处理提供了检测其高频信息中的异常(由于算法处理)的机会。
简而言之,这项任务归结为找到描述这些异常的图像碎片。找到这些补丁的行为也导致了本地化属性。
主要贡献
- 一种新的提高分类性能的注意层&产生指示被操作脸部的注意图
- 一个全面的假脸数据集包括由不同的人脸修改方法生成的 0.8M 真实人脸和 1.8M 假脸,以及伴随的评估协议。
- 一种新的度量,称为逆交集非包容(IINC),用于评估注意力图,产生比现有度量更一致的评估
- 与强基线网络相比,人脸操作检测的最新性能。
它是如何工作的?
现在应该很明显,主要目标是学习生成所需注意力地图的过滤器。本文考虑了标签数据(假零件与真实图像)甚至不可用的可能性,因此提供了两种主要方法。
两种方法是:
- 自我注意 PCA(主成分分析)投影。这将用于无监督(即标记数据不可用)设置。由于其本质,这是一个无参数的方法。
- 方向回归。他们在前一个卷积层中增加一个通道,以生成大小为 F ∈ R B×H×W×(C+1)的特征图。f 可以拆分成 F1 ∈ R B×H×W×C 和 Matt ∈ R B×H×W×1。具有 sigmoid 函数的最后一个通道用作注意图

Extracted from paper and then annotated by me
对于任一类型的方法(PCA 或回归),它们应用 sigmoid 激活函数,然后执行与输入特征(F)的逐通道乘法,并将仅由输入特征的激活/阈值化部分组成的结果提供给后续层。
他们将注意力地图生成(PCA 或回归)、sigmoid 操作和通道乘法封装为一个层,并将其称为基于注意力的层,从而使设置非常模块化。
对于基于直接回归的方法,即监督方法,如果基础事实注意力图可用,他们在他们制定的指标上实现最佳准确性(参见上面的主要贡献部分)。否则,PCA 是更好的方法。
那么我们应该在哪里插入这一层呢?作者通过在基于 XceptionNet 的主干的不同位置注射注意力基础层进行了广泛的消融研究。他们的结果表明,不同的放置选项有利于不同的准确性指标(如下表所示)

Extracted from the paper
上述结果表明,在早期层之后插入导致较低的 EER,而后期放置提高了分类精度。他们的结论是,中间位置(第 4 块之后)在所有指标上都表现出强劲的性能,没有明显的下降。
这个关注层是 XceptionNet 特有的吗?没有。作者将它与 VGG16 和一个定制的网络主干一起使用。在所有 3 个案例中,注意层的插入提高了错误检测的准确性。不过,层次的放置和注意力地图方法的使用(PCA 或回归)似乎确实与主干的类型有一些联系。
各种链接和详细信息
论文有开源实现吗?
号码
这篇论文是在一次会议上发表的吗?
没有。预印本可以在这里找到【https://arxiv.org/pdf/1910.01717.pdf
有解释论文的视频吗?
号码
我的观点、问题和要点
- 由于注意力地图的概念(本文使用的主要技术),我可以理解如何处理以下类别的图像部分——属性、表情和身份交换,但它在第四类(图像完全是合成的)中的应用不太清楚。请注意,他们也确实在这些合成图像上测试了他们的实验,也就是说,他们没有忽略它们,但是对我来说,注意力地图在这一类别上的应用并没有被很好地理解。
- 独特的,聪明的和智能的使用视觉注意力的假货检测问题。
- 我特别喜欢将它模块化的方法,并欣赏对可解释性方面的关注。
- 我对他们创造的度量标准“逆交集非包容(IINC)”没有太多关注,所以请阅读该论文以了解详细信息。
- 最后,该论文非常全面,可读性强,并对许多消融研究进行了尽职调查。
希望你喜欢这个摘要,我可能误解/曲解了论文的某些部分,因此,如果有的话,错误是我的,而不是原论文作者的。
论文总结。刚度:神经网络泛化的新视角

这是对刚度的总结:神经网络泛化的新视角 (01/2019)。
僵硬?
本文旨在从刚度的角度提高我们对神经网络如何泛化的理解。刚度背后的直觉是一个点上的梯度更新如何影响另一个点:
【它】描述了由于基于其中一个应用梯度更新而导致的两个损失变化之间的相关量。(4.1、结果和讨论)
刚度表示为梯度的预期符号g:

改善 X1 和 X2 损失的权重更新是刚性的,如果损失有益于其中一个点而无助于另一个点,则该权重更新被描述为反刚性的。

现在的问题是我们如何选择 X1 和 X2。作者探索了两种方法:按类成员或按距离。
基于类成员的刚度
我们可以看看 A 类中一个点的梯度更新如何影响属于 b 类的另一个点的损失。在本文中,他们制作了一个*类刚度矩阵`,这是按类分组的每个点之间的平均刚度:

该矩阵的对角线表示模型的类内泛化能力。你可以在训练阶段的不同阶段找到一个刚度等级矩阵的例子:

在早期阶段,同一类成员之间的刚度较高(因此出现红色对角线)。大多数单元提高它们的刚度,直到达到过度拟合点:刚度达到 0。
刚度作为距离和学习率的函数
然后通过距离透镜研究刚度,它们区分两种距离:像素方向(在输入空间)和层方向(在表示空间)。

图 9 中可见的一般模式是存在一个临界距离,在该距离内,输入数据点倾向于在梯度更新下一起移动,即具有正刚度。这适用于网络中的所有层,更深的层倾向于具有更小的硬磁畴尺寸。
作者将刚性区域定义为“当应用梯度更新时一起移动的数据空间区域”。

我们可以看到,较高的学习率增加了僵硬区域的大小,这表明较高的学习率有助于泛化。
tldr
- 硬度量化了一组点上梯度更新对另一组点的影响程度
- 僵硬与概括紧密相连
- 当系统过度拟合时,刚度趋于 0
- 更高的学习率增加了点一起移动的区域
补充资源:
- 流形混合:通过内插隐藏状态更好的表现——https://arxiv.org/abs/1806.05236(文章引用)
原载于 data-soup.github.io/blog/
论文摘要:U-Net:生物医学图像分割的卷积网络
U-nets 在医学成像中产生了更好的图像分割。 U-Net:卷积网络用于生物医学图像分割论文发表于 2015 年。
问题
人们普遍认为,深度网络的成功训练需要成千上万个带注释的训练样本。该论文提出了一个网络和训练策略,该策略依赖于对数据扩充的大量使用,以更有效地使用可用的带注释的样本。
卷积网络的典型应用是分类任务,其中图像的输出是单个类别标签。然而,在许多视觉任务中,特别是在生物医学图像处理中,期望的输出应该包括定位,即应该给每个像素分配一个类别标签。此外,成千上万的训练图像通常在生物医学任务中是遥不可及的。
解决办法
U-Net 架构建立在完全卷积网络的基础上,并在某种程度上进行修改,以便在医学成像中产生更好的分割。该论文通过对可用的训练图像应用弹性变形来使用过度的数据扩充。这允许网络学习对这种变形的不变性,而不需要在带注释的图像语料库中看到这些变换。
体系结构

u-net 由两部分组成:编码器/压缩路径(左侧)和解码器/扩展路径(右侧)。
收缩路径由一个 3×3 卷积(无填充)的重复应用组成,每个卷积之后是一个 ReLU 和一个 2×2 最大合并操作,步长为 2,用于下采样。在每个下采样步骤中,我们将特征通道的数量增加一倍。这通过一个紧凑的特征图捕获了上下文。
扩展路径包括特征图的上采样,随后是将特征通道数量减半的 2x2 卷积(“上卷积”),与来自收缩路径的裁剪后的特征图的级联,以及 3x3 卷积,随后是 ReLU。完成特征维度的上采样以满足与左侧要连接的块相同的大小。
扩展增加了“什么”,这有助于获得更多的特征,但是丢失了本地化,本地化信息是从收缩路径连接的。
由于每次卷积都会丢失边界像素,因此裁剪是必要的。在最后一层,使用 1x1 卷积将每个 64 分量的特征向量映射到期望数量的类别。在这种情况下,它是 2,因为输出特征地图有 2 个类;细胞和细胞膜。
本文的主要贡献在于
a .重叠平铺策略

黄色区域中分割的预测需要蓝色区域中的图像数据作为输入。缺失的输入数据通过镜像进行外推,这用于预测图像边界区域中的像素。
b .通过对训练图像应用弹性变形来进行数据扩充。
这允许网络学习对这种变形的不变性,而不需要在带注释的图像语料库中看到这些变换。这在生物医学分割中是重要的,因为变形是组织中最常见的变化,并且可以有效地模拟真实的变形。
c .同类相触物体的分离。
这是使用加权损失来完成的,其中在接触的细胞之间的分离背景标签在损失函数中获得大的权重。这迫使网络学习接触细胞之间的小分隔边界。
这种贡献的真实世界应用
成千上万的训练图像在生物医学任务中是遥不可及的,需要专家和花费大量时间来注释。这可以使过程自动化,从而降低成本和注释时间。
这也可以应用于其他领域,如质量控制、检验和制造。
本文将该技术应用于电镜记录的神经元结构分割、光镜图像的细胞分割和微分干涉显微镜记录的 HeLa 细胞。
参考
[1]罗内贝格,o .,菲舍尔,p .,&布罗克斯,T. (2015,10 月)。生物医学图像分割的卷积网络。
感谢阅读!跟随@ itsmuriuki。
回归学习!
论文总结。竞争隐藏单元的无监督学习
竞争隐单元无监督学习综述。

This gem was found in twitter.com/evolvingstuff/status/1012517941030502401
关于作者:
- 麻省理工学院,IBM 公司,普林斯顿大学
- 约翰·j·霍普菲尔德普林斯顿神经科学研究所
他们之前在同一篇论文中工作过密集联想记忆对敌对的输入具有鲁棒性。
介绍了一种新的无监督学习技术。(几乎)没有反向传播,模型也不是为特定任务而训练的。两位作者来自神经科学和计算机科学背景,他们的工作基于两项生物学观察:
1-突触变化是局部的:
在生物学中,突触更新取决于突触前细胞和突触后细胞的活动,或许还取决于一些全局变量,如任务执行得如何。(第 1 页)
用反向传播训练的 A 和 B 之间的单元的权重不仅取决于 A 和 B 的活动,还取决于前一层的活动和训练标签。所以它不依赖于 A,B 的活动,而是依赖于网络中的其他神经元。这是受 Hebb 的创意启发。
2-动物在没有标记数据和比用反向传播训练的神经网络更少的数据的情况下学习:
其次,高等动物需要丰富的感官经验,才能将早期的[…]视觉系统调整为成年系统。这种体验被认为主要是观察性的,很少或没有标签,因此没有明确的任务。(第 1 页)
无监督的本地训练
作者成功地在 MNIST 和 CIFAR-10 上训练了他们的模型,只有前向传递,这意味着:—这项技术的计算要求较低,其计算复杂性与反向传播中前向传递的计算复杂性相当(来源)。—不需要针对给定任务训练模型来根据数据进行有意义的表示。

Figure 01
蓝色的矩形是作者的“生物学习算法”。首先,数据通过它,没有任何标签或任何关于它将用于什么任务的指示。一旦被训练,一个完全连接的网络被附加到它的顶部,以便专门化模型并做出期望的预测。使用反向传播来训练该部分。
通常为了计算隐藏层hμ的活动,我们通过将输入vi乘以矩阵Wμi来将输入vi投影到隐藏层上,然后应用非线性。在这种新技术中,通过求解这个微分方程来计算hμ活动:

Equation 08
μ是我们要更新的隐藏层的索引τ是流程的时间刻度Iμ是输入电流- 第二项,所有其他隐藏层的总和,引入了神经元之间的竞争。较强的单位会抑制较弱的单位。没有它,所有的神经元都会在输入信号出现时被激活。注意,该术语引入了单元之间的横向连接,因为同一层内的单元可以彼此连接。
r是一个 ReLU,winh是一个超参数常数。
由于训练是局部的,只需要向前传递,这种结构不同于自动编码器。
在活动
在一个关于 MNIST 和 CIFAR-10 的实验中,作者使用他们的生物技术训练了 2000 个隐藏单元来寻找矩阵Wμi:
- 隐藏单元用正态分布初始化
- 隐藏的单位被训练(同样,没有明确的任务或标签)
- 这些单元然后被冻结并连接到感知器上
- 使用 SGD 训练感知器权重
在下图最右边的图中可以看到 MNIST 的训练误差(BP 代表反向传播,BIO 代表提出的方法)。我们可以看到,尽管训练误差较高,但测试误差非常接近端到端训练的模型。

Figure 03
在 MNIST 上,我们可以看到,由提出的生物学习算法(左图)学习的特征不同于用反向传播(中图)训练的特征。
网络通过多个隐藏单元学习数据的分布式表示。然而,这种表示与由端到端训练的网络学习到的表示非常不同,这从图 3 的左侧和中间的比较中可以清楚地看出。
类似地,对于 CIFAR-10:

Figure 07
tldr
没有自上而下的信息传播,仅使用自下而上的信号学习突触权重,并且算法不知道网络最终必须在顶层解决的任务(第 8 页)
- 一种新的无监督训练技术,其中没有定义任务,训练集通过模型进行训练,没有反向传播。一个完全连接的感知器被附加在上面,用反向传播进行训练,较低的无监督子模型被冻结。
- 这种技术在 MNIST 和 CIFAR 上显示出较差但接近艺术概括状态的性能。
- 没有向前/向后,每个单元都可能与其他单元相连,包括在它自己的层上。
补充资源:
- 麻省理工学院的一位作者的视频演示。
- Github 进行繁殖。
- 在 IBM 的博客上发表博文。
最初发表于data-soup.github.io/blog/
如何在 5 分钟内创建一个自动化的票据交易系统
5 秒钟搞定!
使用 Python 摆脱无聊的东西(第 1 部分)

你厌倦了重复做同样的事情吗?
感觉你的生活只是一遍又一遍的做着同样的事情?
事情是这样的,今天我要介绍一个工具来自动化你枯燥的东西——Python。Python 可能是最容易学习的语言。因为你掌握了 Python 技能,你不仅能够提高你的生产力,还能专注于你更感兴趣的工作。
使用 Python 摆脱无聊的东西是一个系列,所以点击这个链接查看整个系列!
我们开始吧!
我将用新加坡股票市场的票据交易来说明自动化是如何实现的。纸上交易可以让你在投入真钱之前练习用虚拟货币投资或交易。这是证明你的策略是否有效的好方法。
这是我将要分享的议程:
第一部分 — 在文本文件中输入您想要交易的股票代码和数量。
第 2 部分— 如何独自完成网络抓取,全程。
第三部分— 整理数据并制成表格。
第 4 部分 —将结果输出到 csv 或 excel 文件中。
跟随整个旅程,你会发现让你无聊的事情自动化并在 5 秒内更新你的价格是多么简单。
第 1 部分—在文本文件中输入您想要交易的股票代码和数量。
启动一个新的文本文件,输入股票代码和特定股票的买入价格,用逗号分隔。

selected.txt
启动一个新的文本文件,输入股票代码和特定股票的买入价格,用逗号分隔,如图所示
第二部分——如何自己进行网络抓取,全程
这是 SGX 网站的快照。

Snapshot 1
我将举例说明如何抓取这个表中包含的所有交易信息。打开谷歌浏览器,右键点击网站,你将能够看到下面的快照。

Snapshot 2
点击 inspect 按钮,然后点击 network 选项卡(下图右上角突出显示的紫色括号)。

Snapshot 3
接下来,点击紫色框中突出显示的行,然后选择预览,如突出显示的绿色框所示,两者都显示在下面的快照 4 中。

Snapshot 4
所以你可以从预览中看到,所有的数据都包含在 JSON 格式中。接下来,单击快照 5 中的紫色框(标题)。

Snapshot 5
我现在正在做的是检查我应该放入什么元素来从这个页面抓取数据。从上面的快照 5 中,您将能够看到请求 URL ,这是您稍后需要放入请求部分的 URL。由于编码问题,请求 URL 中的“% 2c”将被编码为“”。如果您对编码感兴趣,请查看此链接了解更多信息。
现在,让我们准备必要的信息,以便向服务器发送适当的请求。
第 1 部分请求 Url
把所有的 "%2c" 改成","之后,请求的 url 就会变成下面这个链接。
[https://api.sgx.com/securities/v1.1?excludetypes=bonds¶ms=nc,adjusted-vwap,b,bv,p,c,change_vs_pc,change_vs_pc_percentage,cx,cn,dp,dpc,du,ed,fn,h,iiv,iopv,lt,l,o,p_,pv,ptd,s,sv,trading_time,v_,v,vl,vwap,vwap-currency](https://api.sgx.com/securities/v1.1?excludetypes=bonds¶ms=nc,adjusted-vwap,b,bv,p,c,change_vs_pc,change_vs_pc_percentage,cx,cn,dp,dpc,du,ed,fn,h,iiv,iopv,lt,l,o,p_,pv,ptd,s,sv,trading_time,v_,v,vl,vwap,vwap-currency)
第 2 部分标题
请求头是网络数据包的一部分,由浏览器或客户端发送到服务器,以请求Web 服务器上的特定页面或数据。

Snapshot 6
参考快照 6 中的紫色框,这是当你抓取网站时应该放入的标题部分。
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
"Origin": "[https://www2.sgx.com](https://www2.sgx.com)",
"Referer": "[https://www2.sgx.com/securities/securities-prices](https://www2.sgx.com/securities/securities-prices)"}
现在让我们按照下面的要点把所有的东西放在一起。
第 3 部分—干净的数据

到目前为止,您将得到 JSON 格式的响应。我们将使用 Python 熊猫库来清理数据。
首先,加载您之前填写的股票代码并清除它。
with open('selected.txt') as f:
selected_sc = f.readlines()
selected_sc = [x.replace('\n', '') for x in selected_sc]
portfolio = {x.split(',')[0]: float(x.split(',')[1]) for x in selected_sc}
然后,将抓取的数据加载到 JSON 对象中,然后将其更改为 python pandas 对象。
data = json.loads(req.text)['data']
df = pd.DataFrame(data['prices'])
接下来,重命名列名,使其更容易理解。
df = df.rename(
columns={'b': 'Bid',
'lt': 'Last',
'bv': 'Bid_Volume',
'c': 'Change',
'sv': 'Ask_volume',
'h': 'High',
'l': 'Low',
'o': 'open',
'p': 'Change_percent',
's': 'Ask',
'vl': 'Volume',
'nc': 'Stock_code'})
最后,过滤你想投资或交易的感兴趣的股票代码,然后计算差价。
df = df[df['Stock_code'].isin(portfolio.keys())][['Stock_code', 'Last']] df['bought_price'] = df['Stock_code'].map(portfolio) df['percentage_changes'] = (df['Last'] - df['bought_price'])*100 df['percentage_changes'] = df['percentage_changes'].apply( lambda x: '{0:.2f}%'.format(x))
第 4 部分 —将结果输出到 csv 或 excel 文件中。
将数据保存到 csv 文件并🎉我们正式结束了!🎉
df.to_csv('reseult.csv', index=False)
下面是 csv 文件的快照:

完整的代码可以在这里找到。
快乐编码!
最终想法
我目前是一名数据科学家,我可以通知你的是,爬行仍然非常重要。
感谢你阅读这篇文章。欢迎在下面留下你感兴趣的话题的评论。我将会发布更多关于我的经历和项目的帖子。
关于作者
低魏宏是 Shopee 的数据科学家。他的经验更多地涉及抓取网站、创建数据管道,以及实施机器学习模型来解决业务问题。
他提供爬行服务,能够为你提供你所需要的准确和干净的数据。你可以访问这个网站查看他的作品集,也可以联系他获取抓取服务。
在媒体上阅读低纬鸿的作品。Shopee 的数据科学家。每天,低伟鸿和其他成千上万的…
medium.com](https://medium.com/@lowweihong)
并行化您的 Python 代码

Illustration designed by Macrovector / Freepik
卷入一个游戏存在中,你和你的朋友是 T2 矿工。是的,手里拿着黄帽子和铲子。任务-在一定时间内挖一个洞,以便能够提取一些闪闪发光的钻石!所以你的朋友建议你和他们 轮流 挖…
让我们说,你需要 100 分钟来完成这个任务时,做了转智。

Illustration designed by Macrovector / Freepik
但是,如果你们两个可以一起努力,同时完成工作-

Illustration designed by Macrovector / Freepik
这会导致-
- 更少的时间
- 效率更高
- 不浪费可用资源
- 少流汗!
同样的情况也发生在你的计算机上,连续给两个进程分配 CPU 时间基本上会让等待的进程饿死直到给定一个 CPU 周期,这是对可能使用的资源的浪费。
这篇文章是关于如何克服这个问题的。
让我们钻研并行编程吧!
并行计算/编程本质上是组合使用≥ 2 个处理器/内核/计算机来解决单个问题。它是一种计算体系结构,其中几个处理器通过在处理器之间分配工作负载来同时进行计算。
如何让您的代码为您并行工作!
在 python 中,我们有一个由 GIL 领导的怪物——字面上叫做“GIL ”,它代表着“全局”。
- 它是一个互斥体{ 互斥 },保护同一个线程同时访问 Python 对象。
- 它还防止多个线程同时执行 Python 字节码。
‘多重处理’→我们的 GIL 救星!
python 中的多处理 包 类似于线程模块;“Pool”类是一个很好的例子,它提供了非常方便的方法来并行执行不同输入值的函数。
Pool 类将允许我们创建一个进程/工作者池,它可以同时处理分配给它们的任务。例如,采用一个函数,假设我们将该函数的调用分布在所有/部分可用的内核/处理器上,每个处理器将负责我们处理的问题子集的一部分,我们可以有另一个函数来串行运行所有这些事情,并最终比较多处理如何有助于处理大量 IO 密集型计算。
要检查 CPU 内核的数量,您可以使用以下命令:
现在让我们得到 cod-in
简单地使用 for 循环遍历用户给定的所有值。
在下面的要点中,我们看到简单地将相同的函数传递给。“地图”方法使这一切变得轻而易举!
注意 -在‘Pool()’中,一个可选的参数让我们定义我们希望程序使用的处理器数量,缺省值是最大值。
还有另一个注意事项——使用 这个 小代码查看可用的 CPU 数量
还有其他牛逼的映射方法,查看一下 python 中多处理包的官方文档。
又一个无耻的注释——你一定注意到了,在尝试上面的代码时,非并行执行对于少量的输入给出了更好的结果,而对于大量的输入, 并行才是正确的选择 !
一定要测试一下这个,然后 评论——并行性在多少个值上比并发性表现得更好!
希望这是一个有趣的阅读足以让你挖掘到金矿的 多重处理 !!

就是这样!你很适合并行编程。如果您有任何问题,请随时向我发 推文。
关注我 随时更新我的帖子。祝您愉快!🎉
并行处理大型 AWS S3 文件
这篇文章展示了使用 AWS S3 选择将一个大的 AWS S3 文件(可能有数百万条记录)处理成可管理的并行块的方法

Parallel Processing S3 File Workflow | Image created by Author
在我的上一篇文章中,我们讨论了通过 S3 选择来提高处理大型 AWS S3 文件的效率。处理过程有点顺序,对于一个大文件来说可能需要很长时间。那么,我们如何在多个单元之间并行处理呢?🤔嗯,在这篇文章中,我们将实现它,并看到它的工作!
📝我强烈推荐通过 S3 查看我在 上的上一篇文章——选择 来设置这篇文章的背景。
我总是喜欢把一个问题分解成解决它所必需的小部分(分析方法)。让我们试着用三个简单的步骤来解决这个问题:
1.找出 S3 文件的总字节数
与我们上一篇文章的第一步非常相似,这里我们也尝试先找到文件大小。下面的代码片段展示了将对我们的 S3 文件执行 HEAD 请求并确定文件大小(以字节为单位)的函数。
# core/utils.py
def get_s3_file_size(bucket: str, key: str) -> int:
"""Gets the file size of S3 object by a HEAD request
Args:
bucket (str): S3 bucket
key (str): S3 object path
Returns:
int: File size in bytes. Defaults to 0 if any error.
"""
aws_profile = current_app.config.get('AWS_PROFILE_NAME')
s3_client = boto3.session.Session(profile_name=aws_profile).client('s3')
file_size = 0
try:
response = s3_client.head_object(Bucket=bucket, Key=key)
if response:
file_size = int(response.get('ResponseMetadata').get('HTTPHeaders').get('content-length'))
except ClientError:
logger.exception(f'Client error reading S3 file {bucket} : {key}')
return file_size
2.创建一个芹菜任务来处理一个块
这里,我们将定义一个 celery 任务来处理一个文件块(稍后将并行执行)。这里的整个处理过程如下所示:
- 接收这个块的
start和end bytes作为参数 - 通过 S3 获取 S3 文件的这一部分——选择并将其存储在本地的一个临时文件中(在本例中为 CSV)
- 读取这个临时文件并执行任何需要的处理
- 删除这个临时文件
📝我将这个任务称为文件块处理器。它处理文件中的一个块。运行多个这样的任务可以完成整个文件的处理。
# core/tasks.py
@celery.task(name='core.tasks.chunk_file_processor', bind=True)
def chunk_file_processor(self, **kwargs):
""" Creates and process a single file chunk based on S3 Select ScanRange start and end bytes
"""
bucket = kwargs.get('bucket')
key = kwargs.get('key')
filename = kwargs.get('filename')
start_byte_range = kwargs.get('start_byte_range')
end_byte_range = kwargs.get('end_byte_range')
header_row_str = kwargs.get('header_row_str')
local_file = filename.replace('.csv', f'.{start_byte_range}.csv')
file_path = path.join(current_app.config.get('BASE_DIR'), 'temp', local_file)
logger.info(f'Processing {filename} chunk range {start_byte_range} -> {end_byte_range}')
try:
# 1\. fetch data from S3 and store it in a file
store_scrm_file_s3_content_in_local_file(
bucket=bucket, key=key, file_path=file_path, start_range=start_byte_range,
end_range=end_byte_range, delimiter=S3_FILE_DELIMITER, header_row=header_row_str)
# 2\. Process the chunk file in temp folder
id_set = set()
with open(file_path) as csv_file:
csv_reader = csv.DictReader(csv_file, delimiter=S3_FILE_DELIMITER)
for row in csv_reader:
# perform any other processing here
id_set.add(int(row.get('id')))
logger.info(f'{min(id_set)} --> {max(id_set)}')
# 3\. delete local file
if path.exists(file_path):
unlink(file_path)
except Exception:
logger.exception(f'Error in file processor: {filename}')
3.并行执行多个 celery 任务
这是这个流程中最有趣的一步。我们将通过 celery Group 创建多个并行运行的 Celery 任务。
一旦我们知道了 S3 中一个文件的总字节数(来自步骤 1),我们就为这个块计算start和end bytes,并通过 celery 组调用我们在步骤 2 中创建的任务。start和end bytes范围是文件大小的连续范围。可选地,我们也可以在所有处理任务完成后调用回调(结果)任务。
# core/tasks.py
@celery.task(name='core.tasks.s3_parallel_file_processing', bind=True)
def s3_parallel_file_processing_task(self, **kwargs):
""" Creates celery tasks to process chunks of file in parallel
"""
bucket = kwargs.get('bucket')
key = kwargs.get('key')
try:
filename = key
# 1\. Check file headers for validity -> if failed, stop processing
desired_row_headers = (
'id',
'name',
'age',
'latitude',
'longitude',
'monthly_income',
'experienced'
)
is_headers_valid, header_row_str = validate_scrm_file_headers_via_s3_select(
bucket=bucket,
key=key,
delimiter=S3_FILE_DELIMITER,
desired_headers=desired_row_headers)
if not is_headers_valid:
logger.error(f'{filename} file headers validation failed')
return False
logger.info(f'{filename} file headers validation successful')
# 2\. fetch file size via S3 HEAD
file_size = get_s3_file_size(bucket=bucket, key=key)
if not file_size:
logger.error(f'{filename} file size invalid {file_size}')
return False
logger.info(f'We are processing {filename} file about {file_size} bytes :-o')
# 2\. Create celery group tasks for chunk of this file size for parallel processing
start_range = 0
end_range = min(S3_FILE_PROCESSING_CHUNK_SIZE, file_size)
tasks = []
while start_range < file_size:
tasks.append(
chunk_file_processor.signature(
kwargs={
'bucket': bucket,
'key': key,
'filename': filename,
'start_byte_range': start_range,
'end_byte_range': end_range,
'header_row_str': header_row_str
}
)
)
start_range = end_range
end_range = end_range + min(S3_FILE_PROCESSING_CHUNK_SIZE, file_size - end_range)
job = (group(tasks) | chunk_file_processor_callback.s(data={'filename': filename}))
_ = job.apply_async()
except Exception:
logger.exception(f'Error processing file: {filename}')
@celery.task(name='core.tasks.chunk_file_processor_callback', bind=True, ignore_result=False)
def chunk_file_processor_callback(self, *args, **kwargs):
""" Callback task called post chunk_file_processor()
"""
logger.info('Callback called') # core/utils.py
def store_scrm_file_s3_content_in_local_file(bucket: str, key: str, file_path: str, start_range: int, end_range: int,
delimiter: str, header_row: str):
"""Retrieves S3 file content via S3 Select ScanRange and store it in a local file.
Make sure the header validation is done before calling this.
Args:
bucket (str): S3 bucket
key (str): S3 key
file_path (str): Local file path to store the contents
start_range (int): Start range of ScanRange parameter of S3 Select
end_range (int): End range of ScanRange parameter of S3 Select
delimiter (str): S3 file delimiter
header_row (str): Header row of the local file. This will be inserted as first line in local file.
"""
aws_profile = current_app.config.get('AWS_PROFILE_NAME')
s3_client = boto3.session.Session(profile_name=aws_profile).client('s3')
expression = 'SELECT * FROM S3Object'
try:
response = s3_client.select_object_content(
Bucket=bucket,
Key=key,
ExpressionType='SQL',
Expression=expression,
InputSerialization={
'CSV': {
'FileHeaderInfo': 'USE',
'FieldDelimiter': delimiter,
'RecordDelimiter': '\n'
}
},
OutputSerialization={
'CSV': {
'FieldDelimiter': delimiter,
'RecordDelimiter': '\n',
},
},
ScanRange={
'Start': start_range,
'End': end_range
},
)
"""
select_object_content() response is an event stream that can be looped to concatenate the overall result set
"""
f = open(file_path, 'wb') # we receive data in bytes and hence opening file in bytes
f.write(header_row.encode())
f.write('\n'.encode())
for event in response['Payload']:
if records := event.get('Records'):
f.write(records['Payload'])
f.close()
except ClientError:
logger.exception(f'Client error reading S3 file {bucket} : {key}')
except Exception:
logger.exception(f'Error reading S3 file {bucket} : {key}')
就是这样!😎现在,我们不是一个字节一个字节地传输 S3 文件,而是通过并发处理数据块来实现并行处理。没那么难,不是吗?😅
📌您可以查看我的 GitHub 库以获得这种方法的完整工作示例。
🔍比较处理时间
如果我们用这种方法比较我们在上一篇文章中处理的同一个文件的处理时间,处理速度大约比快 68%(使用相同的硬件和配置)。😆
╔═════════════════╦═══════════════════╦════════════════════════════╗
║ ║ **Streaming S3 File** ║ **Parallel Processing S3 File**║
╠═════════════════╬═══════════════════╬════════════════════════════╣
║ **File size** ║ 4.8MB ║ 4.8MB ║
║ **Processing time** ║ ~37 seconds ║ ~12 seconds ║
╚═════════════════╩═══════════════════╩════════════════════════════╝

Streaming S3 File Logs | Image by the Author

Parallel Processing S3 File Logs | Image by the Author
✔️这种方法的好处
- 包含数百万条记录的非常大的文件可以在几分钟内得到处理。我在生产环境中使用这种方法已经有一段时间了,它非常令人愉快
- 计算和处理分布在分布的工作人员中
- 工作池的可用性可以调整处理速度
- 不再有内存问题
📑资源
原发布于 2019 年 1 月 22 日https://dev . to。
机器学习算法中的参数。

Pic Credit: https://mljar.com/blog
理解 ML 算法的初学者指南。
在我与海得拉巴ISB的交往中,我有幸成为沙伊莱什·库马尔的学生。Shailesh 教授对于如何定义一名成功的数据科学家有着独特的观点:
- 数据科学家能够写出针对给定问题必须优化的目标函数。
- 数据科学家能够理解在求解目标函数时需要学习的自由参数的数量。
- 数据科学家能够理解控制模型复杂性的旋钮(或超参数)。
我写这篇文章是为了那些想了解参数在 ML 算法中的作用的人。需要求解的参数数量将直接影响训练过程的时间和输出。下面的信息对那些理解 ML 中各种算法的人是有用的。
- 降维方法
主成分分析:
PCA 用于输入数据的降维,方便快捷。PCA 算法的输出是在向量空间中正交的数据集。PCA 的目标函数可以写成 argmax{W'CW} 其中 C 是输入数据的协方差矩阵,它是对称的、半正定的。为 W 求解上述函数将导致 W 是矩阵 C 的特征向量。设数据是一个 d 维矩阵。 C 将会是d * d .PCA 中的参数数量由总共处于最大值‘d’的特征向量的数量给出。每个特征向量的维数为‘1xd’,,因此需要估计的总参数为d * dx1 = d。许多软件包也给出了特征值,即每个主成分解释的方差。由于所有特征值的总和必须等于数据中的总方差,因此存在用于估计特征值的 d-1 自由参数。旋钮是我们在不损失太多方差的情况下需要考虑的主成分数 (k) 。例如:MNIST 数据集中每个字符的数据被安排在一个 28×28 的图像中,该图像构成一个长度为 784 的向量。这一幅图像的协方差矩阵大小为 784x784,因此参数总数为 784*784+783。
多维标度(MDS):
MDS 的目标是将高维数据投射到低维表面。对于每对观察值,相似性距离δ作为算法的输入给出。结果将是一个 x 维空间中每个数据点的坐标向量。目标函数是最小化在 x 维空间中的投影距离 delta_x 与数据中每对点之间的实际距离 delta 的误差。.即 argmin {(delta_x — delta) }。的编号。要估计的参数是的数量。数据点 x (您想要投影的尺寸)。举个例子:如果你想把 5 种不同的菜系投射到一个二维空间中。参数= 52 = 10。旋钮就是 x. 的大小
- 无监督学习方法
K 均值聚类:
问题是为给定的输入数据集找到 K 个 T21 代表。这些代表被称为聚类中心(或)质心,并且被选择为使得在同一聚类中从每个点到其质心的距离最小。目标函数是 argmin I(k)* 其中 I(k) 是一个点属于聚类的指示函数 k. 模型参数除了聚类质心向量之外什么都不是。如果输入数据集为 d 维,则参数总数为 kd.* 旋钮为 k 的值,该值必须作为超级参数传递给算法。
Parzen 窗口:
Parzen 窗口是一种估计单个随机变量(单变量数据)密度的技术。数据的密度只不过是给定数据的真实概率密度函数(pdf)的近似值。然后汇总每个点的 Parzen 窗口估计值,以获得数据的密度估计值。目标函数是计算 p(x) = SUM(k(x))。在该模型中没有要学习的自由参数,但是您为每个数据点分配一个高斯(影响区域),该高斯被称为核函数,其均值(即以数据点为中心)和方差( sigma )在定义核时已经指定。旋钮是 sigma 的值,它是 parzen 窗口算法的超级参数。
单变量正态(Uni 高斯):
UVN 建模基于基本假设,即输入数据仅由一维组成,其均值(μ)和方差(σ)将采用高斯概率密度函数(pdf)进行估计。然而,与上述方法不同,模型参数实际上是通过最大化(或最小化负值)似然函数或其对数似然函数来学习的。假设输入数据是独立同分布的样本。目标函数是arg min-{ prod(1/sqrt(2 * pi * sigma) e^-(x-mu)/sigma)}。自由参数为μ和σ。*这款没有旋钮。
多变量正态分布(MVN/高斯混合分布):
在上述模型中,用多变量数据集替换单变量数据,我们得到一个多变量正态分布数据,它具有一个完整的协方差矩阵 (sigma) 和一个均值向量 (mu)。目标是最大化给定输入数据集上的似然函数,假设多变量高斯分布的 pdf 由这里的给出。
对于一个 d 维的输入数据,协方差矩阵 (sigma) 在上三角形区域将有 d(d-1)/2* 个条目,对角线上有 d 个条目。估计协方差矩阵将涉及学习 d(d-1)/2 + d* 自由参数。估计平均向量(μ)需要学习 d 参数。因此,自由参数的总数为 d(d-1)/2 + 2d。*该型号没有任何旋钮。
- 监督学习方法
感知器:
一个简单的感知是一个单细胞神经元,可以在一个 n 维特征空间中分离两类。感知器是可以模拟两个类别之间的边界线(或平面)的区别分类器的一个例子。这条线的函数可以写成 y = h(w'x+b)。参数是神经元的权重( w 和 b ),总计 n+1。目标是最小化预期分类误差 aka as loss,可写成 -SUM(ylog(h(w'x+b))。计算损失函数的梯度,并使用梯度下降更新权重。模型的旋钮是 GD 算法中使用的学习率(lr)* 。
逻辑回归:
逻辑回归的形式类似于感知器,即它可以解决两类问题。所使用的激活函数是由h(w ' x+b)= 1/1+e^-(w'x+b).给出的s 形**其余论点同上。
神经网络:
神经网络中的每个节点都可以理解为一个单独的逻辑回归。前馈神经网络被完全连接。在具有 2 个隐藏层的神经网络中,每个隐藏层具有 5 个神经元,参数的总数将是5 (n+1)+(5 * 5)+5 输出。目标函数是使用交叉熵损失最小化分类误差。使用从输出到输入的每个连续层的误差梯度的反向投影来调整权重。模型的旋钮或复杂性是隐藏层的数量和每个隐藏层中的单元的数量,这是设计时与学习速率(如果使用梯度下降来解决优化问题)一起考虑的因素,学习速率是超参数。
朴素贝叶斯分类器:
与上面不同,NB 是一个生成式分类器。朴素贝叶斯分类器的关键假设是特征是类条件独立的。NB 分类器对条件概率的贝叶斯公式起作用,即 p(类/数据)~ p(类) p(数据/类)。p(数据/类别)根据关键假设进行估算。 p(x1,x2,x3…/c)~ p(x1/c) p(x2/c) p(x3/c)…*的编号。估计 p(x/c) 所需的参数取决于特征的类型,即分类特征或数字特征。如果特征是分类的,那么你需要为它的所有级别建立概率值 (l) 。的编号。自由参数是( l-1) * c …(l-1,因为所有级别的概率加起来是 1)。如果特征是数字,那么你需要估计基础分布的参数,例如高斯分布的均值和方差。因此参数的数量会根据输入数据集而变化。没有最小化或最大化的目标,你只需要计算条件概率,建立先验,并使用贝叶斯规则对测试数据进行分类。然而,可以使用上面给出的相同目标函数,例如铰链损失或交叉熵损失,来调整后验概率,以更接近地反映基本事实。这种算法缺乏对模型复杂性的控制。
K-最近邻:
KNN 是一种懒惰的算法,也就是说,当一个需要分类的新数据点被呈现给算法时,它在推理时完成大部分工作。基于给定距离的阈值内的最近数据点,新数据点将被多数类标签分类。旋钮或模型复杂度是阈值距离,它是一个超参数。没有目标函数或参数。
支持向量机:
SVM 是一种特殊类型的判别分类器,其目标是最大化给定类别对之间的决策边界。最大化函数可以使用向量代数来导出为 1/2||w||,,其中可以假设 w 是等式y (w’x-b)-1>= 0,中的参数向量,该等式是可以分离给定类别对的线(或超平面)的等式。的编号。对于 d- 维输入数据集,需要求解的参数为 d+1 。旋钮或复杂度由成本参数(被认为是以升为单位的 gamma )给出。)这将允许对可能导致复杂的过拟合决策边界(当采用非线性内核时)的训练数据集点 San 的错误分类的一些容忍。
参数多态真的很酷
永远塑造了函数式编程的概念。

参数多态性是多态性的一个子集,它是一个有趣的通用编程概念,通常与函数式编程联系在一起。这很有趣,因为通常多态性和可变性不是函数式编程范式的属性,而是面向对象编程范式的属性。不久前,我向我的一位导师询问了这个问题,得到了一个简单的解释:
你说得对,严格来说这更像是面向对象。但它是第一个在 ML(元语言)中,一种基础的函数式编程语言,使用它漂亮的类型推理系统。
所以直接的答案可能是——传统!而只是历史的趋势,事情就像过去的事情。
“
所以是的,虽然从逻辑上来说,参数多态的属性当然更符合面向对象编程的特性,而不是函数式编程。有趣的是,函数式语言通常更关注这种通用概念。这仅仅是因为函数式语言是第一个采用和利用这个概念的语言。
这是什么?

知道多态性在编程范式中的位置是很好的,但是如果我们不知道它是什么,或者如何使用它,那就没有用了。参数多态性允许创建具有通用数据结构的通用函数,以确保静态性,并处理相同的值,而不依赖于它们的类型。
使用参数多态性,您可以将泛型函数应用于构造函数,这允许更快的计算和一致的数据。当然,这对统计计算和函数式编程非常重要。

像 Julia 和 R 这样的语言中的参数多态性允许我们将给定的函数应用于通过方法传递的特定类型。这很好,因为它使函数或动作可变,同时仍然保持构造函数的属性。本质上,任何需要采用泛型类型的显式导入的泛型方法都可以采用所述泛型类型。在上面的例子中,我在一个通用的 predict 方法中使用了构造的模型,该方法被保存为所述结构的属性。然后,这将给定的构造类型插入到一个带有返回的预测函数中。
一个巨大的好处是可变性。“预测”函数是完全可变的,可以根据为它传递的结构而改变。当显式导入时,还可以修改该函数来捕获和处理任何类型。换句话说,假设这个代码在一个包中,最终用户仍然可以在会话中远程操作它。
一些快速历史
因为参数多态性并不真正属于一个特定的范式,因为许多现代语言,无论是哪种类型,都实现了某种形式的多态性,参数多态性的历史,以及它创建的目的仍然是为了今天最常用的东西。参数多态性将当今互联网上的大量大数据管道结合在一起,是统计计算中一个极其重要的概念。
这个概念的历史就是这样开始的,在 1975 年首次引入 ML 语言。此后又被实现成了标准 ML 、 OCaml 、 F# 、 Ada 、 Haskell 、 Mercury 、 Visual Prolog 、 Scala 、 Julia 、 Java 、 C# 等语言。参数多态性的一些实现也更加微妙,并与其他特别的概念混合,就像 C++的情况一样。
多态性是现代计算的一个非常重要的特性。对于任何一种形式,无论是 prenex 多态性、Rank-k 前多态性还是 Rank-n 多态性,都有许多语言支持这一概念。总的来说,对于函数式编程来说,参数多态性无疑对整个统计计算产生了巨大的影响。
数据科学家长调查— R 代码—数据让我感动
几周前,Kate Strachnyi 和我在社交媒体上发布了一项调查,试图在数据科学领域收集人口统计数据和父母的经历。可以在这里看到原调查问题:调查
共有 332 名受访者。如果您曾经从 Survey Monkey 下载过. csv 格式的调查数据,您会发现您下载的是“宽”格式的数据。这意味着调查中每个潜在的响应都有一个指标变量。对于数据分析和创建图表,我们更喜欢“长”格式的数据。长格式是每个问题有一个单独的栏目。
下面是一个来自 Survey Monkey 的原始数据的. csv 图像(一小部分数据,我已经编辑了一些列名,但您已经明白了):

下图是我们想要的长格式,调查中的每个问题都有一栏:

如果你想下载 wide 格式的数据来跟随 R 中的代码,你可以在这里加入我的电子邮件列表,数据集将被发送到你的电子邮件地址。你可以随时退订。
在将数据从宽数据改为长数据之前,我做的第一件事是更改宽数据集中的列。调查中的一些问题是“是/否”问题。这意味着在我的原始数据中,我有多个名为“是”或“否”的列。我需要清理这些问题,使列名更直观,这样我就知道每个回答与哪个问题相关,并且每个列名需要是唯一的。
推荐文章:如果我们即将进入的代码不太清楚,但是你很想学习 R,这个 R 课程绝对不可思议:这里。
首先,我加载 tidyverse,读入我的数据,并检查数据集中的列名:
**library**(tidyverse) ### specifically we'll be using dplyr and ggplot2
**library**(plyr) ## for use of the revalue function
theme_set(theme_bw()) ## setting the theme for graphs## read in the data
mods <- read_csv("[your file path]/mods_data.csv")### checking out my column names
names(mods)
接下来,我指定了一个名称来存储我将要创建的长数据集,我将其命名为“Long_mods”。然后,我将宽数据集传递给它,并通过管道(%>%)将其传递给聚集函数。这个聚集函数是直观的。在这里,我收集了“男性”、“女性”和“非二进制”列,并创建了一个名为“性别”的新列。
### Converting a single column from wide to long:
Long_mods <- mods %>%
gather(key = gender, value = "value",
Female,
Male,
non_binary, na.rm = TRUE) %>%
select(-value)
你会注意到我有 na.rm = TRUE,这意味着当(例如)male = 1 时,我们保留它,但如果 male = na,因为这个人不是男性,它会被删除。这允许我们始终保持相同的行数。对于每个 gather 语句,都会创建一个名为“value”的新列。这将为我们所有的行设置为 1,因为我们删除了 na。
重塑数据可能需要大量的工作。为了演示,下面是将这个数据集(最初有 80 多列)转换为长格式的剩余代码。我必须收集每个问题的回答,为这个回答集合设置一个新的变量名,然后删除每个收集的“value”列。
下面是重塑整个数据集的代码。如果你跟着做,你可以复制粘贴这个。一旦你运行它,你将得到如上图所示的“长”数据集:
Long_mods <- mods %>%
gather(key = gender, value = "value",
Female,
Male,
non_binary, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = current_role, value = "value",
data_worker,
Manager,
pursing_work,
not_in_field, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "kids_in_home", value = "value",
kids_in_home_Yes,
kids_in_home_No,
empty_nester, na.rm = TRUE) %>%
**select**(-value) %>%
gather( key = "years_in_field", value = "value",
less_2,
'2_5_years' ,
'6_10_years',
'11_15_years',
'16_years_more'
,not_in_the_field, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "age", value = "value",
'18_24yo',
'25_34yo',
'35_44yo',
'45_54yo',
'55_64yo' ,
prefer_no_answer, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "employment_type", value = "value",
Employed_full_time,
Employed_part_time,
Self_employed ,
not_employed_not_looking,
Student,
Military ,
Homemaker ,
prefer_not_to_answer_jobtype, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "salary", value = "value",
less29999 ,
'30_59k',
'60-89k',
'90_119k' ,
'120_159k' ,
'160_plus',
wont_answer_salary, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "parental_leave_pay", value = "value",
full_paid,
not_paid,
partially_paid,
other_materity_leave, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "parental_leave_length", value = "value",
no_leave,
less_2weeks ,
'3_5weeks',
'6_8weeks',
'9_12weeks',
'13_16weeks',
'17_weeks_6_months',
'6_month_plus', na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "country", value = "value",
US ,
Europe ,
Asia ,
other_country , na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "wish_parental_leave_longer", value = "value",
Yes_wish_longer ,
No_wish_longer,
Not_sure_longer_leave,
Other_leave, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "ever_declined_promotion", value = "value",
Yes_declined_promotion ,
No_declined_promotion,
Other_declined_promotion, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "ever_declined_travel", value = "value",
Yes_declined_trip_or_conference,
No_declined_trip_or_conference,
never_asked_trip_conference ,
Other_trip_conference , na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "voicing_needs_difficult", value = "value",
Yes_voicing_needs_difficult,
No_voicing_needs_difficult ,
Other_voicing_needs_difficult , na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "have_support", value = "value",
Yes_network_help,
No_network_help,
Other_network_help , na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "equal_partnership", value = "value",
partner_handles_more ,
partner_handles_less,single,
equal_partnership , na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "treated_unfairly_at_work", value = "value",
Yes_unfairly_treated,
No_unfairly_treated ,
Other_unfairly_treated, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "changed_company_balance", value = "value",
Yes_changed_companies,
No_changed_companies ,
Other_changed_companies, na.rm = TRUE) %>%
**select**(-value) %>%
gather(key = "remote_work", value = "value",
Fully_remote ,
Partially_remote,
Fully_in_office,
client_location,
Other_workplace_arrangement , na.rm = TRUE) %>%
**select**(-value) %>%
arrange(id)
现在我们有了长数据集,我们可以开始可视化数据了。以下是调查中其中一个问题的情节截图。因为我在上面设置了主题,所以情节看起来很干净。然后,我只需输入特定的十六进制代码,以匹配我在列中的品牌颜色。

从图表中我们可以看出,最流行的工作安排是全职呆在办公室。我希望这是因为他们真的喜欢呆在办公室里。
为了创建这个图,我创建了一个新的 tibble,它只包含我想在我的图中使用的数据。这是一个“工作安排”变量,我希望能够筛选出那些至少在工作周的一部分时间里有孩子在家的父母。虽然这是一项针对父母的调查,但也有一些受访者不是父母,或者在工作时间没有孩子在家。
work_arrangement_tbl <- Long_mods %>%
## **using** only the relevant **variables**
select(remote_work, kids_in_home) %>%
## filtering for parents that have kids in the home during the work week.
filter(kids_in_home == "kids_in_home_Yes") %>%
## grouping by remote work for the calculation
group_by(remote_work) %>%
## getting the proportion of each category out of the total
dplyr::summarize(distribution = n()/nrow(Long_mods)) %>%
ungroup()
我还想对这些因素进行重新编码,使它们更具美感。我想把“全职”改为“全职”。为此,我使用了 plyr 包中的重估函数:
work_arrangement_tbl$remote_work <-
revalue(work_arrangement_tbl$remote_work,
c("Fully_remote" = "Full-Time Remote",
"Partially_remote" = "Partially Remote",
"Fully_in_office" = "Full-Time in Office",
"client_location" = "Client Location",
"Other_workplace_arrangement" = "Other"))
现在我们已经获得了创建图表所需的正确格式的数据,我们可以继续创建图表了。我对每一步都进行了注释,以描述我们的数据在每一步都发生了什么:
work_arrangement_plot <- work_arrangement_tbl %>%
## changing remote work to be a factor **variable** and ordering the proportions from largest to smallest
mutate(remote_work = remote_work %>% as_factor() %>% fct_reorder(desc(distribution))) %>%
ggplot(aes(remote_work, distribution)) +
## giving the columns the desired color
geom_col(fill = "#4F9C98") +
## We want labels as a percent rather than a decimal
scale_y_continuous(labels=scales::percent) +
## I also wanted the labels above the columns
geom_text(aes(label = scales::percent(distribution)),
vjust = -1.5) +
## Setting y-axis to go to 50% for aesthetics
expand_limits(y = 0.5) +
## Creating labels and subtitles
labs(x = "Work Arrangement", y = "Distribution", title = "Distribution of Work Arrangement",
subtitle = "For Parents With Kids in the Home")
然后运行“work_arrangement_plot”对象,瞧!你得到了一个漂亮的工作安排变量图,上面有每个因素的分布百分比,各列按百分比从大到小排列。
我祝你在重塑数据和进行分析方面取得成功!
推荐文章:如果你正在寻找进一步的 R 技能,你可以在这里阅读我最喜欢的 R 编程课程。
很快,我将为我用这些数据构建的闪亮应用程序撰写文章,并发布来自该数据集的见解和有趣的花絮。同时,你已经可以在我的网站上查看这个闪亮的应用了。
原载于 2019 年 7 月 15 日https://datamovesme.com。
Alexa 给我找个停车位。人工智能能引导你找到一个空车位吗?
使用深度学习从安全监控录像中检测停车场占用率

Empty or Occupied Parking Slot?
在停车场找一个空位是一个棘手的问题。如果输入流量变化很大,管理这些流量甚至会很困难。现在哪些位置是空的?我们什么时候需要更多的空位?通勤者是否发现很难到达某个特定的位置?哪辆车停在哪里?谁停了车?
我们使用地面安装的占位传感器网络或工作人员来确定这些问题的答案。传感器是有效的,但是需要维护,并且在单元+安装中每个节点的成本约为 40-80 美元。
对于工作人员来说,这是一项繁琐且容易出错的工作。即使工作人员得到栅栏和售票控制台的帮助,最多也只能给出一个基础广泛的关于地面上实际占用情况的猜测。
解决这个问题的一种方法可以是使用安全摄像机镜头,通过深度学习来实时检测停车占用率。(文末链接完整代码)
方法和架构
标记停车位占用情况是一个两步过程。首先,我们必须在视野范围内找到停车位。其次,我们必须检测插槽是否被占用。
一种方法是强行解决问题,手动标记所有插槽。然而,每次移动、摇摄或缩放摄像机时,都必须为摄像机的新位置重复这一费力的过程。

Parking Slot from Drone image (Github)
第二种方法可能是使用停车场的白线作为插槽的指南。我们可以使用 canny 过滤器和 hough 变换来检测多边形。然而,并不是所有的停车场都是白色车道。即使是在车道上,距离地面约 6 米的摄像机镜头也会被停放的汽车遮挡。因此,除非我们使用无人机从顶部捕捉镜头,否则这也不会有多大帮助。
我们可以使用静止的汽车本身来发现停车位。在几天的停车场录像中,我们可能会发现所有的车位在某个时间点都被车占满了。在镜头中检测静止的汽车是实际停车位位置的良好预测。当然,当赛车进入/退出插槽时,会有运动。但是我们可以忽略这些噪音。
一旦确定了停车位,剩下的就是检测在新的帧中是否存在汽车。这是一个简单的分类问题。我们可以在接收相机流的台式机上实时运行它(大约 1 秒)。
检测停车位
对于问题的第一部分,我们需要一个对象检测器,它将为每个停车位提供一个边界框。YOLO 和美国有线电视新闻网可以帮助我们。
YOLO 是一种灵活而精确的算法,它给出了每个检测到的物体的边界框,以及它们的分类和可能性的估计。早期的物体检测算法用不同的边界框大小多次扫描一个场景,并试图寻找行进中的候选者。这花费了很长的时间和计算资源。 YOLO 使用 CNN 图层使其成为一次性问题。一旦网络经过训练,检测就可以在计算机上进行,甚至可以在手机上使用更小的网络和优化。

Still Life with a Jug and a Bowl of Apples Samuel Peploe 1924
在风格不同的情况下(比如上面的这幅画),YOLO 确实做出了合理的估计(碗、橙子),但有时也会失误(书、壶)。
MaskRCNN 除了物体检测还有一个图像分割算法。MaskRCNN 将每个像素分类为属于一个对象的实例。这是一项极其困难的任务。MaskRCNN 通过排列感兴趣的区域来构建 FCNN 网络。这个视频将帮助你掌握核心概念。

Cityscape Dataset
在 GPU MaskRCNN 上运行需要大约 200 毫秒来处理 1 帧,所以它的速度大约是 Yolo 的六分之一,Yolo 在 GPU 上可以达到 30 Fps。但另一方面,MaskRCNN 需要大约 20 帧才能给出合理的结果,而 YOLO(它遗漏了小物体)需要大约 5 倍的时间。选择一个 MaskRCNN 更有意义,让它连续两天每小时捕捉 20 帧以上。YOLO 将需要大约五倍的帧数,但会错过一些时隙。
专门化网络
在非常接近物体的情况下,例如检测沿着道路行进的所有人,YOLO 有其弱点。它将一个场景划分为网格,并且可以在每个网格中检测一定数量的对象。在停车场的情况下,停在第一排的车很可能会被遗漏。我试图将网络专门化,只检测汽车。性能略有提高。
使用 RESNET(或任何其他分类器)完成最终检测。我把它专门用在汽车上。我使用了 CNRPARK 提供的数据集,因为它有我们通常在安全摄像头中找到的汽车方向的图像。我使用了 FastAI 的 fit_one_cycle ,在 10 个历元内可以在验证集上获得超过 99.7%的准确率。令人惊讶的是,一旦你有了一个训练好的模型,所需的数据和计算资源是如此之少。

Empty | Occupied dataset from National Research Council (Cnr) Italy
检测停车位
一旦我们有了专门的权重,我们就开始检测停车位的实际任务。在自动驾驶汽车中,你需要在多个帧之间跟踪一个对象。在停车位检测中,我们需要在逐帧移动时跟踪停车位。汽车来来去去,它们的大小会改变,边界框也会改变。
逐帧映射时隙

并集上的交集(IOU)在这类问题中是一个很好的度量。对于每个槽,我们在后续图像中检测与前者具有最高 IOU 的对应槽,并对其进行分配。IOU 低于阈值的空位很可能是已经到来并占用了该停车位的新车辆。
在运行了几个帧之后,也有必要在一个帧内合并不同的边界框,因为一些零星的条目可能会导致堆积。在此步骤中,选择较高的 IOU 阈值,以获得稳定的结果。

Blue Detection for the first time | Red Occupied | Green Vacant
移除瞬时动作
一些帧将捕捉瞬时运动,如汽车进入停车场。必须拒绝这些边界框的实例。根据一段时间内停车场被占用的实例数量运行拒绝,可以让我们控制这些零星的条目。
这将为我们提供一个停车位列表:坐标和培训期间的典型占用模式。
检测时隙内的占用率
检测非常简单。我们将图像裁剪到上一步中边界框所定义的位置。我们现在要做的就是分类这辆车是存在还是不存在。我们使用我们调优的 RESNET,它给我们一个合理的结果。
限制
这个系统还没有达到标准。让我们再一次把它分成两部分,检测插槽和检测占用率。
你不能察觉你看不见的东西它也不能
树木和阴影限制了探测范围。部分遮挡(有树冠)在某些情况下有效,但在其他情况下无效。类似地,地面摄像机(< 6 米)停车场内的大多数汽车将隐藏在画面前面的几辆汽车后面。只有在前面的停车场是空的一些帧中,后面的停车场才会被检测到。
对于非常小的物体,马斯克 CNN 和 YOLO 很难挑选插槽。这可能是一帧中大约 1–5%的停车位。然而,这可以通过人类花费不到一分钟的时间来提供正确的边界框来校正。相比之下,如果他必须注释所有的槽,则需要花费 1-2 个小时。
这将我们带到第二部分:检测插槽的状态。分类器看起来确实工作得很好。然而,小图像、怪异的视角、黑暗和障碍物再次限制了这一步
那么这一切是怎么回事呢?停车检测不能扩展到所有使用情况。在大多数地下停车场,你不可能把摄像机安装在离地面 9 米的地方。在许多地方,树木、柱子等会阻碍视野。
它有它的缺点,也许现有的检测占用的方法也有缺点。如果我们能把它们结合起来,就能创造出一个更有效率的系统。
从中可以延伸出什么

Parking Ticket | QR Code | Vacant Slots
我们可以用这个系统指引通勤者去停车场的空位。现有的停车票可以带有一个二维码,然后使用该系统将消费者引向空地。这减少了该步骤中所需的手动干预的数量。
将车牌识别与占用检测结合起来,我们可以跟踪哪辆车占用了哪个车位。通过同样的方法,我们还可以跟踪谁停了车。

Ground-based sensors effective but costly
通过与地面系统协同工作,设施管理者可以实时了解停车场的库存水平。未来扩充决策可以基于真实的历史数据。它还可以通过捕捉停车者的快照来检测谁停了车。
代码和指令https://github.com/visualbuffer/parkingslot
参考资料:
- 意大利比萨 CNR 研究区【http://cnrpark.it/
- https://github.com/qqwweee/keras-yolo3。基于 KERAS 的启动代码。
- https://github.com/matterport/Mask_RCNN基于火炬的启动码
人工智能正在消除障碍,让人们以微不足道的成本获得洞察力。你正在开发一个我们可以合作的机器学习应用吗?请在我的 Linkedin 上留言。
利用气流在 AWS 中转换拼花地板(第 1 部分)
这篇文章将探讨云计算服务中围绕拼花地板的一切,优化的 S3 文件夹结构,分区的适当大小,何时,为什么以及如何使用分区,以及随后如何使用气流来编排一切。

我不想浪费这个空间来详述气流的特性和它是一个多么完整的产品。网上有很多帖子探索它的功能。我宁愿将这篇文章分成以下四个部分:
- 拼花地板文件格式概述
- s3 文件夹结构的类型以及正确的 S3 结构“如何”节省成本
- 为外部表(红移谱、雅典娜、ADLA 等)提供足够的分区大小和数量
- 用气流片段总结(下一篇文章)
拼花文件格式和压缩类型
选择完美的文件格式和压缩有很多因素,但以下 5 个因素涵盖了相当多的领域:
- 基于列 vs 基于行 : 每个人都想使用 CSV,直到你的数据量达到几乎无法查看的程度,或者它占用了你的数据湖中的大量空间。如果您的数据大于,通常会有过多的列。正如我们所知,并不是所有的列都是信息性的,为了查询一些列,我们需要一种针对数据的列选择进行优化的格式。换句话说,当您将它导入外部表(Athena、Redshift Spectrum、Azure data lake Analytics 等)并从 parquet 数据中选择一列时,它不会也不应该在选择所需列之前加载整个文件。除非你在 Hadoop 集群中使用像 Hive 这样的开源工具,否则你将不得不为扫描的数据量买单。
- 模式进化 : Parquet 支持模式进化。现在,大多数数据源都会在某个时候演化它们的模式(添加/删除列、数据类型更改等),如果您将它接收到一种不支持模式演化的格式,那么您将无法使用以前接收的文件读取该文件。
- 支持压缩:现在,当你选择了一个文件格式,为什么不包含一个压缩格式,但选择正确的压缩格式是棘手的,因为你需要在速度或压缩率之间做出选择。到目前为止,Snappy 赢得了这场战斗,因为它在两个世界之间取得了巨大的平衡。此外,镶木地板支持它。
- 存储空间:我相信很多读者已经知道了,但是 parquet 是一种经过优化的格式,它消耗的空间是 CSV 的 1/10。我知道 S3,Azure blob 或 GCS 都不贵,你可以把它作为一个着陆区,没有任何成本,但几年后,当你最终使用数十兆字节的空间时,你会意识到成本确实上升了。
- 自描述:除了每个文件之外,Parquet 还给出了它的元数据,比如每个列的数据类型,这在列解析或者数据被发送到另一个系统的场景中总是需要的。
我知道文件格式取决于用例。例如:如果你想读取所有的值,那么 Avro 是最适合的,因为它是基于行的格式(读取键值对),但并不是每个用例都有这种需求。Avro 也非常适合写密集型操作。
S3 文件夹结构及其如何节约成本
现在拼花地板和隔墙是如何联系在一起的。因此,到目前为止,我们已经确定了 parquet 是大多数用例的正确文件格式。为了简单起见,我们假设我们定期接收数据,并确定它来自哪个时间段,我们在 s3 存储桶“生产”中给它一个这样的文件名:
数字营销/yyyy-mm-dd.snappy.parquet
一切都进行得很好,直到有人要求你处理这些关键点/斑点,或者将其放入他们的数据仓库(DWH)进行分析,或者用于机器学习(读取特征生成)任务。为什么很难,因为你的代码,你的逻辑,必须首先扫描桶中的所有文件,然后选择那些你需要处理的文件。现在,由于数据是周期性的,我们只想选择那些我们之前没有处理的键/blob。因此,让我们将这些键重新命名如下:
yyyy/mm/DD/yyyy-mm-DD . snappy . parquet
换句话说,我们已经将数据划分为 yyyy/mm/dd 格式。到目前为止一切顺利。代码选择正确的密钥为 DWH 进行处理。几年后,我们收到一封电子邮件,说我们的 DWH 成本正在拍摄,我们需要管理它。但是数据科学家告诉我们,他们至少需要最近 5 年的数据来进行历史分析,如果不是更多的话。此外,他们还担心,由于 DWH 中的所有数据,进行汇总变得过于缓慢。外部表来了。
因此,Redshift spectrum 的以下数据定义语言(DDL)将把数据驻留在文件夹“S3://production/digital-marketing/Facebook/accounts/engagement/”下。将仓库结构(数据库/模式/表)与数据湖的结构对齐总是好的。你可以想象它的好处。
创建外部表 Facebook . accounts . engagement(
page _ fans _ new BIGINT,
page_fans BIGINT,
account VARCHAR(80),
DATE DATE
)
partition by(year char(4),month char(2),day char(2))
行格式分隔的
字段以“|”终止
location ' S3://production/Facebook/accounts/engagement/'
表属性(' skip.header.line.count'='1 ',' has _ has
并且密钥/blob/文件将使用这个名称。
Facebook/accounts/engagement/yyyy/mm/DD/yyyy-mm-DD . snappy . parquet
要在外部表中添加数据,必须运行以下命令。
alter table Facebook . accounts . engagement
添加分区(year='2019 ',month='01 ',day='01')
位置 S3://production/Facebook/accounts/engagement/year = 2019/month = 01/day = 01/';
因此,当您运行上面的命令时,它将在其元数据中添加 3 个分区键,指定特定行来自哪个分区。最好的部分是,现在我们不需要扫描整个表来获取所需的数据。我们将只使用“where”子句来选择我们需要分析数据的年、月和日。但最糟糕的是,它将在元数据中添加 3 个分区键,这是反高潮的。因此,让我们把三个合并成一个。下面的 DDL 将用诸如‘2019–01–01’的值对 batch_date 上的数据进行分区。
创建外部表 Facebook . accounts . engagement(
page _ fans _ new BIGINT,
page_fans BIGINT,
account VARCHAR(80),
DATE DATE
)
partition by(batch _ DATE char(10))
行格式分隔的
字段以“|”终止
location ' S3://production/Facebook/accounts/engagement/'
表属性(' skip.header.line.count'='1 ',' has_encrypted_data'='false
为了添加分区,让我们使用下面的代码片段:
alter table Facebook . accounts . engagement
添加分区(batch _ date = ' 2019–01–01 ')
位置 S3://production/Facebook/accounts/engagement/batch _ date = 2019–01–01/';
现在,这将只在元数据中添加 1 个分区键,即 batch_date,而不是 3 个。
此外,s3/Blob/GCS 文件夹结构很大程度上取决于您在外部表上使用的查询类型。如果每天进行查询,那么 batch_date=YYYY-MM-DD 是最佳选择,其中每个分区至少应为 50–100 MB,但不能超过 2GB。如果您每小时查询一次,则 YYYY/MM/DD/HH。因为如果您每小时查询一次,并且您的结构是每日 YYYY/MM/DD,那么在一天结束之前,不能添加任何分区。如果您每天以小时为单位进行查询,即使文件非常大,也可能是一种大材小用,但是以小时为单位进行查询是有好处的,因为将来分析师需要逐小时分析,因为 1 小时内生成的数据非常大。因此,它可以是每日分区或每小时分区,但不能是每周分区或每月分区,因为分析师不希望等待一个月或一周才能获得新数据。
注意:删除和添加相同的分区(以及更新的文件)成本很高,因为 Redshift Spectrum 或 Athena 是根据扫描的数据量计费的。
外部表最多可以有 20,000 个分区,否则就是一个新表。
分区的大小和数量
到目前为止,我们已经对 batch_date 上的数据进行了分区,但是如果您正在使用 Redshift、Spark 或任何类似的工具,您可以并行卸载该文件。因此,问题是一个分区中有多少文件是最佳的,理想的分区大小应该是多少。
分区数据的文件大小可以从 1MB 到 10GB,但建议分区的最小大小应为 50-100MB 或更大,否则与未分区的数据相比,读取和解析外部表将花费大量时间。
如果数据的总大小小于 10Gbs,则根本不需要分区。在这种情况下,由于数据不是很大,您可以将所有文件放在一个目录中,因为向下钻取目录结构也会影响性能。但是问题是,尽量将文件数量保持在 100 以下,否则查询将再次花费大量时间,因为它必须找到文件,打开并读取它,这比处理结果更耗时。
总结
因此,调查您的数据源的摄取率(MBs/天)。根据经验,当您的摄取率超过 500Mbs 天时,开始考虑分区,并以分区大小在 100 MB-2gb 左右的分区方案为目标。如果数据有时间成分,那么把它作为你的分区键,然后根据查询决定是需要按天分区还是按小时分区。
在下一篇文章中,我们将深入研究定制的气流操作符,看看如何轻松地处理气流中的拼花转换。
下面是第二部分:https://towardsdatascience . com/parquet-conversion-in-AWS-using-air flow-part-2-8898029 c 49 db
[## Gagandeep Singh -数据工程师-澳大利亚广播公司(ABC) | LinkedIn
加入 LinkedIn Gagandeep 曾参与涉及以下领域的结构化和非结构化数据的项目
www.linkedin.com](https://www.linkedin.com/in/gagandeepsingh8/)
利用气流在 AWS 中转换拼花地板(第二部分)
在本帖中,我们将深入探讨定制的气流操作符,并了解如何轻松处理气流中的拼花转换。
如果您使用 AWS,主要有三种方法可以将红移/S3 中的数据转换为拼花文件格式:
- 与下一个选项相比,使用 Pyarrow 可能会花费一些时间,但在分析数据时会有更大的自由度,且不涉及额外的成本。
- 使用雅典娜 CTAS,这涉及扫描数据的成本,但全面更快地转换数据。
- 使用 Unload 来直接拼花 AWS 尚未发布的拼花。

大约有 5 种这样的方法进行了概念验证,但是上面的前两种方法在速度、效率和成本方面非常突出。我们将探索第一个选项,我们将使用代码片段来了解哪些是转换为拼花地板的基本部分。以下是转换它所涉及的步骤:
- 计算要创建的分区:从 SVV _ 外部 _ 分区表中提取分区值,并计算需要创建哪些分区。
- 卸载到 S3: 现在,要使用 Pyarrow,我们需要 S3 中的数据。因此,第二步是在 Redshift 中使用“Unload”命令,如果在 s3 中还没有为上一步中所有需要的分区加载数据。我们可以使用任何适合数据的格式,如“TSV”。
- 解析成所需的数据类型:之后,我们需要将 s3 数据转换成 parquet 兼容的数据类型,这样在通过外部表使用它时就不会出现任何错误。这可以通过在 dataframe 中转换 pandas 数据类型或 parquet 数据类型来实现。
为了简单起见,我们将使用红移谱将分区加载到它的外部表中,但是下面的步骤可以用于 Athena 外部表的情况。
计算要创建的分区
借助于 SVV _ 外部 _ 分区表,我们可以计算出哪些分区已经全部存在以及哪些分区都需要被执行。下面的代码片段使用 CustomRedshiftOperator,它实际上使用 PostgresHook 来执行 Redshift 中的查询。xcom_push 将查询结果推入 xcom,我们可以读取并检查“dummy_external_table”中的分区。
check_partition_already_exists_task = CustomRedshiftOperator(
task_id='check_batch_already_exists',
redshift_conn_id='redshift_default',
sql='select * from SVV_EXTERNAL_PARTITIONS where tablename = \'dummy_external_table\' and values like \'%2019-01-01%\',
xcom_push=True,
retries=3
)
卸载到 s3
根据需要计算的分区,我们可以形成可以在 Unload 语句中使用的查询。下面的代码片段在自定义操作符中使用 PostgresHook,该操作符采用 select_query、s3_unload_path、required IAM role 和 unload_options 将 Redshift 中的数据填充到 s3 中(如果该数据还不存在)。
select_query =这可以是' select * from schema . dummy _ table where sort _ key between ' from _ time ' and ' to _ time ' '
s3_unload_path =这将是 s3 中 unload 推送文件块的路径,我们需要进一步使用这些文件块进行转换。例如:S3://bucket/source _ name/schema _ name/table _ name/landing/
unload_options =转义头分隔符' \ \ t ' allow overwrite max filesize AS 275。我更喜欢在选项中给出 maxfilesize,因为我不想使用 Airflow 实例的所有 RAM,否则 AWs 将使用默认文件大小 6GB。
unload_query = """
UNLOAD ( $$ {select_query} $$)
TO '{s3_unload_path}'
iam_role '{iam_role}'
{unload_options};
""".format(select_query=select_query,
s3_unload_path=self.s3_unload_path,
iam_role=self.iam_role,
unload_options=self.unload_options)
self.hook.run(unload_query, self.autocommit)
self.log.info("UNLOAD command complete...")
作为最佳实践,让我们遵循 s3 结构,其中带有“landing”的前缀将充当卸载查询加载红移数据的着陆区域,而“processed”将用作放置转换后的拼花数据的键。
解析成所需的数据类型
下面的代码片段将熊猫数据帧转换成 pyarrow 数据集,然后将其加载到 s3 中。
table = pa.Table.from_pandas(df)
buf = pa.BufferOutputStream()
pq.write_table(table, buf, compression='snappy', coerce_timestamps='ms', allow_truncated_timestamps=True)self.s3.load_bytes(bytes_data=buf.getvalue().to_pybytes(), key=self.s3_key + '_' + str(suffix) + '.snappy.parquet', bucket_name=self.s3_bucket, replace=True)
到目前为止一切顺利。之后,需要将分区添加到外部表中,瞧!选择查询将显示其中的数据。“添加分区”还将使用customredshiftopoperator,它将在红移集群上运行添加分区查询。
现在,当熊猫数据帧中的特定列具有混合数据类型或者该列中有“NaNs”时,问题就出现了。在这种情况下,pandas 将把列数据类型作为对象“O ”,当 pyarrow 用于 pandas 数据帧时,pyarrow 将使用 pandas 提供的列数据类型,并将其转换为自己的数据类型。在对象数据类型的情况下,它会将其转换为自己的“二进制”,这本质上不是该列的原始数据类型。因此,当您在外部表上执行 select 查询时,它会向您显示解析错误。
因此,为了避免这种错误,我们需要将 pandas dataframe 列“转换”为所需的数据类型,或者在 parquet 中使用 parse_schema 将其隐式转换为 parquet 格式。在这篇文章中,我们将探索转换数据类型的“astype”方法。
在执行代码片段之前,应该有“datetime_col_list ”,它包含日期时间格式的所有列名,等等,用于 bool_col_list 和 integer_col_list。Integer_col_list 中也有浮动列。datetime_col_dict_list 是一个 dict 列表,类似于[{"app_id": "datetime64[ms]"}],类似于 bool 和 integer。
下面的代码片段将首先直接输入列,如果有任何错误,它将逐列输入,如果仍然有任何错误,则逐行输入。如果仍然存在任何错误,则删除该记录。就性能而言,它在 12 分钟内卸载+转换 14GB 的数据,我认为这是可以管理的。此外,我们可以将下面的代码片段放在一个函数中,并针对日期时间和整数调用它。
try:
df[datetime_col_list] = df[datetime_col_list].astype('datetime64[ms]')
except ValueError:
for schema_dict in datetime_col_dict_list:
try:
should_restart = True
while should_restart:
should_restart = False
try:
df = df.astype(schema_dict)
except ValueError as e:
self.log.warn('ValueError - reason: ' + str(e))
for i, item in enumerate(df[list(schema_dict.keys())[0]]):
try:
if not pd.isnull(item):
pd.to_datetime(item)
except ValueError:
logger.info('Corrupted row at index {}: {!r}'.format(i, item))
logger.info(df.loc[i, 'event_id'])
df.drop([i], inplace=True)
should_restart = True
except KeyError as e:
logger.info(schema_dict)
logger.warn('KeyError - reason: ' + str(e))
continuefor col_name in bool_col_list:
df[col_name] = df[col_name].map({'f': 'false', 't': 'true'})
df[col_name] = df[col_name].fillna('').astype('str')for schema_dict in integer_col_dict_list:
try:
should_restart = True
while should_restart:
should_restart = False
try:
df = df.astype(schema_dict)
except ValueError as e:
logger.warn('ValueError - reason: ' + str(e))
for i, item in enumerate(df[list(schema_dict.keys())[0]]):
try:
if not pd.isnull(item):
float(item)
except ValueError:
logger.info('Corrupted row at index {}: {!r} for column: {col}'.format(i, item, col=list(schema_dict.keys())[0]))
logger.info(df.loc[i, 'event_id'])
df.drop([i], inplace=True)
should_restart = True
except KeyError as e:
logger.info(schema_dict)
logger.warn('KeyError - reason: ' + str(e))
continue
定制气流操作员
最后,上面的 3 个片段被包装在自定义操作符中,我们只需要提供必要的细节,它就会自动计算所需的分区,在 s3 中为每个分区创建 parquet 文件,并将分区添加到外部表中。
to_parquet_task = CustomRedshiftToS3Transfer(
task_id='to_parquet',
redshift_conn_id='redshift_default',
schema='___SCHEMA___',
table='___TABLE_NAME___',
s3_key=parquet_key,
where_clause="{col} >= \'{from_time}\' and {col} < \'{to_time}\'".format(
col=redshift_sort_key,
# col=batch_id_col,
from_time=str(pd.to_datetime(to_check, format='%Y-%m-%d')),
to_time=str(pd.to_datetime(to_check, format='%Y-%m-%d') + timedelta(days=1))
),
s3_bucket=Variable.get('bucket'),
parse_schema=table_schema,
unload_options=parquet_unload_options,
aws_conn_id='s3_etl',
is_parquet=True,
engine='pyarrow',
retries=3
)
我在这里使用了‘is _ parquet ’,因为上面的自定义操作符也处理其他数据格式。“where_clause”将构成 unload 语句中 select 查询的一部分,并将使用 from_time 和 to_time 从 redshift 表中选择所需的数据片。
包裹
因此,前一篇文章和这篇文章提供了一些关于什么是 parquet 文件格式,如何在 s3 中组织数据,以及如何使用 Pyarrow 有效地创建 parquet 分区的想法。上面的自定义操作符也有“引擎”选项,可以指定是使用“pyarrow”还是使用“athena”将记录转换为拼花。雅典娜选项将自动选择雅典娜 CTAS 选项,将 s3 中的卸载数据转换为 s3 中的拼花数据。
这里是第一部分:https://towardsdatascience . com/parquet-conversion-in-AWS-using-air flow-part-1-66 ADC 0485405
第 1 部分:打破谜!让我们检测物体
现在,让我们开始寻找一个鲁棒和准确的对象检测算法。

Hawa Mahal Road, Jaipur, Rajasthan (Image Source: unsplash)
大家好!我是尼特莱普尔的巴拉特。我在印度蒂鲁帕蒂印度理工学院计算机视觉实验室第四学期结束后的研究实习期间。我的导师给了我一个任务,找到并实现一个健壮而准确的目标检测算法,这将有助于他们正在进行的研究工作。
1)对象检测:
对象检测是一种与计算机视觉和图像处理相关的计算机技术,用于检测某一类语义对象(如人、建筑物、汽车等)的实例。)在数字图像和视频中。

Fig.2 Machine Learning: Stanford University- Coursera
2)物体探测器类型:
A .)基于区域的对象检测器或两阶段方法:解决这个问题的一个简单方法是从图像中提取不同的感兴趣区域,并使用 CNN 对该区域内对象的存在进行分类。
使用一些其他的计算机视觉技术提取建议,然后调整大小为分类网络的固定输入,分类网络充当特征提取器。然后训练一个 SVM 来在对象和背景之间进行分类(每个类别一个 SVM)。此外,一个边界框回归器被
训练,输出一些建议框的校正(偏移)。总体思路如图 3 所示。这些方法非常精确,但计算量很大(低 fps)。

Fig.3 Google Search Pixel
主要技术有:
1 >滑动窗口
2 > R-CNN
3 >快速 R-CNN
4>更快 R-CNN
5 > FPN…..等等。
B .)单镜头物体检测器或统一方法:这里的区别是,不是产生提议,而是预先定义一组盒子来寻找物体。
使用来自网络后续层的卷积特征图,在这些特征图上运行另一个网络,以预测类别分数和边界框偏移。
步骤如下:
1。用回归和分类目标训练 CNN。
2。从后面的层收集激活,以推断全连接或卷积层的分类和位置。
3。在训练期间,使用“Jaccard 距离”将预测与实际情况联系起来。
4。在推断过程中,使用非最大值抑制来过滤同一对象周围的多个框。
主要技术有:
单一激活图:
1>YOLO:你只看一次:统一、实时的物体检测
2>yolo 9000:更好、更快、更强
多重激活图:
1>SSD:单发多盒探测器
2>yolov 3:增量改进
3>DSSD:解卷积单次检测器
使用多种比例有助于更好地检测图像上不同尺寸的物体,从而获得更高的地图(平均精度
)。 因此在这个项目/博客中使用的技术是 YOLO(单激活地图)和 YOLOv3(多激活地图)。
3)你只看一次:统一的、实时的物体检测(YOLO)——
YOLO 模特(“你只看一次”; Redmon 等人,2016 )是构建快速实时物体探测器的第一次尝试。因为 YOLO 不经历区域提议步骤,并且仅在有限数量的包围盒上进行预测,所以它能够超快速地进行推断。这里是在 keras 中实现经过训练的 YOLO 的 GitHub 库 。
3.1)网格单元:

Fig.4 YOLO Official Paper
YOLO 将输入图像分成一个 S × S 网格。每个网格单元只预测一个物体。例如,下面的红色网格单元试图预测中心落在网格单元内的“狗”对象。
3.2)它是如何工作的?:
- 使用整个图像的特征来预测每个边界框。
- 同时预测图像所有类别的所有边界框。
- 将输入图像划分为一个 s*s 网格。如果对象的中心落入网格单元,则该单元负责检测该对象。
- 每个网格单元预测 B 边界框和这些框的置信度得分。
- 每个网格还预测 C 条件(以包含对象的网格单元为条件)分类概率。

Fig.5 The workflow of YOLO model. (Image source: original paper)
它将图像划分为一个 S × S 网格,每个网格单元预测 B 边界框、这些框的置信度和 C 类概率。这些预测被编码为 S×S×(B∫5+C)张量。
3.3)特性:
- 工具?—单个神经网络、统一的体系结构(24 个卷积层、4 个最大池和 2 个全连接层)
- 框架?— Darknet —最初的实现是在 C 和 CUDA 中。
- 技术背景?—相关方法速度慢、非实时且缺乏推广能力。
3.4)网络架构:

Fig. 6 The Architecture — Detection Network has 24 conv layers and 2 fully connected layers which
are converted into feature map of S * S(B5 +C). (Image source: original paper)
3.5)术语和公式:
- 置信度得分:

反映了对盒子包含一个对象的置信度+盒子的准确度。
- 条件类概率:
以包含一个对象的网格单元为条件。

在测试时,我们将条件类概率和单个盒子置信度预测相乘。

- 总损失函数:定位+置信度+分类损失:


- 非最大抑制:

Fig.7 Non-Max Suppression (Image source Blog)
在预测期间,使用非最大值抑制来过滤每个对象的多个框,这些框可以如图所示进行匹配。

- 丢弃所有 pc <0.6
- Pick the box with largest pc. Output that as a prediction.
Discard any remaining box with IOU > = 0.5 的盒子,并输出上一步的盒子。
3.5)结果:定性分析-
>真检测

Table 1: Predicted output results from MS-COCO datasets.
>错误或变异检测:

Table 2: False Detections and Table 3: False Detections on custom images
3.6)观察到的问题:
- 当与小物体一起出现时,较大物体占优势,如图 a 所示
- 遮挡给检测带来了问题。如图 b 所示,被遮挡的鸟没有被正确检测到。
- 图像的分辨率必须很高,否则边界框可能会偏离其位置。
- 如图 c 所示。为了解决上述问题,继 YOLO 之后出现了许多统一的检测方法:
1.)YOLO9000:更好更快更强
2。)SSD —单次多盒检测器。
3。)DSSD-去卷积单触发多盒检测器。
4。)YOLOv3:增量改进。
在下一部分,我将把 YOLOv3 描述为一项重大改进。
喀拉斯 YOLO 的 Github 知识库。
谢谢你。
你的工具箱中的一个新工具,KL Divergence at Work
最后,将 KL 散度应用于真实数据集

在我的上一篇中,我们对熵、交叉熵、 KL 散度有了一个直观的了解,也通过实例计算了它们的值。如果你错过了,请在进入最后一集之前再看一遍。
在本帖中,我们将应用这些概念,并在真实的数据集中检查结果。此外,它将为我们提供良好的直觉,告诉我们如何在建模各种日常机器学习问题时使用这些概念。那么,我们开始吧。
1.分析数据集
数据集包括两个潜在特征‘f1’和‘F2’以及数据点所属的类,即正类或负类。

Dataset
数据集可视化

Visualizing the Data with a scatterplot
Code used for Visualisation
因此,我们的数据点有两个潜在特征,“f1”和“f2”。数据点属于'+'类(红色)和'-'类(蓝色)。
2.定义目的
我们的目的是为数据集的正负类定义一个理想的分布。到目前为止,我们还不知道它会是什么样子,它的概率密度函数会是怎样的,但是我们可以定义一些好的性质。
理想分布的性质
- 正类的分布应该使得任何数据点属于正类的概率应该是 1,而负类的概率应该是 0。
- 负类的分布应该使得任何数据点属于正类的概率应该是 0,负类的概率应该是 1。
3.如何估计上述理想分布
现在好戏开始了。如何估计理想分布。这是一个开放式的问题,可以尝试许多技术。但是对于这个博客的帖子,我将保持事情简单,不会偏离原始主题太多,将 KL 散度应用于日常的机器学习问题。
高斯/正态分布救援
估计分布的一种方法是使用高斯分布。我们将尝试为正类拟合一个高斯,为负类拟合另一个高斯。有可用的软件包可以为我们找到这些拟合的高斯函数的适当参数。但是,如果你有兴趣了解它是如何做到的,那么你可以在这里阅读更多关于它的。一种叫做期望最大化的算法被用于此。也许,我会在另一篇博文中写下它。让我们使用 python 中可用的 GaussianMixture 包来拟合发行版。

PDF of a multivariate Gaussian Distribution
Fitting the Distribution and visualizing the results

Fitted Gaussians for the positive and negative class
从视觉上看,在完成分配的任务时,分配看起来很好。一个高斯模型适合正类,另一个适合负类。接下来,我们将计算每个数据点属于正负类分布的概率。
4.寻找每个数据点的概率(可选)
在这一部分中,我们将看到,一旦完成了正类和负类的多元高斯拟合,如何计算每个数据点的最终概率。这将是一个数学密集型和可选的。它可以作为黑箱,得到最终的概率。但是万一,你有兴趣了解背后的数学,你可以按照章节 else 跳到下一个。
对于任何数据点' x ',属于分布概率由下式给出

使用上面的公式,我们可以找到可能性,

Probability of datapoint given + distribution

Probability of datapoint given - distribution
接下来,我们可以找到类概率或先验,

Probability of + distribution

Probability of - distribution
其中 n 是数据点的总数。
一旦我们有了可能性和先验,最后一步就是找到后验,即数据点的概率。我们可以用贝叶斯定理来计算。

Probability of datapoint belonging to the + distribution

Probability of datapoint belonging to the - distribution
我们可以利用上面的后验,求出每个数据点属于+ve 或-ve 分布的概率。
5.评估拟合优度
现在,一旦我们拟合了分布,并且计算了每个数据点属于正负分布的概率。我们可以看到这个拟合的分布与我们的理想分布有多大的不同。
我们如何检查呢?当然,使用我们最喜欢的度量标准, KL 散度(kull back–lei bler 散度)。
只是重申,KL 散度只是拟合分布和实际分布之间的差异,即交叉熵和熵之间的差异。还可以看出这两种分布有多大差异。
计算 KL 散度

KL Divergence
其中 H(p,q)是交叉熵,H(p)是系统的熵,其中 pᵢ 是第个事件的实际概率,q ᵢ 是第 I 个事件的估计概率。
重申我们理想分布的性质
- 正类的分布应该使得任何数据点属于正类的概率应该是 1,而负类的概率应该是 0。
- 负类的分布应该使得任何数据点属于正类的概率应该是 0,负类的概率应该是 1。
pᵢ 是来自理想分布特性的事件的实际概率。q ᵢ 是事件的估计概率,使用拟合/估计分布计算。我们用这些概率来寻找 KL 散度。
Calculating the probabilities and KL Divergence
KL 散度出来是 5.74 ,这表示拟合的分布非常接近理想值。但是我们能做得更好吗?
6.努力接近理想的分布
每类一条高斯曲线可能不足以模拟整个分布。我们可以拟合高斯混合分布,看看结果。有多少高斯人?直到我们的 KL 散度接近 0,即理想分布和拟合分布之间没有差异或差异最小。让我们试试那个。
Try fitting more than one Gaussian per Class
结果

KL Divergence on increasing the number of Gaussians per Class

1 Gaussian per Class, KL Divergence = 5.74

2 Gaussian per Class, KL Divergence = 3.18

3 Gaussian per Class, KL Divergence = 1.81

4 Gaussian per Class, KL Divergence = 0.77

5 Gaussian per Class, KL Divergence = 0.20
外卖食品
每类四个高斯分布就足够了,并且非常接近地模拟了 KL 散度几乎为 0 的理想分布。下面的情节也清楚地表明了这一点。

4 Gaussian per Class, KL Divergence approaches 0
7.结论
我们选择了一个包含两个不同类的数据集。我们想找到这两个类的基本分布。所以,我们首先定义了什么是好的,有理想分布的性质,并且能够非常接近地模拟理想分布。这样,我们总是可以尝试找到数据的基本分布,并使用 KL 散度来查看拟合度。希望它为这个主题带来了所需的清晰度,并为其在日常机器学习工作中的应用带来了新的视野。
我的 Youtube 频道获取更多内容:
嗨,伙计们,欢迎来到频道。该频道旨在涵盖各种主题,从机器学习,数据科学…
www.youtube.com](https://www.youtube.com/channel/UCg0PxC9ThQrbD9nM_FU1vWA)
写一篇清晰易懂的好文章需要很多努力。我会继续努力做好我的工作。在 中 关注我,查看我以前的帖子。我欢迎反馈和建设性的批评。任务的完整代码可以从这里获得。
8.参考
- https://en.wikipedia.org/wiki/Probability_density_function
- https://en.wikipedia.org/wiki/Entropy
- 【https://en.wikipedia.org/wiki/Cross_entropy
- https://en . Wikipedia . org/wiki/kull back % E2 % 80% 93 lei bler _ divergence
- https://scikit-learn.org/stable/modules/mixture.html
- https://towards data science . com/part-I-a-new-tool-to-your-toolkit-KL-divergence-5b 887 b5 b420 e
- https://towards data science . com/demystifying-entropy-f2c 3221 e 2550
- https://towards data science . com/demystifying-cross-entropy-e 80 e 3 ad 54 a 8
- http://www . ai shack . in/tutorials/expect-maximization-Gaussian-mixture-model-mixtures/
- https://en . Wikipedia . org/wiki/Expectation % E2 % 80% 93 最大化 _ 算法
- https://brilliant.org/wiki/gaussian-mixture-model/
- https://en.wikipedia.org/wiki/Bayes%27_theorem
- https://en . Wikipedia . org/wiki/kull back % E2 % 80% 93 lei bler _ divergence
第 2 部分——自动化和人工智能能打破商业智能应用的障碍吗?

探索人工智能及其对 BI 采用的影响的系列文章的第 2 部分。
正如我们在本系列的第 1 部分中所看到的,如果 77%的企业“绝对没有”或“可能没有”充分利用分析,并且分析平台的采用率只有糟糕的 32%,那么就需要采取一些重大措施。拥有机器学习和人工智能的增强分析时代能够解决这个采用问题吗?为了找到答案,我们需要了解我们迄今为止是如何在业务分析方面取得进展的。我们需要了解每个分析时代的优势和缺陷,才能知道增强分析是否真的是答案。
我们如何进入增强分析时代,商业智能简史
1985 年至 2005 年:引领潮流
我们第一次看到仪表板和业务记分卡的概念。但是,要实现这一点,需要昂贵的工具,而且使用和配置极其复杂。由于复杂性和不灵活性,见解需要数周才能挖掘出来,只能由 IT 专家提供。这意味着数据和见解在很大程度上也是不可访问的,因此很少有人能够获得用于业务决策的数据。只有大型企业才有能力聘请数据分析师。但正如我们所知,这个时代是使用仪表盘的商业报告的诞生。
2005–2015 年:自助服务分析
到了 2005 年中期,“自助服务”成了流行语。自助服务分析供应商催生了能够独立于 IT 运营的超级用户和数据分析师。BI 供应商使分析师能够使用不太复杂的流程和更简单的用户界面来准备、发现和可视化数据。然而,那个时期的弱点是分析的分散化,这意味着缺乏治理。很少的治理意味着对分析的来源或获取见解的过程没有监督,当多人从相同的数据中产生不同的结果时,这导致了不信任。由于缺乏这些制衡,用户不会采用,部分原因是缺乏信任。尽管 BI 比以往任何时候都更容易获得,但采用率却达到了一个稳定的水平。这个时代大部分商家还在经营。
2016 年—现在:增强分析
最近,我们看到了新一轮的分析浪潮,它承诺为所有人提供自动化和即时洞察力。这种趋势已经像滚雪球一样,包括自然语言搜索查询、机器学习生成的分析和自动数据发现。在接下来的 12 个月内,我们将看到几乎所有分析供应商在其平台中使用机器学习和自然语言界面推出自动化。尽管在以企业一直希望的速度提供数据洞察方面,这将是一个巨大的飞跃,但它也带来了自己的问题——透明度。正在使用的算法和底层计算是什么?我能信任它所依赖的引擎吗?如果洞察和数据发现背后的自动化是一个黑匣子,那么对数字的信任将保持非常低的水平,让人想起无人监管的自助服务时代。这可能会成为分析师而非商业用户的一个问题,商业用户已经毫无疑问地依赖于生活中许多领域的算法,从谷歌搜索结果到脸书的时间线提要。对于企业用户来说,甚至无需与分析工具交互就能交付的自动化见解,可能远远超过他们对算法的任何担忧。
我们如何转向这一波增强分析
三个市场趋势正在推动下一波分析浪潮:数据量增加、种类更多、数据速度更快——这是人们通常创造的三个 v。随着物联网(IoT)的加入,这些问题变得尤为明显,物联网正在收集和生成大量的指标。几乎没有一天不添加新的 web 数据源、API 或客户 360 视图的另一部分,以带来更大的多样性。数据传输的速度是惊人的。要看到它的实际效果,你只需要暂停网上购物,转到社交媒体上,你刚刚考虑购买的商品就会出现在广告中。有了数据的三个 v,我们现在有能力提出更多的问题和更具体的问题,以找到优化业务所需的答案。随着数据的激增,我们可以将问题从“发生了什么?”到“为什么会发生?”但是“为什么”仍然是大海捞针,而且大海捞针还在不断增加。数据的数量、种类和速度可能是发现数据价值的促进因素,也可能是障碍,因为数据分析仍然由手动数据发现驱动,这需要数据分析师的技能。
BI 采用的四大阻碍因素
在第 3 部分中,我们将了解阻碍 BI 和分析采用的四个潜在因素,从而揭示分析自动化如何解决这一问题。
第二部分:机器学习、人工智能和自动化如何打破 BI 采用壁垒 最初发布于 2019 年 4 月 16 日yellowfinbi.com。
为您的机器或深度学习项目提供更多不常见的数据清理器
帕索项目
我将介绍类型和格式转换数据清理器。

几乎所有的 paso ,包括这里讨论的所有函数,都要求 paso 数据清理步骤(如删除空值和 NA 值)已经完成。
这些数据清理器在以下文章中有详细介绍:
介绍
towardsdatascience.com](/uncommon-data-cleaners-for-your-real-world-machine-or-deep-learning-project-f926d8ecb258)
背景故事
在我们以前的项目中,我们发现自己大部分时间都在用非常相似或略有不同的数据清理器清理数据。
在第一个项目中,我们在数据清理开发上花费了大量时间。以至于在第二个项目开始时,项目管理将数据清理分为三类:相同、差异小于 33%和差异大于 33%。
这些类别定义了两个重要原因:
- 作为一个团队,随着数据清理器需求的确定,我们能够消除大量的“重新编码车轮”,并向我们的数据清理器包添加超过 33%的不同类别。
- 我们的开发时间估计变得更加精确。
两年半之后,我们开始了第八个项目,并且发现,到目前为止,不到十分之一的数据清理器属于第三类,即花费开发人员更多时间的类别。更重要的是,我们从开发到生产的时间是前两个项目的一半。我们现在可以将大部分时间花在寻找足够好的机器学习(有时是深度学习)解决方案上。
您的体验可能会有所不同,因为我目前的领域是用于资产配置、平衡和风险的固定收益金融数据。(好吧,是的,一些我在这里不会提到或使用的其他晦涩的东西。)
现在你可能会问我为什么要讲这个故事。因为随着我们沿着这条路走下去,我从一个机器学习科学家进化成了一个软件架构师和软件工程师。
我现在负责修复一个生产服务,并完成最初的架构设计工作和代码开发。我喜欢从开发到生产。有时我会写下并分享我们的一些成就。
我真诚地希望你能使用我们的一些工作。这里使用的函数的所有代码都可以在https://github.com/bcottman/paso/toutil.py找到。
故事已经够多了。你能做什么?给我看看!
使用不同组合的清洗策略(管道)是 paso 的一个重要目标。
讨论将分为以下几个主要部分:
- 首先,我们创建可以改变的数据集。
- 接下来,通过对创建的数据集进行操作来演示每个清理器。
- 然后我们展示了一个与管道很好地混合的调用功能:链接掉 pandas DataFrame 实例。这是我们熟悉的 R 开发模式。我们发现编码 paso 链比编码 sklearn 管道要容易得多。我们甚至认为链式管道比传统管道更容易检查和理解。
- 最后,我们总结一下 paso 的数据清理器,以及我们对未来文章和 paso 版本的计划。
本课的代码在这里和这里文件中给出。获得整个 paso 包的最好方法是 git 克隆base。pip将不起作用,因为此包中有您需要的基于文本的文件(描述)。
目标实用程序:目标数据框架
paso.pre.toutil.toDataFrame(X: pd.DataFrame,
columns: List = None,
inplace: bool = True,
verbose: bool = True)
-> pd.DataFrame:
Parameters:
X: DataFrame
Attempt to convert python datatype argument into
pandas dataframe.
columns:
The column names to be used for new DataFrame.
If a number of column names given is less than a number
of column names needed, then they will be generared
as c_0...c_(n-1), where n is the number of missing
column names.
verbose: default: True) Logging is on.
Returns: DataFrame
该功能基于willmcginnis's convert_input()。
paso 是一个包,其中所有的功能和方法都需要一个数据框架。
由于 paso 函数和方法只消耗 pandas 数据帧,我们需要一个函数来转换各种 python 数据类型,同时还提供 paso 的基本服务。这个功能就是paso.to_util.toDataFrame。
此函数是" paso 规则的例外。第一个参数是 DataFrame "规则,因为它将一维或二维列表、元组、csr_matrix、numpy 数组或 1d pandas 系列转换为 pandas DataFrame。
你也有paso.to_util.isDataFrame(X) -> bool来检查对象是否是一个pandas.DataFrame。
如果不需要 paso 的服务,比如日志记录和缓存,可以使用方法pandas.DataFrame。我建议你不要这样做,但是你可以选择这样做。
一个好的做法是在管道的开始制作类型为DataFrame的数据集。由 paso 函数所做的更改将被保存在 DataFrame 实例的状态中(inplace=True)。在您的实验运行的整个管道中,就地将会发生,以最大化完成速度和最小化内存使用。
如果您需要引用原始数据帧,那么您可以执行类似以下操作:
<dataset_name>_original = <dataset_name>.copy()
但是,系统会警告您,保留原件会消耗一些电脑内存。
由您决定是否保留原始数据集的副本。
几乎总是从本地文件或通过 URL 寻址的非本地文件输入原件,因此,您可能不需要在计算机内存中保留副本。
您可以通过以下方式查看任何对象的大小(以字节为单位):
from sys import getsizeof
getsizeof(pd.DataFrame())=> 24
这表明空数据帧的大小为 24 字节。
创建一个我们可以使用的数据集
现在,我们创建一个人工数据集df_of_ints,一个由int组成的数组
from paso.base import raise_PasoError
from paso.pre.toutil import toDataFrame
arr = np.ndarray(shape=(1000000,6), dtype=int, order='F')
df_of_ints = toDataFrame(arr)
日志输出[=>]:
2019-12-12 13:29:08.164 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with
column names: Index(['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
toDataFrame(arr, columns=['a','b']) 可以为新的 DataFrame 指定前两个列名。
df_of_ints = toDataFrame(arr, columns=['a','b'])
df_of_ints.shape
日志输出[=>]:
2019-12-12 13:29:22.981 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with
column names: Index(['a', 'b', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
输出[=>]:
(1000000, 6)
请注意,前两个列名是如何给出的,toDataFrame检测到这一点,并创建了所需的接下来的四个列名。
这次我们调用toDataFrame,同时保持 paso 注销。只有在调用的函数范围内,日志记录才会被关闭。
df_of_ints = toDataFrame(arr, columns=['X','yyyyyy'],verbose=False)
df_of_ints.columns
输出[=>]:
Index(['X', 'yyyyyy', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
to _ util:to 类别
paso.pre.toutil.toCategory(
X: pd.DataFrame,
bool_: bool = True,
int_: bool = True,
object_: str = True,
inplace: bool = True,
verbose: bool = True,
) -> pd.DataFrame:
Parameters:
X: dataFrame
bool_: default: True - convert to category type.
int_: default: True - convert to category type.
object_: default: True - convert to category type.
inplace: default:
True - change DataFrame arg "in-place"
False: return new resulting DataFrame
verbose: default: True - Logging is on.
Returns: resulting DataFrame
该函数将一个 pandas DataFrame 实例的任何布尔值、对象值或整数值转换为类别类型值。
例外情况是连续(浮点或日期时间)值,它返回未转换的值。
如果要将连续或日期时间类型转换为类别,则在 paso toCategory 之前使用 ContinuoustoCategory 或 DatetimetoComponents。
注: paso 数据清理步骤(如删除空值和 NA 值)已在此步骤之前完成。
datetime特征应在此步骤之前调用toDatetimeComponents(),以便datetime组件(类型为int)转换为category。
该步骤的行为不是将datetime转换为category。
from paso.base import raise_PasoError
from paso.pre.toutil import toCategory
df_cat = toCategory(df_of_ints,inplace=False)
日志输出[=>]:
2019-12-12 13:36:58.748 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : X
2019-12-12 13:36:58.768 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : yyyyyy
2019-12-12 13:36:58.786 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_2
2019-12-12 13:36:58.802 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_3
2019-12-12 13:36:58.814 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_4
2019-12-12 13:36:58.824 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_5
看起来df_of_ints没变,这就是我们在本例中想要的,inplace=False。
df_of_ints.dtypes
输出[=>]:
X int64
yyyyyy int64
c_2 int64
c_3 int64
c_4 int64
c_5 int64
dtype: object
现在看来,df_of_ints的所有值都是从类型int转换为类型category并带有 inplace=True。
df_of_ints.toCategory(inplace=True)
df_of_ints.dtypes
日志输出[=>]:
2019-12-12 09:39:37.193 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : X
2019-12-12 09:39:37.214 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : yyyyyy
2019-12-12 09:39:37.233 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_2
2019-12-12 09:39:37.251 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_3
2019-12-12 09:39:37.269 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_4
2019-12-12 09:39:37.282 | INFO | paso.pre.toutil:toCategory:197 - toCategory integer feature converted : c_5
输出[=>]:
X category
yyyyyy category
c_2 category
c_3 category
c_4 category
c_5 category
dtype: object
链接
我猜有些读者(不是你)对df_of_ints.toCategory(inplace=True)有点困扰。如果是这样的话,你会更加为此烦恼:
toDataFrame(arr, columns=['a','b']).toCategory(verbose=False).dtypes
日志输出[=>]:
2019-12-12 09:39:37.359 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with column names: Index(['a', 'b', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
输出[=>]:
a category
b category
c_2 category
c_3 category
c_4 category
c_5 category
dtype: object
如果您对这种大惊小怪不感到困扰甚至困惑,那是因为您知道 DataFrame 可以是一个python类实例,并且您在一个类实例上调用方法。你甚至可以链方法。也就是说,你可以一个接一个地调用方法。
在这种情况下,我链接了toDataFrame输出的 DataFame 实例的两个方法.toCategory().dtypes。
paso 的Cleaners类和toutils函数的所有方法都要求第一个参数是 DataFrame 实例。Dataframe 作为第一个参数,通过使用基于包 pandas-flavor 的重构装饰器,使它们成为 DataFrame 的方法。
链接可以使它非常容易理解,因为操作是从左到右调用的。这和你阅读(英语和许多其他语言)的方式是一样的。
有些人更喜欢链而不是使用 sklearn 的管道。你会注意到我们的链接在语法上与 sklearn 的管道链接略有不同。
我们的链接非常接近R的外观。从现在开始,我们将使用这两种调用方法。
to_util: toContinuousCategory
def toContinuousCategory(
X: pd.DataFrame,
features:List=[],
drop: bool = True,
inplace: bool = True,
verbose: bool = True,
integer: bool = True,
floaty: bool = True,
quantile: bool = True,
nbin: int = 10,
) -> pd.DataFrame:
Parameters:
X:pd.DataFramedrop: default:True
do not keep original features.integer: default:True
set integer=False if not continuous and not to
transform into category.floaty: default:True
set floaty=False if not continuous and not to
transform into category.quantile: default: True
use quantile bin.
Quantile is similar to v/(maxy-miny), works on any scale.
False, use fixed-width bin. miny,maxy arguments are ignored.nbin:int (default: 10)Returns: DataFrameRaises:
TypeError('"ContinuoustoCategory:inplace: requires boolean type.")
util:today time components
paso.pre.toutil.toDatetimeComponents(
X: pd.DataFrame,
drop: bool = True,
components: list = [],
prefix: bool = True,
inplace: bool = True,
verbose: bool = True) -> pd.DataFrame:
Parameters:
X: DataFrame
drop: default: True
If True then the datetime feature/column will be removed.
components:: default: []
list of column(feature) names for which datetime components
are created.
prefix: default: True
If True then the feature will be the prefix of the
created datetime component features. The postfix will be
_<component> to create the new feature column
<feature>_<component>.
if False only first _PREFIX_LENGTH_ characters of feature
string will be used to create the new feature
name/column<featurename[0:_PREFIX_LENGTH_]>_<component>.
components:list default:
['Year', 'Month', 'Week', 'Day','Dayofweek'
, 'Dayofyear','Elapsed','Is_month_end'
, 'Is_month_start', 'Is_quarter_end'
, 'Is_quarter_start', 'Is_year_end', 'Is_year_start']
or set components to one or component names in a list
Must be components names from default list.
inplace: default: True
True: change DataFrame arg in-place"
False: return new resulting dataframe
verbose: default: True Logging is on.
Returns:DataFrame
toDatetimeComponents transformed into datetime
feature components.
为什么要将一个日期时间分解成单位成分?这导致了一种高级的编码策略,在论文分类变量的实体嵌入中有详细的讨论
最近,在被指派研究一个预测问题后,我读了很多关于实体嵌入的文章。
towardsdatascience.com](/understanding-entity-embeddings-and-its-application-69e37ae1501d)
以下条款指出:
"Categorical variables are known to hide and mask lots of interesting information in a data set and many times they might even be the most important variables in a model. A good data scientist should be capable of handling such variables effectively and efficiently. If you are a smart data scientist, you’d hunt down the categorical variables in the data set, and dig out as much information as you can."
出于多种原因,所有这些都会产生更好的预测能力,您可以将一个datetime分解成int个组件。您可以更进一步,将datetime int组件转换成分类类型的组件。
你可能已经明白这是怎么回事了。如果我有一些非分类特征,比如一列datetimes,我将需要一个函数把日期时间转换成分类特征。
我将在下一篇文章中介绍如何使用分类特征作为嵌入向量。
您可以通过datetimeComponents查看哪些日期时间组件可用。我们的版本基于 fast.ai 包中的一个函数。
from paso.pre.toutil import datetimeComponents
datetimeComponents()
输出[=>]:
['Year',
'Month',
'Week',
'Day',
'Dayofweek',
'Dayofyear',
'Elapsed',
'Is_month_end',
'Is_month_start',
'Is_quarter_end',
'Is_quarter_start',
'Is_year_end',
'Is_year_start']
我首先创建一个 DataFrame 实例来展示toDatetimeComponents的功能。
from datetime import datetime, timedeltarc = 94598; cc = 1; acc =13
darr = np.arange(datetime(1751,7,1),
datetime(2010,7,1),
timedelta(days=1)).reshape(rc,cc)
from paso.pre.toutil import toDatetimeComponents
toDataFrame(darr,columns = []).toDatetimeComponents(inplace=False).head(2)
日志输出[=>]:
2019-12-12 09:39:37.601 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with column names: Index(['c_0'], dtype='object')
2019-12-12 09:39:37.609 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Year
2019-12-12 09:39:37.614 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Month
2019-12-12 09:39:37.621 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Week
2019-12-12 09:39:37.627 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Day
2019-12-12 09:39:37.633 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Dayofweek
2019-12-12 09:39:37.640 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Dayofyear
2019-12-12 09:39:37.794 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Elapsed
2019-12-12 09:39:37.800 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Is_month_end
2019-12-12 09:39:37.805 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Is_month_start
2019-12-12 09:39:37.812 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Is_quarter_end
2019-12-12 09:39:37.816 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Is_quarter_start
2019-12-12 09:39:37.822 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Is_year_end
2019-12-12 09:39:37.828 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: c_0_Is_year_start
2019-12-12 09:39:37.837 | INFO | paso.pre.toutil:toDatetimeComponents:351 - datetime feature dropped: c_0
输出[=>]:

Resulting DataFrame from toDatetimeComponents
在这种情况下,datetimeComponents将每个日期时间值转换成十三个int组件。然后它将这些int类型转换成category类型。
如果 X[[dt_features]]不是日期时间类型(如object类型),那么不会试图将 X[[dt_features]]强制转换为datetime类型。
最好将原始数据字段作为datetime而不是object读取/输入。您可以使用以下方法将 dataframe 列转换为 datetime:
X[feature] = pd.datetime(X[feature])
我们可以进行自动转换,但是我们发现对于一些较大的数据集(> 1 亿行),转换速度太慢。使用数据帧列作为datetime而不是object来启动管道要快得多。
toutil: toContinuousCategory
paso.pre.toutil.toContinuousCategory(
oX: pd.DataFrame,
features: list = [],
drop: bool = True,
int_: bool = True,
float_: bool = True,
quantile: bool = True,
nbin: int = 10,
inplace: bool = True,
verbose: bool = True,
) -> pd.DataFrame:*Parameters:
X: dataset
Keywords:
features: default: []
The column names to be transform from continuous
to category.
drop: default: True)
If True then the datetime feature/column will be removed.
int_: Default: True
set integer=False if not continuous and not to
transform into category.
float_: Default: True
set floaty=False if not continuous and not to transform
into category.* *quantile: Default: True use quantile bin.
quantile is simular to v/(maxy-miny), works on any scale.
False, use fixed-width bin. miny,maxy arguments are ignored.
nbin: default: 10
Alternately ``nbins`` can be integer for number of bins.
Or it can be array of quantiles, e.g. [0, .25, .5, .75, 1.]
or array of fixed-width bin boundaries i.e. [0., 4., 10., 100].
verbose: Default True
True: output
False: silent
inplace: Default: True
True: replace 1st argument with resulting dataframe
False: (boolean)change unplace the dataframe X
Returns: pd.DataFrame
Raises:
TypeError('" requires boolean type.")*
toContinuousCategory将任何连续的浮点数,或 pandas DataFrame 的整数值分组到 bin 中,转换为类别值。
宁滨,也称为量化,用于将连续的数字特征(np.number类型)转换为category类型。
每个容器代表一系列连续的数值。
宁滨数据的具体策略有固定宽度(quantile=False)和自适应宁滨(quantile = True)。
无论输入数据集的类型如何,它都将作为DataFrame返回。如果要设置功能名称,在此功能之前调用toDataFrame。
固定宽度的宁滨只对基于树的模型的数据集有效,没有缩放,例如 random forest、xgboost、lightgbm、catboost 等。非树基模型,如线性、对数、、神经网络等。,不会正确工作,因为它们依赖于值的大小。
线性宁滨的统计问题。
宁滨增加 I 型和 II 型误差;(简单的证明是,随着箱数趋近于无穷大,那么信息损失趋近于零)。此外,改变容器数量将改变容器分布形状,除非分布均匀平坦。
分位数宁滨与单一数据集一起使用。
如果你有一个训练和测试数据集,基于百分位数(分位数)将连续特征转换成类别特征是错误的。分位数基于数据集,除非每个数据集的分布形状相同,否则分位数会有所不同。在极限中,只有两个箱,然后几乎没有关系可以建模。我们主要是在做 t 检验。
如果特征之间存在非线性甚至非单调关系
如果你需要线性宁滨,而不是分位数,使用quantile=False。
如果你想要分位数——宁滨。
尽管有上述警告,您的用例可能需要分位数宁滨。
在这种情况下,将分位数宁滨应用于合并的训练和测试数据集,然后再将它们分开。
基于分位数的宁滨是用于自适应宁滨的一个相当好的策略。分位数是将要素的连续值分布划分为离散的连续箱或区间的特定值或分界点。因此,q-分位数将数值属性划分为 q 个相等(百分比-宽度)的分区。
这些箱变成分类特征。你可能想这样做的年龄,体重,温度,工资,价格等特征..
分位数的众所周知的例子包括 2 分位数,中位数,将数据分布分为两个相等(百分比宽度)的二进制数,4 分位数,标准四分位数,四个相等的二进制数(百分比宽度)和 10 分位数,十分位数,十个相等宽度(百分比宽度)的二进制数。
您应该注意到,新要素是由分位数宁滨附加 q 产生的,而新要素是由固定宽度宁滨附加 w 产生的
将连续浮动功能转换为类别并删除原始列:
from paso.pre.toutil import toContinuousCategory
nc = 6
nr = 1000000
delta = 0.1
farr = np.arange(0,(nr*delta*nc),delta, dtype=float).reshape(nr,nc)
toDataFrame(farr).toContinuousCategory(drop=True).head(n=2)
日志输出[=>]:
2019-12-12 15:27:27.638 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with
column names: Index(['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
2019-12-12 15:27:28.204 | INFO | paso.pre.toutil:toContinuousCategory:533 - toContinuousCategory features:: Index(['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
输出[=>]:

将连续浮动要素转换为固定宽度的类别箱,并保留原始列。
toDataFrame(farr).toContinuousCategory(quantile=False,nbin=3,drop=False)\
.head(n=2)
日志输出[=>]
2019-12-12 09:39:38.578 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with column names: Index(['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
2019-12-12 09:39:38.896 | INFO | paso.pre.toutil:toContinuousCategory:533 - toContinuousCategory features:: Index(['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
输出[=>]:

DataFrame resulting in quantile binning
toutil: toColumnNamesFixedLen
def toColumnNamesFixedLen(
oX: pd.DataFrame,
column_length: int = 1,
col_separator: str = "_",
inplace: bool = True,
verbose: bool = True,
) -> pd.DataFrame:
Parameters:
X: dataset
Keywords:
column_length: Default: 3
Character length for which to truncate all columns.
The column separator value and number for duplicate
column name does not contribute. Therefore, if all
columns are truncated to 10 characters, the first
distinct column will be 10 characters and the
remaining will be 12 characters (assuming a column
separator of one character).
col_separator: Default: "_"
The separator to append plus incremental Int
to create unique column names. Care should be
taken in choosing non-default col_separator
so as to create legal pandas column name.
verbose: Default: True
True: output
False: silent
inplace: Default: True
True: replace 1st argument with resulting dataframe
False: (boolean)change unplace the dataframe X
Returns: A pandas DataFrame with truncated column lengths.
将列名截断到特定长度。如果列长度较短,则列长度保持不变。
toColumnNamesFixedLen会将所有列截断到给定的长度,并附加给定的分隔符和重复列的索引,第一个不同的列名除外。
toDataFrame([[1,2,3,4]],columns=[
“long_name”,
“another_long_name”,
“another_longer_name”,
“data”,
]).toColumnNamesFixedLen(column_length=5)
日志输出[=>]:
2019-12-12 09:39:39.051 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with column names: Index(['long_name', 'another_long_name', 'another_longer_name', 'data'], dtype='object')
2019-12-12 09:39:39.052 | INFO | paso.pre.toutil:toColumnNamesFixedLen:669 - toColumnNamesFixedLen features:: ['lo', 'an', 'an=1', 'da']
输出[=>]:

Resulting DataFrame from toColumnNamesFixedLen
函数式编程——用链接创建可读管道
在这一节。我将展示 python 中的函数式编程风格。这种风格也被称为链式,使用 R 的人应该非常熟悉。
我将创建一个数据框架,这样我们就可以显示我们的一些清洁链(管道)。
arr = np.ndarray(shape=(1000000,6), dtype=int, order='F')
dataset = toDataFrame(arr)
dataset['date'] = pd.to_datetime('11/30/1956')
日志输出[=>]
2019-12-12 09:46:58.614 | INFO | paso.pre.toutil:toDataFrame:112 - toDataFrame with column names: Index(['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5'], dtype='object')
dataset.toDatetimeComponents(inplace=False)\
.toColumnNamesFixedLen(column_length=12)\
.toContinuousCategory(quantile=False,nbin=10,drop=False)\
.head(n=3)
日志输出[=>]
2019-12-12 10:18:49.817 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Year
2019-12-12 10:18:49.861 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Month
2019-12-12 10:18:49.920 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Week
2019-12-12 10:18:49.962 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Day
2019-12-12 10:18:50.013 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Dayofweek
2019-12-12 10:18:50.060 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Dayofyear
2019-12-12 10:18:50.092 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Elapsed
2019-12-12 10:18:50.139 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Is_month_end
2019-12-12 10:18:50.182 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Is_month_start
2019-12-12 10:18:50.236 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Is_quarter_end
2019-12-12 10:18:50.280 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Is_quarter_start
2019-12-12 10:18:50.327 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Is_year_end
2019-12-12 10:18:50.368 | INFO | paso.pre.toutil:toDatetimeComponents:346 - datetime feature component added: date_Is_year_start
2019-12-12 10:18:50.477 | INFO | paso.pre.toutil:toDatetimeComponents:351 - datetime feature dropped: date
2019-12-12 10:18:50.478 | INFO | paso.pre.toutil:toColumnNamesFixedLen:669 - toColumnNamesFixedLen features:: ['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5', 'date_Year', 'date_Month', 'date_Week', 'date_Day', 'date_Dayofwe', 'date_Dayofye', 'date_Elapsed', 'date_Is_mont', 'date_Is_mont_1', 'date_Is_quar', 'date_Is_quar_1', 'date_Is_year', 'date_Is_year_1']
2019-12-12 10:18:51.112 | INFO | paso.pre.toutil:toContinuousCategory:533 - toContinuousCategory features:: Index(['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5', 'date_Year', 'date_Month',
'date_Week', 'date_Day', 'date_Dayofwe', 'date_Dayofye', 'date_Elapsed',
'date_Is_mont', 'date_Is_mont_1', 'date_Is_quar', 'date_Is_quar_1',
'date_Is_year', 'date_Is_year_1'],
dtype='object')
输出[=>]

Resulting DataFrame from chaining
摘要
从 Python 3.6 (PEP 484)开始,引入了类型提示。类型提示(注意:不是强类型检查)使得用下游工具对 Python 代码进行静态类型检查成为可能。
使用类型提示,toutil函数调用签名被总结如下:
# 1
paso.pre.toutil.toDataFrame(
X: pd.DataFrame,
columns: List = None,
inplace: bool = True,
verbose: bool = True) -> pd.DataFrame:
#2
paso.pre.toutil.toCategory(
X: pd.DataFrame,
bool_: bool = True,
int_: bool = True,
object_: str = True,
inplace: bool = True,
verbose: bool = True) -> pd.DataFrame:
#3
paso.pre.toutil.toContinuousCategory(
oX: pd.DataFrame,
features: list = [],
drop: bool = True,
int_: bool = True,
float_: bool = True,
quantile: bool = True,
nbin: int = 10,
inplace: bool = True,
verbose: bool = True,
) -> pd.DataFrame:
#4
paso.pre.toutil.toDatetimeComponents(
oX: pd.DataFrame,
drop: bool = True,
components: list = [],
prefix: bool = True,
inplace: bool = True,
verbose: bool = True) -> pd.DataFrame:
#5
paso.pre.toutil.def toColumnNamesFixedLen(
X: pd.DataFrame,
column_length: int = 1,
col_separator: str = "_",
inplace: bool = True,
verbose: bool = True) -> pd.DataFrame:
关于帕索的其他文章有:
** [## paso 为您的 Python 项目提供日志记录和参数服务
paso 为您的 Python 项目提供日志记录和参数服务
paso 为您的 Python Projectmedium.com 提供日志记录和参数服务](https://medium.com/@dr.bruce.cottman/pasos-offering-of-logging-and-parameter-services-for-your-python-project-c3ae2fd6869a) [## 第 1 部分:平衡和扩充结构化数据
数据扩充很重要,因为它从我们当前现有的数据集生成(准确地)人工数据。的…
medium.com](https://medium.com/@dr.bruce.cottman/part-1-balancing-and-augmenting-structured-data-4ade0df38662) [## 用于真实世界机器或深度学习项目的不寻常的数据清理器
介绍
towardsdatascience.com](/uncommon-data-cleaners-for-your-real-world-machine-or-deep-learning-project-f926d8ecb258)
未来,我们将通过以下文章更深入地介绍 paso :
- 链接整个机器学习管道。
- 第 2 部分:平衡和扩充结构化数据的更多(更好)方法。
- 第 1 部分:分类特征的不同编码方法。
- 第 2 部分:分类特征的深度学习编码。
如果你有一个服务或功能,或者看到一个 bug,那么就把这个 paso 项目留给一个注释。**
你的工具箱中的一个新工具,KL 散度介绍
以有趣且非常直观的方式揭开熵、交叉熵和 KL 散度的神秘面纱

作为一名数据科学从业者,您在工作中多久使用一次 KL 散度的概念?你对熵、交叉熵、或 KL 散度这些概念有多少清晰和把握?一点点,只是理论上的或者看过一次就忘了的类型。
不管是什么,都没关系。你一定在网上看过一些关于这个话题的文章,甚至我也看过,但是它太理论化和无聊了,以至于我们会随着时间而忘记。但是,等等,如果我提出一个完全不同的观点,这个观点使我很好地理解了这个概念,并使它成为我武器库中的一个强有力的武器。相信我,一旦你完成了这些,你就会知道如何在每个小的分类、聚类或其他日常机器学习问题中利用这些概念。
这篇文章之后会有另一篇文章。在这一部分,我们将从了解熵开始,然后是交叉熵,最后是 KL 散度。然后,我们将使用学到的概念,并将它们应用到一个数据集中,这将在下一篇文章中使事情变得非常清楚。我会尽我最大的努力让事情简单和直观,但请随时跳转到参考资料部分,并研究更多的主题。让我们开始旅程吧。
你也可以找到我在 Youtube 上的相同主题的视频。
My Youtube video on the same topic
1.理解熵
互联网上说熵是信息增益的数量。让我简单明了地说,熵就是与事件相关的无序、不确定性、惊奇或不可预测性的数量。这是什么意思?
举个例子,看到我预测的今天的天气,
- 明天下雨的可能性为 50%。
- 明天有 75%的可能性会下雨。
哪个案例给了我们更多的信息增益或知识?
当说到 50-50 的机会时,它意味着更多的不确定性,任何事情都有可能发生,因此有更多的随机性。而在第二种情况下,75%的下雨几率意味着下雨的可能性更大,随机性更小,或者获得新信息。因此,第一种情况比第二种情况具有更高的熵。
让我们再举一个例子,抛硬币的可能结果是什么,给定
- 头部和尾部概率相等的无偏硬币,0.5
- 正面概率为 0.75 的有偏硬币
和以前一样,在第一种情况下,有 50–50 的机会正面或反面。这意味着更多的不确定性,任何事情都有可能发生,因此有更多的随机性。而在第二种情况下,75%的正面机会意味着硬币的结果更有可能是正面,随机性更小,或新信息增益。因此,第一种情况比第二种情况具有更高的熵。
计算熵
系统“H(p)”的熵计算如下:

The entropy of the system
其中 pᵢ 是第 I 个事件的概率。
让我们计算上面例子的熵,并表明无偏硬币比有偏硬币具有更高的熵。
情况一:头部和尾部概率相等的无偏硬币的熵,0.5
=-0.5 * log(0.5)-0.5 * log(0.5)= 1.0
案例二:正面概率为 0.75 的有偏硬币的熵
=-0.75 * log(0.75)-0.25 * log(0.25)= 0.81
2.理解交叉熵
按照我的说法,交叉熵是拟合度,即预测分布与实际分布的接近程度。让我简化一下,
假设我们得到了一枚正面概率为 0.75 的有偏硬币。我们没有意识到硬币的偏见,所以
案例一:我们投掷硬币 100 次,得到 70 个正面和 30 个反面。我们的估计是硬币有偏差,正面概率为 0.70。
案例二:我们投掷硬币 100 次,得到 72 个正面和 28 个反面。我们的估计是硬币有偏差,正面概率为 0.72。
我们更接近情况 II 中的实际分布,因此情况 II 中的交叉熵比情况 I 中的更低。如果碰巧我们能够无任何误差地模拟真实分布,则交叉熵将与熵相同。因此,交叉熵总是大于或等于熵。
计算交叉熵

The Cross-Entropy of the system
其中 pᵢ 是第个事件的实际概率,q ᵢ 是第 I 个事件的估计概率。所用对数以 2 为底。
让我们计算上述示例的交叉熵,并显示情况 II 比情况 I 具有更低的交叉熵,因为它更接近实际分布。
情况一:发现硬币有偏差,头部概率为 0.70。
=-0.75 * log(0.70)-0.25 * log(0.30)= 0.820
情况二:发现硬币有偏差,正面概率为 0.72。
=-0.75 * log(0.72)-0.25 * log(0.28)= 0.814
显然,情况 II 的交叉熵小于情况 I,因为它更接近实际分布。
如果估计是,硬币偏向正面的概率是 0.75,
=-0.75 * log(0.75)-0.25 * log(0.25)= 0.811(最小,这里交叉熵与熵相同)
3.了解 KL 差异
KL 散度只是拟合分布和实际分布之间的差异,即交叉熵和熵之间的差异。还可以查看这两种分布有多大差异。
计算 KL 散度

KL Divergence
让我们举一个同样的例子,我们得到了一个偏向硬币,正面的概率是 0.75。我们没有意识到硬币的偏见,所以
案例一:我们掷硬币一百次,得到 70 个正面和 30 个反面。我们的估计是硬币有偏差,正面概率为 0.70。
KL = 0.75 * log(0.75/0.70)+0.25 * log(0.25/0.30)= 0.0088
案例二:我们投掷硬币一百次,得到 72 个正面和 28 个反面。我们的估计是硬币有偏差,正面概率为 0.72。
KL-散度= 0.75 * log(0.75/0.72)+0.25 * log(0.25/0.28)= 0.0032
显然,第二种情况下的距离比第一种情况下的距离小,因为第二种分布更接近实际分布。
如果估计是,硬币偏向正面的概率是 0.75,
KL-Divergence= 0.75 * log(0.75/0.75)+0.25 * log(0.25/0.25)= 0(熵与交叉熵相同),所以两个分布没有区别。
4.结论
这是第一篇让我们彻底理解熵、交叉熵和 KL-散度的文章。我们也通过直观的方式和通过计算理解了这些术语,并通过例子看到了它们的价值。我希望这澄清了这个话题,给了它一个不同于传统互联网上的观点。在下一篇文章中,我打算在真实的数据集中使用这些概念。它将为每个人提供如何在每个小的分类、聚类或其他日常机器学习问题中利用这些学习到的概念的想法/直觉。不要错过它伙计们,记住我的话,它只会变得更好。
我的 Youtube 频道更多内容:
嗨,伙计们,欢迎来到频道。该频道旨在涵盖各种主题,从机器学习,数据科学…
www.youtube.com](https://www.youtube.com/channel/UCg0PxC9ThQrbD9nM_FU1vWA)
写一篇清晰易懂的好文章需要很多努力。我会继续努力做好我的工作。在 中 关注我,查看我以前的帖子。我欢迎反馈和建设性的批评。
5.参考
- https://en.wikipedia.org/wiki/Entropy
- https://en.wikipedia.org/wiki/Cross_entropy
- https://en . Wikipedia . org/wiki/kull back % E2 % 80% 93 lei bler _ divergence
- https://towards data science . com/demystifying-entropy-f2c 3221 e 2550
- https://towards data science . com/demystifying-cross-entropy-e 80 e 3 ad 54 a 8
- https://towards data science . com/demystifying-KL-divergence-7 ebe 4317 ee 68
选择机器学习模型
挑选完美的机器学习模型的部分艺术,部分科学。

外面闪亮模型的数量可能是压倒性的,这意味着很多时候人们依靠他们最信任的几个,并用它们来解决所有新问题。这可能导致次优结果。
今天,我们将学习如何快速有效地缩小可用模型的范围,找到最有可能解决您的问题类型的模型。我们还将看到如何使用权重和偏差来跟踪模型的表现,并对它们进行比较。
你可以在这里找到伴随代码。
我们将涵盖的内容
- 竞争数据科学与现实世界中的模型选择
- 模特们的盛大集会
- 比较模型
我们开始吧!
与指环王不同,在机器学习中,没有一个戒指(模型)可以统治所有的戒指。不同类别的模型擅长对不同类型数据集的底层模式进行建模。例如,决策树适用于数据形状复杂的情况:

而线性模型最适合数据集可线性分离的情况:

在我们开始之前,让我们稍微深入一下现实世界中的模型选择与竞争数据科学之间的差异。
竞争数据科学与现实世界中的模型选择
正如 William Vorhies 在他的博客文章中所说,“Kaggle 竞赛就像数据科学的方程式赛车。赢家在小数点后第四位击败竞争对手,就像 f1 赛车一样,我们中没有多少人会把他们误认为日常车手。投入的时间和有时极端的技术不适合数据科学生产环境。”
Kaggle 模型确实像赛车,它们不是为日常使用而制造的。现实世界的量产车型更像雷克萨斯——可靠但不浮华。
Kaggle 竞赛和真实世界针对非常不同的事情进行优化,其中一些关键差异是:
问题定义
现实世界允许您定义您的问题,并选择封装您的模型成功的度量标准。这允许你优化一个更复杂的效用函数,而不仅仅是一个单一的指标,其中 Kaggle 竞争只带有一个预定义的指标,并且不允许你有效地定义问题。
韵律学
在现实世界中,我们关心推理和训练速度、资源和部署约束以及其他性能指标,而在 Kaggle 竞赛中,我们唯一关心的是一个评估指标。想象一下,我们有一个精度为 0.98 的模型,它非常耗费资源和时间,而另一个精度为 0.95 的模型速度更快,计算量更少。在现实世界中,对于许多领域,我们可能更喜欢 0.95 的精度模型,因为我们可能更关心推理的时间。在 Kaggle 比赛中,无论训练模型需要多长时间,或者需要多少 GPU,精度越高总是越好。
可解释性
类似地,在现实世界中,我们喜欢更简单的模型,更容易向利益相关者解释,而在 Kaggle 中,我们不关注模型的复杂性。模型可解释性很重要,因为它允许我们采取具体的行动来解决潜在的问题。例如,在现实世界中,查看我们的模型并能够看到特征(例如街道上的坑洼)和问题(例如街道上发生车祸的可能性)之间的相关性,比将预测精度提高 0.005%更有帮助。
数据质量
最后,在 Kaggle 比赛中,我们的数据集被收集并为我们争论。任何研究过数据科学的人都知道,现实生活中几乎不会出现这种情况。但是能够收集和组织我们的数据也让我们对数据科学过程有了更多的控制。
激励
所有这些激励我们花费大量的时间来调整我们的超参数,以从我们的模型中提取最后的性能下降,有时,复杂的特征工程方法。虽然 Kaggle 竞赛是学习数据科学和特征工程的一种极好的方式,但它们并没有解决现实世界中的问题,如模型可解释性、问题定义或部署约束。
模特们的盛大集会
是时候开始选模特了!
当选择我们的初始模型集进行测试时,我们需要注意一些事情:
选择一组不同的初始模型
不同类别的模型擅长对数据中不同种类的底层模式进行建模。因此,一个好的第一步是快速测试几个不同类别的模型,以了解哪些模型能够最有效地捕捉数据集的底层结构!在我们的问题类型(回归、分类、聚类)领域中,我们希望尝试基于树、基于实例和基于内核的混合模型。从每个班级中挑选一个模型进行测试。我们将在下面的“要尝试的模型”一节中详细讨论不同的模型类型。
为每个模型尝试一些不同的参数
虽然我们不想花太多时间寻找超参数的最佳集合,但我们确实想尝试一些不同的超参数组合,以使每个模型类都有机会表现良好。
挑选最强有力的竞争者
我们可以使用这个阶段中表现最好的模型来给我们直觉,让我们知道我们想要进一步深入哪个类别的模型。您的权重和偏好仪表板将引导您找到最适合您的问题的模型类别。
深入研究最佳性能模型类中的模型。
接下来,我们选择更多属于我们上面列出的最佳表现类别的模型!例如,如果线性回归似乎效果最好,那么尝试套索或岭回归也是一个好主意。
更详细地探索超参数空间。
在这个阶段,我鼓励你花一些时间调整候选模型的超参数。(本系列的下一篇文章将深入探讨为模型选择最佳超参数的直觉。)在这一阶段结束时,您应该拥有所有最强模型的最佳执行版本。
做出最终选择——ka ggle
从不同的模型中挑选最终提交的作品。理想情况下,我们希望从多个类别的模型中选择最佳模型。这是因为如果你只从一类模型中进行选择,而恰好是错误的,那么你提交的所有模型都将表现不佳。Kaggle 竞赛通常允许你选择一个以上的参赛作品进行最终提交。我建议从不同的类中选择你最强的模型所做的预测,以在你的提交中建立一些冗余。
排行榜不是你的朋友,你的交叉验证分数才是。最重要的是要记住,公共排行榜不是你的朋友。仅仅根据你的公开排行榜分数来挑选你的模型将会导致训练数据集过拟合。当私人排行榜在比赛结束后公布时,有时你可能会看到你的排名下降很多。您可以在训练模型时使用交叉验证来避免这个小陷阱。然后选择交叉验证分数最高的模型,而不是排行榜分数最高的模型。通过这样做,您可以根据多个验证集来衡量模型的性能,而不仅仅是公共排行榜使用的一个测试数据子集,从而应对过度拟合。
做出最终选择——真实世界
资源限制。不同的模型占用不同类型的资源,了解您是将模型部署在带有小型硬盘和处理器的物联网/移动设备上,还是部署在云中,对于选择正确的模型至关重要。
训练时间 vs 预测时间 vs 准确率。了解您正在优化的指标对于选择正确的模型也至关重要。例如,自动驾驶汽车需要极快的预测时间,而欺诈检测系统需要快速更新其模型,以跟上最新的网络钓鱼攻击。对于医疗诊断等其他情况,我们关心的是准确性(或 ROC 曲线下的面积),而不是训练次数。
复杂性与可解释性的权衡。更复杂的模型可以使用数量级更多的特征来训练和预测,这需要更多的计算,但如果训练正确,可以捕捉数据集中真正有趣的模式。这也使得它们令人费解,难以解释。知道向利益相关者简单解释模型与在放弃可解释性的同时捕捉一些真正有趣的趋势是多么重要,是选择模型的关键。
伸缩性。了解您的模型需要扩展的速度和规模可以帮助您适当地缩小选择范围。
训练数据的大小。对于非常大的数据集或具有许多特征的数据集,神经网络或增强树可能是一个很好的选择。而较小的数据集可能更适合使用逻辑回归、朴素贝叶斯或 KNNs。
参数数量。具有大量参数的模型为您提供了很大的灵活性,以获得真正出色的性能。然而,可能会有这样的情况,你没有所需的时间,例如,从头开始训练神经网络的参数。在这种情况下,开箱即用的模型将是最佳选择!
比较模型
权重和偏差让您用一行代码跟踪和比较模型的性能。
一旦你选择了你想尝试的模型,训练它们,然后简单地添加wandb . log({ ' score ':cv _ score })来记录你的模型状态。一旦你完成训练,你可以在一个简单的仪表板上比较你的模型表现!
你可以在这里找到有效完成这项工作的代码。我鼓励你分叉这个内核并摆弄代码!
就是这样,现在您拥有了为您的问题选择正确模型所需的所有工具!
模型选择可能会非常复杂,但我希望这篇指南能给你一些启发,并给你一个挑选模型的好框架。
在第二部分,机器学习模型的旋风式旅行中,我们将更深入地研究 ML 模型,何时应该使用它们!
如果您有任何问题或反馈,请随时 发微博给我 !
词性-单词标记

将单词分类到它们的词性并相应地标记它们的过程被称为词性标注或词性标注,或简称为标注。
请查看 GITHUB repo 的代码和其他酷项目
词类又称为词类或 词类 。
词性解释了一个词在句子中的用法。有八种主要的词类——名词、代词、形容词、动词、副词、介词、连词和感叹词。
用于特定任务的标签集合被称为标签集。
词性标注器,或称词性标注器,处理一系列单词,并给每个单词附加词性标签。
让我们首先运行下面的 coed,看看我们到底在谈论什么。
下面是输出。这是我们正在谈论的 POS 标签列表。

现在让我们试着输入我们自己的句子,看看 NLTK 库如何给每个单词添加 PosTag。
注意:为了得到词性,我们必须对我们的句子进行分词。
在下面的输出中,我们可以看到 POS 标签是如何被分配给每个单词的。

注意:如果你得到错误,那么你可以不注释第二行,这样就可以下载 averaged_perceptron_tagger。
下一个问题是这些位置是从哪里映射来的???
它们来源于语料库或统称为语料库。
语料库是用于语言研究和编写词典的书面语和/或口语的集合。
当我们提供输入时,在语料库中搜索这些单词以获得标记的位置。
NLTK 中包含的几个语料库已经标记了它们的词性
事实:文本到语音转换系统通常执行词性标记
如果语料库也被分割成句子,它将有一个 tagged_sents()方法,该方法将标记的单词分割成句子,而不是作为一个大列表呈现。
注意,词性标签已经转换成大写,自从 布朗语料库 出版以来,这已经成为标准做法。
因为我们对 POS 标记很熟悉,所以让我们看看如何使用它们,以及打电话后有什么好处。
在 Jupyter 笔记本中运行下面的例子。
在下面的例子中,我们可以看到在提供了“v”即“动词”标记后,我们得到了不同的输出。

如果没有 POS 标签,它会认为你输入的所有东西都是名词。所以这里它认为你在传递名词“爱”(比如“甜蜜的爱”)。
春分???
我们用它来检测句子中的实体。
看看下面的图片。

较小的方框显示单词级标记和词性标注,而大方框显示更高级别的组块。
组块工作在词性标注之上,它使用词性标注作为输入,并提供组块作为输出。
简单来说,这是一种标记的推广,其中一个连续的单词序列被分配一个标记
这也被称为浅层解析。由此产生的一组单词被称为“组块”
在浅层解析中,根和叶之间最多有一层,而深层解析包括不止一层。
浅层解析也叫轻解析或分块。
要创建块树,请在 Jupyter 笔记本中输入以下代码。
下面应该是代码和一个新的窗口将会打开,块树将会显示。


到目前为止,我们已经讨论了词性标注和组块。我们将在未来的帖子和实现中使用上述所有概念。
如果你发现文章有什么改进,请提出意见。
粒子滤波器:非线性和非高斯世界的英雄

粒子滤波技术在非线性、非高斯系统中的优越性决定了其广泛的应用范围。此外,粒子滤波器的多模态处理能力也是其被广泛应用的原因之一。在国际上,粒子滤波已经应用于各个领域。
- 在经济学领域,它被用于经济数据的预测。粒子滤波技术在非线性和非高斯系统中的优越性决定了它的广泛应用。此外,粒子滤波器的多模态处理能力也是其被广泛应用的原因之一。在国际上,粒子滤波已经应用于各个领域。数字无处不在,当它们被收集和记录时,我们称之为数据。机器学习是从数据中学习数学模型的科学。这样的模型,一旦从数据中学习
- 在军事领域,它已被应用于雷达跟踪空中目标、空对空、空对地被动跟踪
- 在交通控制领域,它被应用于汽车或人的视频监控。
- 在视觉分析领域,使用粒子滤波器开发了一种新的模式识别模型,用于分割和跟踪视频序列中的嘴唇轮廓。
- 它也用于机器人的全球定位。
传统分析方法无法解决的问题现在正通过粒子模拟得到解决。
在动态系统的模型选择、故障检测和诊断中,出现了基于粒子的假设检验、粒子多模型、粒子似然比检测等方法。
在参数估计方面,通常将静态参数作为扩展状态向量的一部分,但由于参数是静态的,粒子会很快退化成一个样本。为了避免退化,常用的方法是人为地增加静态参数的动态噪声。
核平滑法,以及 Doucet 等人提出的点估计法,避免了对参数的直接采样,直接在粒子框架下使用最大似然估计(ML)和最大期望值(EM)算法对未知参数进行估计。
揭开神秘粒子滤波的神秘面纱
粒子滤波器(PF: Particle Filter)的思想基于蒙特卡罗方法,该方法使用粒子集来表示概率,可用于任何形式的状态空间模型。核心思想是通过从后验概率中提取随机状态粒子来表达其分布。它是一种序贯重要性抽样方法(序贯重要性抽样)。

简单来说,粒子滤波方法是指通过寻找一组在状态空间中传播的随机样本来近似概率密度函数,并用样本均值代替积分运算,从而得到状态最小方差分布的过程。
这里的样本指的是粒子,当样本数 N→∞时,可以近似任何形式的概率密度分布。
虽然算法中的概率分布只是真实分布的一种近似,但由于其非参数特性,可以摆脱求解非线性滤波问题时随机量必须满足高斯分布的约束,可以表达比高斯模型更广的分布。
它还具有更强的建模变参数非线性特性的能力。因此,粒子滤波能够准确表达基于观测和控制量的后验概率分布,可用于解决 SLAM 问题。
粒子滤波的发展

马尔可夫链蒙特卡罗改进策略
马尔可夫链蒙特卡罗(MCMC)方法通过构造具有良好收敛性的马尔可夫链从目标分布中产生样本。在 SIS 的每次迭代中,结合 MCMC 将粒子移动到不同的位置以避免退化,马尔可夫链可以将粒子推向更接近概率密度函数 (PDF)。使样本分布更加合理。
无味粒子滤波(UPF)
无迹卡尔曼滤波器(UKF)是由 Julier 等人提出的。EKF(扩展卡尔曼滤波器)使用一阶泰勒展开来逼近非线性项,并使用高斯分布来逼近状态分布。UKF 类似于 EKF,具有近似状态分布的高斯分布,但是没有线性化,仅使用了一些称为适马点的样本。
这些点通过非线性模型后,得到的均值和方差可以精确到非线性展开泰勒的二阶项,使得非线性滤波更加精确。Merwe 等人提出使用 UKF 来生成 PF 的重要性分布,称为无迹粒子滤波(UPF)。UKF 产生的重要性分布大于真实状态 PDF 的重叠,估计精度更高。
数据科学和机器人学中的粒子滤波
粒子滤波器现在广泛应用于金融市场模型的估计,特别是随机波动模型。
粒子滤波方法是递归贝叶斯滤波,当模型是非线性的并且噪声不是高斯的时,它提供了一种方便和有吸引力的方法来近似后验分布。
这些技术提供了许多问题的一般解决方案,其中线性化和高斯近似是难以处理的或者会产生太低的性能。非高斯噪声假设和对状态变量的约束的结合也可以以自然的方式进行。此外,粒子滤波方法非常灵活,易于实现,可并行化,并适用于非常普遍的设置。
基于粒子滤波器的机器人定位

An Indoor robot navigating its way inside a office

The lost robot on a map with the approximation of its location
粒子滤波器解决了应用机器人学中的许多问题。假设我们可能有想要追踪的移动物体。也许这些物体是战斗机和导弹,或者也许我们正在跟踪在田野里打板球的人。这其实并不重要。让我们考虑一下三维机器人问题的特点:
- 多模态:我们想同时跟踪零个、一个或多个物体
- 遮挡:一个对象可以隐藏另一个对象,导致多个对象的一次测量。
- 非线性行为:飞机受到风的冲击,球以抛物线运动,人们相互碰撞。
- 非线性测量:雷达给我们一个物体的距离。将其转换为(x,y,z)坐标需要平方根,这是非线性的。
- 非高斯噪声:当物体在背景上移动时,计算机视觉会将部分背景误认为是物体。
- 连续:物体的位置和速度(即状态空间)可以随时间平滑变化。
- 多变量:我们想要跟踪几个属性,比如位置、速度、转弯率等。
- 未知流程模型:我们可能不知道系统的流程模型

The initial assumption of the localized pose is noisy as we can see the points spread across the grid (map). As the robot moves further the particles converge which explains the convergence of the particles.
各种机器人表示,在仓库中导航的室内机器人可以使用粒子过滤器根据测距传感器的输入来定位自己,例如 2D 激光扫描仪或用于自动驾驶汽车的粒子过滤器可以用于融合传感器输入并识别道路上的车道标志。无人驾驶飞机可以使用粒子过滤器来优化光流。
强化学习算法和粒子滤波器

Reinforcement learning, in the context of artificial intelligence, is a type of dynamic programming that learns and perfects an algorithm using a system of reward and punishment.
基于粒子滤波器的强化学习(RL)算法在计算上是廉价的,且具有非常低的内存占用。包括基于粒子滤波器的直接全局策略搜索的 RL 算法将在由所选策略参数化定义的策略空间中执行搜索。
粒子过滤器可以是 RL 算法的核心概念,可以通过创建粒子来指导探索和开发,每个粒子代表一个整体策略。由于粒子滤波器执行全局搜索的能力,所得到的 RL 算法应该能够在策略空间中直接进行全局搜索,这是对传统的基于局部搜索的策略 RL 的显著改进。

这增加了价值,因为它继承了粒子过滤器的许多优点,其中包括:
- 它实现起来非常简单,可以很容易地在嵌入式系统中实现在线学习
- 它可以通过更改σ参数的值,根据可用资源(时间和 CPU 方面)使用自适应计算
- 它可以将 RL 探索的努力集中在策略空间的最重要部分,
- 通过改变初始噪声水平和衰减因子λ,它可以根据精度和时间的要求表现出自适应的收敛速度
粒子滤波的危害
虽然粒子滤波算法可以作为解决 SLAM 问题的有效手段,但是该算法仍然存在一些问题。主要问题是需要大量样本来近似逼近系统的后验概率密度。机器人面临的环境越复杂,描述后验概率分布需要的样本就越多,算法就越复杂。
因此,能够有效减少样本数量的自适应采样策略是算法的重点。此外,重新采样阶段会导致样本有效性和多样性的丧失,从而导致样本耗尽。如何保持粒子的有效性和多样性,克服样本的枯竭也是重采样算法的重点。
在强化学习中,粒子滤波作为全局搜索方法的一员,一般需要更多的尝试才能收敛,因为搜索的范围是最大可能的——整个策略空间。
粒子滤波器没有严格的收敛性证明。理论上,我们用全局搜索方法交换了“局部收敛的证明”,该方法没有全局收敛的证明,但至少保证不会陷入局部最优。
定位粒子滤波
粒子滤波器在自动驾驶汽车上的应用概述
在之前的帖子中,我们谈到了卡尔曼滤波器在帮助自动驾驶汽车定位方面的作用。课程在这里介绍了另一种叫做粒子过滤器的定位方法,这很有趣,我认为它可以以某种方式被塞进一篇文章中,所以我将简要介绍粒子过滤器的概念并实现它。
直觉
顾名思义,粒子过滤器构建了许多粒子来代表我们对汽车位置的猜测。考虑一个场景,一辆自动驾驶汽车在一个有 4 个主要地标的世界中行驶:

汽车上的传感器能够粗略计算汽车和四个地标之间的距离,同时我们初始化大量粒子,每个粒子代表汽车潜在位置的猜测。基于来自实际汽车的测量和来自虚拟汽车(粒子)的测量,我们应该能够生成每个粒子是实际汽车的后验概率,并且随着汽车继续移动,不相关的粒子将被逐渐过滤掉。
所以一般来说,粒子过滤器包括 4 个步骤:
- 生成一组粒子
- 测量每个粒子成为实际汽车(机器人)的概率
- 基于概率权重的重采样
- 重复移动到接近方向
让我们一步一步来。
产生粒子
除了粒子是我们对汽车实际位置的猜测,它应该具有实际汽车的所有功能。
现在让我们首先生成一辆真实的汽车:
[x=27.984 y=67.990 orient=4.0437] # the real location of the car
然后生成随机猜测的粒子:
上面的代码生成了 1000 个粒子,每个粒子都有随机初始化,但噪声固定(0.05, 0.05, 5.0)。
[[x=89.958 y=44.058 orient=0.5153],
[x=67.425 y=71.905 orient=3.0930],
[x=11.044 y=60.100 orient=2.3018],
[x=21.359 y=43.892 orient=4.0847],
[x=6.6223 y=41.454 orient=0.8310]]
通过打印出初始化,我们可以看到每个粒子可以有非常不同的位置和方向。
测量概率
现在让我们让汽车行驶,获得每个粒子概率的想法是,通过使用来自实际汽车的测量值,并给出每个粒子的测量值,我们应该能够计算每个粒子成为实际汽车的可能性。让我们看下面的例子:

假设我们将仅基于地标 2 的距离测量来测量图中粒子的概率。粒子的距离为 3,噪声为 1,也就是说测量服从高斯分布 N(3, 1),表示粒子可以用距离 4.2、3.2、5.1、1.2、…,以不同的概率测量自身。现在问题来了,给定我们的汽车测量值dist = 5,这个粒子也测量距离 5 的自身的概率是多少?答案是计算 5 给定分布的概率 N(3, 1)。
所以在robot类中,我们需要增加两个函数:
我们可以通过以下方式来衡量每个概率:
我们的 1000 个粒子每个都将采取与实际汽车相同的移动,之后,每个粒子将通过rob.measurement_prob(Z)计算其概率给定测量Z。
经过概率计算,靠近实际位置的粒子概率较高,远离实际位置的粒子概率较低。所以下一步要明确,对粒子进行采样,保留大概率的粒子。
重采样
现在每个粒子都被标注了不同的概率,我们希望保留高概率的粒子,在我们的下一轮迭代中使用它们。这样做的方法是基于概率权重进行重新采样。
这是一种相当原始的基于权重的采样方式,我并不完全理解。我猜np.random.choice也可以做到同样的效果,但是请记住,我们需要基于当前的概率权重重新采样一个相同大小的粒子列表。
找到方向
我们已经谈了很多关于距离和位置的问题。但是,即使我们能够得到最接近我们的汽车的位置的粒子,我们如何能够确定它的方向呢?
事实上,我们可以通过汽车行驶到地标的距离来暗示方向。考虑以下场景:

假设我们有两个粒子,粒子 1 和粒子 2,它们同样接近真实的汽车。然而,粒子 2 朝着我们汽车的同一个方向运动,而粒子 1 则相反。显然,随着时间的推移(更多轮次的迭代),粒子 1 的概率将显著降低并最终被过滤掉,而粒子 2 更有可能存活下来。
所以定位方位的方法就是让车多做动作,走的更远。最后,我们需要将代码放入一个循环中:
最终,只有位置和方向最接近的粒子才能存活下来(完整实现此处)。
参考:
在 React 组件(父组件、子组件、兄弟组件)之间传递数据

React 是由 T2·脸书创建的一个 JavaScript 库。React 中的数据处理可能有点棘手,但并没有看上去那么复杂。我目前在 React 中编译了三种数据处理方法
- 从父母到孩子使用道具
- 使用回调从子进程到父进程
- 兄弟姐妹之间:
(一)结合以上两种方法
(二)使用 Redux
(三)使用 React 的上下文 API
这个博客主要包含了这些概念的实现的汇编,这对于任何试图一目了然地掌握事物的初学者来说肯定是有益的。
从父母到孩子使用道具
让我们考虑这样的目录结构,即父组件在应用程序中呈现子组件。
App
└── Parent
├── Child1
└── Child2
这是 React 中最简单的数据流方向,也是最基本的方向。
class Parent extends React.Component {state = { data : "Hello World" }
render() {
return (
<div>
<Child1/> //no data to send
<Child2 dataFromParent = {this.state.data} />
</div>
); }
}
//It is no compulsion to use the data to send as a state, simple vars or const variables could also be used to send data from Parent to Child.
只需使用 this.props.dataFromParent(只是一个用于发送 props 的变量)来访问从父节点发送到子节点的数据。
class Child2 extends React.Component {
render() {
return (
<div>
The data from parent is:{this.props.dataFromParent}
</div>
);
}
}
使用回调从子进程到父进程
让我们假设我需要从 Child1 向 Parent 发送一条消息——“嗨,Popsie,最近怎么样?”。为此,我需要遵循一系列步骤。
第一步: 定义一个回调函数,该函数接受一个我们认为是从 Parent.js 中的 child 访问的参数
第二步: 同样,将定义好的回调函数作为道具发送给 Child1.js
class Parent extends React.Component {state = { message: "" }callbackFunction = (childData) => { this.setState({message: childData})},render() {
return (
<div>
<Child1 parentCallback = {this.callbackFunction}/>
<p> {this.state.message} </p>
</div>
);}
}
第三步: 在 Child1.js 中使用 this . props . callback(data to parent)发送数据
class Child1 extends React.Component{sendData = () => {
this.props.parentCallback("Hey Popsie, How’s it going?");
},render() {
//you can call function sendData whenever you'd like to send data from child component to Parent component.
}
};
兄弟姐妹之间
当我还是初学者时,我很难决定选择哪种方法在兄弟姐妹之间共享数据,我知道有三种方法在兄弟姐妹之间共享数据,它们都有自己的优缺点。
方法一: 将以上两种共享数据的方法结合起来。然而,这种方法不适用于复杂的目录结构,因为人们将不得不编写大量代码来在彼此相距甚远的组件之间发送数据。然后,数据将不得不通过每个中间层被推和拉。

方法 2: 使用一个全局存储来维护所有子组件的状态,这些子组件需要从存储中交互和消费所需的数据— Redux

方法 3: 使用 React 的 Context API 关于 React 为什么升级到 Context API 以及哪一个在哪些方面更好,已经有大量的文章和博客,这两篇文章将有助于理解这一切:
[## React 上下文 API——Redux 的替代品?
将使用 Redux 进行状态管理的 React 应用程序转换为使用 React 的新上下文 API
blog.bitsrc.io](https://blog.bitsrc.io/react-context-api-a-replacement-for-redux-6e20790492b3) [## 你可能不需要 Redux
人们往往在需要之前就选择 Redux。“如果没有它,我们的应用无法扩展怎么办?”后来,开发商皱眉…
medium.com](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367)
我使用过这种方法,并且已经稍微倾向于在 Redux 上使用这种方法。
Context API 的主要优势在于它将开发者从钻柱中解救出来。(Prop-drilling 指的是将变量传递给子组件的技术。主要思想是函数式编程,将参数传递给下一个函数,依此类推)

Image source: Google
考虑目录结构,我们需要在 Child1 和 Child2 之间传递数据。[ Child1 必须发送消息—“SSup 兄弟??"to Child2 ]
我们使用上下文 API 通过以下方法实现这一点:
App
├── Child1
└── Child2
步骤 1: 为两个孩子创建一个提供者组件。这个提供者维护状态(组件和一些用于操作状态的回调所使用的数据)并返回一个 contextObject。提供商 JSX 组件)
第二步: 将状态和回调函数作为道具传递给 Provider 组件内部的所有子组件。
export const MContext = React.createContext(); //exporting context objectclass MyProvider extends Component {state = {message: ""}render() { return ( <MContext.Provider value={ { state: this.state, setMessage: (value) => this.setState({ message: value })}}> {this.props.children} //this indicates that the global store is accessible to all the child tags with MyProvider as Parent </MContext.Provider>) }}
提供者是其子代的老板(所有状态和操作这些状态的回调函数的全局存储)。需要任何东西的人必须首先联系提供者才能访问对象。
(a)要通过 Child1 设置或操作消息,它必须访问提供者并设置提供者的状态。
(b)要按 Child2 查看/访问数据,它必须访问 Provider 以获取状态。
第三步: 使用 MyProvider 组件作为两个子组件——child 1、child 2——的父组件。
class App extends React.Component {render() {
return (
<div>
<MyProvider> <div className="App"> <Child1/> <Child2/> </div> </MyProvider> </div>
);
}
}
第四步:以同样的方式实现想要的结果,但是这一次,使用 ContextObject。消费者如下所述:
两个孩子——孩子 1 和孩子 2 都是提供者的消费者。从今以后,它们在消费者标签中访问提供者。
import MContext
class Child1 extends React.Component {render() { return ( <div> <Mcontext.Consumer> {(context) => ( <button onClick={()=>{context.setMessage("New Arrival")}}>Send</button> )} </Mcontext.Consumer> </div> ) }}
现在 Child2 是如何接收数据的?
简单来说,就是使用消费者标签访问提供商。
import MContext
class Child2 extends React.Component {render() { return ( <div> <Mcontext.Consumer> {(context) => ( <p>{context.state.message}}</p>)} </Mcontext.Consumer> </div> )}}
我希望这为 React 中不同组件之间的数据传递提供了清晰的实现细节。
推荐:
我有机会在 React Conf 2018 上谈论 React 中使用上下文的新方法。这篇博文是一篇文字…
medium.com](https://medium.com/@wisecobbler/using-context-in-react-56a8e7da5431)
通过艾将纳粹浩劫的故事传递给年轻一代
数据科学如何支持大屠杀幸存者证词的传播?

JEWISH ARMY, TEXT OF DIKA JEFROYKIN
我的祖父朱尔斯·迪卡·杰弗罗金从来没有谈起过他在二战期间的岁月。我们只知道他是法国抵抗组织的一员。然而,我们不知道他在抵抗运动中做了什么,也不知道他参与的程度。
我一直想知道在战争年代我的家庭发生了什么,我不断地研究我的家族史。在某个时候,我放弃了,并停止了寻找,认为我再也找不到比他的一些朋友在乔治·洛因格的书“Les réresistences juives pendant l ' occupation”中发表的十行传记更多的东西了。
然后,上个月,我第一次去波兰进行了一次“寻根”之旅。这非常有力,提醒了我传承历史的重要性,尤其是在如今纳粹浩劫幸存者越来越少的时候。
“为了死者和生者,我们必须作证。”, 埃利·威塞尔
我一直有一种感觉,我需要发现我的家族历史,并为下一代做一个“目击者的目击者”。
所以参观完之后,我又开始寻找。一次偶然的机会,我在网上发现,在华盛顿 DC 的美国大屠杀纪念馆保存的一份文件中提到了我祖父的名字。我让他们寄给我,两天前我收到了。我惊奇地发现,这是我祖父关于他在法国抵抗运动中的作用的证词!他的故事的 26 页法文 PDF 草稿。
什么能帮助我?
我想清理这段文字,让每个人都更容易阅读,以传播和保存他的记忆,并能够出版它,以帮助下一代获得关于纳粹浩劫历史的洞察力和知识。我们生活在一个有趣的时代,随着人口的减少,越来越少的年轻人有机会听到幸存者的声音,但可以让年轻人接触幸存者的技术却在不断发展。
作为一名数据科学家,我一直试图利用我的技能做好事,我坚信人工智能可以成为社会变革的伟大工具。使用人工智能来帮助我分享我祖父的故事显然是下一步。
PDF 转换为文本文档
为了能够纠正错误和他的手写笔记,我需要一个文本文档。我使用了基于深度学习的文本识别:基于 LSTM 的谷歌的 OCR 引擎宇宙魔方。
为了重现下面的代码,你需要 pip 安装所有导入的包。此外,你需要安装宇宙魔方。
词频
除此之外,我使用了一些 NLP 技术、方法、技术、算法来进行总结。下面是一些用 nltk 和 gensim 提取摘要的例子。
**# Most frequent words** ['Juifs', 'Joint', "l'AJ", "l'Agence", 'Juive', "l'argent", 'jeunes', 'fait', 'France', 'fonds', '11', 'accord', 'Jefroykin', 'fallait', 'faire']*# '11' is an OCR mistake for 'Il'***# In English, powered by Google Translate** ['Jews', 'Joint', 'AJ', 'Agency', 'Jewish', 'money', 'young', 'made', 'France', 'funds', '11','agreement','Jefroykin','must','do']
所有文本中出现频率最高的词会直接给你主题。我们可以看到作者的名字' Jefroykin ',他合作过的两个组织' Joint '为 American Joint 和' L'AJ '为 Jewish Army ,他的组织。
摘录摘要
今天,人们的注意力持续时间很短,特别是在消费新闻和其他信息时,所以摘要是参与的关键部分。这就是为什么我使用一些 NLP 技术、方法、技术和算法来自动总结较长的文档。这里有一些用 nltk 和 gensim 摘录的例子。
**# NLTK 2 Sentence Summary:** Il y a donc eu un accord confidentiel entre la Direction européenne du Joint située à Lisbonne, et Dika Jefroykin, représentant du Joint et France, #'autorisant à récolter de l'argent -remboursable après la guerre — Le but du Passage en Espagne de Jefroykin était triple, D'abord, diriger un convoi "cobaye" après une longue interruption des passages. Deuxièmement, rétablir une liaison entre le Joint au Portugal sa représentation en Espagne et l'AJ en France, Troisièmement, assurer un accord avec l'Agence Juive pour que les jeunes qui souhaitaient rejoindre la Brigade Juive en Palestine puissent être assurés de pouvoir le faire,**# Gensim Summary:** Il y a donc eu un accord confidentiel entre la Direction européenne du Joint située à Lisbonne, et Dika Jefroykin, représentant du Joint et France, #'autorisant à récolter de l'argent -remboursable après la guerre — Le but du Passage en Espagne de Jefroykin était triple, D'abord, diriger un convoi "cobaye" après une longue interruption des passages.
翻译
然后,我将这些摘要翻译成英文,让更多的人能够看到文本,这要感谢谷歌云翻译 API 。
**# NLTK summary translated** There was therefore a confidential agreement between the European Directorate of the Joint located in Lisbon, and Dika Jefroykin, representative of the Joint and France, # authorizing to collect money-refundable after the war - The purpose of the Passage in Spain of Jefroykin was triple, First, to lead a convoy "guinea pig" after a long interruption of passages. Secondly, to re-establish a liaison between the Joint in Portugal its representation in Spain and the AJ in France, Third, to ensure an agreement with the Jewish Agency so that young people who wished to join the Jewish Brigade in Palestine can be assured of being able to do so ,**# Gensim summary translated** There was therefore a confidential agreement between the European Directorate of the Joint located in Lisbon, and Dika Jefroykin, representative of the Joint and France, # authorizing to collect money-refundable after the war - The purpose of the Passage in Spain of Jefroykin was triple, First, to lead a convoy "guinea pig" after a long interruption of passages.
这两个摘要都为我们提供了第一页的含义,即使一句话的摘要似乎已经足够好了。使用这些技术帮助我完成了一个文本文档,并理解了证词的关键部分。
AI 为好
就像互联网和社交媒体一样,多年前,人工智能也应该被用来继续教育的重要工作——纳粹浩劫和其他过去的种族灭绝,以及今天仍在世界各地发生的暴行。
忘记过去的人注定要重复过去。”,温斯顿·丘吉尔。
此外,是时候将人工智能引入慈善和人类组织了。高科技工作者应该支持它,并为社会公益贡献他们的知识。我管理着一个名为 Data For Good Israel 的组织,我们正试图利用人工智能的巨大力量来做到这一点。让我们传播这个消息吧!
PS:如果你对见证感兴趣,我可以和你分享,给我发消息就好。
我要感谢来自 Zencity 的同事和朋友,他们在这个项目中发挥了重要作用:Inbal Naveh Safir、Traci Siegel、Ori Cohen 和 Yoav Talmi。
Samuel Jefroykin 是 Zencity.io 的一名数据科学家,他试图积极影响城市的生活质量。他还共同创立了 Data For Good Israel,一个致力于利用数据的力量解决社会问题的社区。
通过可怕的数据科学课后作业:2021 年更新

“这只需要你 3 到 5 个小时的时间。”
“作为我们流程的一部分,接下来是简单的标准化带回家作业”
“应该不到 3 个小时(我们真的是这个意思)。”
这些原话你见过多少次了?希望在你的数据科学面试过程中不会太多。但是,如果你是许多正在找工作的数据科学家中的一员,你可能会发现自己正在做一个数据科学带回家的作业,它是一个压缩文件,里面有一个长达 10 页的需求 pdf。
招聘人员承诺,你的任务有一个复杂的评分过程,不会超过几个小时。但是突然到了凌晨 2:30,三天后,15 个小时的编码疲劳开始了,你甚至没有想过尝试一下 GAM 模型,看看它是否能让你的模型的 F1 分数提高 3%。
是的,数据科学带回家的作业应该被取缔,禁止,至少可能在你工作的时候得到补偿,在你被干掉之前,这些工作可能真的会碰到招聘经理的垃圾邮件过滤器。是的,我表面上写了我在 T11 之前有多恨他们。但不幸的是,似乎不知何故,他们会留在这里。
有兴趣聘请一名数据科学家和机器学习工程师吗?不知道如何建立标准化的数据科学挑战?
我甚至没有说这只是未经证实的初级候选人的过程的一部分。上个月在几乎每一次我与 采访询问 客户的反馈会议中,他们提到了一次采访,在那次采访中,他们收到了一份带回家的作业,花了他们十几个小时才完成,结果得到的只是一封迅速拒绝的电子邮件。为什么这种事情还在继续发生?为什么公司浪费应聘者的时间,却没有任何反馈?

事实是,对于许多没有标准化面试流程的公司来说,这个流程就像是一个过滤器。开发技术面试问题需要数据科学团队的知识历史,而与软件工程师相比,这是不存在的。
所以如果你必须,不得不,并且没有其他选择的话,做一个带回家的作业。这里有几个步骤,以确保更顺利的过程。
想练习一系列数据科学带回家的挑战吗?退房 面试查询 为你下次带回家做准备。
提前询问期望和未来反馈
你至少应该从招聘人员或招聘经理那里了解三件事:
- 如果有机会的话,除了做带回家的作业,是否还有其他选择。
- 你会得到对你工作的反馈,而不是一封标准化的拒绝邮件。
- 你了解你将被评分的标准和程度。
首先,仔细检查公司是否会给你提供技术筛选,而不是带回家的作业。如果替代方案意味着让候选人退出面试过程,许多公司都会这么做。数据科学现在炙手可热,我的许多朋友告诉我,他们选择上一个雇主是因为他们在没有带回家的情况下,面试过程进行得更快。
不过,这里还有一个检查,这家初创公司是否会向资深候选人提供技术筛选,然后只允许你为同一职位选择数据科学带回家的挑战?他们真的不应该,因为那是违法的。
第二,如果你提前征求反馈,但没有得到回应,或者不明白你将被评分的标准,我会重新考虑面试过程。为什么要经历自由工作的混乱,而没有至少一些反馈,也不知道别人会如何评价你?这就像是没有任何期望地去参加 SAT 考试
我知道很难去反对正在面试你或将要面试你的人或公司。但是要明白这是一种互利的关系。你们两个人都在这个过程中,他们面试你是因为他们想看到你成功,而不是他们希望你做些无脑的工作。
需要一个邮件模板来复制要求两个条件?这里有一个模板,欢迎你使用。
你好,招聘人员姓名,
谢谢你送来带回家的作业。我很高兴可以开始使用它,并且一定会在 X 天内将它和我完成的解决方案一起发送回来。
另外,我想知道是否可以给我一套关于作业评分的通用指南。我肯定要确保我专注并展示了带回家的正确技能,而不是不小心掉进兔子洞。
最后,如果在我提交了带回家的作业后,我能得到一些反馈,我会非常感激,不管我是否会继续面试。了解我做错了什么,或者我在技术成长方面做得好的地方,这真的意义重大。
谢谢!
提问并陈述假设
数据科学的课后作业是一场冗长的单边面试。在一个典型的现场面试中,如果你的面试官是一个好人,你可以问一些关于提示的问题,他们会给出深思熟虑的回答。在数据科学带回家挑战中,你应该做同样的事情。
在接到带回家的任务后,试着立即列出一份你必须发给招聘人员/招聘经理的问题清单。即使你的问题得到了答案或者没有得到答案,也要确保在你的作业中陈述你的假设。我这么说是什么意思?
如果您决定仅使用简单的插补模型来填充缺失值,而不是使用高级技术,该怎么办?陈述一下。写在评论里吧。做一些事情,让他们明白你在这项任务上花费的时间有限。
如果您最终使用 XGboost 而不是 Tensorflow,因为您意识到您可以更快地迭代,即使它在性能上可能不太强大,会怎么样?陈述一下。在你的简历中告诉招聘经理。他们需要理解你为什么选择你所做的模型。
写下你认为需要让你的评分者知道的一切。招聘经理忘记了编写代码和构建模型需要多长时间。他们是经理。他们不写代码。
放入建模基线

让我们假设数据科学的家庭作业更多的是建模挑战,而不是分析师挑战。系统会提示您从数据集创建模型。您直觉地知道,您必须清理、训练和验证数据集之外的模型,但是您能够并且实际上应该做多少呢?
这里有一个大概需要你至少三个小时的清单。
- 数据清理
- 最小特征选择
- 估算缺失值
- 创建分类管道
- 尝试使用几个 sci-kit 学习分类器进行训练
- 使用网格搜索调整超参数
嘣。现在,您的实现将达到他们期望的一般最低基线。根据你在功能选择上工作的时间长短,可能会多加或减两到三个小时。
同样,如果你不希望付出额外的努力,并希望评分者理解这一点,那么就把它写进去。让他们知道。
让你的代码可读
没有什么比看着杂乱的代码更让数据科学招聘经理拒绝你的了。在你完成所有的数据清理和模型构建之后,记得重构你的代码。将你的任务分成不同的功能。不要让混乱的代码排在 main()函数的下面。试着换个角度看问题,或者问朋友他们是否能理解。
对于数据科学家来说,这是一个很好的代码组织和可读性指南。这是关于以一种容易理解的方式组织你的项目。我偶然发现了这个,但它完全有道理。 Cookiecutter 数据科学框架允许数据科学项目的标准化流程。直接取自他们的网站:
- 更轻松地与您合作进行分析
- 从你对过程和领域的分析中学习
- 对分析得出的结论充满信心
我会注意到,用完整的格式来组织你的项目肯定会花费你几个小时以上的时间。但话说回来,当你决定做带回家的作业时,你已经明白了成本。
编写测试和注释
我有没有提到把你脑子里的一切都记录到纸上?这包括写评论和测试你的代码,如果它适用的话。可读性和代码的效率一样重要,如果你在每个函数上都写了很好的注释块,这将有助于传达你的代码应该如何运行,以及你为什么要这样重构它。遵循一般的 Python 惯例来确保你是可靠的。
用 500 字以下总结你的思维过程!
还记得高中英语的时候,所有的论文都是由引言、内容,然后是结论组成,结论重复了引言。做到这一点,但在 500 字以内。在一天结束时,最有可能的情况是,看你带回家作业的人会花总共五分钟的时间来理解它,然后继续浏览 Reddit。你要让他们尽可能容易地理解你的数据科学带回家的作业是有史以来最好的带回家的作业。
感谢阅读!
- 想要顶级科技公司的数据科学面试问题和带回家的挑战吗?查看 面试查询*和我们的* 面试备考包 !
- 订阅 Youtube 上的 我的频道 获取数据科学模拟面试,指南,以及技巧和窍门。
- 查看我的数据科学面试终极指南以我自己的经验
我是如何通过谷歌云专业数据工程师认证考试的
没有推荐的 3 年实践经验

Google hoodie: on. Game face: on. Photo from the video version of this article on YouTube.
注: 本文献给 2019 年 3 月 29 日前的谷歌云专业数据工程师认证考试。在这个日期之后,有一些更新。我已经将这些包含在 临时演员 部分
所以你想买一件和我在封面照片里的一样的新帽衫?
或者你一直在考虑获得谷歌云专业数据工程师认证,你想知道如何去做。
在过去的几个月里,我一直在使用谷歌云参加课程,为专业数据工程师考试做准备。然后我就拿了。我通过了。几周后,我的连帽衫到了。证书来得更快。
这篇文章将列出一些你可能想知道的事情,以及我获得谷歌云专业数据工程师认证的步骤。
你为什么想做谷歌云专业数据工程师认证?
数据无处不在。人们需要知道如何建立能够处理和利用数据的系统。谷歌云提供了构建这些系统的基础设施。
你可能已经掌握了使用谷歌云的技能,但你如何向未来的雇主或客户展示这一点呢?两种方式。通过项目组合或认证。
一份证书对未来的客户和雇主说,“嘿,我有技能,我已经努力获得认证。”
谷歌的一句话总结了这一点。
展示您在谷歌云平台上设计和构建数据处理系统以及创建机器学习模型的熟练程度。
如果你还不具备这些技能,浏览认证的学习材料意味着你将学习如何在 Google Cloud 上构建世界一流的数据处理系统。
谁会想做谷歌云专业数据工程师认证?
你已经看过数据了。云在增长。它会一直留在这里。如果你还没有看到这些数字,请相信云正在增长。
如果你已经是一名数据科学家、数据工程师、数据分析师、机器学习工程师或正在寻找数据领域的职业转变,谷歌云专业数据工程师认证适合你。
能够使用云技术正成为任何一种以数据为中心的角色的要求。
做一个好的数据工程师/数据科学家/机器学习工程师需要证书吗?
号码
没有证书,你仍然可以使用谷歌云来处理数据解决方案。
证书只是现有技能的一种验证方法。
这要花多少钱?
参加认证考试需要 200 美元。如果你失败了,你将不得不再次支付重考费用。
准备课程和使用平台本身都有相关的成本。
平台成本是你使用谷歌云服务的费用。如果你是一个狂热的用户,你会很清楚这些。如果没有,并且您只是浏览了本文中的培训材料,您可以创建一个新的 Google Cloud 帐户,并在 Google 注册时提供的 300 美元积分内完成所有这些内容。
我们马上会谈到课程成本。
认证持续多长时间?
两年。之后,你需要再次参加考试。
由于 Google Cloud 每天都在发展,证书的要求很可能已经改变了(当我开始写这篇文章时,我发现情况就是这样)。
考试需要准备什么?
谷歌推荐 3 年以上的行业经验和 1 年以上使用 GCP 设计和管理解决方案的专业水平认证。
这两样我都没有。
更像是 6 个月一次。
为了补充这一点,我利用了在线培训资源的组合。
我学了什么课程?
如果你和我一样,没有推荐的要求,你可能想看看下面的一些课程来提升自己的技能。
以下课程是我用来准备认证的。它们是按照完成的顺序排列的。
我列出了通过认证考试的成本、时间表和帮助。

Some of the incredible online learning resources I used to upskill myself for the exam. In order, A Cloud Guru, Linux Academy, Coursera.
谷歌云平台上的数据工程 Cousera 上的专业化
费用: $49 美元每月(7 天免费试用后)
时间:1-2 个月,每周 10+小时
有用性: 8/10
Coursera 上的谷歌云平台数据工程是与谷歌云合作完成的。
它分为五个子课程,每个子课程每周花费大约 10 个小时的学习时间。
如果你不熟悉谷歌云上的数据处理,这种专业化就像是 0 对 1。您将使用名为 QwikLabs 的迭代平台完成一系列实践练习。在此之前,将由谷歌云从业者就如何使用不同的服务,如谷歌 BigQuery,Cloud Dataproc,Dataflow 和 Bigtable 进行讲座。
云专家介绍谷歌云平台
费用:免费
时间:1 周,4-6 小时
有用性: 4/10
不要把有用性分数低当成这门课没用。远非如此。它得分较低的唯一原因是它没有专注于专业数据工程师认证(这可以从标题中收集到)。
在完成 Coursera 专业化认证后,我将此作为复习,因为我只在少数专业用例中使用过谷歌云。
如果你来自另一家云服务提供商,或者以前从未使用过谷歌云,你可能想参加这个课程。这是对谷歌云平台整体的一个很好的介绍。
Linux Academy 谷歌认证专业数据工程师
费用: $49 美元每月(7 天免费试用后)
时间:1-4 周,每周 4 小时以上
有用性: 10/10
完成考试后,回想我上过的课程,Linux Academy Google 认证专业数据工程师是最有帮助的。
这些视频,以及数据档案电子书(课程附带的一个很棒的免费学习资源)和练习考试使这门课程成为我用过的最好的学习资源之一。
考试结束后,我甚至在一些笔记中向团队推荐了它。
Slack Notes
考试中的一些内容是 Linux Academy 或云专家或 Google Cloud Practice 考试中没有的(预期)
1 个问题,其中包含数据点的图表以及对它们进行聚类所需的等式(例如 cos(X)或 X +Y )
了解数据流、Dataproc、数据存储、Bigtable、BigQuery、 Pub/Sub 以及如何使用它们是必须的
。考试中的两个案例研究与练习中的案例研究完全相同,尽管我在考试中根本没有阅读这些研究(问题提供了足够的洞察力)
了解一些基本的 SQL 查询语法非常有帮助,尤其是对于 BigQuery 问题
。Linux Academy 和 GCP 提供的练习考试与考试中的问题风格非常相似。 我会多次这样做,并使用它们来找出你的弱点
一点押韵来帮助 data proc:“data proc鳄鱼和 Hadoop 大象计划点燃一把火,煮一个蜂巢的猪”(data proc 处理 Hadoop,Spark, 蜂巢和猪)
“数据流是流动的光束(数据流处理阿帕奇光束)
“全世界的每个人都能联想到一把制作精良** 酸 洗过的扳手。” (Cloud Spanner 是一个完全为云设计的数据库,它符合 ACID 标准,全球可用)
。很容易知道关系和非关系数据库选项的名称(例如 MongoDB、Cassandra)
。IAM 角色对于每个服务略有不同,但了解如何将用户与设计工作流区分开来是有帮助的(例如数据流工作者角色可以设计工作流,但看不到数据)**这对现在来说可能足够了。每次考试的里程可能会有所不同。Linux 学院的课程将提供 80%的知识。
谷歌云 1 分钟视频
****费用:免费
时间:1–2 小时
有用性: 5/10
这些都是在云专家论坛上推荐的。其中许多与专业数据工程师认证无关,但我挑选了一些我认可的。
在学习一门课程时,有些服务可能看起来很复杂,所以在一分钟内听到对某项服务的描述是很好的。
备考云专业数据工程师考试
费用: $49 认证或免费(无认证)
时间表:1-2 周,每周 6 小时以上
有用性:不适用
我在考试前一天发现了这个资源。由于时间限制,我没有这样做,因此缺乏有用性评级。
然而,在浏览课程概述页面后,它看起来像是一个很好的资源,可以将你在 Google Cloud 上学习的所有关于数据工程的东西汇集在一起,并突出任何弱点。
我把这个课程作为资源发给了我的一个准备认证的同事。
谷歌数据工程备忘单作者特立独行的林
****费用:免费
时间线:不适用
有用性:不适用
这是我在考试后偶然发现的另一个资源。我看了一下,它既全面又简洁。另外,它是免费的。这可以用来在练习考试之间或者甚至在认证之后提醒你自己。
获得李圣杰的谷歌云认证
****费用:每门课程 39 美元(共 3 门课程 49 美元)
时间表:自定进度
有用性:不适用
Sam 是一名大数据工程师和 web 开发人员,他将自己的 Google Cloud 知识融入到课程中。有三种不同的课程,包括专业云架构师、专业数据工程师和助理云工程师。这是您需要的所有谷歌云认证的一站式商店。
课后我做了什么?
在接近完成课程后,我提前一周预约了考试。
有一个截止日期是复习你所学内容的一个巨大动力。
我多次通过 Linux Academy 和 Google Cloud 的模拟考试,直到我每次都能以 95%以上的准确率完成它们。

Passing the Linux Academy practice exam with over 90% for the first time.
每个平台的测验都是相似的,但我发现复习我经常出错的答案,并写下为什么出错,有助于修复我的弱点。
我参加的考试使用了在 Google Cloud 上为两个案例研究设计数据处理系统作为主题(这是从 2019 年 3 月 29 日开始更改的)。从头到尾都是多项选择。
我花了大约两个小时。比我参加的任何模拟考试都要难 20%。
我怎么强调模拟考试的价值都不为过。
如果再来一次,我会改变什么?
多练习考试。更实用的知识。
当然,你总是可以做更多的准备。
建议要求列出了使用 GCP 3 年以上的时间。但是我没有这个,所以我必须处理我所拥有的。
临时演员
考试更新于 3 月 29 日。本文中的材料仍将为您提供一个良好的基础,但是,注意一些变化是很重要的。
谷歌云专业数据工程师考试的不同部分(版本 1 )
1.设计数据处理系统
2。建立和维护数据结构和数据库
3。分析数据和实现机器学习
4。为分析和优化建立业务流程模型
5。确保可靠性
6。可视化数据和倡导政策
7。安全性和合规性设计
谷歌云专业数据工程师考试的不同部分(版本 2 )
1.设计数据处理系统。建立和运行数据处理系统
3。操作化机器学习模型(大部分变化都发生在这里)【新增】**
4。确保解决方案质量**
版本 2 将版本 1 的第 1、2、4 和 6 部分合并为 1 和 2。它还将第 1 版的第 5 节和第 7 节合并为第 4 节。第二版的第三部分已经扩展到包括谷歌云的所有新的机器学习功能。
因为这些变化是最近才发生的,所以许多培训材料还没有机会更新。
然而,浏览本文中的材料应该足以涵盖您所需内容的 70%。我会将它与你自己对以下内容的一些研究结合起来(这些在考试的第二版中有介绍)。
- 谷歌机器学习(ML)API
- 谷歌云机器学习引擎
- 谷歌云 TPUs (谷歌专门为人工智能培训打造的定制硬件)
- 谷歌 ML 术语表
正如你所看到的,考试的最新更新非常关注谷歌云的 ML 功能。
更新 29/04/2019: 来自 Linux 学院课程导师马修·乌拉森(Matthew Ulasien)的一段话。
仅供参考,我们计划更新 Linux Academy 上的数据工程师课程,以反映五月中/下旬开始的新目标。
*****更新 2019 年 1 月 6 日:*Linux Academy 课程导师马修·乌拉森的又一条消息。
我们现在正处于计划阶段。我估计要花一个月的时间来完全更新它。
考试后
当你完成考试时,你只会得到及格或不及格的结果。建议是至少要达到 70%,这就是为什么我在模拟考试中至少要达到 90%。
一旦你通过了测试,你将会收到一封电子邮件,里面有一个兑换码和你的官方谷歌云专业数据工程师证书。恭喜你!
你可以在一个专门的谷歌云专业数据工程师商店使用兑换代码,那里有很多赠品。有 t 恤,背包,帽衫(这些到了库存可能会有变化)。我选择了帽衫。
现在你已经通过了认证,你可以(正式地)展示你的技能,然后继续做你最擅长的事情,建筑。
两年后再见,重新认证。
PS 如果你有任何问题,或者想要澄清什么,你可以在 Twitter 和 LinkedIn 上找到我。在 YouTube 上也有这篇文章的视频版本。
PPS 非常感谢所有在上述课程中出色的讲师,以及 Max Kelsen 为学习和准备考试提供的资源和时间。
纵向生存数据的患者治疗时间表
我是一所研究型大学的生物统计学家,我经常发现自己在处理纵向生存数据。与任何数据分析一样,我需要检查我的数据的质量,然后再决定采用哪种统计方法。
这篇文章包含了一些可重复的例子,展示了我如何更喜欢直观地探索包含纵向暴露或协变量的生存数据。我为每个病人创建了一个“治疗时间表”,最终产品看起来像这样:

每条线代表一名患者,每个方块代表特定点的测量值。我发现这些图表有助于寻找随访时间、治疗和遗漏的模式或差异。它们还允许我在为我的分析进行不同的数据操作时验证我的编码是正确的。
对于下面的例子,我生成纵向生存数据集。我跳过了我是如何制作这些数据的,因为这不是这篇文章的重点,但是如果你对数据生成有疑问,请告诉我。总的来说,我试图从生存数据分析的角度出发,你已经计算了每个人的事件发生时间。
为了制作这些图表,我的第一步是加载包tidyverse,因为我使用了来自dplyr、tidyr、forcats和ggplot2的函数。
library(tidyverse)
下面是数据生成代码。如果你对我是如何制作的感兴趣,你可以查看评论,但如果不是,我建议你复制并粘贴到你的 **R** 控制台来尝试绘图代码。
set.seed(7)
# The data sets I make these visualizations for are typically 100-500 patients in size, but for space purposes I'll set my n to only 50.
n <- 50dat <-
tibble(.rows = n) %>% # empty data frame / tibble with n rows
mutate(pt_id = factor(row_number()), # patient ids are 1-n
pt_trt_prob = runif(n,0,1), # randomly generate a treatment probability for each patient so we can see patterns
months_followup = round(runif(n, 0, 20)), # randomly generate length of time in the study
death = rbinom(n, 1, .5)) %>% # death randomly occurs at any time point. Obviously an unrealistic assumption. :)
group_by(pt_id) %>% # group by patient so we can do more data manipulation
complete(months_followup = full_seq(0:max(months_followup), 1)) %>% # add in all the months patients are in the study
fill(pt_trt_prob, .direction = "up") %>% # fill in the treatment probability I made earlier so I can use this to add treatment for every time point
ungroup() %>% # no longer need patients grouped
mutate(trt = factor(rbinom(row_number(), 1, pt_trt_prob^2)), # fill in treatment for everyone based on their treatment probability
death = replace_na(death, 0)) %>% # also fill in death
select(pt_id, months_followup, trt, death) # remove leftover columns from data generation
我们来看数据。它是“长”格式的,在我的(假)研究中,每个患者的 ID 都是重复的。在每个月,我们都知道他们是否在接受治疗,以及他们是否在那个时间点死亡。第一个例子没有任何遗漏。
knitr::kable(head(dat))

我们现在可以用一种非常基本的方式来绘制我们的数据。我们真的只需要在aes thetics 中指定x-轴是时间,y-轴是主题 id,col or 应该对应治疗,我们的线应该按照主题与 *group* 连接在一起。别忘了最后一个!然后我们可以说我们希望geom_line为每个主题制作一个基本时间轴,时间轴上的点应该是正方形(geom_point映射函数中的shape = 15)。
瞧啊!一个可爱的病人治疗时间表。
dat %>% ggplot(aes(x = months_followup, y = pt_id, group = pt_id, col = trt)) + geom_line() + geom_point(shape = 15)

好吧,不是那个可爱。但是那一点点代码真的是剧情的核心!如果你想添加死亡(或任何其他事件)的标记,并根据随访时间的长度重新排列,只需要一点额外的数据操作和一行额外的ggplot2代码。
dat %>%
group_by(pt_id) %>% # make a new column with all patients last follow up visit
mutate(last_month_followup = max(months_followup), # new variable for month that patients died, if they died
month_death = case_when(death == 1 ~ last_month_followup, TRUE ~ NA_real_)) %>% # reorder pt id by last month of follow up (highest to lowest)
# without fct_rev, chart is arranged in opposite direction (lowest to highest)
ungroup() %>%
mutate(pt_id = fct_rev(fct_reorder(pt_id, last_month_followup))) %>%
ggplot(aes(x = months_followup, y = pt_id, group = pt_id, col = trt)) +
geom_line() +
geom_point(shape = 15) + # add in a new mapping layer of points that correspond to death
geom_point(aes(x = month_death, y = pt_id), col = "black", shape = 4)## Warning: Removed 515 rows containing missing values (geom_point).

不要担心删除缺失值的警告—这是因为我们的month_death最多有NA个月,所以geom_point不知道在哪里放一个‘x’标记。
最后,让我们努力让情节看起来更好一点:
dat %>%
group_by(pt_id) %>%
mutate(last_month_followup = max(months_followup), month_death = case_when(death == 1 ~ last_month_followup, TRUE ~ NA_real_)) %>%
ungroup() %>%
mutate(pt_id = fct_rev(fct_reorder(pt_id, last_month_followup)), # make the treatment variable labels nicer
trt = factor(trt, levels=0:1, labels=c("No","Yes"))) %>%
ggplot(aes(x = months_followup, y = pt_id, group = pt_id, col = trt)) +
geom_line() +
geom_point(shape = 15) +
geom_point(aes(x = month_death, y = pt_id), col = "black", shape = 4) +
theme_bw() +
labs(x = "Months of Follow-Up", y = "Patient ID", col = "Treatment", title = "Patient Treatment Timeline", subtitle = "x indicates month of patient death") +
# edit legend box and make patient ids small
theme(axis.text.y = element_text(size=6), legend.position = c(.6, .9), legend.direction = "horizontal", legend.background = element_rect(linetype="solid", colour ="black")) +
# remove extra space around timeline
scale_x_continuous(expand=c(0.01,0.01)) + # set the color of the lines and points
scale_color_manual(values=c("dodgerblue","firebrick1"))## Warning: Removed 515 rows containing missing values (geom_point).

我从来没有制作过这些情节中的一个用于出版,所以我不介意字幕包含图例信息。如果您希望图例中有month_death标记,您可以更改:
geom_point(aes(x = month_death, y = pt_id), col = "black", shape = 4)
收件人:
geom_point(aes(x = month_death, y = pt_id, shape = month_death), col = "black")
我们可以按照同样的步骤,为一个连续变量制作时间线。
同样,这是数据生成代码,因此您可以自己绘制这些图:
dat_cc <-
tibble(.rows = n) %>%
mutate(pt_id = row_number(),
months_followup = round(runif(n, 0, 12)), # random months of follow up
pt_cov_mean = runif(n, 80, 150), # random mean of a patient's covariate measures
death = rbinom(n, 1, 0.5)) %>% # random death
group_by(pt_id) %>%
complete(months_followup = full_seq(0:max(months_followup), 1)) %>%
fill(pt_cov_mean, death, .direction = "up") %>%
mutate(last_month_followup = max(months_followup),
death_date = case_when(death == 1 ~ last_month_followup,
TRUE ~ NA_real_)) %>%
ungroup() %>%
mutate(cov = rnorm(row_number(),
pt_cov_mean, 10)) # everyone's covariates are close to their original mean (use to see patterns later)
这一次,我在我的模拟协变量测量中纳入了患者水平的遗漏,以显示我们如何使用这些图来查看遗漏的潜在模式。
dat_cc_samp <-
dat_cc %>%
mutate(idx = row_number()) %>%
sample_frac(.4, weight = pt_cov_mean^3) %>%
# sample 40% of data, with weights for the sample determined by the patient's mean covariate. This would mean patients with a higher mean covariate measure are more likely to have missing data.
pull(idx)dat_cc_miss <-
dat_cc %>%
mutate(cov = case_when(row_number() %in% dat_cc_samp ~ NA_real_,
TRUE ~ cov)) %>%
select(pt_id, months_followup, cov, death)
再次查看我们将使用的数据:
knitr::kable(head(dat_cc_miss))

我们可以绘制数据,并用灰色的时间线点和线来观察这种缺失:
dat_cc_miss %>%
group_by(pt_id) %>%
mutate(last_month_followup = max(months_followup),
month_death = case_when(death == 1 ~ last_month_followup,
TRUE ~ NA_real_)) %>%
ungroup() %>%
mutate(pt_id = fct_rev(fct_reorder(factor(pt_id), last_month_followup))) %>%
ggplot(aes(x = months_followup, y = pt_id, group = pt_id, col = cov)) +
geom_line() +
geom_point(shape = 15) +
geom_point(aes(x = month_death, y = pt_id), shape=4, col="black") + theme_bw() +
labs(x = "Months of Follow-Up", y = "Patient ID", col = "Covariate", title = "Patient Timelines: Continuous Covariate", subtitle = "x indicates month of patient death, gray indicates missing covariate") +
theme(axis.text.y = element_text(size=6), legend.position = c(.7, .9), legend.direction = "horizontal", legend.background = element_rect(linetype="solid", colour ="black")) +
scale_x_continuous(expand=c(0.01,0.01)) +
scale_color_gradient(low="dodgerblue", high="firebrick1", na.value = "lightgray", breaks=c(90, 120, 150, 180))## Warning: Removed 143 rows containing missing values (geom_point).

或者,我们可以通过创建一个包含sum(!is.na(cov))的新列,并根据该列重新排列我们的pt_id,根据我们对每个患者的cov变量的测量数量重新排列我们的时间线。
dat_cc_miss %>%
group_by(pt_id) %>%
mutate(last_month_followup = max(months_followup), # a column containing how many measures we have for each patient
n_measures = sum(!is.na(cov))) %>%
ungroup() %>%
mutate(pt_id = fct_rev(fct_reorder(factor(pt_id), n_measures)), # reorder IDs by number of measures we have for each patient
month_death = case_when(death == 1 ~ last_month_followup,
TRUE ~ NA_real_)) %>%
ggplot(aes(x = months_followup, y = pt_id, group = pt_id, col = cov)) +
geom_line() +
geom_point(shape = 15) +
geom_point(aes(x = month_death, y = pt_id), shape=4, col="black") +
theme_bw() +
labs(x = "Months of Follow-Up", y = "Patient ID", col = "Covariate", title = "Patient Timelines: Continuous Covariate", subtitle = "x indicates month of patient death, gray indicates missing covariate") +
theme(axis.text.y = element_text(size=6),
legend.position = c(.7, .9),
legend.direction = "horizontal",
legend.background = element_rect(linetype="solid", colour ="black")) +
scale_x_continuous(expand=c(0.01,0.01)) +
scale_color_gradient(low="dodgerblue",high="firebrick1",na.value = "lightgray", breaks=c(90, 120, 150, 180))## Warning: Removed 143 rows containing missing values (geom_point).

从该图中我们可以看出,缺失测量值较多的患者具有较高的协变量测量值。如果我们按照非缺失测量值的比例重新排列患者,这一点会变得更加清楚。
dat_cc_miss %>%
group_by(pt_id) %>%
mutate(last_month_followup = max(months_followup), # the proportion is the total number of measures divided by the total months of followup
prop_measures = sum(!is.na(cov))/last_month_followup) %>%
ungroup() %>%
# reorder IDs by proportion of measures we have for each patient
mutate(pt_id = fct_rev(fct_reorder(factor(pt_id), prop_measures)),
month_death = case_when(death == 1 ~ last_month_followup,
TRUE ~ NA_real_)) %>%
ggplot(aes(x = months_followup, y = pt_id, group = pt_id, col = cov)) +
geom_line() +
geom_point(shape = 15) +
geom_point(aes(x = month_death, y = pt_id), shape=4, col="black") +
theme_bw() +
labs(x = "Months of Follow-Up", y = "Patient ID", col = "Covariate", title = "Patient Timelines: Continuous Covariate", subtitle = "x indicates month of patient death, gray indicates missing covariate") +
theme(axis.text.y = element_text(size=6), # move legend to the side by removing legend location
legend.background = element_rect(linetype="solid", colour ="black")) +
scale_x_continuous(expand=c(0.01,0.01)) +
scale_color_gradient(low="dodgerblue",high="firebrick1",na.value = "lightgray", breaks=c(90, 120, 150, 180))## Warning: Removed 143 rows containing missing values (geom_point).

如果这是我的真实数据,我会和我的研究合作者一起跟进,以获得更多关于遗漏机制的信息。
如果您对数据集有任何问题或建议,请告诉我。快乐治疗时间表绘制!
原载于 2019 年 11 月 29 日【https://www.khstats.com】。
犯罪模式
对过去三年警察数据的分析
犯罪是许多人每天都担心的事情。无论是确保出门时锁好门,还是避开不安定的社区,或是安装安全警报,预防犯罪占据了我们生活的重要部分。尽管事实上在过去的 25 年里,英国的犯罪率几乎一直在下降。
在这篇文章中,我将调查由英国警方提供的公开数据集,以揭示任何有趣或令人惊讶的模式、趋势或异常现象。
这里使用的数据可以从 data.police.uk 网站上公开获得,包含了从 2016 年 10 月到 2019 年 9 月英格兰 43 个地理警察部队中的超过 1900 万报告的 个人犯罪和反社会行为、威尔士& NI(加上两支特种部队:英国交通警察和司法部)的信息。这包括关于地点、日期、犯罪类型和最新调查状况的信息。和我的文章一样,分析和可视化是用 r。
罪恶的循环
可视化这些数据的最简单方法是查看一段时间内犯罪的绝对数量:

Graph showing the number of reported crimes each month across all police forces in the UK from October 2016 to September 2019.
- 显而易见,这里存在某种季节性趋势,犯罪数量在冬季月份下降,在年中上升到最高点-最高值出现在 2018 年 7 月(全英国报告的犯罪数量为 621,005 起)。
为了更清楚地显示这种循环模式,我们可以将每年的图表叠加起来,跨日历月显示。所有 45 支部队每个日历月报告的犯罪总数见下图。

The total number of reported crimes by calendar month and year from Oct 2016 to Sep 2019.
- 犯罪水平的这种季节性趋势是一个有据可查的现象,自 19 世纪以来一直是犯罪学家研究和辩论的来源。然而,对于为什么会出现这种情况,仍然没有一致的意见,因为季节性的确切特征取决于你所研究的犯罪、地点和时间段。在这里,季节性遵循日历,夏季报告的犯罪数量更多,但情况并非总是如此。
- 这种模式没有单一的原因,简单的解释如天气已经被证明是错误的。然而,温度/天气很可能是环境和社会影响之间复杂相互作用的一部分,从而导致这种季节性。
导致这种模式的一些因素包括:
- 事实上,在夏季的几个月里,会有更多的人出国(这意味着入室盗窃更有可能/可行)。
- 一个潜在的循环:高犯罪率导致警察“镇压”——导致犯罪减少——随后资源撤出——导致更高的犯罪率,等等。
- 还有证据表明,高于平均温度会增加人类攻击性的可能性。
各种违法行为
上面的这些图表掩盖了不同类型犯罪之间的差异。在英国,每起已报告的犯罪都被归为 14 类中的一类,如“入室盗窃”、“毒品”或“暴力和性犯罪”。类别和解释的完整列表在的中。
下图显示了各类犯罪数量随时间的变化情况。
请注意,为了使图表更容易阅读,我将犯罪数量最少的六个类别(“毒品”、“其他犯罪”、“抢劫”、“持有武器”、“盗窃他人财物”和“盗窃自行车”)合并为一个“其他”类别。这不能与“其他盗窃”相混淆,其他盗窃包括员工盗窃、勒索和不付款就走。

Number of reported crimes over time, by type of crime. From Oct 2016 to Sep 2019.
- 两种最常见的犯罪类型是“反社会行为”和“暴力和性犯罪”。
- 2016 年 10 月以来,ASB 不断下跌,VSO 不断增加。
- ASB 似乎非常接近前面描述的季节性趋势。这是有道理的,因为它往往涉及到人们在外面(13 种 ASB 中只有 2 种与近邻有关,即大多数只能通过离开房子来犯下),所以可能会随着温度的变化而发生很大变化。这也可以从本质上与 ASB 非常相似的“公共秩序”罪中看出。
- 另一方面,VSO 完全不遵循周期性模式。VSO 可以(而且经常)在家庭或室内进行,所以温度不会有太大的影响。
当地违法行为
所提供的数据还提供了所报告犯罪的地理细节,包括 LSOA(“低层超级输出区”)。出于统计报告的目的,英国在地理上被划分为 34,000 多个地方自治市。它们大小不一,较大的区域在农村地区,较小的集水区在城市中心。下面你可以看到这些在英格兰和威尔士是如何划分的。

Map of the 34,700 Lower Layer Super Output Areas (LSOAs) in England & Wales as of 2016.
你可以在一些地方看到,LSOAs 是如此之小,以至于它看起来是一个深色的块。这无意中导致了人口密度的热图——因为在人口密集的地区,人口密度往往更小。
我们可以通过在图中包含 LSOA 的人口密度来验证这一点。下图显示了每个 LSOA 的人口密度(每平方公里的人口数),颜色越深,人口越密集。

Map showing the population density of LSOAs across England and Wales. Darker colours indicate a higher population density, measured as the number of people per square kilometre. The log value of population per sq. km. is used for readability.
在这里,我们可以看到英格兰和威尔士的大城市的位置,以及该国人口最稀少的地区。
在我们的警方数据中,98%的犯罪都与 LSOA 相关联,因此我现在可以将人口和犯罪的数据集结合起来,以可视化每个 LSOA 的人均犯罪水平。生成的地图如下所示。

Map showing the average number of crimes per person per month in the three years leading up to September 2019.
很明显,这张地图并不像我们希望的那样有用。有少量的地方自治市的犯罪率极高,而且这些地方都位于全国的城市中心(这些地方也很小,所以很难在地图上找到)。
要了解这种差异有多大,请考虑以下情况:
- 在 LSOA,人均每月犯罪数量的中位数是 0.006 起犯罪。
- 90%的 LSOAs 的人均月犯罪率低于 0.0155 起。
- 最大值比中值 高 82 倍 ,比 90%的 LSOAs 高 34 倍 。这是在伦敦威斯敏斯特市中心,平均每人每月 0.532 起犯罪。
英格兰和威尔士犯罪率最高的 10 个地方行政区位于伦敦、曼彻斯特、伯明翰、纽卡斯尔、加的夫和格洛斯特,这些都是英国最大的城市(格洛斯特和加的夫除外)。
城市遭受更高的犯罪率也许不足为奇——这是有据可查的事实——但这将是另一篇博文的主题。我使用的数据包含了每起已报告犯罪的大致坐标,因此可以在英国一些最大的城市中绘制一些有趣的地图。
时髦的重罪
为了将本文的所有主题联系在一起,最后要看的是调查全国范围内最常见的型型 型犯罪是否存在差异,以及是否有任何可见的模式。
再看下面一张 LSOAs 的地图,每张地图都根据 14 种犯罪中哪一种最常见来着色。

Map showing the most commonly reported crime for each LSOA in the three years leading up to September 2019.
正如本文前面所讨论的,两种最广泛的犯罪类型(“反社会行为”和“暴力和性犯罪”)远比其他类型更常见,这反映在上面的地图中,它们淹没了其他类型的比较。
为了更深入地挖掘,我们可以从分析中移除这些类型的犯罪,并检查剩余的数据。

Map showing the most common type of reported crime (excluding ‘Anti-social behaviour’ and ‘Violence and sexual offences crimes’) in the three years leading up to September 2019.
这里可以得出一些更有趣的结论:
- 如果你将这张地图与之前的人口密度地图进行比较,你会发现人口密度与车辆失窃之间有很强的相关性——城镇中的汽车比农村地区更容易被盗。
- 离开市中心稍微远一点,在建筑密集区的郊区,T2 的入室盗窃似乎很常见。在伦敦周围,中部和西北部的城市都能看到。
- 这与非常偏远的地区形成了鲜明的对比,在那里刑事破坏和公共秩序犯罪似乎是最常见的犯罪。
- 地图上为数不多的几个彩色点之一显示了威尔士的毒品犯罪报告。这与威尔士目前正在打击的毒品流行率的上升相对应,特别是在加的夫和南海岸。
- 在东南海岸地图右下方的多佛可以看到另一种颜色的下降。这是通过汽车或火车从英国到欧洲其他地方的门户。拥有武器的高犯罪率清楚地表明有人试图通过欧洲向该国走私武器。
感谢阅读
如果你喜欢,那么请分享这个,关注并给数据片一个关注,以保持最新的文章!
正如我提到的,将会有一篇后续文章,在那里我将可视化城市中的犯罪,并回答一些问题,如:犯罪在住宅区、工业区还是零售园区更常见?还有警察局的存在对当地的犯罪有影响吗?我还将调查对犯罪的调查有多成功,比较不同地点、犯罪类型和一年中不同时间的定罪率。如果你有任何其他的建议或细节需要我分析,请不要犹豫,在下面评论或发电子邮件给 editor@data-slice.co.uk。
为谷歌铺路!
我从许多数据科学访谈中学到的经验

Photo by Paweł Czerwiński on Unsplash
有一个时候你应该换一份工作,不管这个文化有多牛逼,这个公司有多神奇,那就是你停止成长的时候。
当我的时间到了,我采访了东西海岸的许多组织。从金融机构到科技巨头,从纽约和华盛顿特区到西雅图和旧金山,我乐于接受新的冒险。与此同时,在每一步和每一次面试结束时,我都在思考,作为一名候选人,我和面试团队本可以做得更好。
你将要读到的并不是如何找到下一份工作的路线图,而更像是我对整个过程的回忆。
我要讲的大部分内容已经在 LinkedIn 上发表了。我知道很多人已经在经历或即将开始我几个月前刚走完的路,我希望我关于寻找合适的公司、处理拒绝、准备面试等的笔记。让他们的旅程比我的更精彩。
以下是我讨论的主题列表:
- 这不是容易但它是值得它。
- 求职与网上约会
- 我评价一个职位的标准!
- 你如何帮助公司找到你?
- 终极面试技巧:忘记解决方案,建立系统!
- 成为数据科学家的技能:我的看法
- 我准备技术面试的系统
- 非技术性面试技巧
- 致我所有经历拒绝的朋友们!
- 排斥后处方
- 想被录用吗?努力提高你的技能!想要升职吗?学习机构知识!
- 给面试官:在候选人的舒适区见他们!
不容易但是值得它!
招聘人员的电子邮件、介绍电话、技术问题、周末项目、现场参观、拒绝信、谈判等等似乎是一个永无止境的过程;一个我反复重复的循环。
这个过程令人筋疲力尽,但最后,至少对我来说,是值得的,值得努力。我学到了很多东西,坦率地说,在这个过程中,我成为了一名更好的数据科学家。采访不同行业、不同规模和不同地点的公司,讨论他们想要雇佣你的各种令人兴奋的问题,是令人大开眼界的。每次面试都让我对需求、挑战和期望有了更好的认识,从而帮助我为下一次面试做准备。
求职 vs 网恋!
在我看来
找工作很像网上约会;单身的人很多,但匹配的人太少。你的工作不是和第一个和你约会的人结婚,而是找到一个有共同兴趣和长远前景的人。
我评价一个职位的标准!
你挑选未来组织的标准是什么?品牌、地点、薪酬待遇、工作描述、你将参与的项目,甚至是津贴和福利以及弹性工作时间;哪个因素最重要?如果你在不同的公司有多种选择,你会选择什么,为什么?以下是我的观点:
- 很少有人能对你的生活质量产生长期影响:你的生活伴侣、你的顾问、经理和你的同事。确保你明智地选择它们,这能让你的生活更加有趣和充实!
说到事业,我从来不后悔优先考虑人而不是项目/公司/大学。他们可以成就或毁灭你和你的未来;他们可以鼓励/阻止你前进的每一步;它们会成为你个人或职业成长的主要力量,或者成为你实现目标的主要障碍。
我的下一次职业生涯也不例外。试图找到合适的团队加入是当务之急。 - 职责
通过与招聘团队的交谈,我想知道我是否对这个角色和职责感兴趣。有一次我面试了一家票务领域的公司,那里的整个团队都对体育赛事充满热情。对于不喜欢运动的我来说,这显然不是一个正确的选择。
不要仅仅依赖职位描述,因为它只会给你一个关于这个角色的想法。尤其是对于那些在招聘数据科学家方面经验不足的公司来说,工作描述中的要求对于他们招聘的职位来说可能不是必需的。 - 成长机会
未来的公司是否鼓励学习/提高技能并为其提供工具?如果我想转岗,我能在同一个组织中找到新的职业机会吗?我能很容易地找到并联系到导师吗? - 职业道路
我所考虑的职位与我未来 3-5 年的目标有多一致。我用这个原则拒绝了几个在经济上有意义但与我的长期目标不一致的提议。 - 薪酬方案
现在很重要,将来也永远重要。但不是最重要的因素。金钱和财务健康是努力工作和影响力的结果,如果你找到一个鼓励学习、支持成长和奖励努力工作的组织,你就不用担心其他的了!
如何帮助公司找到你?
考虑以下事实:
- 2019 年 5 月开业 730 万,
- 由于科技的发展,任何人只需点击一下鼠标就可以申请任何职位,
- 然而,63%的招聘人员认为人才短缺是他们最大的问题。
这就是为什么你需要成为自己的倡导者&帮助公司将信号从噪音中分离出来。事实是,没有人比你更了解你的目标和抱负,在你的简历上列出一系列的技能和项目是不公平的。
如果你想增加被合适的职位和合适的公司聘用的机会,这里有一些方法可以尝试:
- 你的 LinkedIn 个人资料就像你的储蓄账户。即使你睡着了,它也对你有效。保持更新,使用正确的关键词,让访问者容易找到你的相关信息。记住,87%的招聘人员使用 LinkedIn 来检查候选人。
- 你的关系网
他们是你的关系网,这是有原因的。一个引荐可以让你的机会增加 5 倍,所以给他们发信息,恭敬地请求引荐、指导、建议等。每个连接尝试两次后停止。你有更重要的事情要做。 - 冷邮件:
我是这样做的:在 LinkedIn 上找到&申请合适的职位。如果招聘人员列在工作页面上,我会给他们发一条消息(评论中的示例)。
我用来发冷邮件的一封信的样本:
你好[…],
在阅读[…]的职位描述时,我发现我目前的职责和职位描述有很大的重叠。请允许我详细说明:
在过去的几年里,我
—在[…]、
工作过—在[…]、
—定义和衡量产品指标、
—熟悉[…]
—训练有素的机器学习模型、
—准备[…]分析、
—与业务团队广泛合作、
—构建分析仪表板。
我申请了这个职位[工作 ID 在这里],我希望你能考虑我的申请。
最佳
你的名字,
目前的头衔
终极面试技巧:忘掉解决方案,建立系统!
秘诀是要意识到,在大多数面试中,应聘者和面试官寻求的是两种不同的东西:当应聘者专注于解决方案时,面试官寻求的是系统。前者依赖于技术能力,而后者是关于你建立的展示它们的过程。
这个系统是一个循序渐进的过程,你用它来帮助面试官理解你评估和解决问题的方法,并传达你的想法和观点。她感兴趣的不仅仅是正确的答案!如果解决方案是故事,你的系统就是你如何讲述你的故事,它是你知识深度和你沟通能力的最好证明。
作为一个例子,我讨论我的系统来回答一个技术问题,比如一个 SQL 问题:
- 重复这个问题,以及你应该向面试官传达什么,以确保你们双方达成共识。
- 要求一两分钟,考虑解决方案。这不仅给你时间整理你的想法,也给面试官留下了一个积极的印象:你说话之前先思考!
- 向面试官解释你的策略并检查你的假设。比如“假设数据中有缺失值或重复值安全吗”?
- 让面试官参与进来:编写尽可能易读的代码,并在每一步解释你在做什么。
- 检查您的解决方案,并提供如何改进的建议。
- 求反馈!在许多情况下,反馈帮助我改进了代码。有时,它会引发围绕其他可能的问题解决方案的有趣对话。
成为数据科学家的技能:我的观点!
我过去常常问我应该获得什么技能,我应该知道多少才能过渡到数据科学的职业生涯。而且大多数时候答案都不够好!
把你想如何学习它们留给你,以下是我的看法:
- SQL
如果你习惯使用窗口函数,你就可以开始了! - 统计学
你可以计算置信区间,设计实验,解释 p 值。你熟悉方差分析,并且理解线性回归背后的假设。 - Python
编写简单的代码来处理字符串,理解 Numpy 库,变得足够好,可以用 Pandas 处理大型数据集,用 Matplotlib 可视化结果。 - 机器学习
你很好地理解了基础知识:从偏差-方差权衡,参数和非参数算法之间的差异,分类与回归模型,到过采样和欠采样方法,等等。你了解传统算法是如何工作的:从线性回归和 SVM 到集成方法。你至少精通两种算法,如逻辑回归和随机森林。您了解评估您的模型的不同指标,以及如何针对您的问题选择正确的指标。
我准备技术面试的系统
- 20 分钟
我阅读了职位描述,强调了关键点,并思考了我的经历是如何关联的。这看起来可能不是技术问题,但是这部分给出了技术问题的背景。 - 30 分钟
通过与招聘人员的初步交谈,我试图弄清楚我应该期待哪种类型的技术面试。 - 30 分钟
我在网上搜索公司,如果可能的话,搜索他们正在招聘的部门,思考他们正在努力解决的项目和问题。如果对这个领域不熟悉,我会花几分钟研究这个领域。 - 10-15 分钟
我在 LinkedIn 上查看了面试官(如果有的话),以了解她的专业知识。有趣的是,出于某种原因,我越少这样做,或者基本上我越不在乎谁面试我,我在实际面试中做得越好。 - 45–60 分钟
我在 Glassdoor 和其他一些网站上查看了该公司的面试问题。如果是电话面试,我只复习了电话面试问题之类的。 - 60–120 分钟
根据面试情况,我复习了我的 SQL/Python/统计学/机器学习笔记(我会在不久的将来分享)。
面试非技术性提示
- 表现得像个话题专家,而不是求职者!
把面试当成一次谈话,把面试官当成你的同事。将问题与你过去的经历联系起来,讲一个故事,让面试官参与进来,问她这样的问题:你/你的公司会如何处理这种情况? - 我意识到下班后的晚上对我来说更好。自己想办法。
- 你很有可能和许多面试官一样合格。谦虚但自信。
- 如果你在技术上做好了准备(假设你的技能符合工作要求),数据科学面试很容易!你练习得越多,准备得越充分。
- 不要担心被拒绝:你几乎总是会有第二次机会(过一段时间),同时,还有许多其他伟大的公司。
- 你已经被告知无数次了,但是请记住,你还要面试公司代表。一些你现在没有问或没有注意到的事情,以后会成为一个大问题。
- 把对你最关心的公司的面试留到最后,这样你就有足够的时间从过去的错误中学习和改正。
致我所有遭遇拒绝的朋友
我感受到你的痛苦和沮丧!几周前我经历了同样的过程,我被许多公司拒绝了。面对挫折时,我们通常会责怪自己不够好。然而,至少对我认识的大多数人来说,这与事实相差甚远。通过多次采访(有一段时间似乎没完没了),我意识到我最大的挫败感来自两个公司群体:
- 那些以前没有雇佣数据科学家经验的公司(通常是中小型公司)。在这里你可能会看到最不准确的职位描述,以及一些无关紧要的问题。
- 那些拥有数据科学团队但没有良好的招聘流程的公司。在这里,你可能需要根据面试你的人和面试官正在做的/感兴趣的项目来回答一些非常专业的问题。
不要为你无法控制的事情责备自己!相反,从每一次面试中学习,继续前进,为下一次面试做好准备。有很多公司需要你的才华、激情和努力。你获得的经验越多,你就能更好地应对任何情况。
拒绝处方后
- 你做得很好,但是面试官没有理解你!
- 这些问题令人困惑。它们不是面试类型的问题。
- 我没有得到足够的解释或时间。
- 你不应该被拒绝!
- 等等
你在以上所有方面都是绝对正确的。但仅限于被拒后的前半个小时!前 30 分钟过后,在忘记面试的内容和你表现中可能存在的缺陷之前,从你构思问题的方式、你处理 SQL 问题的方式或者对公司/产品不够熟悉,仔细想想哪里出了问题!把它写下来,确保你处理好它。事实是,在面试中有很多事情可能会搞砸,但没关系!不可以的是两次犯同样的错误!你只是没有时间做这些…
外面有很多很棒的公司,所以如果你搞砸了面试,不要担心!
在早期,我搞砸了一些科技巨头的几次面试,让我陷入绝望。我担心,如果这种情况继续下去,我会失去很多好的选择,并可能不得不在某个对我的职业生涯没有帮助的地方结束。与此同时,我已经在一家大公司找到了一份工作,和一群了不起的人一起工作,在任何情况下我都不想换工作。
一些事情帮助我克服了这种恐惧。例如,在 LinkedIn 上简单地搜索一下,就会发现无数来自伟大但鲜为人知的组织的机会,这些组织拥有有趣的、有时甚至是颠覆性的技术,我们许多人从未听说过。事实是,这个世界并不局限于脸书或谷歌。事实上,有一些超乎你想象的公司拥有健康的文化和支持团队,愿意帮助你成长并成为最好的你。
把你的重要决定,包括承担一个新的角色,建立在你的希望而不是恐惧之上:如果你正在寻找并且还没有找到合适的组织,不要放弃。继续找!
想被录用吗?努力提高你的技能!
想要升职?学习机构知识!
领域知识和机构知识:当我过去的公司的 CEO 鼓励我将拒绝交易作为我加入团队后的第一个项目时,我很失望!刚出学术界,我就希望有一个复杂的项目,比如建立最好的机器学习模型。取而代之的是,我被分配去计算所有交易中被拒绝的比率,以此来简化它。我不知道的是,我的任务即使不是最重要的,也是支付行业最重要的问题之一。
我缺少的不是技术技能。从技术上讲,我可以做任何类型的分析。在 3 个不同的科学领域发表了 14 篇论文,涉及从宇宙学到非线性光学和计算生物化学的各种高要求主题,赢得了多个研究奖项,并在大约一年的时间里致力于我的数据科学技能,我已经准备好做任何事情来展示我的技术能力;要发光!缺失的部分不是数学和统计。这是领域知识和商业头脑!
除了技术能力之外,数据科学家还需要开发其他技能,以便能够识别影响业务的项目,并赋予利益相关者权力,使他们优先于任何其他类型的工作。要达到这一点,即培养商业头脑,两者的共存是必要的;领域知识和机构知识!
领域知识帮助你理解你所在行业的主要概念。另一方面,机构知识关注的是你工作的公司:它的愿景、竞争优势、运营和创收方式、优先事项、历史和未来计划等等。
支付行业的领域知识专家可能会将欺诈和拒绝交易识别为非常重要的问题。机构知识有助于专家根据哪个更符合公司的愿景进行选择。
与你所在领域的专家、你公司的人交谈,订阅时事通讯,阅读公司文件,所有这些都可以帮助你拓展你的领域和机构知识。
仔细听对话,记住名字,掌握反复提到和重复的重要概念。耐心点!
好消息是:我已经意识到,虽然我们是因为技术技能而被聘用的,但让我们不可替代的是制度和领域知识。用一个有更好技术技能的人来替换一个人通常并不困难,但是替换一个对这个领域和她工作的机构非常了解的人就非常困难了。因此,在短期内,你的竞争优势是你的技术技能;从长远来看,你的领域和机构的知识成为一个!
在候选人的舒适区会见他们!
我认为,如果你想看到应聘者的最佳状态,就在他们的舒适区对他们进行评估:这是给面试官的一个建议!
在面试过程中,无论是电话面试还是现场面试,候选人的大脑都在同时处理无数不同的事情。在这种情况下,如果你的判断仅仅基于他们理解与新角色相关的问题和挑战的能力,那么你就没有充分发挥他们的能力来评估他们。
相反,我建议面试的一部分应该专注于进入候选人的舒适区,通过询问关于他们过去工作的详细问题来挑战他们。这确实应该超越通常在面试开始时进行的简短介绍。
这将有助于对候选人的认知和技术能力、他们的思维过程和解决问题的方法、他们如何应对挑战以及他们如何向你展示这些挑战建立一个更全面的了解。
虽然这需要有经验的面试官,但它让面试过程变得公平,并有助于组织吸引可能更适合该职位的申请人。
用 numpy 解释 PCA 和 SVD
主成分分析和奇异值分解到底有什么关系,如何用 numpy 实现?

主成分分析和奇异值分解是探索性数据分析和机器学习中常用的降维方法。它们都是经典的线性降维方法,试图在原始高维数据矩阵中找到特征的线性组合,以构建数据集的有意义的表示。当涉及到降低维度时,它们受到不同领域的青睐:生物学家经常使用 PCA 来分析和可视化来自群体遗传学、转录组学、蛋白质组学和微生物组的数据集中的源差异。与此同时,奇异值分解,尤其是其简化的截断奇异值分解,在自然语言处理领域更受欢迎,以实现对庞大而稀疏的词频矩阵的表示。
人们可以发现从 PCA 和 SVD 得到的表示在某些数据中是相似的。实际上,PCA 和 SVD 是密切相关的。在这篇文章中,我将使用一些线性代数和几行 numpy 代码来说明它们之间的关系。
0.线性代数复习者
让我们首先快速回顾一下线性代数的一些基础知识,因为 PCA 和 SVD 都涉及一些矩阵分解。
- 矩阵转置:重新索引一个二维矩阵 A 来切换行和列索引,有效地用 a_{ji} 替换它的所有元素 a_{ij} 。转置的符号是矩阵上上标的⊤或 ' 。在 numpy 中,可以调用。T 或。transpose()NP . ndarray 对象的方法来转置一个矩阵。
- 点积与矩阵乘法:两个矩阵A(n×m)B【m×p】的乘积=AB两个矩阵可以相乘 n×p. 合成矩阵 C 中的元素 c_{ij} 计算如下:

Elements in the product matrix of two matrices are the dot products of the corresponding row vectors and column vectors
你可能会意识到,乘积矩阵 C 中的元素分别是矩阵 A 和 B 中对应的行向量和列向量的点积。
- 矩阵求逆:只有方阵才能求逆,一个矩阵a(n×n)与其逆的乘积a^(-1)是一个单位矩阵 I ,其中对角线上的元素是 1,其他地方的元素都是 0**
- ****共轭转置*:定义为共轭矩阵的转置。通常用上标 a 或 H (埃尔米特语)表示。共轭矩阵是通过取原始矩阵中所有元素的复共轭而获得的矩阵:

Conjugate transpose
回想一下复数,一个数由一个实部和一个虚部组成。例如, a + i b 是一个复数,其中 i 是等于-1 的平方根的虚数单位。 a + i b 的复共轭为 a - i b. 由于我们处理的大部分数据集都是实数的矩阵,所以矩阵的共轭转置等价于普通的转置。

- ****酉矩阵:定义为共轭转置也是其逆矩阵的方阵。对于酉矩阵,我们有它的转置等于它的逆:

Unitary matrix, where the conjugate transpose equates the matrix inverse
- ****协方差矩阵:协方差量化两个随机变量 X 和 Y 之间的联合变异性,计算如下:

Covariance
协方差矩阵 C 是来自数据矩阵 X (n 个样本× m 个特征)的特征的两两协方差的方阵。从协方差的定义观察,如果两个随机变量都以 0 为中心,则随机变量的期望变成 0 的,协方差可以计算为两个特征向量 x 和 y 的点积。因此,所有要素都居中的数据矩阵的协方差矩阵可计算如下:

Covariance matrix of a 0-centered matrix X
好的,这带来了一些大学线性代数的美好回忆。现在我们来认识一下这个帖子的主角们。
1.主成分分析
PCA 旨在寻找线性不相关的正交轴,这些轴也称为主分量(PC)在 m 维空间中,以将数据点投影到这些 PC 上。第一台 PC 捕获数据中的最大差异。让我们通过将 PCA 拟合到二维数据矩阵上来直观地理解它,二维数据矩阵可以方便地用二维散点图来表示:

Making sense of PCA by fitting on a 2-D dataset (source)
因为所有的 PC 都是相互正交的,所以我们可以使用二维空间中的一对垂直线作为这两个 PC。为了让第一台电脑捕捉到最大的方差,我们旋转了一对电脑,使其中一台电脑与数据点的分布最佳对齐。接下来,可以将所有数据点投影到 PCs 上,它们的投影(PC1 上的红点)本质上是数据集的最终降维表示。Viola,我们刚刚将矩阵从二维减少到一维,同时保留了最大的方差!
PCs 可以通过协方差矩阵 C 的特征分解来确定。毕竟特征分解的几何意义就是通过旋转为 C 找到特征向量的新坐标系。

Eigendecomposition of the covariance matrix C
在上式中,协方差矩阵C(m×m)分解为一个特征向量矩阵W(m×m)和一个对角矩阵 m 特征值λ。特征向量,即 W 中的列向量,实际上就是我们要寻找的 PC。然后,我们可以使用矩阵乘法将数据投影到 PC 空间。出于降维的目的,我们可以将数据点投影到第一个 k 个PC 上作为数据的表示:

Project data onto the first k PCs
PCA 可以非常容易地实现,因为 numpy 是执行特征分解的关键函数( np.linalg.eig )已经内置:
2.德拉贡诺夫狙击步枪(Snayperskaya Vinyovka Dragunov 的缩写)
SVD 是实矩阵和复矩阵的另一种分解方法。它将一个矩阵分解成两个酉矩阵( U , V )和一个奇异值的矩形对角矩阵(σ*)的乘积:

Illustration of SVD, modified from source.
在大多数情况下,我们处理的是实矩阵 X ,得到的酉矩阵V也将是实矩阵。因此, U 的共轭转置就是简单的正则转置。
SVD 也已经在 numpy 中实现为 np.linalg.svd 。要使用 SVD 转换数据:
3.主成分分析和奇异值分解的关系
PCA 和 SVD 是密切相关的方法,并且都可以应用于分解任何矩形矩阵。我们可以通过对协方差矩阵 C 进行奇异值分解来研究它们的关系:

从上面的推导中,我们注意到结果与 C 的特征值分解形式相同,我们可以很容易地看出奇异值(【σ)与特征值(λ)之间的关系:

Relationship between eigenvalue and singular values
要使用 numpy 确认这一点:
那么这意味着什么呢?这表明我们实际上可以使用 SVD 来执行 PCA,反之亦然。事实上,PCA 的大多数实现实际上使用在遮光罩下执行 SVD,而不是对协方差矩阵进行特征分解,因为 SVD 可以更有效,并且能够处理稀疏矩阵。此外,还有简化形式的奇异值分解,计算起来更加经济。
在下一篇文章中,我将深入研究不同 SVD 解算器和实现的复杂性,包括 numpy、scipy 和新开发的自动签名库谷歌 JAX 。
参考资料:
主成分分析:特征向量和特征值

每当你在处理数据的时候,你总是会面对相对的特征。后者是我们在描述数据时考虑的变量。也就是说,如果你正在收集一些关于米兰房屋的数据,典型的特征可能是位置、尺寸、楼层等等。
然而,经常发生的情况是,你的数据提供给你许多功能,有时是数百个…但是你需要全部吗?好吧,记住简约法则,我们宁愿处理一个只有很少特征的数据集:训练起来会容易得多,也快得多。另一方面,我们不希望在删除某些功能时丢失重要信息。
我们如何处理简单性和信息量之间的权衡呢?这个问题的答案是主成分分析(PCA)的结果。
主成分是由初始变量的线性组合构成的新变量。这些组合是以这样的方式完成的,即这些新变量是不相关的,并且初始变量中的大部分信息被存储到第一分量中。因此,这个想法是, k 维数据给你 k 主成分,但是 PCA 试图把最大可能的信息放在第一个主成分中,这样,如果你想减少数据集的维数,你可以把你的分析集中在前几个成分上,而不会遭受信息损失的巨大损失。
在这种分析中,度量信息量的是方差,并且主成分可以在几何上被视为高维数据的方向,其捕捉最大数量的方差并将其投影到较小维度的子空间,同时保留大部分信息。因此,第一主成分占最大可能的方差;第二个分量将直观地说明第二大方差(在一个条件下:它必须与第一个主分量不相关)等等。
为了更深入地理解 PCA,我们需要引入一些进一步的概念。
协方差矩阵
让我们考虑一个场景,其中我们只有两个特征,x 和 y。我们可以在 2D 图中表示我们的数据如下:

现在,我们可以计算所谓的协方差矩阵:它是一个对称的, dxd 矩阵(其中 d 是特征的数量,因此在这种情况下 d =2 ),其中存储了每个特征的方差和交叉特征协方差:

因为 Cov(x,y)等于 Cov(y,x),所以如前所述,矩阵是对称的,并且特征的方差位于主对角线上。
协方差矩阵可以根据数据的形状采用不同的值。让我们检查一些场景:

当两个特征正相关时,协方差大于零,否则,它具有负值。此外,如果没有证据表明它们之间存在相关性,则协方差等于零。
如您所见,协方差矩阵定义了数据的分布(方差)和方向(协方差)。这个矩阵还可以分配两个元素:一个代表向量和一个表示其大小的数。向量将指向数据的较大分布的方向,数字将等于该方向的分布(方差)。这两个元素分别是特征向量和特征值。让我们想象一下:

绿色的方向是特征向量,它有一个对应的值,叫做特征值,特征值描述了它的大小。让我们更详细地看看它是如何工作的。
特征向量和特征值
为了更好地理解这些概念,让我们考虑下面的情况。我们被提供了二维向量 v1,v2,…,vn。然后,如果我们将线性变换 T(一个 2x2 矩阵)应用于我们的向量,我们将获得新的向量,称为 b1,b2,…,bn。

但是,其中的一些(更具体地说,与特征的数量一样多)有一个非常有趣的特性:确实,一旦应用了变换 T,它们会改变长度,但不会改变方向。这些向量称为特征向量,代表特征向量倍数的标量称为特征值。

因此,每个特征向量都有一个对应的特征值。现在,如果我们考虑我们的矩阵σ,并将所有相应的特征向量收集到矩阵 V 中(其中,作为特征向量的列数将等于σ的行数),我们将获得如下结果:

其中 L 是存储所有特征值(与特征向量一样多)的向量。如果我们考虑两个特征(x 和 y)的例子,我们将获得以下结果:

然后,如果我们按照特征值降序排列我们的特征向量,我们将得到第一特征向量占数据中最大的分布,第二特征向量占第二大的分布,等等(在描述新空间的所有这些新方向是独立的,因此彼此正交的条件下)。
现在,如果我们想降低数据集的维度,我们应该怎么做呢?假设我们从 5 个特性开始,我们想要处理 2 个特性。因此,程序如下:
- 计算σ矩阵我们的数据,这将是 5x5
- 计算特征向量矩阵和相应的特征值
- 按降序排列我们的特征向量
- 构建所谓的投影矩阵 W,其中将存储我们想要保留的 k 个特征向量(在本例中,2 是我们想要处理的特征的数量)。因此,在我们的例子中,我们的 W 将是一个 5x2 矩阵(通常,它是一个 dxk 矩阵,其中d=原始特征的数量,k=期望特征的数量)。
- 通过投影矩阵 W 变换原始数据,原始数据可以表示为矩阵 X(其中,n=观察值的数量,而d=特征的数量),从而获得新的数据集或矩阵 Y,其将为 nxk 。
这个新的,转换后的空间 Y 的两列,是我们用来代替原始变量的主要成分。如上所述,它们被构造成存储尽可能多的信息。
PCA 广泛用于机器学习任务:事实上,为了使我们的算法有效,训练过程必须尽可能快,但这并不意味着我们可以在没有特定标准的情况下降低维度,从而有丢失相关信息的风险。
PCA 不是特征选择
它实际上是做什么的,什么时候可以用,什么时候不能用。
几乎没有数据科学家会要求更少的数据,但是维数灾难使得必须采取一些措施来管理数据集中的许多变量。主成分分析(PCA)是一种非常有用的工具,但关于 PCA 有一些常见的误解和/或错误,妨碍了初级数据科学家正确应用它。
PCA 是数据从一个坐标系到另一个坐标系的旋转。新数据科学家犯的一个常见错误是将 PCA 应用于非连续变量。虽然在技术上可以对离散变量或作为热编码变量之一的分类变量使用 PCA,但您不应该这样做。简单地说,如果你的变量不属于一个坐标平面,那么就不要对它们应用 PCA。应用后,在我们的新坐标系中,第一维具有最大的方差,然后第二维具有最大的剩余方差,依此类推。

在相对较小的数据集上,前几个组件可以解释数据集中几乎所有的差异。我见过其他数据科学家错误地认为,这意味着最后几个组件可以忽略不计,前几个组件才是最重要的特性。PCA 是特征选择的有效方法的唯一途径是,如果最重要的变量恰好是那些变化最大的变量。然而,这通常不是真的。作为一个例子,假设您想要对一个 NFL 球队进入季后赛的概率进行建模。NFL 球队的胜利数(0 到 16)比球队的总冲刺码(以千计)更有助于预测进入季后赛的概率,但 PCA 会选择冲刺码作为第一部分的最大贡献者。
有应用 PCA 的好时机。假设我们对调查标准普尔 500 股票的波动性感兴趣。我们可以将 PCA 应用于一个有 500 列(每家公司一列)和 1000 行(每只股票在过去 1000 天的收盘价)的数据集。价格变化大的股票对第一部分的贡献最大。一旦你完成了主成分分析,你现在就有了不相关的变量,它们是旧变量的线性组合。理想情况下,前几个成分可以解释标准普尔 500 中几乎所有的差异。处理这几个变量要比处理 500 个变量(每个公司一个)容易得多。这是一个很好的应用程序,因为每只股票都有相同的标度,也就是说,它们都用美元表示价格。知道什么时候可以,什么时候不可以应用 PCA,对于使用它是必不可少的!
PCA vs TSNE——El clásico
PCA vs TSNE——这场比赛让我们所有人都兴奋不已

Photo by Hermes Rivera on Unsplash
皇家马德里对巴塞罗那的比赛总是让我们兴奋不已。两支球队都很棒,但都有不同的比赛风格。在数据科学领域,我们有自己的 El clásico——PCA vs TSNE。两者都是流行的降维技术。两者都有自己的风格。
有足够多的关于主成分分析和 TSNE 的文献从数学的角度或者从视觉化的角度探讨了两者的区别。在这个故事中,我想用一种数据故事讲述的方法来展示两者的区别。我将举两个例子,一个是电信客户数据集,另一个是漫威复仇者数据集。激动。然后继续读下去
示例 1 —电信数据集的降维
电信公司收集了大量客户信息。客户数据集信息量很大。这里显示了一个这样的样本数据集
Sample Telco dataset
该数据集具有大约 20 个字段,这些字段具有各种信息,例如客户人口统计、订购的服务以及计费费用。它是分类字段和数值字段的混合。
现在让我们在这个数据集上应用两种技术 PCA 和 TSNE,并比较结果
步骤 1 —比较输出
使用这两种技术将维数减少到 2 维的结果如下所示

PCA vs TSNE — comparing the output
看着这些观想,我们可以做一些观察。PCA 形成了两个集群。TSNE 已经形成了三个集群,并且有一些数据点从集群中分离出来。
因此,只要比较两种算法的输出,就会发现一些重要的差异。那么这意味着什么呢?让我们在下一步回答这个问题
步骤 2 —解释集群
为了理解这种差异,我们必须首先解释集群的含义。出于解释的目的,下面给出了每个集群的名称。在 PCA 端,簇的名称是 Cluster0 和 Cluster1。在 t-SNE 端,集群的名称为集群 0、集群 1 和集群 2

Giving some cluster names
一个简单快捷的解释星团的方法是使用雷达图,如图所示。雷达图显示了每个聚类的重要特征(或列)的平均值

Radar chart to interpret cluster
我们观察到
- 对于 PCA,每月费用是集群之间的最大区别
- 对 TSNE 来说,老年人这一特征是集群之间的最大区别
让我们用一些颜色来验证这些观察。我们可以使用月费给 PCA 可视化中的点着色。类似地,用老年公民给 TSNE 观想中的点上色

这证实了我们已经观察到的雷达图。在 PCA 侧,低月费的集群是清楚可识别的。在 TSNE 一侧,一群老年人清晰可见
步骤 3——理解差异
那么,为什么 PCA 选择月费作为主要的差异化领域之一呢?原因是 PCA 是一种数学方法,它试图基于最高方差尽可能地分离点。月费是客户支付的金额,因客户而异
所以 PCA 本质上是通过 分离 点****基于最高变化场。
TSNE 选择了老年人作为重点区分领域之一。原因是 TSNE 是一种概率方法,它试图根据两个接近的点来自相同人口分布的概率来尽可能接近地对点进行分组。因此,一般来说,在现实生活中,老年人也可以被认为是一种类型人群,且他们很有可能表现出消费电信服务的类似行为
所以 TSNE 基本上是通过 分组 点 尽可能接近点的特征
现在,让我们探索另一个基于漫威字符的数据集,并更深入地了解 PCA 和 TSNE 的内部工作
示例 2-漫威字符数据集的降维
漫威给了我们一些很棒的超级英雄电影。幸运的是,它还提供了一个有趣的数据集,这样数据科学家就可以玩了。此处显示了该数据集中的一个样本
Marvel comic character data sample
这个数据集大约有 185 个字段。相当多的领域,你需要一些超级武器来理解这些大量的领域。幸运的是,在主成分分析和 TSNE 方面,我们拥有超级武器,可以进行一些很酷的降维
这里显示的是两种降维技术的输出。

我们可以观察到在 PCA 中有一个可见的簇,而在 TSNE 中没有形成簇。TSNE 没有任何集群的原因是因为所有的超级英雄都是非常不同的,并且没有相似的超级英雄的潜在群体。超级英雄是独一无二的。没有钢铁侠 100 的人口。只有一个铁人。
所以一个超级英雄和另一个超级英雄很接近的可能性很小。用数据科学术语来说,数据非常稀疏。当数据非常稀疏时,TSNE 不会形成任何聚类
为了解释 PCA 聚类,我们可以通过功率来给点着色,功率是变化最大的场。对 TSNE 来说,因为没有星团形成,所以给点着色没有意义。分析 TSNE 输出的唯一方法是用漫威角色的名字标记一些点,以便做出一些解释

在 PCA 输出中,我们看到该簇具有中低功率的字符。随着我们越走越远,角色的力量越来越大。
在 TSNE,没有群集,但我们可以分析一些字符之间的密切程度。我们可以看到洛基和灭霸很接近。这可能是因为它们在数据集中代表了一种邪恶的对齐方式。同样,冬季士兵和美国队长也很接近。这是因为他们有相似的军队背景和相似的性格。火箭浣熊和黑豹也是如此,它们具有相似的动物特征和力量
基于这个数据集,我们可以做一个更重要的观察
只有当人口分布中有足够的点时(意味着当数据不稀疏时),TSNE 才会形成聚类。然而,点的接近度可以用来观察两点之间的相似性
我的朋友们,这是用两个数据故事对两种降维技术的简单解释。总结一下
- PCA 基本上是通过尽可能地分离点来工作的
- TSNE 基本上是通过将点尽可能靠近地分组来工作的
- 只有当数据不稀疏时,TSNE 才会形成簇
这两种技术都有自己的风格,在不同的情况下都很有用
所以现在正如你所理解的不同,这不是一个对另一个。都是很棒的技术,都有自己的风格。

Photo by Jonathan Tomas on Unsplash
额外资源
网站(全球资讯网的主机站)
你可以访问我的网站进行零编码分析。https://experiencedatascience.com
请订阅每当我发布一个新的故事时,请及时通知我。
** [## 每当 Pranay Dave 发表文章时,您都会收到电子邮件。
每当 Pranay Dave 发表文章时,您都会收到电子邮件。通过注册,您将创建一个中型帐户,如果您还没有…
pranay-dave9.medium.com](https://pranay-dave9.medium.com/subscribe)
你也可以通过我的推荐链接加入 Medium。
[## 通过我的推荐链接加入 Medium—Pranay Dave
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
pranay-dave9.medium.com](https://pranay-dave9.medium.com/membership)
https://www.youtube.com/c/DataScienceDemonstrated 的 YouTube 频道
这里是我的 Youtube 频道
的链接**
PDF 不是概率。
x 处的概率密度可以大于 1,但是它怎么能积分为 1 呢?
众所周知,概率的最大值是 1 。
但对于某些 PDF(如指数分布的 pdf,下图),当 λ= 1.5 和 𝒙 = 0 时,概率密度为 1.5 ,明显大于 1 !

From Wikipedia: The PDF of Exponential Distribution
1.一个 PDF 的值怎么可能大于 1,概率仍然积分为 1?
即使 PDF f(x) 取大于 1 的值,如果它所积分的区域小于 1 ,它加起来只能是 1 。让我们举一个最简单的 PDF 的例子——定义域【0,0.5】上定义的均匀分布。均匀分布的 PDF 为 1/(b-a) ,在整个中恒定为 2。

The PDF of uniform distribution
总概率就是图 f(x) ,
下的总面积也就是 2 * 0.5 = 1 。
如你所见,即使 PDF 大于 1 ,因为它在小于 1 的域上积分,所以它可以累加到 1 。
2.概率密度函数和概率的区别
PDF f(x)不是概率吗?
否。因为 f(x)可以大于 1 。
(PDF 中的“PD”代表“概率密度”,不是概率。)
f(𝒙) 只是 PDF 图在 X = 𝒙 处的高度。(你是否混淆了 𝐗 vs 𝒙 的符号?点击这里查看。)
整个“PDF =概率”的误解是因为我们习惯了“ PMF =概率”的概念,事实上,这是正确的。但是,一个 PDF 和一个 PMF 不是一回事,也不应该和一个 PMF 做同样的解读,因为离散随机变量和连续随机变量的定义是不一样的。
对于离散随机变量,我们在单点查找 PMF 的值以找到它的概率 P(𝐗=𝒙) (例如还记得我们如何将插入泊松 PMF 吗? )
对于连续的随机变量,我们在某个区间上对一个 PDF 进行积分,求其 X 落在那个区间的概率。
**f(x) ≠ P(X = 𝒙)*** **f(x): PDF for a continuous r.v.** * **P(X = x) : PMF for a discrete r.v.**
当然,都很好。然而,你可能想知道…
为什么我们必须整合 PDF?
我们能不能像处理 PMF 值一样,只对 PDF 值求和?
不会。因为,对于连续随机变量,𝐗取任意特定值𝒙的概率为 0。****
3.为什么连续随机变量在每一点的概率为零?
我们来看前面的例子——均匀分布在【0,0.5】。****
x=1 时的概率密度为 2。但是为什么 x=1 时的概率为零呢?****
要回答上面的问题,我们需要先回答下面的问题:
【0,0.5】中我们总共有多少个实数?
****无限∞。(数学上彻底一点,不可数无穷。)
0.1 , 0.01 , 0.001 , 0.0001 ,…可以在最小的小数前面一直插入 0。
因此,一个连续的随机变量有无限#的可能取值,即使定义域很小且固定。假设【0,0.5】中每个值的概率密度取一个极小的值,例如 000000001。尽管如此,无限(不可数)多个值的总和将达到无穷大,不管它们的值有多小。****
那么,为了使概率之和为 1 ,单点的概率应该是 1/∞ ,也就是 0** 。**
嗯,这也说不通。如果你加上无穷多个零,你仍然会得到零。总概率加起来应该是 1,而不是 0。
问题是,我们不能对连续变量使用离散 PMF(一个值有一个匹配概率)的概念。我们不能像对离散变量那样定义连续变量的概率。
4.那我们怎么从概率密度 f(x)计算概率呢?
我们将借用“积分”的思想。
如果 X 正好在点𝒙的概率为零,那么围绕点 𝒙 的一个极小的区间呢?说,【𝒙,𝒙+d𝒙】?假设 d𝒙 是 0.00000000001。
那么 X 落在【𝒙,𝒙+d𝒙】的概率就是 f(𝒙) 夹在【𝒙,𝒙+d𝒙】中间的曲线下的面积。
如果 d𝒙 无限小,这个近似值对p(𝐗=𝒙来说就足够好了。
**f(𝒙)d𝒙** : The probability of **X** in **[𝒙, 𝒙+d𝒙]**.
**f(𝒙)**: Probability density.
**d𝒙** : Interval length.
需要注意一些事情:
1.如果你看一下 PDF 和 PMF 的定义,它实际上只是把离散情况下的求和(PMF)变成了连续情况下的积分(PDF)。
2.为什么我们使用术语*【密度】【质量】*?**
这类似于物理学中的质量密度——将密度积分得到质量。如果你把一个质量想成一个概率,我们就对一个概率密度进行积分得到一个概率(质量)。
3.𝒙点的概率密度意味着什么?
意思是𝒙附近单位长度( d 𝒙)有多少概率集中,或者𝒙.附近概率有多密集
4.我们需要修正维基百科上的指数分布图。Y 轴的水平 P(X) 听起来像是概率。我们需要把它改成 f(x) 或者“概率密度”。

Wikipedia: The PDF of Exponential Distribution
你可能喜欢的其他直观的文章:
何时使用泊松分布?
towardsdatascience.com](/poisson-distribution-intuition-and-derivation-1059aeab90d) [## 指数分布直觉
它的派生词和例子
medium.com](https://medium.com/@aerinykim/what-is-exponential-distribution-7bdd08590e2a)**
使用 Python 处理 PDF

Photo by James Harrison on Unsplash
介绍
作为一种语法相对简单的高级解释语言,Python 对于那些没有编程经验的人来说是完美的。流行的 Python 库被很好地集成在一起,提供了处理像 Pdf 这样的非结构化数据源的解决方案,并且可以用来使它更加合理和有用。
PDF 是最重要和使用最广泛的数字媒体之一。用于提交和交换文档。pdf 包含有用的信息、链接和按钮、表单域、音频、视频和业务逻辑。
1-为什么使用 Python 处理 PDF
如你所知,PDF 处理属于文本分析。
大多数文本分析库或框架都是仅用 Python 设计的。这为文本分析提供了杠杆作用。还有一点,你永远无法在现有的机器学习或自然语言处理框架中直接处理 pdf。除非他们为此提供一个明确的接口,否则我们必须首先将 pdf 转换成文本。
2-用于 PDF 处理的 Python 库
作为一名数据科学家,你可能不会拘泥于数据格式。
pdf 是一个很好的数据源,大多数组织只发布 pdf 格式的数据。
随着 AI 的成长,我们需要更多的数据进行预测和分类;因此,忽略 pdf 作为数据源可能是一个错误。实际上,PDF 处理有点困难,但是我们可以利用下面的 API 来简化它。
在这一节中,我们将发现顶级 Python PDF 库:
PDFMiner 是一个从 PDF 文档中提取信息的工具。与其他 PDF 相关工具不同,它完全专注于获取和分析文本数据。PDFMiner 允许用户获得文本在页面上的确切位置,以及其他信息,如字体或线条。它包括一个 PDF 转换器,可以将 PDF 文件转换为其他文本格式(如 HTML)。它有一个可扩展的 PDF 解析器,可以用于文本分析之外的其他目的。
PyPDF2 是一个纯 python 的 PDF 库,能够拆分、合并、裁剪和转换 PDF 文件的页面。它还可以向 PDF 文件添加自定义数据、查看选项和密码。它可以从 pdf 中检索文本和元数据,还可以将整个文件合并在一起。
pdfrw 是一个 Python 库和实用程序,用于读取和写入 PDF 文件:
- 版本 0.4 已经过测试,可以在 Python 2.6、2.7、3.3、3.4、3.5 和 3.6 上运行
- 操作包括子集化、合并、旋转、修改元数据等。
- 最快的纯 Python PDF 解析器
- 多年来一直被印刷商用于印前生产
- 可与 rst2pdf 一起使用,忠实地再现矢量图像
- 可以单独使用,也可以与 report lab 结合使用,以便在新的 pdf 中重用现有的 pdf
- 特许的
石板
Slate 是一个 Python 包,它简化了从 PDF 文件中提取文本的过程。这取决于 PDFMiner 包。
3-设置环境
第一步:从【Python.org】中选择要安装的 Python 版本。
****第二步:下载 Python 可执行安装程序。
****第三步:运行可执行安装程序。
步骤 4: 验证 Python 是否安装在 Windows 上。
步骤 5: 验证是否安装了 Pip。
****第六步:将 Python 路径添加到环境变量中(可选)。
步骤 7 :为你的 IDE 安装 Python 扩展。
我在 visual studio 代码中使用 Python 3.7。有关如何设置环境和选择 Python inter inter ter 来开始用 VS 代码编码的更多信息,请查看 VS 代码文档中的Python 入门。****
第 7 步:现在你可以用你的 IDE 执行 python 脚本了。
步骤 8 :安装 pdfminer.six
**pip install pdfminer.six**
第 9 步:安装 PyPDF2
**pip install PyPDF2**
搞定了。现在,你可以开始用 python 处理 pdf 文档了。
4-多个大型 Pdf 文档文本提取解决方案

pdf 文本提取解决方案基于三个主要步骤:
- 将 PDF 文档分割成一组文档(逐页分割)
- 处理分割文档并提取文本。
步骤 1:设置烧瓶应用端口和前缀:
**import datetime
import numpy as np
import json
def myconverter(o):
if isinstance(o, datetime.datetime):
return o.__str__()
if isinstance(o, np.bool_):
return o.__str__()
def writeToJSONFile(filepath, data):
with open(filepath, 'w') as file:
json.dump(data, file, default=myconverter, indent=4)
ports = {
"data_extraction": 5000
}
PREFIX = "/api"**
第二步:分割 PDF 文档:
该脚本包括:
- 从上传文件夹中收集 Pdf 文件
- 逐页分割 Pdf 文件
- 将分割的 pdf 页面保存到输出文件夹
**import os
from PyPDF2 import PdfFileReader, PdfFileWriter
def splitting(upload_folder, split_folder):
'''Do collect PDF files, split pages and save them
'''
entries = os.listdir(upload_folder)
path = os.path.abspath(split_folder)
for entry in entries:
uploaded_file = os.path.join(upload_folder, entry)
output_file_folder = os.path.join(path, entry)
if not os.path.isdir(output_file_folder):
os.mkdir(output_file_folder)
pdf = PdfFileReader(uploaded_file, strict=False)
for page in range(pdf.getNumPages()):
pdf_writer = PdfFileWriter()
pdf_writer.addPage(pdf.getPage(page))
output_filename = \
os.path.join(output_file_folder, f'{page+1}.pdf')
with open(output_filename, 'wb') as out:
pdf_writer.write(out)**
第三步:从 PDF 文档中提取文本:
该脚本包括:
- 收集分割的 PDF 页面。
- 将文本文件提取并保存到输出目录
**import os
from io import StringIO
import re
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
def pdf_to_text(path):
'''Extract text from pdf documents
'''
manager = PDFResourceManager()
retstr = StringIO()
layout = LAParams(all_texts=False, detect_vertical=True)
device = TextConverter(manager, retstr, laparams=layout)
interpreter = PDFPageInterpreter(manager, device)
with open(path, 'rb') as filepath:
for page in PDFPage.get_pages(filepath, check_extractable=True):
interpreter.process_page(page)
text = retstr.getvalue()
device.close()
retstr.close()
return text
def extraction(split_path, text_path):
'''Extract and save text files to output dir
'''
# entries names
entries = os.listdir(split_path)
# repeat the process for each entry
for entry in entries:
# define a custom list cotain entries files paths
custom_list = os.listdir(os.path.join(split_path, entry))
# list must be sorted
custom_list.sort(key=lambda f: int(re.sub(r'\D', '', f)))
# repeat the process for each file path
for file_path in custom_list:
text_output = pdf_to_text(
os.path.join(split_path, entry, file_path))
# save text file of each entry
with open(os.path.join(text_path, f"{entry}.txt"),
"a",
encoding="utf-8") as text_file:
text_file.write(text_output)**
步骤 4:提取相关实用程序的烧瓶包装: (分离和提取烧瓶 API)
**'''Flask wrapping of the extraction related utilities
'''
import os
from flask import Flask, request, jsonify
from werkzeug.utils import secure_filename
from global_common import ports
from global_common import PREFIX
from extraction import extraction
from splitting import splitting
app = Flask(__name__)
port = int(os.environ.get("PORT", ports["data_extraction"]))
path = os.getcwd()
# Project directories defined As follow:
# -data_dir-: data .
data = os.path.join(path, 'data')
if not os.path.isdir(data):
os.mkdir(data)
# -upload_dir-: contain files uploaded.
uploads = os.path.join(data, 'uploads')
if not os.path.isdir(uploads):
os.mkdir(uploads)
# -preparation_dir-: contain processed & prepared files.
prepare = os.path.join(data, 'files_preparation')
if not os.path.isdir(prepare):
os.mkdir(prepare)
# -output_dir-: contain generated text files.
outputs = os.path.join(data, 'outputs')
if not os.path.isdir(outputs):
os.mkdir(outputs)
# Verify and validate files extensions...
ALLOWED_EXTENSIONS = set(['.pdf'])
def allowed_file(filename):
'''Assess if the file extension is in the allowed listdir
'''
lowercase_extension = os.path.splitext(filename)[1].lower()
return lowercase_extension in ALLOWED_EXTENSIONS
@app.route(PREFIX + '/upload', methods=['POST'])
def upload():
'''Upload files to process
'''
if request.method != 'POST':
resp = jsonify({'message': 'Operation not supported'})
resp.status_code = 500
return resp
# check if the post request has the file part
if 'files[]' not in request.files:
resp = jsonify({'message': 'No file part in the request'})
resp.status_code = 500
return resp
files = request.files.getlist('files[]')
errors = {}
success = False
# check if file allowed or not allowed.
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(uploads, filename))
success = True
else:
errors[file.filename] = 'File type is not allowed'
if success and errors:
errors['message'] = 'File(s) successfully uploaded'
resp = jsonify(errors)
resp.status_code = 404
return resp
if success:
resp = jsonify({'message': 'Files successfully uploaded'})
resp.status_code = 200
return resp
resp = jsonify(errors)
resp.status_code = 404
return resp
@app.route(PREFIX + '/extraction', methods=['POST'])
def extract_function():
'''Do extract data from files
'''
if request.method == 'POST': # check request method
if not os.listdir(uploads): # if uploads dir is empty return -> error
resp = jsonify({'message': 'Files not found'})
resp.status_code = 500
return resp
try:
# splitting : split docs into single pages.
splitting(uploads, prepare)
# extraction: extract text from pages.
extraction(prepare, outputs)
resp = jsonify({'message': 'Files successfully extracted '})
resp.status_code = 200
return resp
except:
resp = jsonify({'message': 'error occurs while extraction'})
resp.status_code = 404
return resp
else:
resp = jsonify({'message': 'Operation not supported'})
resp.status_code = 500
return resp
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=port)**
第五步:编写 Dockerfile 文件
**FROM python:3.7
COPY . /app/
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python3"]
CMD ["app.py"]**
第六步: 编写 App(编写 Docker-Compose 文件)
**version: '3'
services:
web:
build: '.'
ports:
- '5000:5000'**
步骤 7:测试烧瓶应用程序(上传&提取)
另外,我在的 pdf-text extractGitHub 项目中附上了 postman 集合。
**curl -X POST [http://127.0.0.1:5000/api/upload](http://127.0.0.1:5000/api/upload)
curl -X POST [http://127.0.0.1:5000/api/upload](http://127.0.0.1:5000/api/upload) --form 'files[]=@"toto.pdf"' --form 'files[]=@"toto2.pdf"'
curl -X POST [http://127.0.0.1:5000/api/extraction](http://127.0.0.1:5000/api/extraction)**
GitHub 上有完整版的 Extraction。
请通过查看
分叉 和 主演 资源库是支持项目的最佳方式。
**** [## ahmedkhemiri 95/pdf-text extract
Python 多 PDF 文档文本提取— Python 3.7 作为一名数据科学家,你可能不会拘泥于数据格式。PDFs
github.com](https://github.com/ahmedkhemiri95/PDFs-TextExtract)****
如果你对我的文章有任何反馈、评论或有趣的见解,请随时通过我的 LinkedIn 社交媒体频道联系我
你可以在 Github 上关注我:【https://github.com/ahmedkhemiri95】T22
**** [## Ahmed Khemiri -微软认证培训师-微软| LinkedIn
在世界上最大的职业社区 LinkedIn 上查看 Ahmed Khemiri 的个人资料。艾哈迈德有 3 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/ahmed-khemiri-b33400134/)****
预测不可靠的调峰。

神经网络调峰:第三部分
一个 19 世纪的物理方程如何能让电力公司节约超过 60%
与 合作研究开放建模框架 。
这是关于神经网络调峰的三部分系列文章中的第三部分。考虑一下另外两个:
电力分销商可以通过降低高峰需求费用来节省数十万美元。一些…
www.brownanalytics.com](https://www.brownanalytics.com/energy_forecasting)
即使是预测能源消耗的最佳模型也不足以捕捉大部分调峰的可能价值。当一个预测只有 3%的误差时,结果是损失一半的可能节省并不罕见。考虑最小的误差如何显著影响这些电力公司预期的调峰成本节约(此处测试):

考虑到这一点,在考虑第二天的负载预测的不确定性时,考虑公用事业公司应该部署哪种调度策略是很重要的。谢天谢地,我们可以用一个等式来为这种不确定性做打算,对冲我们的赌注,并重新获得一些在不完美预测中损失的价值。
设置
3D 热量方程如下所示。3Blue1Brown 的对该主题的介绍启发了这种方法。就我们的目的而言,您只需要知道热量方程是一个方便的函数,可以在曲线下的面积保持不变的情况下展平曲线。我们希望在使用所有可用资源的同时分散我们的派遣。


Left: The 3D heat equation, where T is temperature, alpha is some constant and t is time. Right: How the heat equation affects a curve. Source: 3Blue1Brown
考虑对明天负荷曲线的预测。我们知道,一般来说,3%的平均绝对误差是正确的。我们不能将所有鸡蛋放在一个篮子里(即,将我们所有的电池调度放在一个小时内)。我们根据预测的负载来计算理想的存储调度,然后将热量方程应用到附近的时间。这确保了最小的预测误差不会导致储蓄的显著下降。
下面是转化为 python 的热量方程及其对存储调度的影响:

其他详细信息
电力公司可能犯的最大错误是试图在错误的时间给电池充电。在我测试过的众多型号中,中午充电的电池持续增加本月的峰值(T2)。由于这个原因,最好总是安全地玩,并且总是在消耗处于绝对低的时候充电。从我收集的信息来看,这种保险策略只损失了大约 1-2%的最佳可能价值,而且非常值得,至少在预测软件变得更好之前是如此。
为了做出调度决策,我们使用线性编程 python 包puLP。软件包的细节可以在这里找到——它对任何类型的运筹学都非常有用。我们陈述了一个目标函数,如“最小化最大负载”,同时提供了电池的限制。
结果呢
我测试了从 2002 年到 2018 年,热量等式将如何影响德克萨斯州八个 ERCOT 地区的收入。为了模拟预测精度,我添加了一个噪声的正态分布,在负载周围有 3%的标准偏差。我假设我们有 700 千瓦的存储,每小时最多可以调度 500 千瓦。功能和测试可以在本 jupyter 笔记本中找到。
重要的是要澄清,测试是在每天的峰值上执行的,以收集更大的样本。我依赖的假设是月峰值和日峰值之间没有显著差异。

Percent of optimal value obtained if heat equation is applied with given values in the COAST region of ERCOT.
上面的热图显示了应用不同的 alpha 值和时间步长时获得的最佳值的百分比。在 ERCOT 的沿海地区,如果热量方程不适用,公用事业公司将收集 28%的最佳调峰值。如果适当地应用热量方程,它们捕获 46%的最优值。德克萨斯州每个地区的结果如下表所示。应用这个扩展公式可以帮助每个地区。这一公式将增加 20%到 64%的储蓄。

此外,最大化节约的常数对于每个地区来说都是相对相似的,这表明这些常数随着时间的推移是相对稳定的,并且是可推广的,尽管自然地,每个公用事业公司在应用它们之前都必须进行独立的分析。
对德克萨斯州 NCENT 地区的快速分析显示,即使有 99%的准确率,应用热量方程仍然可以提高储蓄。在我们的预测能力达到随机效应所允许的完美程度之前,应该应用并完善考虑到我们不确定性的优化公式。
疑问?更正?数据科学笑话?联系我或者在 我的网站 上看到更多好玩的项目。
皮尔逊相关系数解释。
我开始意识到,对于可以在数据集上执行的不同类型的关联,存在许多困惑。让我先从皮尔逊相关系数开始,澄清一下这个问题。
什么是相关性?
相关性是一种双变量分析,衡量两个变量之间的关联强度和关系方向。就关系的强度而言,相关系数的值在+1 和-1 之间变化。值为 1 表示两个变量之间的完美关联程度。随着相关系数值趋向于 0,两个变量之间的关系将变弱。关系的方向由系数的符号表示;加号表示正相关,减号表示负相关。
通常,在统计学中,我们衡量四种类型的相关性:
- 皮尔逊相关
- 肯德尔等级相关
- 斯皮尔曼相关
- 点双列相关。
皮尔逊相关系数
如题所示,我们将只讨论皮尔逊相关系数。我会尽量简短,但内容丰富,这样你就可以自己动手了。皮尔逊相关系数是对两个变量之间线性关联强度的度量,用 r 表示。您会遇到皮尔逊 r 相关
皮尔逊相关回答的问题
- 年龄和身高有统计学上的显著关系吗?
- 温度和冰淇淋销量有关系吗?
- 工作满意度、生产率和收入之间有关系吗?
- 哪两个变量在年龄、身高、体重、家庭规模和家庭收入之间有最强的相关性?
假设
1.对于皮尔逊 r 相关性,两个变量都应该是正态分布。即正态分布描述了变量的值是如何分布的。这有时被称为“钟形曲线”或“高斯曲线”。一个简单的方法是使用夏皮罗-维尔克检验分别确定每个变量的正态性。

Normal Distribution
2.应该有没有明显的异常值。我们都知道异常值是什么,但我们不知道异常值对皮尔逊相关系数 r 的影响。皮尔逊相关系数 r 对异常值非常敏感,这会对最佳拟合线和皮尔逊相关系数产生非常大的影响。这意味着-在分析中包含异常值会导致误导性结果。

Outliers
3.每个变量应该是连续的,即间隔或比率,例如体重、时间、身高、年龄等。如果一个或两个变量在测量中是有序的,那么可以进行 Spearman 相关。
4.这两个变量有一个线性关系。散点图将帮助您判断变量之间是否存在线性关系。如果数据点是直线(而不是曲线),则数据满足线性假设。如果您拥有的数据不是线性相关的,您可能需要运行非参数。

Linear and non-Linear Relationships
5.这个观测值是成对观测值。也就是说,对于自变量的每一次观测,必然有因变量的相应观测。例如,如果你在计算年龄和体重之间的关系。如果有 12 个体重的观察值,你应该有 12 个年龄的观察值。即没有空白。
6.同质性。我把最好的留到了最后。hard 很难发音,但概念很简单。同方差描述了一种情况,其中误差项(即自变量和因变量之间关系中的“噪声”或随机扰动)在自变量的所有值上都是相同的。散点图使得检查这一点变得容易。如果这些点位于最佳拟合线的两侧,那么数据是同方差的。另外,同异方差的反义词是异方差(违反同异方差),当误差项的大小在自变量的值之间不同时,就会出现异方差。

Homoscedasticity

Heteroscedasticity
您还可以查看:
Pebble 的前数据主管谈论进军数据科学的策略

苏珊·霍尔科姆目睹了一切。
2014 年,她加入了世界上第一家智能手表公司 Pebble,成为他们第一位专注于数据的员工。她将继续领导他们的整个数据科学工作,并面临艰巨的挑战,即如何处理一种在此之前几乎没有人遇到过的数据。
苏珊也是一个有天赋的社交高手:如果你不知道如何接触合适的人,你不可能从一个物理学研究生辍学成为硅谷最热门的创业公司之一的数据主管。
我们与 Susan 坐下来讨论了成就伟大数据科学事业的技能(和个性),以及她为那些希望在数据科学领域引起招聘经理注意的人提供的屡试不爽的建议。请点击这里查看,别忘了在 Twitter 上关注她,地址为 @h0lc0mb !
额外收获:一定要去看看苏珊的网站:datasciencecareermap.com
请听下面的对话:
基于 RetinaNet 的航空图像行人检测
用数据做很酷的事情!
介绍
航空图像中的目标检测是一个具有挑战性和有趣的问题。随着无人机成本的降低,产生的航空数据量激增。拥有能够从航空数据中提取有价值信息的模型将非常有用。 Retina Net 是最著名的单阶段探测器,在这篇博客中,我想在来自斯坦福无人机数据集的行人和骑车人的航拍图像上测试它。请参见下面的示例图像。这是一个具有挑战性的问题,因为大多数物体只有几个像素宽,一些物体被遮挡,阴影中的物体更难检测。我读过几个关于空中图像或汽车/飞机上的物体检测的博客,但是只有几个关于空中行人检测的链接,这是特别具有挑战性的。

Aerial Images from Stanford drone dataset — Pedestrians in pink and Bikers in red
视网膜网
RetinaNet 是一款单级检测器,使用特征金字塔网络(FPN)和焦点损失进行训练。特征金字塔网络是这篇论文中介绍的一种多尺度物体检测结构。它通过自上而下的路径和横向连接将低分辨率、语义强的特征与高分辨率、语义弱的特征结合起来。最终结果是,它在网络的多个级别上产生不同比例的特征图,这有助于分类器和回归器网络。
焦点损失被设计成解决具有不平衡的单阶段对象检测问题,其中存在非常大量的可能背景类别,而只有少数前景类别。这导致训练效率低下,因为大多数位置是没有贡献有用信号的容易否定的,并且大量的这些否定例子淹没了训练并降低了模型性能。焦点损失基于交叉熵损失,如下所示,通过调整伽马参数,我们可以减少分类良好的示例的损失贡献。

Focal Loss Explanation
在这篇博客中,我想谈谈如何在 Keras 上训练一个 RetinaNet 模型。我没有对 RetinaNet 背后的理论做出足够的评价。我用这个链接来了解这个模型,并强烈推荐它。我的第一个训练模型在空中探测物体方面表现很好,如下图所示。我还在我的 Github 链接上开源了代码。

Retina Net on Aerial Images of pedestrians and bikers
斯坦福无人机数据集
斯坦福无人机数据是无人机在斯坦福校园上空收集的航拍图像的海量数据集。该数据集是理想的对象检测和跟踪问题。它包含大约 60 个空中视频。对于每个视频,我们有 6 个类的边界框坐标——“行人”、“骑车人”、“滑板者”、“手推车”、“汽车”和“公共汽车”。行人和骑车人的数据集非常丰富,这两个类别覆盖了大约 85%-95%的注释。
在斯坦福无人机数据集上训练 Keras 的 RetinaNet
为了训练视网膜网络,我在 Keras 中使用了这个实现。它有很好的文档记录,并且工作时没有错误。非常感谢 Fizyr 开源他们的实现!
我遵循的主要步骤是:
- 从庞大的斯坦福无人机数据集中选择图像样本来构建模型。我拍摄了大约 2200 张训练图像和 30,000 多个公告,并保留了大约 1000 张图像进行验证。我已经把我的图像数据集放在 google drive 这里给任何有兴趣跳过这一步的人。
- 生成 Retina Net 所需格式的注释。Retina Net 要求所有注释都采用格式。
path/to/image.jpg,x1,y1,x2,y2,class_name
我将斯坦福注释转换成这种格式,我的训练和验证注释被上传到我的 Github 。
- 调整锚点大小:Retina 网的默认锚点大小为 32、64、128、256、512。这些锚的大小对大多数物体来说都很好,但是因为我们是在航拍图像上工作,一些物体可能小于 32。这个回购协议提供了一个方便的工具来检查现有的锚是否足够。在下图中,绿色的注释被现有的锚点覆盖,红色的注释被忽略。可以看出,即使对于最小的锚尺寸,很大一部分注释也太小了。

Retina Net with default anchors
所以我调整了锚,去掉了最大的 512 号锚,代之以一个 16 号的小锚。这导致了显著的改进,如下所示:

After adding a small anchor
- 有了这些,我们准备开始训练。我保留了大多数其他默认参数,包括 Resnet50 主干,并通过以下方式开始训练:
keras_retinanet/bin/train.py --weights snapshots/resnet50_coco_best_v2.1.0.h5 --config config.ini csv train_annotations.csv labels.csv --val-annotations val_annotations.csv
这里的重量是可可重量,可用于跳跃开始训练。用于训练和验证的注释是输入数据,config.ini 具有更新的锚大小。所有文件也在我的 Github repo 上。
就是这样!模型训练起来很慢,我连夜训练。我通过在测试集上检查平均精度(MAP) 来测试训练模型的准确性。从下面可以看出,第一个经过训练的模型具有非常好的 0.63 的 MAP。在从空中容易看到的汽车和公共汽车类上,性能尤其好。骑自行车的人级别的地图很低,因为这经常被行人混淆。我目前正致力于进一步提高自行车手职业的准确性。
Biker: 0.4862
Car:0.9363
Bus: 0.7892
Pedestrian: 0.7059
Weighted: 0.6376
结论
Retina Net 是一个使用特征金字塔网络的强大模型。它能够在非常具有挑战性的数据集上检测空中的物体,其中物体尺寸非常小。我花了半天时间训练了一个视网膜网络。经过训练的模型的第一个版本具有相当好的性能。我仍然在探索如何进一步适应视网膜网络架构,以在空中检测中具有更高的准确性。这将在我的下一篇博客中讨论。
我希望你喜欢这个博客,并尝试自己训练模型。
我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助许多初创公司部署了基于人工智能的创新解决方案。在 http://deeplearninganalytics.org/的入住我们的酒店。
你也可以在https://medium.com/@priya.dwivedi看到我的其他作品
如果你有一个我们可以合作的项目,请通过我的网站或 info@deeplearninganalytics.org 联系我
参考
基于非最大抑制算法的行人检测
检测路上行人的完整管道

Photo by David Marcu on Unsplash
被困在付费墙后面?点击这里阅读完整故事与我的朋友链接!
行人检测仍然是计算机科学中尚未解决的问题。虽然已经研究了许多目标检测算法,如 YOLO、SSD、RCNN、快速 R-CNN 和更快 R-CNN,并取得了巨大的成功,但是拥挤场景中的行人检测仍然是一个公开的挑战。
近年来,在人的密度高的真实世界场景中,即机场、火车站、商场等,迫切需要行人检测。尽管已经取得了很大的进步,但是在这些场景中检测行人仍然是困难的,这由现有技术方法的显著性能下降所证明。在这篇文章中,我将介绍一种被称为非最大抑制的高效可扩展算法,用于解决拥挤场景中的行人/人物检测。
应用程序
- 自动驾驶汽车。识别道路场景中的行人
- 安全。限制某些人进入某些地方
- 零售。分析超市内顾客的行为
- 时尚。识别特定品牌和穿着它们的人
数据
我从 这里 下载了测试用的图片。然后,我将图像压缩到 300*200 的大小,并使用这些图像作为这个项目的测试图像。
非最大抑制
结合支持向量机(SVM)** 的定向梯度历史(HOG)** 对于检测图像中的对象已经相当成功,但是这些算法的问题是它们检测图像中对象周围的多个边界框。因此,它们不适用于我们在拥挤的道路上检测行人的情况。这就是非最大抑制(NMS)来拯救更好地细化检测器给出的边界框的地方。在该算法中,我们提出了额外的惩罚来产生更紧凑的包围盒,从而变得对 NMS 阈值不太敏感。对于具有贪婪 NMS 的管道下的人群,理想的解决方案是设置高阈值以保留高度重叠的对象,并为所有实例预测非常紧凑的检测框以减少误报。
环境和工具
- sci kit-学习
- sci kit-图像
- numpy
- opencv
代码在哪里?
事不宜迟,让我们从代码开始吧。github 上的完整项目可以在这里找到。
我从计算我们的算法检测到的两个边界框之间的重叠区域开始。

然后我定义了一个函数,将包围盒矩形和阈值因子作为输入。我还按照右下角坐标值的降序排列了所有的边界框。之后,我将所有不在重叠区域 0.5 倍范围内的盒子附加到另一个盒子上。
加载所有需要的库的时间。
然后我创建了一个函数,将所有边界框的四个端点坐标添加到一个空列表中。
之后,我为图像定位创建了一些解析器参数,缩小它,可视化它,并对它应用阈值。
然后是算法的核心部分。我使用了一个 pickle 文件,它是在数千张有行人和没有行人的图像上训练后生成的。此外,我将图像转换为灰度,并在其上应用了步长。
之后,我创建了一个脚本来查找和定位所有在 nms 函数定义的阈值内的边界框,如上所示。请注意,下面的脚本乍一看可能有点吓人,但它只是简单的数学。
最后,我写了一些行来显示应用 NMS 前后的图像,并保存了我得到的输出图像。
评估行人检测模型
对象检测问题中的每个图像可能具有不同类别的不同对象。因此,在图像分类问题中使用的标准精度度量不能直接应用于此。这就是 mAP(平均精度)发挥作用的地方。
地面实况; 真值(机器学习)
对于任何算法,总是在与真实数据的比较中评估指标。我们只知道训练、验证和测试数据集的基本事实信息。
对于对象检测问题,基础事实包括图像、图像中对象的类别以及图像中每个对象的真实边界框。

Sample Example
计算地图
让我们说,原始图像和地面真相注释是我们在上面看到的。训练和验证数据以相同的方式对所有图像进行注释。该模型将返回大量预测,但其中大多数预测的相关置信度非常低,因此我们只考虑高于某个报告置信度的预测。我们通过我们的模型运行原始图像,这是在置信度阈值化之后对象检测算法返回的结果。
我们首先需要知道来判断这些检测中每一个的正确性。告诉我们给定边界框的正确性的度量是并集上的交集。
计算欠条
交集/并集是预测框和基础事实框的交集和并集之间的比率。

结果

Input Image

Output Image
结论
虽然像 Mask R-CNN 这样的算法已经突破了边界,并被认为是最先进的实例分割算法,但像行人检测这样的问题仍然提出了许多公开的挑战。如果图像包含聚集在一个位置的许多人,非最大抑制算法仍然失败。这个项目远未结束。事实上,它提出的问题比它回答的问题还多。无人驾驶汽车的未来在很大程度上取决于高效的行人检测算法。
参考资料/进一步阅读
追踪行人的完整管道。
medium.com](https://medium.com/@abhinav.sagar/pedestrian-tracking-in-real-time-using-yolov3-33439125efdf) [## 自适应 NMS:改进人群中的行人检测
人群中的行人检测是一个非常具有挑战性的问题。本文通过一种新颖的非最大值方法来解决这个问题
arxiv.org](http://arxiv.org/abs/1904.03629) [## Python - PyImageSearch 中对象检测的非最大抑制
康涅狄格州很冷。非常冷。有时候早上起床都很难。老实说,如果没有…
www.pyimagesearch.com](https://www.pyimagesearch.com/2014/11/17/non-maximum-suppression-object-detection-python/)
在你走之前
相应的源代码可以在这里找到。
使用非最大抑制的行人检测。行人检测仍然是计算机领域尚未解决的问题…
github.com](https://github.com/abhinavsagar/Pedestrian-detection)
联系人
如果你想了解我最新的文章和项目,请关注我的媒体。以下是我的一些联系人详细信息:
快乐阅读,快乐学习,快乐编码。
惩罚均值:调整异常值的新方法?

在处理真实世界的数据时,知道如何处理异常值是一个标准,例如,这篇文章涵盖了检测异常值的统计和机器学习方法。一旦我们知道数据中有异常值,我们可以做以下两件事之一:
- 扔掉它们。
- 使他们正规化。
在接下来的部分中,我们将简要介绍两篇学术文章,介绍用于异常值正则化的离散函数,但是如果值为正,我们也定义一种正则化它们的新方法。我们所理解的离散函数是,给定一个变量 x ,它只能从给定的集合ω中取值,其中每个元素都是相互“分离”的。
1.L-1 范数误差函数稳健性和异常值正则化
这篇由丁宏强和蒋波撰写的文章的动机是使用线性函数来拟合数据。假设你在一个二维空间中有( x , y )个真实数据,如图 1。其中拟合值(红线)由 f、 表示

Figure 1. The blue points represent observed data, the red line represents a linear fitted model and the dashed line a δ threshold.
那么我们说,如果理论预测值 f 与真实值 y 之差大于 δ,那么这个数据点就是异常值。 换言之,

Figure 2
符号函数在哪里

Figure 3. The sign function
并且 δ 是具有将观察值 y 拉回红色虚线的效果的公差参数,而与距离平均值有多远无关。
2.矢量数据的异常正则化与 L21 范数稳健性
由同一作者在第一篇文章的一个月后写的这篇文章是上述技术的矢量化方法。在这种情况下,我们将真正的观察点移动一些单位远离观察点【f】,** 如图 4 所示。**

Figure 4
等同于之前,我们现在在边界处有 y (由图 1 中的虚线表示)。我们在图 4 中看到,通过归一化向量,拉回是有保证的。
总的来说,这些技术结合了期望极限值 、δ 、前沿的信息。现在我们将看到一种不同的方法来整合某个阈值之间的实值【y】,但是如果您想深入了解该函数的等效连续表示,请查阅论文。
3.惩罚平均值
如果我们假设我们的数据是正态分布的(至少 30 个观察值),传统的规则是丢弃一个观察值 x ,如果它离平均值有两个标准偏差。惩罚均值不仅调整上述数据,而且当 x 偏离均值一个标准偏差时也调整。正如我们在图 5 中看到的。

Figure 5
在第一种情况下,我们注意到 2σ/(x-μ) 小于 1,因此 x 被拉回一个标准差和两个标准差之间,稍后我们将看到理论上的证明。而在第二种情况下,将σ移到第二项的分母后,我们注意到

Figure 6
由于1<(x-μ)/σ<2的事实,这对于确保满足不等式μ<x<μ+σ具有巨大的效果。现在,让我们看看当观察值位于中值μ的左侧时会发生什么,正则化函数发生变化,因为现在我们正在搜索大于 1 的参数γ,使得 x 向右移动。

Figure 7
例如,当 x < μ - 2σ 时,我们有那个(μ-x)/2σ>1,因此新的值 x 将大于它本身。对于理论上的证明,我们将在下一节正式证明。
4.区间对齐
现在我们要确定正则化后 x 的值。这一部分是技术性的,所以你可以跳过它,但它是有用的尝试肌肉大脑(只是代数!).
案例一。x < μ-2σ
After some algebra, we arrive at

Figure 8
where, we notice that
案例二。μ-2σ<x<μ-σ
基于这样一个事实

Figure 9
我们采用的方法是在最后一个不等式中限制横向极限。关于上限,我们执行以下操作。

Figure 10
对于下限,我们再次注意到 1 > σ / (μ-x) ,有了这个我们就有了那个

Figure 11
然后将所有术语组合在一起,间隔为

Figure 12
案例三。x+σ < x < x + 2σ
The initial inequality is

Figure 13
whereas before we’re trying to find upper and lower bounds. First, we notice that

Figure 14
To find an upper bound for this value, we’re going to do some algebra with the denominator. We should arrive at

Figure 15
Concerning the other bound in Figure 13, the algebra is analogous, but we would use a different trick as follows (where we have factorized some terms in the denominator x + σ + σ²/(x-μ))

Figure 16
where the last inequality is satisfied only if x > σ((2σ/μ)-1)。 考虑到最后一个操作,我们得出的区间是

Figure 17
案例 4。x > x + 2σ
现在,我们将正式证明我们之前说过的,即 x 取比 x + 2σ 小的值。证据如下

Figure 18.
注意到(2σ)/(x-μ)<1,我们得出结论x 满足这样一个事实,即将取值到 x + 2σ 的左边。**
5.结论
我发现这两篇论文的一个不方便之处是阈值δ决定正则化值的位置。如果理论上找不到δ的最佳值,这可能会导致主观操纵数据。此外,如果假设数据中的噪声是高斯噪声,那么其中的 5%将采用固定值μ + δ或μ-δ。
在罚平均中,我们发现 x-(σ /(μ-x))并不总是正的,所以取绝对值来代替是方便的,但是预期的效果或使观察规律化并不令人满意。一般来说,这种方法对于位于μ右边的值更有效,因为对于 x < μ,如果值足够小,正则化根本无效,正如我们在第四节中所展示的。
最后,我们还没有涵盖可以应用这种正则化的实际案例,这可能是第二部分。
参考
【丁与江,2017】c .丁与 b .江。L-1 范数误差函数稳健性和异常值正则化。arXiv:1705.09954v1,2017。
【丁与江,2017】c .丁与 b .江。向量数据的异常正则化和 L21 范数稳健性。arXiv:1706.06409,2017。
分类惩罚回归

Image by Jill Wellington from Pixabay
在之前的中,我们研究了使用 JMP 的套索和弹性网正则化方法。这些模型是为了预测连续变量而构建的,所以这次我们将研究分类变量。更具体地说,这是一个二分法变量,它决定了葡萄酒的质量是“好”还是“坏”。我们将使用普通和惩罚逻辑回归构建几个模型,包括套索和弹性网技术的基本和自适应形式,最后我们将使用我们的“最佳”模型基于新数据进行预测。
我们将采用与之前研究略有不同的模型分析方法。在创建单个分类模型时研究它们的统计数据给我们的信息非常少,所以我们将保存每个新创建的模型的预测。这些预测将在以后进行比较,以确定每个模型的准确性和其他统计数据。
在这项研究中,我们将看看在 2009 年的一项研究中影响人类专家如何对葡萄酒质量进行排名的一些因素。参与者用 1-10 的等级给一些葡萄酒打分,并记录下每个样品的各个方面。出于本研究的目的,4 分或更低的评级被视为“差”,而 5 分或更高的评级被视为“好”这给了我们一个二元反应变量,每种酒要么是好的,要么是坏的。该数据集中总共包括 12 个预测值,如酸度、柠檬酸、残糖、酒精含量和颜色。
第一步是准备将这个数据集输入到我们的模型中。我们再次使用了 60/20/20 的培训/验证/测试划分。然而,因为这是横截面数据,JMP 的验证列工具将观察值分配给每个名称,以确保随机性。大约 3,900 个观察值用于训练每个模型,而 1,300 个观察值用于验证和测试每个模型。然后,我们创建了一个包含所有变量的基本逻辑回归模型。这有助于稍后提供一个基线,以查看使用所有预测变量的模型与其他更专业的模型相比表现如何。
基本和自适应套索方法
在最初的分析之后,我们开始寻找广义回归,以追求更准确的预测模型。广义回归在必要时对某些变量施加惩罚,以减少它们对模型的影响。我们从套索开始,用基本方法和自适应方法创建了预测模型。套索法使用了 12 个预测变量中的 8 个,减少了密度、pH 值、硫酸盐和总二氧化硫对模型的影响。自适应套索采取了不同的方法,仅使用 7 个预测变量。pH 值、硫酸盐和二氧化硫总量的影响再次减弱。

Adaptive Lasso Variable Estimates
基本和自适应弹性网格方法
下一组模型使用弹性网络方法,再次使用基础和适应性方法。弹性网模型确定 12 个变量中有 9 个对模型很重要。总二氧化硫、pH 值和硫酸盐水平再次降低。我们已经可以预计,最终的模型可能不会使用这 3 个变量,这表明它们对葡萄酒的质量等级影响很小。使用自适应弹性网显示了类似的结果,同时也排除了柠檬酸和氯化物。使用新的弹性网络模型,我们创建了两组新的预测,并保存每组预测,以便与 lasso 和普通逻辑回归模型进行比较。

Parameter Estimates for Adaptive Elastic Net Model
模型比较
保存了 5 组预测后,我们现在可以比较模型的准确性,看看某个特定的方法是否更好。正如我们在下面看到的,每个模型似乎都有一个相对较低的 R 平方值。然而,这个问题不同于以往的研究,我们的反应变量是如何分类的。在这项研究中,我们的响应变量是分类变量,而不是与实际值有可测量距离的数值。换句话说,预测和真实值之间不可能有距离,因为每个预测不是好就是坏。因此,R 平方值提供的信息非常少,因此我们需要查看其他度量来比较模型。更具体地说,我们想要观察错误分类率和 ROC 曲线下的面积。
错误分类率给出了预测出错频率的估计值。我们希望将这一比率降至最低,以减少对葡萄酒质量进行错误分类的机会。另一个指标,ROC 曲线下面积(或 AUC)理想地尽可能高。
ROC 曲线绘制了模型对 1 减去其特异性的灵敏度。这有助于表明我们的模型的真阳性率(模型正确猜测好酒的能力)是如何受模型的假阳性率(劣质酒被归类为好酒的频率)影响的。计算该图下的面积,并将其用作模型准确性的另一种度量。一个完全准确的模型的 AUC 为 1.0,表明其预测 100%正确。我们希望找到一个具有最高可能 AUC 的模型,这与更高的模型准确性相关。

Comparison of Classification Model Predictions
我们可以在上面的模型比较中看到 R 平方值有多低。由于我们的响应变量的性质,很难从这些值中提取任何有意义的数据,因此我们可以安全地忽略这些测量。相反,如上所述,我们将在测试数据集上比较每个模型的误分类率和 AUC。我们可以看到自适应套索和弹性网模型的错误分类率最低,相差很小,因此我们将这两个模型视为最佳模型。自适应套索的 AUC 为 0.7862,而自适应弹性网的 AUC 为 0.7861。因为这两个值非常接近,所以有理由认为这两个值中的任何一个都最适合我们的数据。为了这项研究,我们将使用适应性套索作为我们的最佳选择。
这里需要注意的一点是,每个型号的值非常相似。我们模型的错误分类率是 0.039 或 0.040,AUC 也在一个很小的范围内。这是因为在我们的数据集中只有几个变量和大量的观察值。在有限的变量池中,每个模型的构建方式几乎没有什么差异,因此预计模型会具有相似的精度。

Variable Use in Adaptive Lasso Model
在上图中,我们可以看到在自适应套索模型中选择使用的变量。正如所料,pH 值、硫酸盐和总二氧化硫在模型中的权重为 0。柠檬酸和氯化物也被认为是多余的或不需要的,所以它们的值被缩小到 0。挥发性酸度、颜色和残糖似乎对葡萄酒等级有很大的影响。挥发性酸度的负系数约为-5.54,表明与葡萄酒等级呈负相关。换句话说,随着酸度的增加,被归类为“好”的概率会降低。残糖有相反的关系,糖含量的增加往往会增加一个好的评级的机会。酒色[红白]在自适应套索模型中也有正系数。这表明红葡萄酒比白葡萄酒更有可能被评为好酒。

Variable Importance on Model
我们可以更进一步,研究每个变量在我们的预测模型中的重要性。在上图中,残糖似乎是葡萄酒质量分类的最大因素。约 47%的预测受残糖水平影响,41%受密度影响,34%的分类受挥发性酸度影响。基于我们的发现,有理由假设含糖量较高、密度较低的葡萄酒更有可能被归类为好酒。
对新数据的预测
我们还可以使用我们的模型来预测新酒的分类。假设新酒有以下数据,我们可以预测它会被归类为好还是坏:
固定酸度=8.9
挥发酸度= .90
柠檬酸=.35
pH=3.20
残糖=4.8
氯化物=.09
游离二氧化硫=4
总二氧化硫=40
密度=.99
硫酸盐=.65
酒精=9.0
颜色=红色

我们知道我们选择的自适应套索模型只使用了 7 个变量。将这些值输入到我们的模型中提供了 0.9516 的概率。因此,我们可以估计这种特殊的葡萄酒被归类为“好”的概率约为 95%。
这项研究的数据可以在我的GitHub(wine quality study . jmp)上找到。
JMP 的惩罚回归

Image by Kevin Schneider from Pixabay
这个简短的研究使用惩罚回归来预测银价,基于一些金融指标。惩罚逻辑回归或正则化是一种逻辑模型,它惩罚或减少某些变量的影响。当数据集有大量变量,但对哪些变量在回归模型中特别有用几乎没有指导时,使用正则化技术。为了避免模型过度拟合数据,正则化技术会施加一个惩罚来减少一些变量的影响,而不会直接将它们从方程中删除。这理想地给了我们一个模型,表明哪些变量对预测值的影响比其他变量更大。建立惩罚回归模型有许多方法,但这项研究将着眼于套索和弹性网方法。对于软件,我们将使用 JMP,它有一些有用的内置工具在需要时调整我们的模型。
该数据集包括描述汇率、利率和股票市场的 90 个不同变量的信息。我们只对描述基本变量的回报和滞后回报值的 60 个子集感兴趣。我们使用不同的方法建立了多个模型,以确定哪一个模型可以根据全球指标提供最准确的白银价格预测。套索和弹性网方法是我们的主要焦点,既是一种基本形式,也是一种适应性技术。我们还使用柯西分布建立了一个模型,以理想地忽略异常值的影响,并在标准化数据集上提供更准确的预测。
我们首先使用所有潜在变量建立一个标准的最小二乘模型。数据集被分割成 60/20/20 的部分,以创建训练、验证和测试数据集(这种分组也用于剩余的模型构建)。基本的标准最小二乘模型使用所有 60 个感兴趣的变量。这有助于在假设所有变量对银的预测值有显著影响的情况下提供基线。它还通过研究每个变量在这个天真模型中的影响,让我们预览了哪些变量更有可能出现在我们的最终模型中。
“套索”技术
套索技术能够将变量的大小减少到绝对 0,有效地消除它们对预测模型的影响。在寻找最佳拟合模型的过程中,我们同时使用了基础和自适应套索方法。我们的第一个模型,使用基础套索方法,在测试集上产生了大约 0.365 的 R 平方值。虽然不是一个过高的值,36.5%是一个相对有用的模型。0.0189 的 RMSE 也表明这是一个好模型。我们还可以看到,该模型的 lambda 值为 0.06。当λ较小时,该模型类似于最小二乘估计。然而,随着它的增加,变量值接近或达到 0。在这个模型中,λ相对较小,表明变量不需要太多的正则化。

Basic Lasso Model Results
套索和弹性网的自适应版本试图在变量对响应变量有实际影响时减少对变量的惩罚。自适应模型应该提供类似于那些你在模型中发现的对响应变量有实际影响的变量的估计。不同之处在于,适应性模型仍然使用所有变量,只是惩罚无用的变量,使其影响可以忽略不计。
使用自适应套索方法构建一个新模型提供了一个稍微不同的结果。我们的 R 平方值下降到 0.302,而 RMSE 上升到 0.02。套索和弹性网的自适应版本试图在变量对响应有实际影响时减少对变量的惩罚。我们可以在仅为 0.011 的新λ值中看到这一点的证据。该模型在识别应该具有零系数的预测因子时变得更加可靠,同时减少了对某些变量施加的惩罚。

Adaptive Lasso Method Results
弹性网格法
弹性网格法的最大优点之一是它结合了套索法和岭法的技术,套索法基于系数值的总和进行惩罚,岭法基于系数值的平方和进行惩罚。简而言之,弹性网方法基于相关性形成变量组。然后,当它找到一个强预测值时,它会将整个相关组包含在模型中。这在理想情况下避免了仅仅因为变量相关就忽略变量而丢失信息,但是仍然会施加惩罚以减少某些变量可能产生的影响。我们创建了两个模型,一个使用基本方法,另一个使用自适应方法,来研究与套索方法的区别。

Standard Elastic Net Method
标准弹性网模型在测试集上给出了 0.365 的 R 平方值,这非常接近套索模型。RMSE 几乎是相同的,以及我们的 lambda 惩罚这个模型。这表明,当使用弹性网格方法时,由套索模型选择和惩罚的变量被给予相同的处理。使用这种技术的自适应方法给出了与自适应套索模型相似的结果。我们在测试集上收到了一个 R 平方为 0.301、RMSE 为 0.021 的模型,它模拟了自适应套索模型的结果。λ值再次变小,仅为 0.011,表明在某些变量上更加宽松。

Adaptive Elastic Net
使用柯西分布

Adaptive Lasso Method Using Cauchy Distribution
我们还想尝试使用柯西分布建立一个模型。简而言之,柯西分布有助于消除主要异常值的偏差。理论上,这有助于创建更可靠的模型,因为极值对总体分布的影响较小。我们使用自适应套索技术和柯西分布建立了一个模型。这计算出 0.408 的广义 R 平方值和 5.347 的显著λ损失。这表明使用柯西的模型对不太重要的变量有更多的限制。
模型比较
建立了 6 个模型后,我们最终可以比较结果,以确定最适合我们的数据。将预测值放入一个比较模型中会稍微改变值,但是我们仍然能够从发现中做出一些决定。我们可以看到,基本的套索和弹性网方法似乎对测试数据具有最高的准确性,R 平方值约为 0.408。最差的模型是我们的标准最小二乘模型,它在测试数据集上几乎没有可靠性。我们的套索和弹性网模型的 RASE 约为 0.0189,AAE 是另一个理想的低值,仅为 0.0149。因此,我们可以假设弹性网或套索方法将给出我们的最佳拟合模型。因为弹性网稍微好一点,我们将使用这种方法进行最终分析。

Comparison of Models
调查的结果
回到我们的基本弹性网络模型,我们可以看到 7 个变量对 RSLV 的预测值有可测量的影响。一些变量涉及国际汇率;RFXF、RFXA、RFXC、RFXS 和 LRFXY 都表明相关国家的货币价值对白银价格有影响。虽然不是特定于一个国家,RTIP 是计算通货膨胀的措施。作为双金属标准之一,难怪如此多的货币因素对预测白银的价值有影响。

Variable Selection via Elastic Net Method
在下图中,我们可以看到按对模型的重要性列出的变量。RFXF,瑞士法郎的价值,解释了超过 35%的预测模型。指示石油价格的 RUSO 通常不会与白银的价值联系在一起。然而,令人惊讶的是,它在这个模型中占了 20.5%。材料行业指数 RXLB 仅排在第三位,约为 18.6%。由于白银在电子产品和其他制成品中的重要性,有理由预计材料指数将是这种金属价值的高度预测指标。

Variable Importance in Elastic Net Model
这项研究中使用的数据集可以在GitHub(silverweekly study . jmp)上找到。
潘德雷肯四:多智能体强化学习与命运大秩序

Three initial characters left to right Jeanne d’arc alter (Jalter), Ishtar, Artoria Pendragon
多代理强化学习在一个定制的游戏环境中训练 4 个代理,并让他们玩手机游戏命运大令

Github 回购此处
一年多前,我在《走向数据科学》上发表了我的第一个系列博客帖子,我亲切地称之为项目潘德雷肯,讲述了我如何建立一个强化学习(RL)环境,成功培训 RL 代理,并为制作了一个 API,允许他们提取信息并向手机游戏《命运大令》(FGO)发送命令。这篇文章将讲述我是如何重新选择这个项目并训练 4 个 RL 代理一起玩这个游戏的。
我为 FGO 建造机器人的一个主要动机是,游戏要求玩家多次通关,以便为事件、关卡角色、技能等收集所需的材料。我说的多次是指 5 分钟 40-50 次,这种情况并不少见。所以能够让一个机器人为我执行这些重复的任务是非常好的。
命运大订单快速汇总
FGO 是一个游戏,你选择 1 到 6 个角色,并使用该团队通过各级战斗,直到所有的敌人或团队被击败。在任何给定的时间点,你可以有 3 个角色在场上,他们每个人都有 3 种能力。这些能力可以是攻击增益,治疗法术等。

Initial combat screen showing three characters with their 3 abilities below their portraits
在 FGO 战斗的主要机制是,战斗是通过从你的牌组中抽取 5 张牌中的 3 张牌来完成的。我之前的 FGO 机器人 pendragon alter 专注于以有效的方式挑选这些卡片。

example of 5 cards being dealt for a round of combat
每种类型的卡都有自己的属性,以不同的组合使用它们有不同的好处。例如,玩三张相同颜色的牌会在基础牌的基础上增加额外的奖励。奖金的性质取决于卡的类型。艺术卡(蓝色)充能强大的终极能力,巴斯特卡(红色)造成额外伤害,快速卡(绿色)创造致命一击的可能性,双倍伤害。我试着解释对这篇文章很重要的游戏机制,但是你也可以看看上一篇文章,更详细地了解组成 FGO 的其他卡片机制。
自从训练了那些最初的捡卡 RL 代理,我没有做太多的工作来增加额外的功能来改善我的机器人或提高他们的游戏水平。然而最近我很受鼓舞去改进它们,因为在过去的一年里 RL 发生了很多有趣的事情。

example round of combat from Pendragon Four where bot 2 plays a skill while bots 1 and 3 choose to pass. The card bot picks cards from the dealt hand. All actions are sent through a custom API to do the combat actions.
启发训练另一轮命运大令机器人
对我来说,看到 OpenAI 和 Deepmind 等研究小组的进展以及他们在将强化学习应用于游戏方面的成功真的很令人兴奋,这些游戏包括 Dota 2 (OpenAI Five)和 Starcraft 2 (Alphastar),它们都达到了极高的游戏水平。虽然他们确实有缺点,但我仍然觉得令人印象深刻。最初,我发现最令人兴奋的是 Alphastar,因为我是星际争霸 2 的长期玩家。然而,通读 OpenAI Five 文献激励我致力于更多的强化学习,目标是对我的 FGO 机器人采取额外的步骤。
当我为 FGO 制作我最初的 RL 代理时,我专注于有效地挑选卡片。这使得我的机器人可以清除大量的游戏内容,但这意味着我没有使用我上面提到的个人角色能力。这些能力非常强大,可以帮助人类玩家更快地成功通关,并清除更难的内容。所以我的想法是,如果我能把能力的使用结合到我的机器人中,这将是有益的。我不确定最好的方法是什么…我陷入了困境,直到我通读了 OpenAI 发布的帖子,这些帖子说明了他们如何训练 5 个独立的机器人,并让它们一起工作来玩。
进入正题
试图让一个代理人处理所有三个角色会造成混乱的行动空间,所以为每个角色训练一个代理人的想法帮助我在心理上简化了它,至少让我考虑开始解决这个问题。虽然我说这帮助我澄清了我头脑中的问题,但它也让我非常认真地考虑在一个我从未做过的环境中同时训练 3 个或者 4 个 RL 代理。
我的想法是有 3 个代理人(三个角色各一个),他们的行动空间是他们可以使用的法术加上一个允许他们通过的节点(行动空间大小为 4)。然后增加一名代理人来挑选卡片。如果我能训练这 4 个代理人合作,那么我很可能能够制造一个 FGO 机器人,它可以清除更难的内容,或者至少比我以前版本的机器人更快地清除内容。
考虑到这个最终目标……完成这个目标需要什么?对我以前的游戏环境做了很多升级,以前的游戏环境只是为了选卡而设计的。
- 初始约束
- 代表字符:
- 环境升级
- 建筑代理

All three bots opt to pass on round 2 turn 3. One of the main things the bots had to learn was when to pass vs when to play skills since they are only allowed to use a skill once per game.
初始约束
虽然我希望能够训练我的机器人在一个完美的 FGO 再造中,拥有所有的角色,技能,敌人等等,但对我来说,建造所有这些是不可行的。即使对于 OpenAI 和 Deepmind 这样的团队来说,看到他们限制游戏的哪些方面以及随着他们构建更复杂的代理,这一列表如何随着时间的推移而变化也总是很有趣。
对于这个版本的多代理 FGO 机器人,我有一些值得注意的限制。
- 我目前只有 3 个角色可用,我用它来进行初始训练和测试。
- 技能可以用,但是每局只能用一次。我没有增加冷却机制来允许机器人多次使用它们。
- 机器人被训练成按照一定的顺序战斗。在培训中,我将代理放在位置 1、2 或 3(视觉上是从左到右),这构成了团队组成的顺序。在未来,我的想法是在地点 1 和 2 放置支援型角色,地点 3 保留给伤害处理角色,这样地点 1 和 2 可以学习支援角色 3。我将在团队合作部分详细讨论这一点。
- 崇高幻象(NPs):是 FGO 中人物拥有的强大终极能力,通常会造成大量伤害。NP 在使用前必须充电到 100%,这在“NP 量表”中进行跟踪。我没有选择何时使用它的机器人。相反,在训练中,我让他们一充电就使用他们的 NPs。
- 机器人必须前后作战:它们不能选择目标,所以它们按照从前到后的顺序与敌人作战。这将使机器人玩某些关卡更加困难,因为更强的敌人通常被放在后面,人类玩家会选择他们并首先集中火力打倒他们,而不是首先杀死他们前面的敌人。
好了,现在让我们开始吧!

Sample turn where 2 bots (Jalter and Ishtar) decide to play skills, one (Artoria) passes and and card bot selects combat cards
代表字符
因为这个项目的主要目标之一是为每个角色添加单独的代理人,让他们作为一个团队一起通关…我首先需要解决的事情之一是如何表现每个角色。这是我去年制作最初的机器人时想做的事情,但当时我是一名非常弱的软件工程师,所以我把它推迟到了以后的日期,我想现在是时候了。
我所做的是设置了一个名为[HeroicSpirit](https://github.com/sugi-chan/multiagent_pendragon/blob/hal_way_to_policy_gradient/fgo_environment/heroic_spirt.py#L72)的通用类,它包含了代理的神经网络以及获取动作、训练/更新网络、跟踪技能和三个角色技能本身的方法。这个通用类为管理和维护我在游戏中的角色设置了框架,然后我为我想要训练的每个特定角色创建了HeroicSpirit子类。
在大多数情况下,每个子类只需要调整新角色的特性。这将是像角色特定的技能和能力,而簿记功能在主HeroicSpirit类中保持。如果我只是添加通用特性,这有助于最小化我需要更改/更新的代码量。
例如,当我开始编写原型代码时,我没有意识到要跟踪主动能力、每个角色当前的伤害调整值,并将这些能力的效果映射到他们应该针对的角色上需要付出多少努力,所以我不得不在HeroicSpirit上添加许多功能,这在三个地方进行更新会很烦人。
目前我有三个HeroicSpirit的子类,分别代表三个角色,他们的能力我已经编程出来了JAlter、Ishtar、ArtoriaSaber。对于我之前提到的每个角色来说,他们的技能在游戏效果中的表现是不同的,这是我必须要考虑的。目前,我用一个字典来表示技能,这个字典跟踪各种各样的值,这些值依赖于技能。例如Ishtar的第一个技能是战斗导向的全队伤害提升,用下面的字典来表示:
{‘name’: ‘sk1_’+self.spot+’_’+self.name+’_manifestation_of_beauty’, ‘target’: [‘hero1’,’hero2',’hero3'],
‘hp_boost’: 0,
‘np_boost’: .00,
‘critical_boost’: .00,
‘critical_star_boost’: .0,
‘dmg_boost’: (‘all’, .2),
‘duration’: 3}
所以对于每一个技能,我都会追踪它的名字,目标,以及各种效果,比如生命值提升,终极充能(np_boost),暴伤提升,暴星提升,伤害提升,持续时间。大多数技能只会对其中的几个领域产生影响,因此对于这个技能来说'dmg_boost': ('all', .2)意味着 20%的伤害提升会应用到团队中由target领域['hero1','hero2','hero3']上的点所代表的每个人身上。'duration': 3表示技能持续 3 回合。
另一方面,Ishtar的第二个技能侧重于充能她的终极能力,而不是直接战斗。
{'name': 'sk2_'+self.spot+'_'+self.name+'_gleaming_brilliant_crown', 'target': [self.spot],
'hp_boost': 0,
'np_boost': 50,
'critical_boost': .00,
'critical_star_boost': .0,
'dmg_boost': ('all', .0),
'duration': 1}
因此,虽然所有相同的类别都在技能词典中被追踪,但这个法术只针对Ishtar她自己,所以目标指向团队中 Ishtar 所在的任何位置,并且没有伤害提升。相反,伊师塔获得她终极能力的 50%充电。这个能力也只能持续一个回合。
我以三个字符开始,因为这是我想尝试并立即开始的最低限度。我之所以从这些角色开始,是因为他们都是以伤害为导向的角色,他们的能力是以战斗为导向的。在这个初始阶段,这有助于我简化我必须建立的东西的数量。未来的工作将是添加支持角色,这些角色的能力通常针对其他团队成员,对于清除游戏中较难的内容至关重要。
这些角色类建立了角色能做什么的框架,一般来说,我们将如何跟踪他们在做什么,所以现在我们必须为他们建立一个游戏环境。
环境升级
为了升级游戏环境,我不得不做很多事情来处理额外的复杂性,比如让每个角色同时激活,追踪 buff 和他们的效果,应用 buff 来修改他们的伤害输出。旧环境大约 400 行代码,新环境大约 1200 行代码。很多只是增加了新的功能来跟踪游戏的各个方面。
这种环境的基本结构是每场游戏由 3 轮战斗组成(反映 FGO 游戏水平),每一轮都变得更加困难,因此第三轮比第一轮要困难得多。在这种环境下,我通过增加机器人必须面对的对手的生命值和伤害来做到这一点。目前我还没有花时间去打造复杂的对手,但这是我将来可能会做的事情。
在我的HeroicSpirit类中,我有根据环境计算的奖励来采取行动和训练代理的功能。因此,在这个环境中,我需要能够处理这些输出,并将它们应用到游戏本身,并准备好将累积的奖励反馈给机器人。这归结为每个角色的几个步骤。
对于三个字符中的每一个:
- 生成他们的游戏状态和动作。我现在使用的游戏状态是回合数,回合数,一个活跃 buffs 的二进制列表(长度 9)和一个已经使用技能的二进制列表(长度 9)
- 如果他们在那回合没有选择
pass,那么我就获取那个技能,解析它,将那个技能的效果添加到角色的活动效果列表中。我通过将每个角色拥有的技能存储在一个“爱好者字典”中来追踪它们。因此,如果一个角色施放了一个影响团队中每个人的技能,那么每个角色都会将该技能添加到他们的“buff dictionary”中,以便在计算伤害时可以应用这些修正值。 - 做簿记来记录技能已经被使用,这样机器人就不会试图在冷却时间重复使用技能。
一旦所有的角色都走了,我会检查每个“buff 字典”中的所有 buff,并将每个角色 buff 中的相关修改应用到他们的伤害或其他战斗统计中。一旦这样做了,我就移除不再激活的 buffs,即duration = 0。在该回合的 buffs 被应用后,我会在游戏中选择战斗部分,如果相关的话,会应用机器人的效果。例如,如果一个角色有一个 20%的伤害缓冲,并且他的卡被选中,那么该卡的伤害将会增加 20%,以此类推。

Bot 1 and bot 3 (Jalter and Artoria) cast skills while bot 2 (Ishtar) passes
构建代理:奖励和无效移动
在我以前做过的 RL 项目中,我使用了深度 Q 学习器,并试图让它在任何给定的游戏状态下所有的动作都有效。然而,在这个项目中,我改用了策略梯度法,并允许无效的移动。这两件事都增加了我代码的复杂性,因为我以前从未实现过这些东西,但我认为它们值得升级。
政策梯度
深度 Q 学习者是代理人,他们的行为每一轮都会得到奖励,他们在任何给定的状态下都在有效地寻找最高的奖励。这是一个很好的方法,但对我来说,它经常需要游戏不同部分的工程奖励,我觉得我必须把我的意志/游戏风格强加给机器人。我仍然用我的拣卡机器人做这件事,它是我和三个角色代理一起训练的。解决这个问题的另一种方法是收集代理的所有动作和游戏状态,一旦游戏结束,就根据输赢之类的东西给小组分配奖励。这就是政策梯度方法的本质(正如我现在所理解的,我可能很容易就错了)。
起初,这似乎是一个有趣的想法,因为即使在亏损的情况下,也可能有好的和合理的举动,但它们将受到负面的惩罚。这是真的!但希望是,如果这确实是一个好的举措,那么随着时间的推移,它将有一个净积极的回报,代理人将学会在某些情况下做得更多。安德烈·卡帕西的 Pong to Pixel 博客在这个话题上非常出色!
目前,我没有对政策梯度使用纯+1 或-1 风格的奖励。如果他们使用了技能,我会在某些回合中使用修正值来尝试和激励技能的使用。对于游戏的大部分来说,机器人只是pass,这是有道理的,因为它们只有 3 个技能,我只允许使用一次,但游戏可能会持续 10-15 轮。所以他们倾向于习惯于在没有区别奖励的情况下度过大部分时间。
无效移动
接下来要考虑的重要事情是如何处理无效的移动。在这个游戏例子中,无效的移动是已经使用过的技能,现在处于冷却期,如果机器人试图在游戏中选择它,它实际上不会做任何事情。在过去,我通过设定游戏状态来避免这个问题,在游戏状态中,所有的移动在任何给定的时间都是有效的。这里的权衡是行动空间变得比考虑有效移动时要大得多。
例如,在我的拣卡机器人中,动作空间是 60,这代表了你可以从 5 张卡片中挑出 3 张卡片的每一种方式,其中顺序很重要。我这样做是因为虽然 60 的动作空间很大,但所有 60 个动作都是有效的。另一方面,如果我考虑到无效的选择,那么我可以有少至 3 个动作(每种牌类型一个),并让它通过查看它尚未挑选的牌来选择玩哪手牌。
我之所以一直避免这样做,是因为我真的不确定最好的方法是什么/怎么做。当深入研究如何解释无效移动时,我遇到了一个非常有用的堆栈溢出帖子。
“忽略无效动作就好了。”-堆栈溢出
好酷!听起来很简单…
我最后做的是,在训练期间,一旦技能冷却,我会将技能节点的输出抑制到一个较低的数值(数组-1 中最低的数值)。该输出阵列的抑制版本被反馈到网络中作为网络的目标。机器人的游戏状态中也会跟踪哪些技能已经被使用。这里的希望是,机器人将了解它不能选择一个以前使用过的技能。
我认为奖励和无效移动是建造这些机器人的机械部分,所以现在我只需要弄清楚一些更哲学的东西。我要怎么做才能让机器人作为一个团队一起玩?团队合作对一堆神经网络来说意味着什么?
如何教会一个机器人团队合作?
我之前提到过,在阅读了 OpenAI 在过去一年左右的时间里所做的工作后,我受到了启发,试图制作我的下一个 FGO 机器人系列。对我来说,最有趣的部分之一是这个想法,他们可以训练单个的机器人,并让它们作为合作团队一起玩。OpenAI 有一个团队合作参数,可以在 0 到 1 之间调整,较高的值表明机器人应该重视团队奖励而不是最大化自己的奖励。
这让我开始思考如何融入团队合作,或者至少是某种形式的沟通。虽然我考虑过使用另一个总体网络来管理团队合作,但我实际上最终采用了一个非常简单的想法,即让机器人在每个回合都按设定的顺序运行,并且在每个机器人运行后,我修改游戏状态以显示它们采取了什么行动。这里的希望是,机器人将能够根据早期机器人的行动做出更明智的决定。
对于我当前的机器人/环境,游戏状态由两个长度为 9 的数组(每个角色 3 个点)以及回合数和回合数表示。两个长度为 9 的数组是跟踪 3 个机器人动作的数组。第一个数组表示该回合中哪些技能有效,第二个数组表示哪些技能已经施放。在新游戏第一轮的第一回合,这两个数字都是 0。下面是游戏第 1 轮第 1 回合的游戏状态示例。
game_state_og = [Round 1] + [Turn 1] + [0,0,0,0,0,0,0,0,0] + [0,0,0,0,0,0,0,0,0]
这个原始的游戏状态是第一个角色在游戏的第一回合看到的。看到这个阵法后,他们选择使用他们的第一个技能。游戏状态被修改,使得每个数组的第一个元素被设置为 1,因为角色 1 使用了他们的第一个技能,并且该第一个技能当前是活动的。
Character_1_action = skill_1
game_state_1 = [Round 1] + [Turn 1] + [1,0,0,0,0,0,0,0,0] + [1,0,0,0,0,0,0,0,0]
然后我们将这个new_gamestate1作为输入提供给第二个字符。所以第二个角色可以看到角色 1 使用了一个技能,而且是主动的。基于这些知识,它可以决定是否也要使用一个技能。假设角色 1 使用了技能,角色 2 也跟着使用了技能 3。新的游戏状态如下。
Character_2_action = skill_3
game_state_2 = [Round 1] + [Turn 1] +[1,0,0,0,0,1,0,0,0] + [1,0,0,0,0,1,0,0,0]
角色 1 状态的游戏状态被修改,然后传递给角色 3。
Character_3_action = skill_2
gamestate_3 = [Round 1] + [Turn 1] +[1,0,0,0,0,1,0,1,0] + [1,0,0,0,0,1,0,1,0]
所以角色 3 处于做决定的最佳位置,因为它可以在花费任何技能之前看到角色 1 和 2 做什么,并且它可以与其他两个机器人一起行动。
我希望这种团队合作的框架将允许机器人一起工作,这将出现在游戏中的一种方式是机器人联合使用多种技能。现在我打算把这个特性叫做“ 技能弹幕 ”。
“技能屏障”
在 FGO,很多技能的强大之处在于你可以将它们与其他角色的技能结合使用。将不同的效果叠加在一起,玩家可以放大不同角色的效果,从而大幅增加团队的伤害输出。
目前,我已经训练了代理人来处理这些角色,这意味着他们在放大伤害,但是游戏中最强的伤害增益通常来自于“支持”角色。辅助角色造成的伤害更少,所以他们带来的直接火力比伤害交易者类型的角色要少。他们通过带来强大的技能来弥补这一点,这些技能可以决定一场艰难比赛的成败。
我的想法是在地点 1 和地点 2 放置辅助角色,在地点 3 放置主要伤害处理者。所以当伤害处理者看到两个支持角色已经施放了他们的 buffs,那么角色 3 也会使用一个技能来进一步增加他们的伤害。
下面是三个代理技能弹幕的例子。

skill barrage example. Round 1 Turn 2, all three bots use skills one after another.
培养
我将在这里写一些关于训练模型的一般笔记,但很快会添加一个关于这个主题的更详细的帖子(这篇文章太长了,大多数人都不想看)。
我用 Tensorflow 后端在 Keras 中构建了所有 4 个代理,并在单个 2080 TI GPU 上训练它们。对于那些在 GPU 上使用 Tensorflow 的人来说,Tensorflow 会将所有可用的 GPU 内存分配给任何加载用于训练的模型。在这个管道的以前版本中,我不能同时运行多个 Tensorflow 机器人,这使得内存分配很困难。
最后,我用在网上找到的一段代码绕过了这个管道。基本上,它的作用是将模型可以分配给自己的 GPU 空间限制在某个百分比。我在这里训练的模型实际上非常小,所以我将其限制在我的 GPU 内存的 20%。
# extra imports to set GPU options
import tensorflow as tf
from keras import backend as k
###################################
# TensorFlow wizardry
config = tf.ConfigProto()
# Don't pre-allocate memory; allocate as-needed
config.gpu_options.allow_growth = True
# Only allow a total of 20% the GPU memory to be allocated
config.gpu_options.per_process_gpu_memory_fraction = 0.2
# Create a session with the above options specified.
k.tensorflow_backend.set_session(tf.Session(config=config))
因为我在训练的 4 款车型中的每一款,实际上在我的 2080 TI 上我都进行了多次训练。
经过一些修补,我制作了一个足够硬的游戏环境,可以进行像样的训练,我的第一个版本太硬了,机器人总是输,没有得到多少积极的回报。一旦我把它调到一个合适的水平,我看到 4 个机器人的随机猜测胜率约为 38%,在大约 20-30K 的游戏后会达到 72%左右的峰值。在做了一些额外的工作和大量的实验后,我的成功率达到了 84%左右。
我将在另一篇文章中介绍有趣的细节,因为我已经在这篇文章中写了很长时间了。

Two turns of bots passing while skills are available to play. As I mentioned before, one of the things the bots had to learn to do is pass rather than just use all their skills immediately
结果
一旦完成,我必须更新我以前构建的 API,以允许新的 4 bot 版本点击能力,并从游戏中获得所需的信息。我在我的第一篇 pendragon 帖子中展示了很多最初的框架。
主要思想是,当一个机器人选择使用一个技能时,我记录下技能按钮的位置,并可以使用 python 将鼠标发送到该位置并单击。一旦就位,我就可以开始运行机器人来对抗实际的 FGO 任务,我对结果感到惊喜。
他们相对快速地完成任务,对于一个与他们的环境结构相似的普通任务,他们需要 7-8 次才能完成。相比之下,我认为一个人可以在 5-8 回合内通过这个特定的团队。
关于 bot 行为的一些初步想法:
1\. The bots tend to use most skills early in round 1 and 2
这是我个人认为次优的行为,因为第一轮和第二轮通常比第三轮容易得多。然而,对于机器人来说,未来是不确定的,他们希望确保自己能活到第三轮。
这些机器人以政策梯度的方式接受训练,根据输赢给予奖励。因此,虽然他们可能有很好的机会很快清除第三轮,如果他们保存更多的技能。机器人需要确保它们能活到那个时候。如果他们保存了所有技能,他们甚至可能撑不到第三轮,或者可能在途中受到太多伤害。因此,尽早使用技能并为第三轮保留一些技能似乎是他们使用的更稳定的策略。这让他们可以进入游戏的后期,至少有机会获胜。
2\. The bots do perform skill barrages where 2–3 bots are firing skills together
目前,机器人确实像我希望的那样成功地使用了技能屏障,其中 2-3 个经常一起使用技能。然而,这些技能可能不会一起超级好。例如,一个机器人可能会使用一个技能来增加他们的致命一击伤害,而其他机器人可以搭配其他技能来增加致命一击伤害,他们会做一些事情,如充电自己的 NP 能力或类似的事情。这些技能没有任何真正的协同作用,但机器人一起使用它们。
这可能意味着机器人需要更多的时间来探索游戏环境,或者我所建立的环境对它们来说还不够艰难,不需要那种程度的通信。他们从来没有看到他们的能力做了什么,他们只是看到了来自环境的奖励,基于他们使用后是赢是输。
3\. Bots do still save some skills for the round 3 late game
因此,虽然我抱怨机器人玩 sub 优化,并在早期使用技能,但他们也倾向于在游戏后期保留一些能力。我认为这是机器人意识到清除任务的第一部分是重要的,清除高生命值使他们更有可能获胜。如果他们想赢得一个好的机会,他们仍然需要为最后的战斗保存一些能力。这是一个很好的长期规划,我希望机器人能够学习。

Bot 1 (Jalter) saved a skill for late game which helps to boost the team’s damage in the final round which can often have stronger/harder to kill enemies
结束语
为了达到这一点,训练和部署这些机器人来玩游戏命运大令花了我大约一个月的工作和大量的失败/实验。我的大多数帖子都是基于其他人已经完成的项目,通常可以找到一些松散的文档或建议来遵循。这一次我大部分时间都在忙着解决问题,并经常因为我的不同想法而惹恼我的朋友。
从技术上来说,我以前的机器人可以播放很多 FGO 的内容,我想用机器人来玩。但是,一旦我找到了一种看似合理的方法,我就很难抗拒试图构建远远超过它的机器人的诱惑。我对数据科学有点上瘾,但这并不是一件坏事。
展望未来,我对这个项目有很多目标:
- 为游戏添加支持
- 使用某种多代理框架清除最终游戏内容
- 让潘德雷肯四号被接受为会议演讲/论文,这样我就可以做一个关于视频游戏的技术演讲了。(一个会做梦的男孩)
人们不信任人工智能。我们需要改变这种情况。

The issue of mistrust in AI systems was a major theme at IBM’s annual customer and developer conference, THINK, this year. Image via Ron Poznansky.
在过去的一周里,我有幸参加了 IBM 的年度客户和开发者大会 THINK 并在会上发言,此次大会吸引了 25,000 多名与会者来到旧金山。当然,在一篇简单的博客文章中总结如此规模的事件几乎是不可能的——但我想分享一些在我参加的对话和会议中让我印象深刻的关键观点。尤其是其中一个主题,是我在 THINK 时关注的焦点,那就是我们对人工智能系统的信任问题。
说白了,大部分人都不信任 AI——至少,还没到把它投入生产的程度。经济学人2018 年进行的一项研究发现,94%的企业高管认为采用人工智能对解决战略挑战很重要;然而,麻省理工学院斯隆管理评论在 2018 年发现,只有 18%的组织是真正的人工智能“先驱”,已经在他们的产品和流程中广泛采用了人工智能。这种差距说明了我们在人工智能社区中面临的一个非常现实的可用性问题:人们想要我们的技术,但它在目前的状态下并没有为他们工作。我认为缺乏对谎言的信任是这个问题的主要原因之一。
人们现在还不信任人工智能工具有一些很好的理由。首先,偏见是一个热点问题。最近的 高调的 事件理所当然地获得了媒体的大量关注,帮助让机器学习偏见的概念家喻户晓。组织有理由对实施最终可能产生种族主义、性别歧视或其他偏见的系统犹豫不决。
事情是这样的:人工智能在设计上是有偏见的。正如 Forrester 首席分析师 Brandon Purcell 在我们关于人工智能公平性和可解释性的思考小组讨论中雄辩地指出的那样,机器学习模型被称为“鉴别器”是有原因的。有故意的、必要的偏见——例如,在问答系统中,同行评议的论文比维基百科的文章权重更大——还有无意的、有害的偏见——建立一个无法识别有色人种的面部识别模型。但“不良偏差”是算法决策系统中难以解决的问题,部分原因是我们尚未能从人类决策系统中消除它。我们生活在一个有偏见的世界,充满了有偏见的数据,这些数据会训练出有偏见的模型。

The author (right), Brandon Purcell of Forrester Research (middle), and Rohan Vaidyanathan of IBM (left) discuss AI fairness and explainability at THINK 2019.
除了偏见问题,另一个阻碍信任的关键话题是可解释性——或者说缺乏可解释性。有很多讨论,特别是在 GDPR 生效之后,在理解一个给定的模型如何得出一个个体决策的背景下,关于可解释性的讨论。这种类型的可解释性在更复杂的机器学习系统中很难实现,这些系统采用了“黑盒”技术,如神经网络或 XGBoost 模型。
从监管和伦理的角度来看,这种可解释性是非常重要的。然而,我认为,还有另一个可解释的元素对于建立人工智能模型的信任同样重要,那就是理解人工智能系统影响的能力——能够将模型输出与有形结果联系起来。对机器学习模型进行审计跟踪,对于了解这些模型在一段时间内的表现至关重要。如果没有能力审计他们的模型的功能(并确保一致的利益,或知道在性能下降的情况下何时进行纠正),许多组织将会犹豫是否真正信任任何人工智能系统。

Image via giphy.
在 THINK 上反复出现的人工智能采用的最后一个挑战是许多组织面临的技能差距。在 IBM 的一项 2018 年研究中,63%的受访者认为缺乏技术技能是人工智能实施的障碍。通过我自己与客户的交谈,我了解到大多数组织要么没有专门的数据科学团队,要么他们有一个过度扩展的非常小的团队。据德勤称,根据目前的供需情况,到 2024 年,美国预计将面临 25 万名数据科学家的短缺。
对于没有专用资源的公司,业务负责人最终负责人工智能项目。没有工具来帮助他们理解这些系统实际上是如何工作的,他们很难将实验从实验室带入生产。难怪麻省理工斯隆管理评论的 2018 年研究报告发现,大多数组织——82%的受访者——未能在试点或概念验证项目之外采用人工智能。
所以,人们不信任人工智能——我认为我们作为技术专家的首要任务应该是尽快改变这种情况。有几个原因可以解释为什么对人工智能系统缺乏信任让我如此困扰。首先,我不断回到麻省理工学院斯隆管理学院的研究。在有家公司在大规模采用人工智能方面取得了飞跃——18%的组织被归类为“先驱”这些公司拥有强大的数据科学团队,他们有资源来构建自己的解决方案,以克服我上面提到的三个障碍。在人工智能的帮助下,这些公司将主宰市场。我坚信健康竞争作为客户和员工利益保障的重要性。因此,我想帮助尽可能多的公司跟上变化的潮流。我想确保各种各样的企业能够使用少数大公司已经在利用的相同工具。如果我们不帮助小家伙们信任 AI,他们将被市场整个吞噬。
其次,我担心如果我们不能成功地建立值得广泛信任的模型和系统,我们将永远无法实现 AI 的全部积极潜力(当然;你可以称我为技术理想主义者。我相信人工智能的真正承诺是创造公平的竞争环境——打破数百年来主导市场的规模经济。但是,如果大多数人和组织都不想参与,竞争环境就不可能是公平的。
后面的怀疑论者会很快指出,技术的民主化也产生了许多不好的结果——我意识到,当涉及到广泛采用时,并不全是美好的事情。但我认为,如果我们保持技术的封闭和不透明,坏人造成巨大伤害的风险会更高。如果我们不为每个人提供他们需要的工具来理解人工智能系统,就何时何地使用这些系统做出明智的决定,我们将为那些怀有恶意的人留下更多的空间来利用人们的缺乏理解。
那么我们如何解决 AI 缺乏信任这个迫切的问题呢?它从解决不信任的根源开始。我在 THINK 采访过的人有一些想法。为了解决偏见问题,旨在扩展训练数据以消除盲点的数据集,如人脸数据集中的多样性,是一个良好的开端。像 AI Fairness 360 这样的工具有助于数据科学家识别他们模型中的偏差。检查和缓解来自像算法正义联盟这样的团体的专业知识对于让这些工具有效工作是必不可少的。

The Diversity in Faces dataset includes 10 facial coding methods, offering a jumping off point for researchers working on facial recognition models. Image via IBM Research.
提高人工智能系统易读性的技术也是必须的——我工作的产品 Watson OpenScale 就专注于这个问题。我们特别需要为非技术或半技术受众设计的工具,这些工具以企业主可以理解的语言为人工智能模型带来透明度。智能监管将是推动这些领域工作的关键。欧盟的 GDPR 和即将到来的加州 CCPA 已经要求“算法决策”系统具有更高水平的可解释性。围绕系统中允许的偏差水平的法规可能会推动技术专家开发新的方法来确保人工智能系统的公平性。
对人工智能系统缺乏信任是我们迫切需要解决的更大问题的症状。我们今天拥有的狭义人工智能的技术能力正在迅速发展,比我们管理它们所带来的风险的能力快得多。研究人员已经在开发一些模型,他们担心被恶意使用而不愿分享这些模型。如果人工智能社区今天不把我们的努力集中在解决这项技术的问题上,明天就会有人为我们解决这些问题——以极端的、限制创新的法规的形式,或者,在我看来更糟糕的是,公众对人工智能完全失去信心和否认。
好消息是,我们正在像 THINK 这样的行业活动中讨论人工智能中的不信任问题。在过去的一年中,我看到技术社区围绕偏见、可解释性和易读性等挑战团结起来,这让我充满希望。我希望鼓励技术和非技术利益相关者更广泛地参与这些讨论。这将需要一个村庄来建立每个人都可以信任的人工智能系统。
像你这样的人喜欢这样的东西
推荐引擎的问题是

奥纳坦·海特害怕说出任何道德上模棱两可或伦理上微妙的话。对于商业伦理和道德心理学教授来说,这是一个荒谬的职位。然而,在海德特任教的纽约大学商学院,学生和管理人员已经联合起来,为那些在课堂上遇到政治挑战、道德复杂或其他冒犯性材料的学生开设了一条帮助热线。保证匿名。虽然帮助热线旨在成为一股向善的力量——一个随时为那些在高等学术机构的橡胶路面环境中道德和政治观点受到挑战的学生提供压力的阀门——但这不是撒玛利亚人。电话接线员不是在鼓励听众或志愿者成为朋友,他们是强大的管理者,在 NYU 学术人员的集体头上悬着达摩克利斯。
这条求助热线是海特过去几年一直在研究的一长串社会公正现象中最近的一个。利用他的社会科学工具包和他的道德万花筒,海特试图理解:为什么十几岁的女孩自杀人数创纪录,为什么学生团体如此强烈地反对一些客座演讲人的观点,以至于他们拒绝让其他学生听到他们的观点,为什么他的同事因无意冒犯而被解雇,等等。
正如任何称职的社会科学家一样,海特对这些现象有一套理论。他认为,自 90 年代初以来,西方父母一直处于一种歇斯底里的状态。海特认为,对新思想的过度敏感,对官方认可的保护国的需要,以及自残的可怕北行轨迹,都是反脆弱父母教育的结果。根据 Haidt 的说法,90 年代儿童诱拐率的缓慢攀升跨越了父母与子女比率的下降斜率,这些指标在一个临界点相遇,这是翻转父母养育方式的完美支点。孩子们突然每时每刻都受到监督,生怕装着鸡蛋的篮子会发生什么可怕的事情。
这种长期的泡沫包裹无疑解释了其中一些现象的情感力量。但是根除无人监管的游戏是党派和思想过敏的学院的唯一先决条件吗?难道不应该有其他非父母的力量来塑造孩子的原则和观点吗?
过去,父母们不得不编造出斗鸡眼的诡计来劝阻孩子们不要看电视(这种情况在晚间新闻开始时出现,让孩子们眯着眼,同时带着关切的目光)。现在,对于一个核心家庭来说,如果没有在无聊、紧张、沉默、非喜剧性的蒙太奇、对话等时刻出现的间隙屏幕,共同观看一部电影似乎是不可能的。这种变化的原因是什么?
不可否认,早期的电视是一个吸引注意力的怪物,但它必须变戏法。广播网络按照精心安排的收视率/广告优化轮流运行——从卡通片到肥皂剧,再到新闻和商业节目,带着一个运转良好的美国中产阶级家庭的圆滑。
从 90 年代中期开始,各种各样的设备要么被发明出来,要么价格降低到足以普及互联网接入。台式电脑、笔记本电脑、上网本、智能手机、平板电脑。随着每一代设备的相继出现,家庭关注被打破。挑战现有者的新媒体公司不再需要担心平衡代际娱乐的旋转盘子,而是可以为用户优化——特定屏幕的单一、不可减少的所有者。而且,通过正确的技术组合,他们可以建立一个电视网络的内容供一个人欣赏,他们可以大规模地这样做。
但是,一个新生的互联网创业公司如何找到足够多的关于你的信息来代表你建立一个电视网络呢?外推。
对于经常使用现代网络的人来说,你在网站上的几乎每一个动作都会被记录下来,这并不奇怪。浏览量、点击量、点赞数和评论数被制成表格,储存在巨大的数据中心。然而,可能让你震惊的是,利用这些表面事件来获得进一步的细节。你的收入等级、社会地位、婚姻状况、年龄、种族和性别都在统计上相差很远。从这里开始,从数学上很容易找到那些最像你的用户。每一个数据都是高维空间中的一个坐标,每个人都是一个点。你的观看偏好和购买偏好是它们之间距离的假象。
你想看什么,什么会让你参与的问题,可以简化为你和你邻居的内容历史的集合运算。
海特遇到的学生很可能被溺爱,但这不仅仅是父母的错。社会已经将其社会化的责任让给了互联网,却没有意识到每个人的网络都是他们自己的,远非详尽的、隔离的、令人麻醉的熟悉。
普遍的个性化但持续强化已有原则的预期结果会是什么,尤其是当这种个性化在我们的成长阶段开始的时候?
互联网的支持者声称,它的主要好处之一是任何人都有能力为个人表达和创造力构建一个载体,但相反的能力呢?有能力为自己构建一个无底的消费渠道,在这个渠道中,我们的价值观不会受到审视?
这种经验框架不需要那些有抑郁倾向的人在他们的个人互联网上遇到不断增加的负面情绪吗?互联网听起来不像是抑郁发作所表明的认知治疗的完美对立面吗?
部落主义难道不是支配我们醒着的大部分时间里看什么和读什么的数学运算的自然结果吗?未来的几代人将会停留在不妥协的青少年意识形态中,还是我们会找到另一种交流方式?一个统一与和谐的世界,一个超越病毒传播的世界,一个让我们面对自古以来智人就在思考的难题的世界?
垃圾进,垃圾出。
原载于 2019 年 8 月 9 日【https://carldawson.co.uk】。
使用深度学习进行人物跟踪
用数据做很酷的事情!
简介
目标跟踪是计算机视觉的一个重要领域。它包括跟踪一个物体的过程,这个物体可以是一个人,一个球或者一辆汽车。对于人物跟踪,我们将从一帧中所有可能的检测开始,并给他们一个 ID。在随后的画面中,我们试图延续一个人的身份。如果这个人离开了相框,那么这个 ID 就会被删除。如果一个新的人出现,他们就用一个新的 ID 开始。
这是一项困难的任务,因为人们可能看起来很相似,导致模型切换 id,人们可能被遮挡,例如当行人或玩家隐藏在其他人后面时,或者物体可能消失并在后面的帧中重新出现。
深度学习让我们在跟踪方面有了惊人的表现。请看下面来自多目标跟踪( MOT 数据集)测试集的视频的深度排序算法的结果。

Pedestrain Tracking through Deep Sort
跟踪的基础
让我们首先回顾跟踪的基础知识。假设我们有帧中所有对象的边界框信息。在现实世界的应用中,我们需要提前进行包围盒检测,因此跟踪器需要与检测器相结合。但是现在让我们假设我们只在跟踪上工作。给定第 1 帧中 ID 的 bbox 信息,我们如何在后续帧中分配 ID?
- 基于质心的 id 分配——最简单的形式是,我们可以通过查看边界框的质心来分配 ID。我们通过计算第 1 帧中每个边界框的质心来做到这一点。在第 2 帧中,我们查看新的质心,根据与先前质心的距离,我们可以通过查看相对距离来指定 id。基本的假设是帧到帧的质心只会移动一点点。只要形心彼此隔开,这种简单的方法就能很好地工作。可以想象,当人们彼此靠近时,这种方法会失败,因为它可能会切换 id
- 卡尔曼滤波器 —卡尔曼滤波器是对简单的基于质心的跟踪的改进。这个博客很好地解释了卡尔曼滤波器。卡尔曼滤波器允许我们根据物体的位置和速度来模拟跟踪,并预测它可能在哪里。它用高斯模型模拟未来的位置和速度。当它接收到一个新的读数时,它可以使用概率将测量值分配给它的预测值,并自我更新。它内存小,运行速度快。由于它同时使用了运动的位置和速度,因此比基于质心的跟踪具有更好的结果。

Kalman Filter — Prediction and Measurement Update
深度排序算法
我喜欢深度排序算法。太直观了。在所有上述数学中,我们人类在跟踪中一直使用的一个基本要素是对边界框的视觉理解。我们不仅根据距离、速度,还根据那个人的长相来追踪。深度排序允许我们通过计算每个边界框的深度特征并使用深度特征之间的相似性来添加该特征,从而也将该特征纳入跟踪逻辑。
我们如何计算深层特征?
本文使用在数百万幅人类图像上训练的模型,并为每个包围盒提取 128 维向量,该向量应该捕获该包围盒的关键特征。使用深度特征可以使该模型在人们遮挡或非常靠近的情况下更好地跟踪,如下图所示。

Tracking with Deep Sort
总的来说,我们的实验表明深度排序算法工作得非常好,但它也有一些限制:
- 如果边界框太大,过多的背景被“捕获”在特征中,降低了算法的有效性
- 如果人们穿着相似就像体育运动中发生的那样,会导致相似的特征和身份转换
结论
跟踪是计算机视觉中的一个重要问题,有着广泛的应用。深度排序算法非常强大,运行速度很快,对于许多情况来说是一个很好的起点。他们的回购写得非常好,很容易尝试。我鼓励你拿出他们的代码来试一试。
我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助许多初创公司部署了基于人工智能的创新解决方案。请到 http://deeplearninganalytics.org/的来看看我们吧。
你也可以在https://medium.com/@priya.dwivedi看到我的其他作品
如果你有一个我们可以合作的项目,请通过我的网站或 info@deeplearninganalytics.org 联系我
参考
用机器学习跟踪人
检测客户习惯,跟踪他们的活动

Photo by sydney Rae on Unsplash
你的窗户有效地吸引人吗?对于购物者来说,你的商品有多少是容易买到的?如果你是一个心胸开阔的小杂货店老板,或者如果你是一家服装店的视觉采购员,你需要知道这些是人工智能可以帮助回答的典型问题。零售商不仅采用人工智能解决方案来改善客户体验,还直接增加了他们的业务。这样,一个恰当的问题可以是:一家特定的商店将户外人流转化了多少?
需要同时评估营销效果和总服务成本之间的完美权衡,以影响消费者行为并实现成本优化。在这种情况下,人工智能解决方案似乎代表了一种高价值的可能性。
在这篇文章中,我制作了一个有趣的应用程序,旨在跟踪商店里的人,并关注他们的历史活动,以检测他们的行为。随着机器学习和计算机视觉技术的实际发展,我们手中有大量强大的工具来完成像这样的任务,并且表现出色……你不需要重新发明轮子,你需要知道如何使用轮子来让你的汽车变得更好。
数据集
对于这个特殊的挑战,我们不需要带有标记数据的数据集,也就是说,我们使用已经在类似领域中专门化的预训练模型。我们的能力是正确地使它们适应我们感兴趣的情况。人物检测是计算机视觉中的一个经典应用领域,许多模型被训练来区分这个标准类别,取得了很高的性能。
我们只需要找到有意义的数据来测试我们的方法。我想找到显示固定商店布局内购物者活动的视频格式的数据。安全摄像头视频非常适合这个目的!它们是网络上容易获得的资源,反映了我们需要一个“老大哥”来监视店内的一切。我在 YouTube 上找到了我们实验的合适人选,并把它们下载到了我的电脑上。
这些视频是一个离散的质量,并没有那么沉重(平均 3 分钟),所以他们对我们来说是完美的!

Shop Layouts extracted from videos. These videos are available on YouTube. From left: Video1 (1 minute), Video2 (6 minutes), Video3 (3 minutes)
模型
为了完成我们的任务,我选择了 ImageAI 库 。这个开源项目使每个人都能够轻松地将最先进的人工智能功能集成到我们新的和现有的应用程序和系统中。通过几行代码,您可以访问一系列用于图像预测和对象检测的最先进的机器学习算法。所有的脏活(预处理、模型查询、输出结果……)都已经为我们做好了,并且活在幕后。
那么,我们的工作已经完成了吗?幸运的是,还没有…附加值是由我们构建的智能工作流直接提供的,它能够回答我们臭名昭著的问题。
工作流程
我们有 mp4 格式的原始视频;作为我们行动计划的第一步,我们必须将视频转换成图像。这很容易,只需从原始视频中以固定的给定频率采样帧即可实现。在 python 中:
def extract_frames(video_path, fr=12):
frames = []
cap = cv2.VideoCapture(video_path)
total_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
id_frame = 0
while id_frame<=total_frames:
cap.set(1, id_frame)
success, frame = cap.read()
if type(frame) != type(None):
frames.append(frame)
id_frame+=fr
return frames
我将频率速率(fr)固定为 12:即,如果给定视频的频率为每秒 30 帧,我们每秒收集一帧,这是跟踪人的一个很好的近似值。
第二阶段,我们初始化所有的 ImageAI 工具。只有几行,我们可以访问非常强大的预训练对象检测模型。
detector = ObjectDetection()
detector.setModelTypeAsTinyYOLOv3()
detector.setModelPath("yolo-tiny.h5")
detector.loadModel()
custom_objects = detector.CustomObjects(person=True)
我选择了微小的 YOLO 来进行对象检测(很容易从 h5 格式的 ImageAI 文档页面下载),因为它允许在性能和速度之间进行很好的权衡。这个网络运行良好,所有的工作流程能够在不到一秒的时间内处理一帧图像!
有趣的部分来了。我们必须将我们收集的数据(来自视频的图像)转化为对我们有用的东西:它同时易于理解,并且包含有价值的信息。完美的选择是热图表示,它允许我们赋予我们从人员检测任务中存储的信息以价值。如果还不清楚,让我来画…

由于 ImageAI 的强大功能,这可以通过一种非常优雅的方式实现:它们的检测器返回检测到的对象以及相关的边界框坐标作为输出。对于人来说,我们只提取盒子的底部,以提供一个可解释的重叠信息和商店的平面图。有了这些容易获得的结果,我们可以构建一个以热图表示形式的频率图(在右侧与商店地图重叠)和一个路径掩码(在左侧)。为了给予进一步的可解释性,我们排除了帧的底部部分:这些区域受到“错误检测”的影响,即我们的模型太聪明了,并且识别出人也具有破坏热图计算的半个身体。
这些结果告诉我们购买者遵循的路线以及他们会在哪里停留更长时间。
“最热区域”是人们更加关注的地方。在最长的视频中(我最近处理的两个),这些区域立刻就清晰了!现在想象一下,如果有更多的视频资料供我们使用,我们能够检测到多少客户行为?
摘要
在这篇文章中,我制作了一个计算机视觉解决方案来跟踪商店里的人。人物检测是机器学习中的一项综合性任务,因此我们试图将这一知识(具有相对较高的性能)应用于不同的领域。给定一个显示正常购物程序的视频,我们能够检测购物者的行为和路径。这些是每个零售商的黄金信息,以便以不同的方式改善他们的业务:商品展览,商店布局,员工效率,库存水平等等。
保持联系: Linkedin
愚人节 2019:感知驱动的数据可视化
利用最强大的心理学技术探索 OKCupid 数据以加速分析

本文为 2019 年愚人节恶作剧。既然庆祝活动已经结束,请滚动到文章末尾的真实课程部分,进行一分钟真正的学习。
进化赋予了人类一些非凡的能力,从直立行走到操作重型机械,再到高效的在线择偶。
人类已经进化出快速处理人脸的能力,你可以使用感知驱动技术来加速你的分析。
其中最令人印象深刻的是我们感知面部结构和表情微小变化的能力,因此数据科学家已经开始利用我们与生俱来的超能力来获得更快、更强大的数据分析。
进化驱动的数据分析
准备好被令人难以置信的新分析技术震撼吧!
切尔诺夫的脸以优雅和清晰而著称,通过利用人类最擅长的面部识别来传达信息。

Chernoff faces 背后的核心思想是,每个面部特征都将映射到数据的一个属性。更大的耳朵意味着什么,微笑、眼睛大小、鼻子形状等等也是如此。我希望你看到它的运行会很兴奋!
让我们用 OKCupid 数据来看一个现实生活中的择偶例子。
数据处理
我首先下载了一个数据集,里面有近 60K 个泄露的 OKCupid 个人资料,你可以在这里找到来跟进。真实世界的数据通常是杂乱的,在对您的数据科学目标有用之前,需要进行大量的预处理,这也是事实。例如,他们有大量认真的和 100%可靠的自我介绍文章,所以我做了一点快速过滤,把我的数据集浓缩成与我相关的东西。我用了 R 我发现最有用的函数是 grepl() 。
首先,因为我住在纽约,所以我过滤掉了 17 个我附近的资料。接下来,我清理了数据,以显示我最在意的特征。例如,我是水瓶座,占星学上的相处显然很重要,同样重要的还有对猫的热爱和在进行灵魂对话的意愿。
在第一个预处理步骤之后,我的数据集看起来是这样的:

下一步是将字符串转换成数字,这样 Chernoff face 代码就能正常运行。这是我将从 R 的 aplpack 包中提交到 faces() 函数中的内容:

下一步,魔术!
露出的面孔
现在我们的数据集已经准备好了,让我们运行我们的 Chernoff faces 可视化!哒哒。

下面是如何阅读的简便指南。这难道不令人惊讶的优雅和如此迅速地看到到底发生了什么吗?比如最大的脸是最高最老的人,而微笑者会给我唱甜甜的 C++ 十四行诗。很容易在一瞬间看到这一切。人脑真是不可思议!

数据隐私问题
不幸的是,通过认知机器深度学习所有这些面孔,我们正在侵犯 OKCupid 用户的隐私。如果你仔细观察并记住这些图像,你也许能从人群中挑选出他们。小心那个!在向你的老板展示这些强有力的图像之前,确保你通过在一个不相关的数据集上重新运行代码来重新匿名化你的结果。

日期和约会
切尔诺夫脸?!你真的应该检查出版日期,尤其是在四月初的时候。当这位顽固的统计学家提到占星术时,我希望你已经开始怀疑了,并且在我说到去匿名化的废话时,你已经确定了。
我爱你,无论是哪个恶作剧者转发给你的。❤
真正的教训
我总是被切尔诺夫的面孔逗乐(并渴望找个借口与你分享一些我最喜欢的分析琐事),尽管我从未真正见过他们在野外发挥作用。尽管这篇文章是为了搞笑,但也有一些真正的教训要吸取:
- 期望花时间清理数据。 虽然最终的可视化只需要击几个键就可以实现,但我的大部分工作是准备要使用的数据集,你应该会在自己的 数据科学 冒险中看到这一点。
- 数据可视化不仅仅是 直方图 。当谈到如何呈现数据时,有很大的创造性空间,尽管并不是所有的东西都会在一个易于初学者使用的包中实现。虽然你可以通过 R 只使用单一的函数 faces(数据)来获得 Chernoff faces,但是如果你觉得有创意并且愿意在图形方面付出努力,那么天空就是极限。如果你追求最深刻的自我表达,你可能需要像 C++这样的东西。
- 与我相关的东西可能与你无关。我可能关心爱猫,你可能关心别的。分析只对其预期的目的有用,所以如果你继承了别人的数据集或报告,要小心。可能对你没用,或者更糟,误导。
- 没有正确的方法来呈现数据 ,但是考虑 viz 质量的一种方法是理解速度。这些面孔不能有效地将信息传递到你的大脑中——你可能不得不去查阅桌子来弄清楚你在看什么。当你为现实做分析时,这是你想要避免的。
- 切尔诺夫的面孔在被发明 的时候听起来才华横溢,同样的道理,“认知”这个和那个在今天听起来才华横溢。不是所有让你内心深处的诗人发痒的东西都是好主意……当论点诉诸进化论和人类大脑时,要格外警惕逻辑的跳跃。不要忘了在你把神奇的东西运用到你的业务中之前,先测试一下 数学。
如果你想试着自己创造这些面孔,这里有一个教程。如果你更喜欢读我的一篇关于数据可视化的直白的文章,试试这篇文章。

Chernoff face visualization explained on Wikipedia.
感谢阅读!人工智能课程怎么样?
如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:
Enjoy the entire course playlist here: bit.ly/machinefriend
喜欢作者?与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格联系。
线性分类的感知器算法
了解感知器算法是如何工作的,以及它们背后的直觉。

基本感知器算法是由参考文献 1 在 20 世纪 50 年代末首次提出的。这是一个用于监督学习的二元线性分类器。二元线性分类器背后的思想可以描述如下。

其中 x 为特征向量, θ 为权重向量, θ 为偏差。符号函数用于区分x 是正(+1)还是负(-1)标签。存在用不同标签分离数据的决策边界,这发生在

决策边界将超平面分成两个区域。在θ⋅x+θ₀>,的区域,数据将被标记为正,在 θ⋅ x + θ ₀ <的区域,数据将被标记为负。如果一个给定数据中的所有实例都是线性可分的,则对于每第 i 个数据点,存在一个 θ 和一个 θ ₀,使得 y⁽ⁱ⁾(θ⋅x⁽ⁱ⁾+θ)【t100,其中 y⊙
图 1 用二维情况说明了上述概念,其中x=[x₁x₂]ᵀ、θ=[θ₁θ₂】和 θ ₀是偏移标量。请注意,边距边界与正则化相关,以防止数据过度拟合,这超出了这里讨论的范围。**

Figure 1. The concepts of binary linear classifier with the 2-D case.
感知器
找到决策边界的一种方法是使用感知器算法。只有当决策边界错误分类数据点时,感知器算法才会更新 θ 和 θ ₀。算法的伪代码描述如下。
**# Perceptron Algorithm**# initialize θ and θ₀ with 0*
θ = 0 (vector)
θ₀ = 0 (scalar)*# totally T epoches to iterate*
for t = 1 .. T do
* # totally m data points*
for i = 1 .. m do
*# misclassify data points*
if y⁽ⁱ⁾(θ ⋅ x⁽ⁱ⁾ + θ₀) ≦ 0
then
θ = θ + y⁽ⁱ⁾ ⋅ x⁽ⁱ⁾
θ₀ = θ₀ + y⁽ⁱ⁾return θ, θ₀*
感知器算法遍历所有带标签的数据点,并相应地更新 θ 和 θ ₀。更新规则背后的直觉是将 y⁽ⁱ⁾(θ⋅x⁽ⁱ⁾+θ₀)推得更接近一个正值,如果 yθ≡xθ≦**

感知器收敛
构成感知器算法所犯错误数量界限的因素是数据点的最大范数和正负数据点之间的最大差值。感知器收敛定理已在参考文献 2 中得到证明。给定一组通过原点可线性分离的数据点, θ 的初始化不会影响感知器算法最终收敛的能力。

迭代次数 k 具有有限值意味着一旦数据点通过原点是线性可分的,无论 θ 的初始值是什么,感知器算法最终都会收敛。这些概念也代表了 θ ₀.的存在
然而,一旦数据点是线性不可分的,这种感知器算法可能会遇到收敛问题。有两种感知器算法变体被引入来处理这些问题。一个是平均感知器算法,一个是 pegasos 算法。
平均感知器
与感知器算法类似,平均感知器算法使用相同的规则来更新参数。然而, θ 和 θ ₀的最终返回值取每次迭代中 θ 和 θ ₀的所有值的平均值。算法的伪代码描述如下。
***# Average Perceptron Algorithm**# initialize θ, θ₀, sum_θ, sum_θ₀, and counter with 0*
θ = 0 (vector)
θ₀ = 0 (scalar)
sum_θ = 0 (vector)
sum_θ₀ = 0 (scalar)
counter = 0*# totally T epoches to iterate*
for t = 1 .. T do
*# totally m data points *
for i = 1 .. m do
*# misclassify data points *
if y⁽ⁱ⁾(θ ⋅ x⁽ⁱ⁾ + θ₀) ≦ 0
then
θ = θ + y⁽ⁱ⁾ ⋅ x⁽ⁱ⁾
θ₀ = θ₀ + y⁽ⁱ⁾ sum_θ = sum_θ + θ
sum_θ₀ = sum_θ₀ + θ₀
counter = counter + 1return (sum_θ/counter), (sum_θ₀/counter)**
佩加索斯
pegasos 算法具有超参数 λ ,为要调整的模型提供了更大的灵活性。无论数据点是否分类错误,都会更新 θ 。详情见参考文件 3。算法的伪代码描述如下。
***# Pegasos Algorithm**# initialize θ, θ₀, and counter with 0*
θ = 0 (vector)
θ₀ = 0 (scalar)
counter = 0*# totally T epoches to iterate*
for t = 1 .. T do
* # totally m data points *
for i = 1 .. m do
counter = counter + 1
η = 1/√counter
if y⁽ⁱ⁾(θ⋅x⁽ⁱ⁾ + θ₀) ≦ 1
then
θ = (1 - ηλ)θ + ηy⁽ⁱ⁾⋅x⁽ⁱ⁾
θ₀ = θ₀ + ηy⁽ⁱ⁾
else
then
θ = (1 - ηλ)θ
θ₀ = θ₀return θ, θ₀**
可视化感知器算法
图二。通过不同的感知器算法可视化决策边界的更新。请注意,给定的数据是线性不可分的,因此感知器算法绘制的决策边界会发散。平均感知器算法和 pegasos 算法都很快达到收敛。pegasos 算法的 λ 在这里使用 0.2。

Figure 2. Updating the decision boundaries by the different perceptron algorithms. May take time to load.
示例代码
在 Jupyter 笔记本上写的感知器算法的样本代码可以在这里找到。你可以自己处理数据和超参数,看看不同的感知器算法表现如何。
参考
[1]
F.罗森布拉特,“感知机:大脑中信息存储和组织的概率模型”,《心理评论》,1958 年。土井: 10.1037/h0042519**
[2]
米(meter 的缩写))Mohri 和 A. Rostamizadeh,“感知机错误界限”, arxiv ,2013 年。https://arxiv.org/pdf/1305.0208.pdf
[3]
南 S.-Shwartz、Y. Singer、N. Srebro 和 A. Cotter,“Pegasos:SVM 初始估计次梯度求解器”,数学规划,2010 年。doi:10.1007/s 10107–010–0420–4**
用双耳和双声道音频训练的音乐源分离 CNN 的感知评估

研究概述
本研究探索了使用不同空间音频格式来训练音乐源分离神经网络的想法。DeepConvSep 是由 Marius Miron、Pritish Chandna、Gerard Erruz 和 Hector Martel 设计的库,用作测试不同卷积神经网络进行源分离的框架。然后详细说明听力测试,并分析测试结果,以便对模型进行感知评估。得出关于使用空间音频格式来训练源分离神经网络的有效性的结论。
介绍
音频神经网络寻求使人工智能能够像人类一样说话和听觉。在该领域的发展中存在许多障碍,因为音频包含大量的信息,从数万个不同的频率和相位分量,以及外部的、不需要的噪声和多种来源的可能性。为了解决这个问题,目前存在几种方法来将音频压缩和重新格式化成与神经网络的输入更兼容的数据向量。
空间音频格式(如双耳和双声道音频)的出现为比立体声更“自然”的音频创造了潜力。然而,对于这些更“自然声音”的音频格式是否有利于神经网络学习和理解声音的尝试,之前几乎没有研究。这项研究试图通过训练和感知评估用不同音频格式训练的几个源分离神经网络模型来试验这一想法。
源分离
概念基础
源分离是旨在将音频混合分离成它们各自的源元素的过程,无论它是音乐还是语音等。自动语音识别(ASR)、音乐后期制作和音乐信息检索(MIR)等领域都受益于源分离技术的改进研究[1]。源分离可以进一步细分为两个不同的类别。面向音频质量(AQO)的源分离寻求从混合中分离源信号,同时最好地保持它们的感知音频保真度。相反,面向重要性(SO)的方法从分离的源信号中提取高级语义信息[1]。这项研究的重点是 AQO 方法在评估源分离的有效性。
研究灵感
源分离是神经网络的理想任务,因为它本质上是一个分类任务。DeepConvSep 由 Marius Miron、Pritish Chandna、Gerard Erruz 和 Hector Martel 设计,是一个卷积神经网络(CNN)库,专为信号分离评估活动(SiSEC)而构建[2,3]。为了分离当代/流行音乐,用 Demixing Secrets DSD100 数据库训练原始神经网络模型。DSD100 由一组 100 首歌曲组成,每首歌曲都有相应的混音和词干,分为四个类别(人声、贝斯、鼓和其他),最初是为 SiSEC 2017 开发的。
虽然 DSD100 模型最初是使用单声道音频输入训练的,但研究人员 Gerard Erruz 探索了使用多声道甚至空间音频输入训练模型的想法[1]。Erruz 最初假设双耳频谱线索,如双耳电平和时间差(ILD 和 ITD)可以对帮助 CNN 更准确地执行源分离产生积极影响。为了测试这一点,DSD100 以几种配置(“cross”、“xeix”和“random”)进行双耳处理,以输入 CNN。
为了对这些空间训练的源分离神经网络进行感知评估,进行了听音测试。
为了量化这种性能改善,使用了五种客观测量方法:源失真比(SDR)、源干扰比(SIR)、源噪声比(SNR)、源伪像比(SAR),以及图像与空间失真比(ISR)的合并,以说明立体声输出中的空间失真[1]。Erruz 的结论是,某些指标(SDR,SIR)表明,用双耳音频输入训练的模型(除了将所有训练信号放置在随机位置的配置)优于用立体声或单耳输入训练的模型[1]。
虽然这些客观测量看起来是展示模型性能差异的合理方式,但是这种源分离任务仍然必须诉诸 AQO 方法,并且呈现感知上高保真的音频输出。为了感知地评估这些空间训练的源分离神经网络,进行了听音测试。此外,使用相同 CNN 架构的模型通过双声道编码的 DSD100 进行训练,以探索使用空间处理音频的替代形式来进一步提高源分离性能的可能性。
测试方法
刺激物的选择
必须训练几个 CNN 模型来代表不同的方法。原始的 DeepConvSep 架构被保留下来,因此数据库输入的差异将成为主要变量。一个模型使用标准 DSD100 作为立体声训练模型进行训练。双耳和双声道训练模型都以“交叉”形式配置,因为已经注意到特定通道中的信号优势提高了分离性能[1]。图 1 展示了数据集的空间定位。

图一。双耳解码的“交叉”布局。“D”、“V”、“B”和“O”分别对应于鼓、人声、低音和其他。[1]
DSD100 使用两个!Ears 的双耳场景模拟器和 SADIE 头部相关传递函数(HRTF)数据库,以创建 BDSD100(双耳 DSD100) [4]。ADSD100 (ambisonic DSD100)是通过使用 SN3D 归一化球面谐波对数据库进行 ambisonic 编码而创建的。该实验使用一阶环绕声,产生一个双耳解码的 4 声道信号。双耳解码是通过将使用谷歌的共振音频工具包进行双声道编码的双声道音频与赛迪 that 的双声道音频进行卷积,并将结果信号求和为左右声道[5]。
使用 DSD100 的各种版本训练模型,然后使用每个模型分离数据库的 50 首测试歌曲。测试刺激是从这 50 首歌曲中选择的,所有这些歌曲都不是原始模型训练的一部分。使用单个均方误差训练阶段和表 1 所示的 DeepConvSep CNN 的默认参数来训练所有模型。

表 1。CNN 训练参数[1]。
一台配备特斯拉 K80 显卡和英特尔至强 E5–2690 V3 CPU 的微软 Azure 云计算机用于模型训练。
测试配置
对总共 14 名参与者进行了听力测试,他们都自认为听力正常。大多数参与者都是经验丰富的音频工程师,尽管小组中的音频经验水平各不相同。测试在德比大学的半电波暗室中进行,参与者使用一对 Beyerdynamic DT770 PRO (80 欧姆)闭背耳机进行测试。采取这些措施是为了减少未经声学处理的空间中任何房间反射的影响,这种影响会扭曲定位线索。然而,考虑其他类型的耳机可能是有意义的。例如,已经注意到自由空气等效耦合(FEC)耳机具有更准确的双耳刺激再现,因为它们在覆盖耳朵时不会改变耳道的声阻抗[6]。

图二。德比大学半消声室中的听力测试装置。
使用 DragonFly Red DAC 在计算机上进行测试。使用网络音频评估工具[7]设计了隐藏参考和锚的多重刺激(MUSHRA)测试。网络音频评估工具被设置为将测试信号校准到-23 LUFS,参与者不能改变信号电平。选择 MUSHRA 测试格式是为了符合 ITU 的编码系统中间质量水平主观评估方法(ITU-R bs . 1534–1)[8]。MUSHRA 格式在几个源分离听音测试中一直是一个主要范例,并以其评估测试信号与已知参考信号相比的退化的能力而闻名[9]。
该测试总共包括 8 首歌曲,参与者被要求评价所选元素的分离程度(如贝斯、鼓等)。)或不同音频样本的“稳定性”如何。参与者被告知,如果没有不切实际的振幅和声相调制,音频就更稳定,如果相反,音频就不稳定。评级是在 0 到 100 的范围内进行的。通过从 DSD100 数据库中提取测试歌曲各自的词干,创建了一个隐藏引用。通过对 3.5 kHz 的隐藏参考应用低通滤波器来创建低范围隐藏锚点。剩下的选项包括由双声道、双耳和立体声训练模型执行的分离。实验的零假设是双声道和双耳训练的模型在感知上与原始立体声训练的模型没有区别。测试的样本页面如图 3 所示。

图 3。MUSHRA 听力测试的样本页面。
结果分析
在 MATLAB 中对分离和稳定性响应数据进行单向方差分析(ANOVA)测试,以捕捉总体趋势并确认统计显著性。分离和稳定性反应的 p 值分别为 0.3863 和 0.001。这表明,由于 p 值高于证明显著性的 0.05 (5%)阈值,分离均值未被证明具有统计学显著性差异。就分离质量而言,不同的格式可能太难区分,并且它们中没有一个可能完成了预期的任务。可能还很难确定分离质量对参与者意味着什么,这可能是相关听力考试中未来改进的领域。然而,稳定性评级低于 p 分数阈值,因此拒绝了零假设。因此,当使用双耳和双声道音频来训练立体声分离模型时,观察到的稳定性损失得到了证实,这表明当训练源分离模型时,这可能不是使用的理想方法,因为稳定性可能对感知评估有很大影响。这种分析还产生了缺口盒图(见图 4 和图 5)。

图 4。比较不同训练的 CNN 模型之间的分离等级的凹口盒图。“Ambi”、“anch”、“bina”、“ref”和“ster”指的是 ambisonic、anchor、双耳、参考和立体声。

图五。缺口盒图比较不同训练 CNN 模型之间的稳定性评级。
所有源分离模型的总体得分都很低,立体声模型的中值稍高,意见方差较低。然而,由于模型箱线图的所有凹口几乎对齐,这一观察结果落在置信区间之外,表明没有强有力的证据表明中位数实际上是不同的。双声道和双耳模型都有更大的方差和更多的异常值,这可能意味着这些格式产生了感知混乱的结果,产生了各种各样的意见。参考中有许多异常值,这些异常值很可能代表对听力任务不完全清楚的参与者;这些异常值也可能始终一致。
稳定性得分有较大的方差,但也再次得出了更明确的结论。立体声模型自信地胜过双耳和双声道模型,因为它的中值得分更高,并且在其他模型的置信区间之外。奇怪的是,立体声模型的评分差异很大。识别音频稳定性可能太不明确,并且可能在将来使用一些改进作为目标度量。在很大程度上,当分离立体声音乐时,用双声道和双耳音频训练的模型表现出较低的稳定性;这可能归因于这些格式之间的空间定位技术的差异。
由于模型对各种类型的音乐和乐器的反应非常不同,所以从测试选择的任何特定流派或乐器中没有得出重要的结论。然而,据观察,立体声模型在人声分离方面的得分始终比其他两个模型好。也许与其他工具相比,声音分离更容易区分,因此参与者能够做出更果断的选择。
结论
用双声道和双耳音频训练的模型并没有胜过立体声训练的模型。使用立体声素材训练的模型可能在分离立体声音频时表现最佳。感兴趣的是进一步研究由它们各自训练的模型分离的各种不同的音频格式,以观察当将模型训练与预期的目标素材配对时的任何性能改进。当使用更高分辨率的环绕立体声输入时,为了观察任何差异,也可以使用更高阶的环绕立体声。
这个实验也对最初研究中使用的客观测量方法投下了怀疑的阴影[1]。当量化源分离系统性能时,可能必须有更好的测量方法,以确保不同源分离模型的质量能够被准确和彻底地理解。
总的来说,很明显这种源分离算法在感知上是无效的。虽然模型和输入材料的特定组合产生了极好的分离结果,但是性能是不一致的,并且必须采取进一步的措施来改进源分离神经网络结构。这项研究的结果表明,在这一点上,改变音频训练材料的空间格式可能不是源分离的进步之路。
参考
[1] G. Erruz,“卷积神经网络的双耳源分离”,2017。硕士论文。庞贝法布拉大学。https://zenodo.org/record/1095835#.XNLm2-hKiUk(访问日期:2019 年 3 月 28 日)
[2] P. Chandna,M. Miron,J. Janer,E. Gomez,“使用深度卷积神经网络的单声道音频源分离”,2017 年潜变量分析和信号分离国际会议。
[3] A. Liutkus,F. R. Stö ter,Z. Rafii,D. Kitamura,B. Rivet 等人,“2016 年信号分离评估活动”,第 13 届潜变量分析和信号分离国际会议(LVA/ICA 2017),法国格勒诺布尔,2017 年,第 323–332 页,10.1007/978–3–319–53547–0 _ 31 页。hal-01472932
[4]“二!耳朵”,【http://twoears.eu/ (访问时间:2019 年 4 月 8 日)。
[5]谷歌,“共鸣音频”,https://resonance-audio.github.io/resonance-audio/(访问时间:2019 年 4 月 8 日)。
[6] T. McKenzie,D. Murphy 和 G. Kearney,“一阶环绕声的扩散场均衡”,第 20 届国际数字音频效果会议(DAFx-17),英国爱丁堡,2017 年 9 月 5 日至 9 日,第 8 页。
[7] N. Jillings、D. Moffat、B. De Man 和 J. D. Reiss,“网络音频评估工具:基于浏览器的听力测试环境”,2015 年第 12 届声音和音乐计算大会。
[8] E. Cano,D. FitzGerald,K. Brandenburg,“声源分离算法质量评估:人类感知 vs 量化度量”,2016 年第 24 届欧洲信号处理会议(EUSIPCO),2016 年,第 1758–1762 页。
[9] H. Wierstorf,D. Ward,R. Mason,E. M. Grais,C. Hummersone,M. D. Plumbley,“混音音乐的源分离感知评估”,音频工程学会大会 143,2017。
原载于 2019 年 5 月 9 日https://danrothdatascience . github . io。
表演草坪设备
解决业务效率问题的练习

Photo by Helloquence on Unsplash
以下迷你项目试图回答虚构性能草坪设备委员会提出的几个问题,这是一家专门从事拖拉机和割草机生产的公司。相关数据包括客户满意度、市场销售报告、生产效率和成本效益分析。这些练习的目的是探索用 Excel 报告基本统计数据的各种方法。下面展示的所有作品和图片都是用 Excel 创建的。
经销商和最终用户满意度

Figure 1: Average Dealer Satisfaction with PLE by Region
图 1(上图)显示了 PLE 运营的每个地区 5 年间的平均经销商满意度。这些评级是通过一项调查收集的,该调查允许受访者对他们对 PLE 的满意度进行 1-5 级评分,其中 5 级为最高满意度。对收集的数据进行加权平均,得出上图。我们可以看到,南美经销商对 PLE 的评价一直是最高的,而中国经销商的评价往往是最低的。南美和欧洲市场对该公司的历史评级约为 3.75-4.00,变化不大。环太平洋地区在过去 5 年中取得了最大的进步,平均评分从 2010 年的不到 3.25 上升到 2013 年的超过 4.00。虽然 2014 年的平均评级略有下降,但所有其他市场的评级都保持或高于 2013 年。

Table 1: Average Satisfaction from Dealers by Region and Year
表 1 显示了从经销商处收集的满意度评分的平均值和标准偏差,评分等级为 1-5。我们可以看到,除了欧洲,所有地区的收视率每年都有所提高。例如,北美在 2010 年的平均评分为 3.78,到 2014 年增加到 4.11。尽管中国最初的平均评级较低,但评级仍从 2012 年的 3.00 提高到 2014 年的 3.69。欧洲将其平均评级从 3.93 提高到 4.12,但随后在 2013 年略微下降到 4.07。大多数区域的标准偏差也逐年增加,表明对调查的回应值范围更广。这表明回答中有更多的可变性,但平均值的增加表明数值趋向于更高的等级。

Figure 2: Average End-User Satisfaction with PLE by Region
图 2(上图)显示了从发送给 PLE 产品最终用户的类似调查中收集的数据。本次调查的数据也被收集和加权,以创建按年份和地区的平均值,其中 5 被认为是最高满意度。总体而言,最终用户的满意度比经销商调查显示的要高。在这个数据集中,北美市场的排名几乎每年都在上升。南美通常在最终用户满意度方面排名第三或第四,2014 年,它在所有市场中排名最低,平均得分为 4.11。与平均经销商满意度相似,中国终端用户市场的满意度往往最低。南美和环太平洋地区每年的趋势都很接近,从 2011 年到 2013 年,差异不到 0.05。欧洲的变化可能是多年来最大的,从 2012 年的低点 3.90 增加到 2013 年的高点 4.07。

Table 2: Average Satisfaction from End-users by Region and Year
表 2 提供了类似的信息,尽管是从最终用户调查中收集的。尽管南美市场在 2010 年至 2011 年期间略有下降,但每个地区在 5 年期间的总体增长都有所增加。从 4.0 的平均评分开始,南美的评分在 2011 年下降到 3.95,然后在 2012 年回升到 3.99,随后几年稳步上升。2010 年后,北美地区在 PLE 服务区域中得分最高,2014 年的平均得分为 4.22。

Figure 3: Total Complaints Filed Quarterly by Region
上图 3 详细显示了每个地区每个季度受理的投诉数量。北美市场始终占向 PLE 提交的所有投诉的至少 50%,有时占 60%。北美市场也是最大的市场,这可能解释了为什么来自该市场的投诉量最大。从 2011 年开始,来自南美市场的投诉比例逐渐增加,而来自欧洲市场的投诉量相对稳定。我们还可以确定中国在 2012 年进入该市场的时间,因为在第一季度,新市场开始出现投诉。

Figure 4: Customer Service Call Response Times by Quarter

Table 3: Response Times to Customer Service Calls by Quarter
最后,图 4 和表 3 强调了客户服务部门的平均呼叫响应时间。此指标有 2 年的数据,按季度细分。乍一看,我们可以看到,从 2013 年 Q1 奥运会(3.829)到 2014 年第四季度(2.477),平均响应时间有所缩短。两年来,四分位距也有所改善;2013 年,75%的响应时间在 2.988 到 5.042 之间。到 2014 年第 4 季度,75%的响应时间在 1.62 到 3.476 之间。2014 年第四季度记录的最长时间为 4.872,而 2013 年 Q1 的最长时间几乎是 8.019 的两倍。虽然时间全面下降,但 2014 年第三季度有一个异常值为 7.419,远远超出第三个四分位数范围 4.075。
米 市场表现

Figure 5: Total PLE Mower Sales each Quarter by Region
图 5 和图 6 分别显示了各地区割草机的季度销售情况。图 5 显示大多数 PLE 割草机在北美市场销售。通常,在任何给定的季度中,超过 80%的割草机销量来自北美市场。欧洲是第二大消费市场,平均每个季度约占 10-15%。南美占比不到 5%,环太平洋地区和中国占比更低。我们还可以看到每年北美和欧洲市场之间的购买模式。北美市场的销售额在第一和第二季度有所增长,而欧洲市场在第三和第四季度有所增长。

Figure 6: Monthly Mower Sales by PLE and Industry
图 6 显示了 PLE 和整个行业割草机销量的比较。割草机的销售几年来一直很稳定,没有太大变化。然而,每年的这个时候都会有一个明显的周期:销售额通常会在夏季达到最高,然后随着时间的推移而下降。PLE 和整个行业的销售都受到这一趋势的影响,如下图所示。通过分析 5 年间的销售数据,我们可以计算出 PLE 和行业割草机销售之间的相关性约为 0.9904。这表明在每次销售上升和下降之间有很强的关系。

Figure 7: Total PLE Tractor Sales each Quarter by Region
图 7 显示了类似的拖拉机销售数据,尽管结果大相径庭。北美市场通常只占 PLE 拖拉机销售的 30-40 %,而不是像割草机销售那样主导市场。欧洲在这些销售中占有较大份额,每个季度占 30-35%。南美和太平洋地区平均各占季度销售额的 10-15%。在过去几年中,欧洲的拖拉机销量下降,而北美的销量上升。南美市场的销量也有所增加,在 PLE 拖拉机销量中占据了更大的份额。

Figure 8: Monthly Mower Sales by PLE and Industry
同样,我们可以观察 PLE 和拖拉机销售行业之间的关系。图 8 追踪了拖拉机在同一 5 年间的销售情况。像割草机一样,拖拉机的销售往往会随着每年月份的变化而起伏。然而,与割草机的趋势不同,拖拉机的销量每年都在稳步增长。2010 年 7 月,行业销量达到 15,905 辆,2011 年 4 月增加到近 19,000 辆,最终在 2014 年 6 月达到 27,374 辆的峰值。尽管 PLE 的销售数据只是一小部分,但它们从 2011 年开始就遵循了大致相同的趋势。销量从 4 月份的 2340 辆逐步增长到 2014 年 6 月创纪录的 4476 辆。PLE 和行业拖拉机销售之间的相关性略低于割草机销售,为 0.9603,但它仍然表明两者之间的密切关系。
扩张成本

Table 4: Fixed Costs of Capacity Increase versus New Facility Construction
上表探究了在堪萨斯城和圣地亚哥新建工厂和扩建现有工厂的成本。通过将总施工成本分解为施工后的新单位成本,我们可以更容易地比较每个方案的价值。这考虑了每项提案需要收回多少成本。考虑到每个地点的新成本和平均运输成本,我们可以看到割草机的单位成本从 39.85 美元到 77.57 美元不等,拖拉机的单位成本从 40.30 美元到 78.01 美元不等。第一个四分位数分别为 54.95 美元和 55.40 美元,第三个四分位数分别为 62.49 美元和 62.95 美元。
根据第一张表中的数据,最有价值的项目(或新项目单位成本最低的项目)最有可能是孟买和新加坡的新工厂。孟买的两项提议都属于第一个四分位数,这意味着它们属于 25%最便宜的提议。将堪萨斯城的容量扩大到 20,000 也属于第一个四分位数,使其成为最便宜的升级选项。圣地亚哥的其余升级选项超过了第三个四分位数,成为成本最高的 75%的项目之一。第四个四分位数,也称为第 100 个百分位数,等于数据集中的最大值。在这种情况下,将 Santiago 的容量升级到 5,000 将导致最高的单位成本以收回成本。
业务运营效率

Figure 9: Ratio of Deliveries Made On Time by Month
我们可以在图 9(上图)中看到 4 年间的准时交付率。2010 年 3 月达到最低点,1 116 次交付中有 27 次没有按时交付,导致从 98.1%降至 97.6%。2010 年 9 月是一个明显的高峰,当时 1223 次交付中只有 13 次延误。该月 98.9%的交付是准时的,这将保持到 2012 年 7 月的最高点。上面的红色虚线描绘了成功交付率每月逐渐增加的趋势线。虽然有一些明显的高点和低点,但大多数交付率遵循稳定的模式。

Figure 10: Average Defects from Suppliers Each Quarter (per million items received)
上图显示了每个季度从供应商处收到的缺陷数据。这些数字表明从供应商处收到的每百万件产品中平均有多少件是次品。最高时,PLE 平均每百万接收 848.33 个缺陷产品。从 2010 年 Q1 奥运会到 2012 年第三季度,缺陷产品的数量开始减少,类似的比率一直保持不变。到 2012 年第四季度,平均值为 701.33。2013 年略有下降,全年平均为 669 件。从 2013 年第四季度开始,次品率开始快速下降。2014 年每个季度平均下降 40–80 个,2014 年第四季度的平均缺陷率为 439.67 个。
割草机刀片生产

Table 5: Detailed statistics from mower blade tests
上表描述了收集的割草机刀片重量数据集。测试的 350 个叶片的平均重量约为 4.99,标准偏差为 0.1093。叶片重量几乎没有变化。50%的观察值落在 4.92-5.06 之间,范围只有 0.14;95%的观察值落在 4.77 到 5.21 之间。假设数据是正常的,有 2.78%的概率测得的体重超过 5.2。类似地,4.04%的测量值预计低于 4.8。在收集的数据集中,2%的权重在 5.2 以上,2.29%的权重在 4.8 以下,分别相差 0.78%和 1.76%。
查看下图,我们可以进一步确认重量通常在严格的规格范围内。几乎所有的测量值都在 4.7 和 5.3 之间,有两处异常。画一条平均体重线,我们得到的斜率为-7e-5,以-0.00007 的微小速度下降。虽然有一个负斜率,但它小到可以忽略不计。我们可以有把握地假设刀片将继续以 5.00 的稳定平均重量生产。

Figure 11: Mower blade weights over time
总的来说,我们可以假设刀片制造过程是稳定和可预测的。然而,数据集中有两个异常值。发现 37 号叶片为 4.63,略高于平均值的 3 个标准偏差。样品 171 被记录为 5.87,这大大超出了任何值的预期范围。这可能是由许多问题造成的,如个别缺陷、材料问题或测量或记录值的错误。因为平均值、中值和众数都是相似的值,并且这些值均匀地分布在平均值的两侧,所以正态分布是该数据的最佳拟合模型,如下直方图所示。

Figure 12: Histogram of blade weights over time
在最后一个实验中,下图 13 中的指数分布显示了割草机测试的结果。这种方法最好地显示了 30 次测试中每一次有多少次失败的频率,每一次测试随机抽样 100 台割草机。总的来说,被观察的 3000 台割草机中有 54 台没有通过测试,约占 0.18%。我们可以看到不到 15%的测试报告没有失败,而大多数测试报告了一两次失败。大约 35%的样本报告 1 次故障,大约 38%的样本报告每 100 台割草机 2 次故障。2 次之后,这一比例急剧下降,只有 15%的测试报告 3 次失败。100 个割草机样本中,没有一个测试报告超过 5 次故障。根据收集的数据,我们可以估计接下来测试的 100 台割草机中大约有 2.07 台会出现故障。

Figure 13: Failures in mower blade tests
在 TensorFlow 中执行分类

在本文中,我将解释如何使用 Python 中的 TensorFlow 库执行分类。我们将使用加州人口普查数据,并尝试使用个人的各种特征来预测他们属于哪个收入阶层(> 50k 或<=50k). The data can be accessed at my GitHub profile in the TensorFlow repository. Here is the 链接来访问数据。我的代码和 Jupyter 笔记本可以在下面访问:
我目前正在做的 Tensorflow 项目。通过创建帐户,为 harshsing 16/tensor flow 的发展做出贡献…
github.com](https://github.com/HarshSingh16/Tensorflow/blob/master/Classification_Tensorflow.ipynb)
导入库和数据集
让我们从导入必要的库和数据集到我们的 Jupyter 笔记本开始。

让我们看看我们的数据集。所以,有 15 列。在这 15 列中,有 6 列是数值型的,其余 9 列是分类型的。下图提供了有关列类型和相应描述的信息。请注意,在这个例子中,我们不会使用变量“fnlwgt”。

查看我们的目标列:
我们现在来看看我们的目标栏“收入”。如前所述,我们正试图对个人的收入等级进行分类。所以,基本上有两类——“≤50K”和“> 50K”。

然而,我们不能让我们的目标标签保持当前的字符串格式。这是因为 TensorFlow 不把字符串理解为标签。我们必须将这些字符串转换成 0 和 1。如果收入等级大于 50K,则为“1 ”,如果收入等级小于或等于 50K,则为“0”。我们可以通过创建一个 for 循环,然后将标签附加到一个列表中来实现。我还用我们刚刚创建的新列表直接更新了现有的“收入”列。下面是执行转换的代码:

标准化我们的数字特征:
我们现在想要规范化我们的数字特征。归一化是将数值
特征可采用的实际值范围转换为标准值范围的过程,通常在区间[1,1]或[0,1]内。规范化数据并不是一个严格的要求。然而,在实践中,它可以提高学习速度。此外,确保我们的输入大致在同一个相对较小的
范围内是有用的,这样可以避免计算机在处理非常小或非常大的数字时出现的问题(称为数字溢出)。我们将使用 lambda 函数来做到这一点。代码如下:

创建连续和分类特征:
下一步是为数值和分类数据创建特征列。将特性列视为原始数据和估计值之间的中介。特性列非常丰富,使您能够将各种各样的原始数据转换成评估人员可以使用的格式,从而允许进行简单的实验。这里有一个来自 TensorFlow 网站的例子,说明了功能栏是如何工作的。这里讨论的数据是著名的 Iris 数据集。如下图所示,通过估计器的feature_columns参数(Iris 的DNNClassifier)指定模型的输入。特征列将输入数据(由input_fn返回)与模型连接起来。

为了创建特性列,我们必须从[tf.feature_column](https://www.tensorflow.org/api_docs/python/tf/feature_column)模块调用函数。这张来自 TensorFlow 网站的图片解释了该模块中的九个功能。如下图所示,所有九个函数都返回分类列或密集列对象,除了bucketized_column,它从这两个类继承:

现在是时候为数据集创建要素列了。我们将首先处理数字列,并通过使用[tf.feature_column.numeric_column](https://www.tensorflow.org/api_docs/python/tf/feature_column/numeric_column)将它们转换成特性

接下来,我们将处理分类特征。这里我们有两个选择-
[tf.feature_column.categorical_column_with_hash_bucket](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_hash_bucket) :如果您事先不知道分类列的一组可能值,并且有太多可能值,请使用此选项[tf.feature_column.categorical_column_with_vocabulary_list](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_list) :如果您知道一列的所有可能的特征值的集合,并且只有少数几个,则使用此选项
因为在我们的例子中,每个分类列中有太多的特征值,所以我们将使用散列函数。请确保指定的哈希值大于列的类别总数,以避免将两个不同的类别分配给同一个哈希值。

接下来,我们希望将所有这些变量放入一个名为 feat_columns 的列表中。

执行培训和测试分割
我们将使用sklearn库来执行我们的训练测试分割。因此,我们必须将我们的标签与特征分开。这是因为来自sklearn 的模块 train_test_split 模块要求您显式指定特性及其目标列。

我们现在将导入我们的 train_test_split 模块。我们将在测试集中保留 33%的数据。这将为我们提供足够数量的观察结果,以准确评估我们模型的性能。

定义输入函数和线性分类器:
我们现在创建一个输入函数,将熊猫数据帧输入到我们的分类器模型中。模块 tf.estimator.inputs 提供了一种非常简单的方法。它要求您指定功能、标签和批量大小。它还有一个名为**shuffle,**的特殊参数,允许模型以随机顺序读取记录,从而提高模型性能。

接下来,我们将定义我们的线性分类器。我们的线性分类器将训练一个线性模型来将实例分类为两个可能的类别之一——即 0 代表收入小于或等于 50K,1 代表收入大于 50K。同样,TF . estimator . linear classifier允许我们只用一行代码就能做到这一点。作为参数的一部分,我们必须指定我们的特性列和类的数量。

训练模型:
最后,激动人心的部分!让我们开始训练我们的模型。显而易见,我们必须指定输入函数。**steps** 参数指定训练模型的步数。


预测
现在是时候进行预测了。首先,我们需要重新定义我们的输入函数。虽然定型模型需要您指定目标标注和要素,但在生成预测时,您不需要指定目标标注。预测结果将在稍后与测试数据上的实际标签进行比较,以评估模型。所以让我们开始吧!

现在让我们将输入函数输入到 model.predict 中。请注意,我已经在 model.predict 函数周围调用了 list 对象,这样我就可以在下一步轻松访问预测的类。

万岁!我们现在有了我们的预测。让我们看看对测试数据中第一个观察值的预测。在下图中,我们可以看到我们的模型预测它属于 0 类(参考 class_ids )。我们还有一堆其他的预测,比如类的概率,逻辑等等。然而,为了进行我们的模型评估,我们只需要class _ id。下一步,我们将尝试创建一个我们的class _ id的列表。

如上所述,我们现在将从字典的预测列表中创建一个仅包含class _ id键值的列表,这些预测将用于与真实的 y_test 值进行比较。

查看前 10 个预测的类别。

车型评价
我们现在已经到了项目的最后阶段。我们现在将尝试评估我们模型的预测,并通过使用 sklearn 库将它们与实际标签进行比较。

这是我们的分类报告:

我还打印了一些其他评估指标,这些指标将让我们非常清楚地了解我们模型的性能。我们的模型具有 82.5%的总体准确性和 86.5%的 AUC。好的分类器在曲线下有更大的面积。显然,我们的模型已经取得了一些非常好的结果。

最后备注:
我希望这篇文章能让您很好地理解在 TensorFlow 中执行分类任务。期待听到大家的想法和评论。请随时通过 LinkedIn 联系我。
表演探索,罗宾汉风格
如何对全新的产品进行有价值的探索,只需要简单的嵌入技巧
本帖原载于 Taboola 的工程博客 。

我们在 Taboola 的核心业务是为网上冲浪者提供个性化的内容推荐,无论他们可能在哪里冲浪。我们使用最先进的深度学习方法来做到这一点,从我们不断增长的文章和广告池中学习向每个用户显示什么。但是,当我们挑战自己展现更好的模型和更好的预测时,我们也发现自己经常面临另一个问题——我们如何不听从我们的模型。或者换句话说:我们如何更好地探索?
正如我刚才提到的,我们的文章池正在增长,这意味着每分钟都会添加越来越多的项目——从人工智能的角度来看,这是我们必须解决的一个主要问题,因为当我们完成新模型的训练并将其推向生产时,它已经必须处理其训练数据中从未存在的项目。在之前的一篇文章中,我已经讨论过我们如何使用加权抽样来允许对低点击率(点击率)的项目进行更多的探索,同时尽量不损害高点击率项目的流量。在这篇文章中,我将进一步扩展这个困境,并讨论我们如何允许对我们的模型从未见过的项目进行有意义的探索。

理解嵌入和 OOV
理解嵌入的使用——以及它们代表什么——是理解所讨论的探索方法的关键,所以让我们简单回顾一下它们是如何工作的。嵌入是模型学习的一组权重(或更简单地说,一个向量)来表示输入要素的特征。假设我们有两个项目,其中项目 A 在生活在美国和加拿大的 20 到 30 岁的游戏玩家中非常成功,项目 B 在生活在欧洲德语国家的 30 到 40 岁的烹饪爱好者中非常受欢迎。模型应该在哪里存储这些学习到的信息?当然是在每件物品的嵌入物中!因此,嵌入是模型已经学习的特定于值的向量,它允许模型区分彼此,并更准确地估计它们的性能。对于我们的每个项目,模型学习一个新的向量。如果我们的模型训练正确,另一个项目是成功的项目 A 的人群将有一个嵌入向量,这将是接近他在我们的向量空间。
但是,假设我们的训练数据有另一个项目,项目 C,这是如此之新,它只出现在训练数据一次。这种单一的外观是否足以让模特了解这件物品的任何信息?当然不是。所以为它创建嵌入是没有意义的——因为它显然只是噪声。然而,我们仍然需要一个嵌入,所以我们应该怎么做呢?
每当我们构建一个特性的嵌入时,我们决定每个值必须具有的最小外观的阈值,以便有它自己的嵌入。出现次数少于所需阈值的所有值随后被聚集,并用于构建将用于所有这些值的单个嵌入。这种嵌入被称为词汇外嵌入(OOV),因为它用于所有没有进入具有自己嵌入的值列表(或词汇)的值。此外,OOV 嵌入还用于所有全新的项目,这些项目是该模型从未见过的。
很容易理解为什么 OOV 嵌入对任何值都是有害的。它是由许多许多完全不同的价值观构成的,这些价值观很少相互关联。这意味着 oov 通常是嘈杂的,并且具有非常低的 CTR 估计值——即使这些项目没有任何问题,但它们只是在模型训练时显示得不够。
但这里有一个恶性循环——如果一个新项目由于缺乏外观而接受了 OOV 嵌入,它将得到模型非常低的 CTR 估计——这又不会允许它接受许多外观。这可能会使模型坚持它总是显示的相同项目,这对我们不好。所以… 我们该去舍伍德森林了!

罗宾汉探险
我们在 Taboola 最重要的目标之一是预测用户是否会点击我们展示给他们的项目。因此,我们的训练数据由我们展示的所有项目(也称为印象)和用户的反应(点击与否)构成。由于我们展示某个项目的次数与其收到的点击次数之间存在明显的正相关关系,因此很容易看出我们最畅销的项目也是在我们的训练数据中出现次数最多的项目。这意味着这些也是具有最强大和训练有素的嵌入的项目。
因此,我们在这里做的技巧非常简单——就像嵌入有一个最小的外观阈值,低于它的值会变成 OOV,我们给外观添加了一个最大阈值,高于它的值也会变成 OOV。我们对其进行了调整,因此只有最顶端的项目会超出此阈值,并将被添加到 OOV 存储桶中。这为我们提供了一个由我们拥有的最赚钱的项目组合而成的 OOV——所以现在所有的新项目都接受了一个超级强大的嵌入,这使得该模型给它们更高的 CTR 估计值。我们将这种方法命名为罗宾汉探索,因为我们从我们最好的项目中“窃取”数据,并将其移动到我们的 oov 中。
是的,它确实有效:

与我们的默认模型相比,罗宾汉方法的不同嵌入组的印象(黄色)和收入(绿色)。
罗宾汉方法的表现符合预期——我们的 oov 和弱势嵌入群体获得了更多的流量,也产生了更多的收入,但这是以牺牲我们的畅销书为代价的。中等强度的嵌入几乎没有变化。减少畅销书的流量是我们在影响有限流量时可以处理的事情——我们在这里的目的是进行探索,也就是给新品增加流量,没有必要对我们的畅销书进行探索——我们已经知道它们是我们的畅销书。还有更好的消息:当谈到我们的总收入时,这种方法绝对没有显示出任何负面影响。

总之,我们的 Robin-Hood 方法(将数据从顶层项目转移到 oov)已被证明是以最少的技术努力进行有意义的探索的一种有价值的方法。我们希望它也能证明它对我们的读者是有价值的。一路平安!
使用 Numpy 广播执行多维矩阵运算

Tensor manipulation…
Numpy 的 broadcasting 特性可能会让这个库的新用户有些困惑,但它允许非常干净、优雅和有趣的编码。绝对值得努力去习惯。在这篇短文中,我想展示一个很好的广播实现来节省一些循环甚至计算时间。
先说一个简单的案例。
基本概念是,当两个大小为 (m,1) 和 (1,m) 的向量相加或相乘时,numpy 会广播(复制向量)以便允许计算。例如,将一个向量【1,2,3,4,…10】乘以其自身的转置版本,将得到乘法表。例如:

Broadcasting a vector into a matrix. A miniature multiplication table.
在这个例子中,我们将一个大小为 (3,1) 的一维向量(V)与其大小为 (1,3) 的转置版本相乘,得到一个 (3,3) 矩阵,它是V的外积。
如果您仍然感到困惑,下图将该过程分为两个步骤,使其更加清晰:

Broadcasting in slow motion.
你可以把广播想象成简单地把我们的两个向量复制成一个 (3,3) 矩阵,然后执行元素式乘法。
我们刚刚将一个一维数组广播到一个二维矩阵中,然而,我们可以用它将一个二维数组(或矩阵)广播到一个三维数组(张量)中。想象一下,您需要对相同长度的多个向量执行V*V.T。你可以简单地把它们堆在一起,然后一次性得到所有的结果。
个案研究
在这种情况下,我希望为虹膜数据集生成一个欧几里德距离矩阵。欧几里得方程是:

显然,可以使用两个嵌套的 for 循环来获取表:
然而,它也可以使用矩阵运算来执行(这两种运算都要快 100 倍,而且要酷得多)。
我们取数据集(图中表示为'A'),

A rectangle representing the iris data set, in which case m=150 and n=4.
创建它的 3D 副本,并将其旋转到图中所示的方向'B'。

We can use numpy’s rot90 function to rotate a matrix. See code below.
然后我们创建另一个副本并旋转它,如'C'所示。

Copy and rotate again.
B-C将生成(通过广播!)一个 3D 立方体('D'),大小为 (m,m,n) ,代表计算

对于原始数据集向量( n =4)的每个实例( m )和特征( n )。

Notice that we are summing up the features for all instances at once!
然后,我们简单地将整个立方体提升到 2 的幂,将代表特征的轴上的结果相加(见上图),然后平方。
结果就是距离矩阵('E')!

This is the Euclidean distance matrix. It might seems like it only contains the letter ‘E’, but in fact it holds the distance between all instance pairs. Twice. The diagonal is the distance between every instance with itself, and if it’s not equal to zero, then you should double check your code…
如果你在所有这些矩阵的翻腾中迷失了,回头看看计算欧几里得矩阵的方程。我们所做的就是同时求解几个向量的方程。
代码:
摘要
每当在几个实例上执行相同的计算时,广播是一种非常干净有效的方法。作为一个练习,我鼓励你尝试在 2 个 3D 阵列上使用广播(假设你有 n 个向量,属于 k 个类)。这意味着你将需要广播到第四维度。当然不可能想象,但是实现它遵循同样的逻辑。
使用自然语言处理的香水推荐
Doc2Vec、潜在语义分析和情感分析一起在聊天机器人界面中提出相关建议。

Photo by Jessica Weiller on Unsplash
介绍
(如果你想尝试这种模式,你可以通过发短信“嗨”到(424)343–3755 来开始。你自己的个人 AI 聊天机器人会回复,会帮你发现个性化的香水推荐。)
自然语言处理在推荐系统和信息检索中有许多有趣的应用。作为一名香水爱好者和数据科学家,小众香水社区中使用的不寻常且高度描述性的语言启发我使用 NLP 来创建一个模型,以帮助我发现我可能想要购买的香水。“小众”香水是由小型精品香水公司制造的稀有香水。类似于葡萄酒,有一个围绕着小众香水的整个亚文化,它有自己的诗歌词汇,非常适合 NLP!
我希望这个模型做两件事:
- 我希望能够描述一款香水,并根据我的描述获得相关推荐。由于所使用的建模方法,也由于香水的语言如此丰富,该模型可以推荐与情绪、感觉、个性或度假等事件的描述相匹配的香水
- 把情绪考虑进去。我希望能够描述我不喜欢的东西以及我喜欢的东西,并且仍然能够收到相关的推荐。
数据
我写了一个 python 脚本,从一个流行的小众香水网站上收集数据。他们似乎并不介意。;-)
我收集了三个文本数据源,并把它们连接到每种香水的一个文档中:
- 描述
- 复习
- 香水中的香调列表
下面是我个人最喜欢的香水 Delma 的三个文本数据源的例子。


一些有趣的结果!
我用一个集成了 Doc2Vec 和潜在语义分析(LSA)的模型在 python 笔记本中创建了一个聊天机器人界面。Doc2Vec 和 LSA 表示潜在空间中的香水和文本查询,然后使用余弦相似度来匹配香水和文本查询。最相关的前五种香水作为推荐返回。
这是一个与聊天机器人互动的例子。对圣诞节香水的简单查询会返回五种适合季节的香水。这一请求的第一款香水有非常切题的没药味!

Christmas perfume recommendations
这里有几个更有趣的例子:

The query “Looking for my signature beach scent. I plan to wear it to the beach or pool” returns perfumes with notes of seasalt, coconut, and seaweed. My own beach scent, Eau de Soleil Blanc by Tom Ford, appears second on the list! I can attest that this perfume does smell like a day at the beach on a tropical vacation!

The query “I’m looking for a perfume for a vacation in Italy to a small Italian island.” returns perfumes with Sicilian orange and lemon, and a perfume from a perfume house Carthusia called capri.
为什么感情如此重要?
考虑这条聊天机器人消息。“我喜欢桃子和梨。酒鬼香草甜味美食家。”

注意第四种推荐的香水有椰子和烟草的味道。如果我讨厌那些笔记怎么办?我更新了我的查询以包含这个信息,并得到了一个更新的推荐列表。“我喜欢桃子和梨。醉人的香草甜味美食家。我不喜欢烟草,或者椰子。”

第四款香水从推荐中消失!
模型
该模型的第一步是从聊天机器人消息中识别每个句子的情感。我用 VADER 来做这件事。(超级好用,给了我很大的效果。如果你有一个想使用情感分析的项目,我强烈推荐你尝试一下。)我把所有正面和中性的句子串联成一串,所有负面情绪的句子串联成另一串。我现在有两个文件可以用来寻找类似的香水。
这些香水有文字描述、评论和注释列表。该模型由两个文档嵌入组成,一个来自 LSA,另一个来自 Doc2Vev。为了训练 LSA 和 Doc2Vec 模型,我将每种香水的香水描述、评论和注释连接到一个文档中。然后,我使用余弦相似度来查找与聊天机器人消息查询中的肯定句和中性句相似的香水。我删除了与否定句相似的香水推荐。
为了计算聊天机器人消息和香水文档之间的余弦相似性,我分别从 LSA 嵌入和 Doc2Vec 嵌入中计算余弦相似性,然后对这两个分数进行平均以得出最终分数。
LSA 简单地用 TF-IDF 对文档中的单词进行了符号化,然后用 SVD 将这些特征压缩成嵌入内容。LSA 是一种单词袋(BoW)方法,意思是不考虑所用单词的顺序(上下文)。这就是弓形方法的缺点。然而,我已经看到许多 BoW 方法在实践中优于更复杂的深度学习方法,因此 LSA 仍然应该被测试并被认为是一种可行的方法。
Doc2Vec 是一种从文本文档中学习嵌入的神经网络方法。由于其体系结构,该模型考虑了文档中的上下文和语义。文档的上下文和单词之间的关系被保存在所学习的嵌入中。
通过将 Doc2Vec 与 LSA 捆绑在一起,我能够获得很好的一对一匹配,例如当我请求时返回玫瑰香水,我还能够利用语言的复杂性,并在我描述更抽象的东西(例如情绪或事件)时返回相关结果。
结论
如果你想尝试这种模式,并获得一些个性化的建议,你可以发短信给直播机器人(424)343-3755。聊天机器人是使用 Flask、Twilio 可编程 SMS、Airtable 和 AWS EC2 实例部署的。我的公共 github repo 上也有代码。

Using Twilio, I deployed this model as a chatbot sms service.
使用 Python 促进能源和经济发展
可再生能源在增强人民权能方面可以发挥关键作用。
我开发了一个工具来模拟 20 年的太阳能,并在我的城市波尔图用几个月平均电费进行了测试。目标是针对每个月的平均账单,给出最佳的太阳能解决方案(哪个和多少太阳能电池板,如果是电池,哪个电池是个好主意),以最大化投资回报。这份报告对消费者或公司都很有用。
代码中最重要的部分可以在 Github 查看。
查看下面这个项目的报告!

ELECTRIC AESTHETIC by Tesla
项目的相关性
这个项目的想法是在我试图思考如果我经营一家可再生能源公司我会做什么的时候产生的。当然,也有其他软件可以计算可再生能源的潜力,但都不是免费的、准确的,也不可能同时比较多个太阳能电池板。由于对可再生能源充满热情,我认为这将是 python 数据科学库的第一个完美项目。
工具
Python、Numpy、Pandas 和 scikit-学习
说到科学计算和数据科学,两个关键的 python 库是 NumPy 和 pandas。
Numpy 在使用上有令人难以置信的优势:NumPy 是用 Python 和 C 编写的,因此 NumPy 数组比 Python 列表更快。然而,与 Python 列表不同,NumPy 数组不灵活,所有元素都应该是同一类型。
当谈到用 Python 分析数据时,Pandas 是一个相当大的游戏改变者。Pandas 接收数据(CSV 文件、SQL 数据库等。)并创建一个具有行和列的 Python 对象,称为数据框,它看起来非常类似于统计软件中的表(允许对数据结构进行数学运算,忽略数据结构的元数据,使用关系运算,并且很容易处理丢失的数据)。这通过提供数字表格和时间序列数据结构,实现了简单快速的数据分析和操作工具。
Scikit-learn 是一个强大而高效的数据挖掘和数据分析工具。该库专注于建模数据,是进行机器学习项目时使用的主要库之一。
PVlib 和 Sandia 模块
PVlib python 是一个社区支持的工具,它提供了一组用于模拟光伏能源系统性能的函数和类。PVlib 是由桑迪亚国家实验室开发的,它实现了实验室开发的许多模型和方法。
桑迪亚国家实验室正在推动一个光伏(PV)专业人员协作小组(PV 性能建模协作或 PVPMC)。该小组对提高光伏性能模型和分析的准确性和技术严谨性感兴趣。此类模型用于评估当前绩效(绩效指数)并确定光伏发电项目的未来价值(以预测的发电量表示),进而影响金融界如何看待光伏项目和技术的投资风险。
PVlib python 的源代码托管在 GitHub 上。
创建太阳能电池板价格数据库
本节用到的重要代码可以在 Github 上查看: Function_Find_Panels , Find_Panels_DB , Prices_aux
由于缺乏关于桑迪亚数据库中太阳能电池板价格的信息,人们找到了新的方法来了解这些公司如何评估他们的产品。基于废弃的两个不同的太阳能电池板价格信息网站,创建了两个数据库。这两个网站有不同的相关信息:
DB1:品牌;太阳能电池板模型;最大功率;日常用电;效率;最小功率;每个面板的成本;生产国;保修;
DB2:品牌;太阳能电池板模型;权力;每个面板的成本;尺寸;重量;生产国
因为没有关于要测试的太阳能组件价格的明确或可用的数据。我必须创建一个插值方法,根据可供购买的电池板的价格、功率和年份来预测每个太阳能公司收取的每瓦特价格。
创建了一个新的数据库,将太阳能电池板品牌与其预测价格与 scikit-learn 库中的两种方法相关联:(1)线性回归方法——使用单个自变量来预测因变量的值;(2) Ransac 方法——一种迭代算法,用于从完整数据集的内联子集中稳健估计参数。

Example of Two Different Solar Panels Price Interpolation
从上面的图中可以看出,对于太阳能价格,两种回归方法并无不同。另一方面,LG 展示了一个非常有趣的案例,说明为什么应该选择 RANSAC 回归器来插值模块的价格。如果选择线性回归,则假设 LG 面板的价格随着功率的增加而下降,这没有多大意义。出于这个原因,RANSAC 回归器被选为插入太阳能组件价格。

根据我所能收集到的关于太阳能电池组件价格的最多信息,对两个数据库进行了比较(Sandia 模块和太阳能电池板价格),并根据比较结果对 35 块电池板进行了测试(见左侧表格)。
面板名称和 Sandia_Name 在左侧图像中带有“(…)”,以避免创建过大的表格。
能耗
消耗函数改编自加州每小时电力负荷曲线。据推测,同样的曲线模式也适用于葡萄牙。来自图像的曲线通过绘图数字化仪包扫描,该数字化仪包将曲线转换成 x,y 坐标文本文件。
使用 numpy,我创建了一个函数来拟合我之前在 plot digitizer package 上标记的点。运行了几个具有不同回归度的 polifit 函数,发现误差较小的回归度为 n=15(见下图)

Duck Curve Polygonal Regressions
了解季节性消费模式也很重要。为此,葡萄牙能源月刊采用了同样的程序。
现在到了棘手的部分:
每天的变化必须符合每年的能源消耗变化。换句话说,每个月内的每日能量消耗必须与一年内每个月的变化进行核对。为此,我将每个值除以所有瓦特值的总和。这样,所有值的和就是一。
电力消耗的年度变化是不同的。与消费群中总和等于 1 不同,每 12 个值除以平均值是有意义的。这样就很容易将这些值乘以每月的平均账单。
通过 numpy 功能,创建了一个 24x12 的矩阵。在矩阵中,有每个月每个小时消耗的电力值(假设从同一个月开始的几天内没有消耗变化)。
最后,使用 pandas 库的日期范围(与 PVlib 中使用的相同),为每小时的计算分配一个特定的电力消耗。
创建电池数据库
电池数据库是由三个提供电池信息和价格的网站组成的。

创建了一个包含 22 种电池型号的数据库。这些模型来自 AXITEC Energy、LG、SolaX Power、BYD、Tesla、PYLONTECH、VARTA 和 Enphase,存储在一个 csv 文件中,包含左侧示例中的信息。
能源和节能计算
为了确保尽可能准确的结果,计算是按小时进行的。太阳能电池板的能量是用一年中和几年内每小时的辐照度变化来计算的。以 20 年为时间线,单个面板的每一轮计算加上消费数据大约需要 40 秒。为所有电费计算所有电池板的产量将花费大量的时间。为了解决这个问题,我想用三个步骤来找到每月电费的最佳解决方案:
第一步——每块太阳能电池板都要进行测试,以对潜在的节约进行分类。计算出的节约量是第 2 步中计算出的实际节约量的近似值。
第 2 步——每个月平均账单的五个最佳潜在节约将成为第 2 步计算。在此阶段,将模拟精确节约。
步骤 3——对于每个月平均账单,将使用电池数据库中的每个电池测试步骤 2 中的两个最佳案例。
第 1 步—快速节约的结果
涉及太阳能电池板性能的第一次计算是使用 PVlib 库进行的。对于每个月的平均账单,计算需要多少太阳能电池板,以确保每个电池板装置的年产量至少足够接近年消耗量。
为每个太阳能组件安装创建了三个模型,以给消费者更多的选择并提高透明度。第一种,使用最接近的整数,即一个面板的年消耗量除以年能量所得的值(n=0)。第二种情况比第一种情况多一个面板(n=1),第三种情况比第一种情况少一个面板(n=-1)。

每块太阳能板的节能量计算方法是,20 年内生产的能源乘以葡萄牙每千瓦时能源的价格,再减去太阳能板的价格。建议的太阳能电池板数量的总节约量,节约量乘以建议的太阳能电池板数量。对于每次安装,都会创建一个新的数据库。上表代表了月平均账单为 50€且 n=0 时的最佳情况。
步骤 2 —精确节约结果
第 1 步的结果是此阶段的基础。现在只计算前一阶段的五个最佳结果。
介绍了一种计算总节约量的新方法。在此阶段,为了提高精确度,每小时都会计算一次节省量。上面计算的消费函数参与其中。该方法包括计算节约额,即 20 年时间内不安装该装置所花费的资金、初始投资、仍需向电网购买的资金(主要是夜间)以及向电网出售电力所获得的资金之间的平衡(尽管在葡萄牙,该值仍比向电网购买电能低 4 倍)。
为了每小时计算太阳能电池板是否会产生满足消费者需求的能量,完全不产生或者甚至产生超过需求的能量,使用具有 175×316 个元素的 AC _ df(20 年中的小时数)的以下 for 循环:
for k in range(len(ac_df)):
Grid_Power[k] = consumo[k] - ac_df.iloc[k].loc['Watt-hour']if Grid_Power[k] < 0:
Grid_Buying_Power[k] = 0
Grid_Selling_Power[k] = Grid_Power[k]if Grid_Power[k] > 0:
Grid_Buying_Power[k] = Grid_Power[k]
Grid_Selling_Power[k] = 0
同样,对于每个月的平均账单,计算了三种解决方案。

此步骤的最终结果显示了五个面板,并根据新的节约计算对它们进行了重新排序。在左图中,有月平均 75€(当 n=0 时)的最佳面板的最终信息。看完这些结果后,一个细心的人会说,这个面板上显示的结果根本不可能。不幸的是,在 20 年内购买 1129.6€的电力是不可能的。在葡萄牙,夏令时约为 40%。因此,如果没有电池,从电网购买的总电量将只有没有太阳能电池板时的一半。在其他几个面板上也发现了这一错误,这些面板已从计算中删除。
第 3 步—电池的节能效果
第 3 步计算考虑电池存储。节能计算与第 2 步类似,但这一次使用电池储存能量,以供夜间使用。预计在所有情况下,向电网购买的剩余电量将接近于零。
在这些计算中,使用了每个月平均账单的 2 个最佳面板(步骤 2)。
为了每小时计算能量产生和电池存储的平衡,使用以下 for 循环:
for k in range(len(ac_df)):if k == 0:
batt_stock[k] = batt_aux + ac_df.iloc[k].loc['Watt-hour']
-consumo[k]else:
batt_stock[k] = batt_stock[k - 1] +
ac_df.iloc[k].loc['Watt-hour'] - consumo[k]
Grid_Selling_Power[k],Grid_Selling_Power[k] = 0, 0if batt_stock[k] < 0:
Grid_Buying_Power[k] = - copy.deepcopy(batt_stock[k])
batt_stock[k] = 0if batt_stock[k] > Battery_Capacity_Wh[num]:
Grid_Selling_Power[k] = batt_stock[k] -
battery_Capacity_Wh[num]
batt_stock[k] = copy.deepcopy(Battery_Capacity_Wh[num])
Grid_Buying_Power[k] = 0
最佳解决方案是根据最高的节约价值选择的。
结果
每个月平均电费的最终结果如下:











关于结果的注释
虽然结果非常精确,但还是做了一些近似:(1)每个太阳能电池板都被认为在 20 年的时间里有相同的 15%的效率损失(见这里的计算);(2)计算中只使用了一个反演器。这可能会导致不同的结果,因为逆变器可以在特定类型的太阳能电池板上更好地工作;(3)没有考虑太阳能模块之间的能量损失。
最后的想法和结论
虽然一些从电网购买的太阳能电池板的价值出现了小的倒退,从计算中排除了一些太阳能电池板,减少了研究中太阳能电池板和品牌的数量,但这个项目的目标还是达到了。事实证明,太阳能电池板可以是一项伟大的长期投资,电池可以使这项投资更加有利可图。
我们可以在一段时间内消耗的瓦特小时数和你的房子需要的太阳能电池板数量之间画一条线。安装的电池板数量应该能够在同一时间段产生该功率。这样,太阳能装置将产生 200%的情感利润和 400%的真实的金钱利润。
可再生能源是赋予新一代人权力的一个很好的方式。平均每年节省 620€可以是人们生活中的一大飞跃,通过明智的投资,节省下来的电费可以成为企业家和公司的游戏规则改变者。
后续步骤
这篇文章标志着第一个 perkier.tech 项目。
perkier.tech 创建创新的工程项目,旨在帮助世界变得更美好,同时让我研究和学习新的东西。

perkier.tech logo
该能源项目的下一步将是利用微电网和区块链技术创建一个独立于电网的小型社区,同时实施新的发电方式(即小型风力涡轮机)。
在接下来的几个月里,我将学习更多关于智能电网和区块链电码的知识。
欢迎反馈和建议!我很想听听你关于如何实施创新工程的新想法。
你可以在 diogoncsa@gmail.com 找到我或者在 LinkedIn 找到我
持续同源:一个带有例子的非数学介绍
在数据科学中使用拓扑数据分析(TDA)工具

Growing disks around points to calculate 0d persistent homology, discussed in detail below.
在一家全是拓扑学家的公司工作,我经常需要编写和解释拓扑数据分析工具(TDA)。TDA 生活在代数拓扑的世界里,它融合了数学中抽象的代数和拓扑概念。抽象代数从来不是我的强项,我也没有上过正式的拓扑课,所以我为我经常使用的工具之一创建了以下非数学的解释和例子,持久同调。
概括地说,持续同源性的焦点是:
当一个人增加一个阈值时,我们在多大尺度上观察到数据的某些表示的变化?
联系这里描述的持久同源性的许多用途的数学是深刻的,并且不在这篇文章中涉及。这里需要注意的是,我们在这里描述的不同用法有着截然不同的直观含义。我们将了解以下方面的不同含义/用途:
- 持续同源的维度(如 0d 持续同源、1d 持续同源、 n -d 持续同源)
- 数据类型(例如信号与离散欧几里得点)
- 两点“接近”意味着什么(例如高度过滤)
这绝不是下面将要讨论的内容的全面总结。这些区别将通过例子和如何使用/解释结果的详细描述变得清楚。
欧几里得数据
本节着眼于与欧几里得数据(例如, n 维分离点的集合)持续同源的几个维度。注意,整个讨论和其中的计算也适用于其他度量空间中的点集(例如,曼哈顿距离)。
0d 持续同源性
欧几里得空间中的持久同调可以最好地解释为在每个点周围同时生长球。这里 0d 持续同源的关键焦点是连接组件 —随着点周围的球扩展,0d 持续同源在球接触时记录。
让我们把这个概念形象化。这里,我们使用两个噪声集群的示例,并随着我们增加每个点周围的球的半径来评估连通性:

Two noisy clusters of data (left), and the corresponding 0d persistence diagram (right).
让我们来看看,当我们把阈值从负无穷大扫到无穷大时,会发生什么。
负阈值不会发生任何事情(没有半径为负的球)。
第一个有趣的阈值是 0。值为 0 时,每个点的连通分量为born——每个点都由一个球表示,且没有球相交。
0d 持续同源是这些球相交时的追踪。更具体地说,它记录一个连接组件中的球何时第一次与一个不同的连接组件的球相交。当我们的第一组两个球接触时,我们称之为球 A 和球 B ,它们将成为同一个连接组件的一部分。因此,我们将有我们的第一个连接组件的死亡,以及我们在持久性图上的第一个点。持续同源的正常先例是当两个球接触时,先出生的球存活;然而,这里所有的点都是 0,我们将简单地通过索引值选择哪个点死亡。所以我们说球 A “死亡”并且它的(出生,死亡)对被添加到持久性图中。随着这两个球的半径继续增加,并且对应于 A 或 B 的球中的任何一个击中点 C 的球,则 AB 连接的组件将与 C 连接的组件合并,导致其中一个组件死亡,并在持久性图上增加第二个点。

Connectivity of two noisy clusters, as measured by 0d persistent homology. Disk colors at any given time represent each unique connected component.
(下面的讨论将利用上面 gif 中的暂停来解释发生了什么,但作为参考,您也可以在这里交互式地完成这个示例。)
该图更清楚地显示了当单个连通分量中的任意点的球撞击来自另一个连通分量的另一个点的球时,分量合并,一种颜色持续,另一种颜色消失。每一种颜色的消失都对应着一个死亡,因此在持久性图上又增加了一个点。
我们来关注一下这个例子的一个具体方面。对眼睛来说,应该很清楚这些数据有两个嘈杂的集群的一些外观。这将对持久性图产生可预测的影响。随着磁盘从 0 增长到 0.66,我们看到多个(出生、死亡)对迅速出现在右侧的持久性图上。这并不奇怪,当每个圆盘的半径增加时,彼此靠近的点会快速接触。
然而,一旦我们达到 0.66,我们在左边看到每个集群中的磁盘被连接成两个不相交的集群(浅蓝色和橙色)。因此,这些组件有增长的空间,而不会触及不相交的组件,导致暂时没有额外的死亡,从而在持久性图上出现新的点时暂停。
然而,最终,当我们增加阈值使得这两个集群在半径 3.49 处接触时,其中一个集群死亡(在本例中,浅橙色),这在持久性图上创建了最后的*(出生,死亡)对。
当阈值超过 3.49 时,所有的球已经接触。因此,这些球将永远不会击中任何其他尚未与其自身相连的球,这意味着不会有任何额外的死亡(例如,持久性图上不再有点)。
(有的选择有一个死亡值无穷大的额外(出生,死亡)对,但这是一个更主观的决定。)
如上所述,聚类的噪声导致持续图上的值更接近 0,并且两个聚类的分离导致 3.49 处的单独的、更高的持续值。因此,在持久性图上,更接近于 0 的(出生、死亡)对和上述任何点之间的间隙具有特殊的意义。特别是,这个间隙表示数据相对于其噪声的聚集程度。对于上面的图片,如果我们进一步分离这两个有噪声的集群,那么最高持久性值和较低持久性值之间的差距会更大。同样地,如果我们把嘈杂的星团推得更近,这个差距就会缩小。
持久性值的这种差距可能增加的另一种方式是如果每个聚类的噪声减少。为了强调这一点,下面是一个随机数据集中到两个集群的例子。更准确地说,在左边我们看到了一个时间序列的点云,我们从随机数据开始,然后我们向前移动到时间上,直到点云由两个点组成。右侧显示了相应的 0d 持久性图:*

Collapsing data to 2 sinks, and the corresponding persistence diagram as we collapse the data.
*当我们有随机数据时,在持久性图中,没有远离 0 附近的噪声值的高持久性值。
然而,随着数据开始分离,我们看到一个越来越持久的点从对应于噪声的持久性值中分离出来。
还要注意,当数据收敛到两个点时,噪声的持续值向 0 移动。当数据收敛到两个点时,聚类不仅变得更加明显,如持续图上最持续的点和其余点之间的间隙增加所表示的,而且当给定聚类内的点之间的距离收敛到 0 时,噪声本身变得不那么嘈杂。一旦数据汇聚到这两个点,所有的点都是 0,但是在每个集群内的*,这些点会立即合并,因为磁盘增长了很小的量。**
为什么在 K-means 上使用 0d 持久同源性
K-means 聚类算法特别受欢迎,因此值得花点时间来强调由 0d 持续同源性收集的额外信息,这些信息在使用 K-means 时会丢失。
特别是,运行 K-means for k =2 不会告诉您任何关于 2 个集群相对于任何其他数量的集群的稳定性的信息,而 0d 持续同源性提供了在持续图上分离值时集群稳健性的度量。
K-means 确实对此提供了一些解决方案,例如通过搜索拐点,它探索了减少误差和聚类数量之间的权衡。当然,这涉及 K-means 的多次运行,并且为了探索通过 0d 持续同源性可以探索的尽可能多的选项,人们将不得不针对 k =1 到K*=数据点的数量运行它。
此外,回想一下 K-means 的结果不一定是稳定的,或者换句话说,用相同的参数运行 K-means 两次并不能保证得到相同的答案,这对于无监督的机器学习来说是一个潜在的问题。另一方面,持久的同源性是一个稳定的结果。此外,0d 持久同调带来了对噪声鲁棒性的一些很好的
数学证明。*
1d 持续同源
对于 1d 持续同源,我们仍然在点周围炸球,就像我们对 0d 持续同源所做的一样。然而,在这里,我们跟踪点之间连通性的更微妙的概念。现在,我们不再仅仅跟踪连接的组件,而是在循环形成和消失的时候进行跟踪。说实话,一个循环*的严格定义需要更多的数学知识,这对于本文来说是不合适的(参见芒克雷斯的代数拓扑书籍了解更多),但是直观的含义将有望从这个例子中变得清晰。
考虑下图中一圈嘈杂的数据:*

A noisy circle of data (left), and the corresponding 1d persistent homology calculation (right).
(下面的讨论将利用上面 gif 中的暂停来解释发生了什么,但作为参考,您也可以在这里交互式地完成此示例。)
在这个持久性图上有一点特别重要。注意左边在 0.25 左右形成一个环(gif 中间的停顿)。此时,数学形式使我们能够说,一个单一的环已经形成,其中包括环内的白色空间。随着阈值的增加,环会变厚,从而缩小白色空间,直到最后不断增长的圆盘最终填充环的内部,大约为 0.6。因此,循环在 0.25 处出生,在 0.6 处死亡*,给我们右侧的高异常值 1d 持续值。
这些环的半径很小,因此很快被膨胀的圆盘填满,导致死亡值接近它们的出生值,因此在持久性图上靠近对角线的地方产生点。
另一种看待 1d 持久同源性的方式是让数据“更具循环性”下面我们用一个网格点,把它分成三个循环:*

Observing 1d persistent homology as we force a grid of data into 3 loops.
*首先,我们最终有三个高度持久的点对应于三个循环,这并不奇怪。一个重要的限定词来自于此。让我们把注意力集中在右上方最小的圆上。在暂留图上,其对应的 1d 暂留值大约为(0.3,0.9)。注意,随着圆圈内部变空,持久性图上的值从对角线向上移动。然后,即使圆外仍有点收敛到循环,它也会稳定下来。我们正在了解循环内部的行为/鲁棒性,但是 1d 持续同源不能保证数据是一个圆。
我们可以用一个静态的例子进一步强调这一点。下面使用的两个数据集由跨越一个圆的一组点组成,第二组点使用相同的圆数据,在圆外有一个额外的点网格。比较从下面两个数据集得到的两个持久性图:*

Perfect circle of data, and its corresponding single 1d persistence value.

The same perfect circle of data, with a grid of points outside it. This gives us the same 1d persistence value, plus some noisy 1d persistence values resulting from loops quickly forming and closing among the grid points.
对于具有外部网格的圆,我们得到一些快速出生和死亡的循环,如沿着对角线的点所示,但是主循环对于两组点具有相同的持久性值。
n-次元持久同调
值得注意的是,这种方法可以推广到 n 维欧几里德数据(随着维度的增加,我们吹大的球只是变成了球体/超球体,我们寻找这些球包围的多维空洞,而不是包含在循环中的圆)。然而,我们不会在这里进一步探讨。
使用欧几里德数据的持续同调测量的示例
*机器学习的一个重要方面是特征提取。如果不使用代表他们试图预测的特征,最强大的模型也不能预测任何事情。这就提出了一个问题,我们如何为几何复杂的物体生成特征?如上所述,持续同源提供了一个解决方案。
举个例子,我们将使用这篇应用统计学年报论文中的数据来检验大脑中的动脉与年龄之间的关系。首先,让我们先看看我们是否能从视觉上区分年轻的大脑和年老的大脑:*
**
2 brain artery trees. On the left, a 20-year old. On the right, a 72-year old.
这两种脑动脉树之间似乎没有任何显著的差异。接下来让我们尝试使用 1d 持续同源性来量化这些差异:
**
1d persistence diagrams for the 20-year old (left) and 72-year old (right).
同样,在这些图中可能有些小的不同,但是总体上,它们看起来有些相似。让我们寻找群体差异,区分 50 岁以下和 50 岁以上的年轻人和老年人的大脑。我们的示例数据集共有 98 个案例,分为年轻组中的 58 个案例和年长组中的 40 个案例。以下是按组汇总的所有 1d 持久性图:
**
1d persistence diagrams for all 98 brain artery examples, separated into brains from individuals over age 50 (left) and brains from individuals below age 50 (right).
这些数字似乎有细微的差异,但这里的特征仍然不是很清楚。关于这些图表的更细致的特征,参见应用统计学年报论文,其中它们持久的基于同源性的特征被证明在检测脑动脉树的年龄差异中是有用的。不过,为了简化起见,让我们只考虑 1d 持久性值的数量:
**
The relationship between the number of 1d persistence values in a brain artery tree and age in our dataset. On the left, we include the full figure. On the right, we consider the same relationship, but excluding the few outliers to better show the subtle, linear relationship.
正如这些图表所示,年龄和 1d 持久性值的数量之间存在微妙但具有统计意义的(p-值< 0.0005)关系。平均而言,在我们的数据集中,年轻人的脑动脉树中比老年人有更多的环路。虽然我们没有发现数量上的强关系,正如像增强这样的技术所证明的那样,一组微妙的关系可以在机器学习中一起工作,导致令人印象深刻的分类。
信号
对于信号,我们关心的是我们认为连续的东西的离散近似。因此,与前面讨论的与欧几里得数据的持续同源性不同,我们在这里对点之间的“x”距离如何分布不感兴趣。相反,我们只关心基于每个点的值的点之间的连通性(例如“y”距离)。
像前面的 0d 持续同源讨论一样,当我们增加一个阈值时,我们仍然在跟踪连通分量;然而,炸球不再是我们正在做的事情的类比,因为我们不关心点之间的水平分布。相反,我们可以认为这是在信号上扫一条线,如下图所示:

A toy example of a signal (left), and the corresponding signal persistence diagram (right).
在这种情况下,我们可以用各自的颜色来表示信号的连通分量。因此,持久性图上的点将对应于颜色的消失,所以让我们用下图来讨论颜色和持久性图之间的联系:

The same toy example signal as above, but coloring the connected components with unique colors as we sweep up the threshold.
*当我们向上扫描阈值时,我们看到我们短暂地拥有多达三个独立的组件(绿色、橙色和粉色)。当这些组件中的两个在阈值 4.5(gif 中间的停顿)相遇时,我们看到橙色组件“死亡”,在持久性图上为它出现一个点。信号持续同源性遵循通常的先例;较新的连接组件死亡并合并到较旧的组件中。因此,橙色成分变成绿色,因为它现在是绿色成分的一部分。当我们继续向上扫描时,我们看到没有出生或合并发生,直到我们到达信号的全局最大值。按照通常的持久性同源约定,我们将较年轻的组分合并到较老的组分中,并在持久性图上标记一个点,粉红色胜过绿色。然而,与欧几里德 0d 持久同源性不同,我们在持久图上为最后存活的组分创建了一个额外的点。回想与欧几里德 0d 的持久同调,当最后两个集群相遇时,我们在持久图上只放一个附加点。欧几里德 0d 持续同源的想法是,当我们继续增加每个点周围的球的大小时,不会再发生合并,因此集体集群永远不会死亡。这里,我们采取的立场是,一旦阈值超过信号的全局最大值,一切都死了。
我们为什么选择这种不同的做法来发出信号呢?答案与最后一个持久性值中包含的信息有关。信号的最后一个持久性值包含了信号的边界——它的出生值是信号的全局最小值,它的死亡值是信号的全局最大值。然而,用欧几里得 0d 持久同源性,我们在记录最后一点时没有获得额外的信息。该点总是在 0 处出现,或者在无穷远处消失,这不会显示任何信息(对于所有数据集都是如此),或者我们选择在它与最后一个聚类合并时消失,也不会显示任何信息,因为这将使最后一个持久化点成为倒数第二个持久化点的重复。*
信号持续同源性使用的例子
信号持续同源性可以帮助我们的一个例子是
压缩。作为一个具体的例子,我们将使用 1000 个时间点的随机行走的一些玩具数据:

A random walk signal to compress.
开始时,我们当然储存 1000 点。然而,使用信号持续同源性,我们可以存储更少的点,同时仍然相当精确地重构信号:

A reconstruction of the signal, using only the persistence values. Note we’ve already achieved nearly 75% compression.
我们已经实现了近 75%的压缩,显然,我们的压缩信号“保持”了原始信号的形状,我们可以通过将两个信号叠加来强调这一点:

An overlay of the original signal (blue) with the reconstructed signal (orange). Note they appear nearly identical.
然而,这个过程并不完美。我们丢失了一些信息;我们只是无法在信号的最大范围内看到它。让我们放大信号的一部分:

An subset of the overlay of the original signal (blue) with the reconstructed signal (orange). Note we have lost the non-critical points on the reconstructed signal.
这里需要注意的重要一点是,持续同源性只保留了斜率从正变为负的临界点*。斜率在不同正值之间或不同负值之间变化的点没有被保留。这是有意义的,因为没有(出生,死亡)对记录这些值。此外,如果点之间的插值,甚至是两个临界点之间的插值,是非线性的,那么该曲率将会由于这种压缩技术而丢失(尽管可以存储额外的曲率信息用于更精确但不太紧凑的压缩)。
现在让我们考虑一下,如果 75%的压缩率对我们来说不够好,我们会怎么做。也许我们只能传输少量的数据,或者也许我们有更大数量级的信号。我们可以通过选择保留哪些持久性值来再次利用持久性同源性,即保留最高的持久性值,并因此优先考虑信号中最重要的结构。下面是一个演示,展示了当我们保留更少的持久性值时,我们返回的信号的简化:*

A reconstruction of the signal (left) as we remove progressively more signal persistence values (right). Note the size of the resulting reconstruction in the title of the figure on the left, and recall the original size of the signal was 1000 values.
对于上面压缩的交互式可视化,请参见这里的了解更多。
当我们移除更高的持续值时,我们对信号的最终重构进行越来越剧烈的改变,但是即使当我们存储少至大约 50 个点(95%的压缩)时,我们也保持了信号的体面骨架。
如果您想在自己的例子中尝试这种压缩技术,我已经将这一概念转化为一个名为topological-signal-compression的独立 Python 包。代码可以通过 PyPI (例如pip install)安装。其他代码示例(包括真实信号数据和一个音乐示例)可在文档中找到。
在我的 arXiv 论文中可以找到关于涵盖理论、用例以及机器学习实验的技术的更详细的讨论:用于推断和近似重建的信号的拓扑简化。
图像的高度过滤
*与图像的持续同源与信号的持续同源有很多相似之处。像信号持续同源一样,我们关心的是我们愿意认为是连续的东西的离散近似。此外,点与点之间的连接是基于每个点处的值,而不是点与点之间的欧氏距离。最后,就像信号持续同源一样,我们将能够根据图像中像素的“高度”(例如颜色)来跟踪连接组件的出生和死亡。
我们将看看下面的场景:*

An toy example of an image with one dimension of color, which we will think of as height.
或者,从视觉上强调高度和颜色之间的三维关系:

A 3d representation of the above image.
我们像这样扫起一个门槛:

Sweeping up a threshold along the image from low to high height.
或者如在三维空间中看到的:

A 3d representation of sweeping up a threshold along the image.
使用高度过滤,当我们向上扫描阈值时,我们跟踪连接的成分,就像我们在信号持续同源性中所做的那样。因此,我们得到以下高度过滤图:

Visualizing connected components as unique colors as we sweep up the threshold on the toy example image.
高度过滤的使用示例
高度过滤的一个可能用途是图像分割。我们将展示一个木质细胞的例子:

An actual image of wood cells.
运行高度过滤最简单的方法是对单一高度进行过滤,因此我们将从将图像从三个颜色通道(RGB)转换为一个颜色通道开始。我们还将对图像进行模糊处理,以促使该分割管道聚焦于更强健的特征(例如,最大限度地减少对细胞壁中某些点的亮度斑点的聚焦):

Taking the image of wood cells, putting in grayscale, and blurring.
在我们将高度过滤运行到用户指定的水平后,我们就能初步感受到我们可以如何区分不同的细胞:

Visualizing the connected components generated from the blurred, grayscale image.
看起来很有希望!作为一项完整性检查,让我们看看原始图像上每个连接组件的中心:

The wood cells figure, with a point at the center pixel of each connected component.
我们的表现很出色,但并不完美。有一些假阳性,可能有一两个特别窄的遗漏细胞。
至于我们分割图像的效果如何,下面是原始的木材细胞图像,识别的像素由顶部的连接部分着色:

The original wood cells image overlayed with our connected components as calculated using the height filtration.
我们在一些更窄形状的细胞的细胞壁内捕捉像素并不出色。不过,总的来说,我们捕获了每个单元格内的大部分像素。值得强调的是,要运行这个算法,我们只需要手动调整两个参数,模糊参数和高度阈值。此外,该算法运行迅速,不需要任何标记数据或训练程序来实现这些强有力的结果。
如果你对沿着不同的阈值跑步时的高度过滤感到好奇,请参见这里的获得一个交互式小工具。
结论
我希望我已经激起了你对持久同源的兴趣,并扩展了你对 TDA 如何扩充数据科学家工具箱的好奇心。
为了响应一些对用于生成本文中图形的代码的请求,我发布了一个包含我的 Python 代码的库。但是,请注意,我不会支持这个回购,所以我不保证这些脚本目前是有效的。对于所有持续的同源性计算,我使用了一个名为gda-public的开源 TDA 包,可以在 PyPI 和 GitHub 上获得。信号压缩功能得到支持(和维护!)在一个叫做topological-signal-compression的包里,这个包在 PyPI 上有。我所有的可视化都是在matplotlib或holoviews完成的。要在一个地方访问我在本文中链接的所有小部件,请参见这里的了解更多信息。感谢阅读!
人员重新识别
什么是人员重新识别
在计算机视觉中,在其他时间或地点识别感兴趣的人是一项具有挑战性的任务。它的应用范围很广,从通过相机跟踪人们到在大型画廊中搜索他们,从将相册中的照片分组到零售店的访客分析。像许多视觉识别问题一样,姿势、视点、光照和遮挡的变化使得这个问题变得不重要。
论文评论
AlignedReID: Surpassing Human-Level Performance in Person Re-Identification(arXiv 2017)
- AlignedReID 提取与局部特征联合学习的全局特征
- 局部特征学习通过计算两组局部特征之间的最短路径来执行对齐/匹配
- 全局特征学习极大地受益于局部特征
- 联合学习后,只需要全局特征来计算图像间的相似度。

aligned Reid 的框架(张璇等,2017)
全局分支和局部分支共享相同的卷积网络来提取特征图。通过直接在特征图上应用全局池来提取全局特征。对于局部分支,在水平汇集之后应用一个 1 × 1 卷积层,这是具有水平方向的全局汇集。应用三元组硬丢失,根据全局距离通过硬样本挖掘选择三元组样本。

通过寻找最短路径计算的 AlignedReID 局部距离的例子(张璇等人,2017)
黑色箭头显示右侧相应距离矩阵中的最短路径。黑线显示了左边两个图像之间的对应对齐。
Beyond Part Models: Person Retrieval with Refined Part Pooling and A Strong Convolutional Baseline (ECCV 2018)
学习用于个人检索的有区别的部分通知特征有两个贡献:
- 一种称为基于部分的卷积基线(PCB)的网络。给定一个图像输入,它输出一个由几个部分级特征组成的卷积描述符。通过统一的分区策略,PCB 实现了与最先进方法相媲美的结果。
- 一种改进的零件共享(RPP)方法。均匀划分不可避免地会在每个部分中引入异常值,这些异常值实际上与其他部分更相似。RPP 将这些异常值重新分配给它们最接近的零件,从而得到零件内部一致性增强的精细零件。

PCB 的结构(孙一凡等人,2018)
输入图像从主干网络向前通过堆叠的卷积层,以形成 3D 张量 T。PCB 用传统的池层代替原始的全局池层,以在空间上将 T 下采样成 p 个列向量 g。随后的 1× 1 核大小的卷积层降低 g 的维度。最后,将每个降维的列向量 h 分别输入到分类器中。每个分类器都由一个全连接(FC)层和一个连续的 Softmax 层实现。p 个 g 或 h 片段被连接以形成输入图像的最终描述符。

多氯联苯结合精炼零件池(孙一凡等人,2018 年)
当我们关注空间划分时,3D 张量 T 被简单地表示为矩形而不是立方体。T 之前的层被省略,因为它们与之前的图相比保持不变。部分分类器预测每个列向量属于 p 个部分的概率。然后从所有列向量中以相应的概率作为采样权重对每个部分进行采样。差距表示全球平均池。
Camera Style Adaptation for Person Re-identification (CVPR 2018)
提出了一种用于深层人物再识别的摄像机风格自适应方法。
- 使用 CycleGAN 为每对摄像机学习摄像机感知风格转移模型,用于从原始图像生成新的训练图像。真实图像和风格转换后的图像形成新的训练集。
- 为了减轻由 CycleGAN 引起的增加的噪声水平,对生成的样本应用标签平滑正则化(LSR)。

管道的方法(图来自论文)
摄像机感知风格转移模型是从不同摄像机之间的真实训练数据中学习的。对于每个真实图像,利用训练的传递模型来生成符合目标相机风格的图像。随后,真实图像(绿框)和风格转移图像(蓝框)被组合以训练重新识别的 CNN。交叉熵损失和标签平滑正则化(LSR)损失分别应用于真实图像和风格转移图像。
Generalizing A Person Retrieval Model Hetero- and Homogeneously (ECCV 2018)
给定一个有标记的源训练集和一个无标记的目标训练集,本文旨在提高 re-ID 模型在目标测试集上的泛化能力。异质同质学习(HHL)方法被用于同时加强两个属性:
- 相机不变性,通过由未标记的目标图像和它们的相机风格传递的对应物形成的正对来学习
- 域连通性,通过将源/目标图像视为目标/源图像的负匹配对。

管道的方法(图来自论文)
它由两个损失函数组成:1)用于分类的交叉熵损失,通过标记源样本学习;2)用于相似性学习的三元组损失,其对模型施加相机不变性和域连通性,并且通过标记的源样本、未标记的目标样本和相机风格转移的样本来学习。
人员搜索
什么是人肉搜索
人物搜索是一项任务,其目的是在没有边界框注释的情况下在图库的图像中搜索相应的实例。相关联的数据类似于个人重新识别中的数据。关键的区别在于边界框在此任务中不可用。实际上,可以将行人检测和人员识别结合起来视为一项任务。
论文评论
Joint Detection and Identification Feature Learning for Person Search(CVPR 2017)

图取自论文
人员搜索问题设置更接近真实世界的应用,并且比人员重新识别更具挑战性,因为检测行人将不可避免地产生错误警报、错误检测和错位。本文没有将人员搜索问题分解为两个独立的任务——行人检测和人员重新识别,而是在单个卷积神经网络中联合处理这两个方面。提出了一种在线实例匹配(OIM)损失函数来有效地训练网络,该函数可扩展到具有大量身份的数据集。

提议的框架(图取自论文)
行人提议网络生成候选人的包围盒,将其送入识别网络进行特征提取。该特征被投影到 L2 归一化的 256 维子空间,并且用提议的在线实例匹配损失来训练。行人建议网和识别网共享基础卷积特征图。

在线实例匹配(图取自论文)
左边部分显示了图像中已标记(蓝色)和未标记(橙色)的身份建议。我们维护一个查找表(LUT)和一个循环队列(CQ)来存储特性。转发时,每个标记的身份与所有存储的特征相匹配。向后时,我们根据 id 更新 LUT,将新功能推送到 CQ,并弹出过时的功能。注意,这两种数据结构都是外部存储器,而不是 CNN 的参数。
Person Search in Videos with One Portrait Through Visual and Temporal Links(ECCV 2018)
在一些现实世界的应用中,人们需要从长视频中通过少量的肖像来搜索特定的人。与人的重新识别相比,这是一个非常困难的问题,因为环境可能非常多。为了解决这一挑战性问题,作者提出了一种通过视觉和时间链接的标签传播策略,其中视觉链接由视觉外观特征之间的相似性定义,而时间链接由轨迹生成。使用经由竞争共识技术的渐进传播来提高传播过程的可靠性。

图形中的视觉链接和时间链接(黄庆秋等,2018)
在图中执行身份传播,其中节点由检测到的人定义。该图包含两种链接,视觉链接和时间链接(这两种链接都是稀疏的,以提高图的鲁棒性)。

竞争性共识与线性扩散的比较(黄庆秋等,2018)
以防止在标签传播期间身份信息被噪声链路污染。使用竞争一致性模型,目标节点的邻居在传播期间相互竞争,并且仅保留最大的同一性元素。
最初发布于amberer . git lab . io。
个性化的、生成性的叙述
使用 Tweepy、IBM Watson 和 GPT-2,根据用户的个性档案制作个性化内容

Photo by Greg Rakozy on Unsplash
为了我在 Metis 的顶点项目,我决定继续我在自然语言、电影人物塑造和 IBM Watson 个性洞察方面的工作。
在我之前的项目中,我已经成功地将单一类型电影角色的五大人格形象化。然而,对于这个项目,我决定将工作向前推进几个步骤。然而,在我开始之前,请允许我介绍一些背景…
愿景

Imagery from my immersive experience company
在踏上成为数据科学家的旅程之前,我的工作主要植根于沉浸式体验设计行业。虽然我已经在这个行业工作了近十年,但在 2015 年,我成立了自己的沉浸式体验公司,名为截图制作。
在截图中,我们通常一次为一个观众成员制作亲密的沉浸式体验,目标是将个人置于普遍的人类体验中,并挑战他们在更大的整体中考虑他们的位置和责任。我们经常为每个观众定制互动和对话,在某些情况下,我们还会根据观众对展前问卷的回答,创作出完整独特的剧本。
虽然这种形式为巨大的情感冲击提供了渠道,但它根本不可扩展。我们经常发现自己每个演出周末只能容纳 100-150 名观众,并且不得不收取(令人望而却步的)高票价,以达到收支平衡的制作成本。
因此,这个项目的核心问题是:我们能否利用数据科学,特别是生成性深度学习,通过长形式的互动叙事提供类似的有影响力的个性化体验,从而同时影响数十万个人?
另一个人说:
我们是否可以利用从用户的社交媒体档案中获得的个性见解,结合虚构的对话,以可扩展的格式制作强大、互动和完全个性化的叙事?
在变得过于兴奋之前,请允许我说,简短的回答是:目前没有。由于文本生成仍然是一个相对年轻的领域,我在 Metis 的时间也相对较短,所以我没有努力去创作长篇叙事。相反,我利用在 Metis 工作的机会制定了一个简短的目标,作为实现这个更大目标的垫脚石:
成功地创建一个生成模型,将用户的大五人格特征与一个虚构的角色结合起来,并从该角色的角度生成一个短段落或推文。
为此,我确实成功了。
方法、数据和流程
就像我之前的项目一样,我利用的数据来自加州大学圣克鲁斯分校编辑的大量电影文集。该语料库按类型划分,包含 960 个电影剧本,其中电影中的对话已从场景描述中分离出来。
这个过程是对数据进行清理和预处理,这样我得到了一个熊猫数据框架,这个框架按角色进行了分解,所有的对话实例都是行(完整的代码,请查看我的项目报告)。接下来,我过滤了具有 100 行或更多的字符的数据帧,每行至少由 3 个单词组成,这样我就可以返回任何给定电影的主角和主要角色以及实际上对确定他们的个性有一定意义的行。
在这里,我创建了一个关系数据库,其中包含以下嵌套结构的信息:电影类型、电影名称、电影角色,以及所有类型中每个角色的所有对话:
接下来,我创建了一个脚本,通过 IBM Watson Personality Insights 程序运行每个人物的对话,并返回他们详细的五大人格特征。注意,这只是通过 IBM Watson 运行每个字符的函数,但是 Watson 需要单独配置:
此时,我已经有了 3000 多个人物及其相关个性特征的数据库,所以我转到了 Twitter,在那里我使用 Tweepy 从任何给定用户那里提取最近 200 条推文,并编写了一个类似的脚本来通过 IBM 运行他们的个性:
然后将 Twitter 个人资料与关系数据库中的所有人物进行比较,并使用余弦相似度,我打印出了每个流派中与使用此功能的用户最相似的人物:
最后一步,我利用 OpenAI 的 GPT-2 架构为每个角色生成一条简短的 tweet。GPT-2 实例是使用马克斯·伍尔夫创建的 GPT-2 简单 python 库训练的(谢谢马克斯!),对语料库中每个角色的对话进行单独训练,并进行微调,以打印出一条推文长度的原始内容。
结果的预览
所有这些调查的结果令人鼓舞!我用我的团队中的一些人的 Twitter 账号以及一些著名的个人资料进行了测试,如埃隆·马斯克(他与《星际迷航》中的斯波克最相似),我觉得大多数情况下的结果都很有道理。
有了这些,我构建了一些基本的模型,让用户可以像 Flask 应用程序一样与之交互。下图展示了@realDonaldTrump 手柄的真实效果:

Home page for the app

Results page
在上面,我们看到特朗普与《意大利工作》中的查理·克罗克 99%相似。对这个角色的描述是:“一个伦敦犯罪团伙的头目,从监狱里释放出来,打算在意大利做一件大事,从一辆装甲保安卡车上偷走金条。”
是的……听起来很对我的胃口。
顶部的引用是生成性的、微调的 GPT-2 模型的输出,该模型基于查理·克罗克的全部对话进行训练。
后续步骤

Photo by Drew Beamer on Unsplash
虽然我对这个项目的结果非常满意,但这仅仅是个开始。
正如本文开头所提到的,长期目标是实际创建一个动态的、长形式的叙事交付,在几个月的时间内展开,跨许多设备和媒体(文本、视频、AR/VR)交付,并动态响应用户与它的交互方式。这显然是相当雄心勃勃的(在为期 12 周的数据科学课程中肯定不可能实现),但我将在未来一年不懈努力,使之成为现实。
除了仅仅创建一个长篇叙事,我还对使用个性方面向用户开放不同的思维模式非常感兴趣。假设一个用户在他的大五人格档案中对新体验的开放度很低。在故事开始时,他们会从一个有着类似封闭新体验倾向的角色那里获得内容。然而,随着旅程的继续,故事中人物的视角将会变得更加开放,直到旅程结束时,他们处于光谱的另一端。
这里的希望是从本质上充当类似 Youtube 算法的对立面。Youtube 根据你的观看历史引导用户浏览越来越多的“极端”内容。例如,观看可能具有一些右翼倾向(或评论)的视频的用户可能会得到具有稍微更右倾观点的视频,等等。最终的结果是,用户从一个超特定的角度被内容所困扰。
对于这个项目,我想为任何给定的用户建立一个基线人格,将他们与相应的“虚构”角色结合起来,并通过定制的叙事交付让他们接触到不同的思维模式。换句话说,我希望向用户开放以前未探索过的思维模式,拓宽他们的观点,并最终帮助他们打破既定的思维模式,而不是进一步将他们束缚在现有的模式中。
应用+结论
这个项目的应用是令人兴奋和多样的。
我开始将这个项目视为扩展沉浸式体验类型的一种方式,虽然我认为这仍然有效,但我在治疗应用中看到了更多的好处和影响。
有充分的证据表明,我们越是使用某些神经通路,它们就变得越根深蒂固。我喜欢把这些路径想象成雪橇滑下雪山的轨迹。雪橇越往山下走,痕迹就变得越深,直到雪橇很难再走其他路下山。消极的模式,比如增加,可以这样想:除了一次又一次地从事破坏性行为,似乎不可能做任何事情。
然而,我们同样知道,我们的大脑非常灵活,通过神经可塑性,我们实际上可以重新连接我们的大脑回路。我们有无数的方法可以做到这一点:通过注意力、冥想、迷幻药、心理治疗等等。但作为一名体验设计师,我总是被故事的深刻影响所吸引和感动。
我相信,故事是一种独特而强大的方式,可以让我们拥有不同于自己的体验,我希望在这个完成的项目中,我将(随着数据和生成性深度学习的增加)能够有效地挑战个人,找到与他人体验的共鸣和联系,从而改变他们自己的操作模式。
附录
人脑仿真和模拟的哲学方面
科学、技术和哲学必须协同工作

科幻小说探索了人工智能、心灵上传和意识转移的许多后果,但倾向于将科学或技术机制归结为一些模糊的解释或完全忽略它。未来主义者和超人类主义者急切地等待这些想法成为科学事实,以便他们可以体验意识或永生的新前景,但他们可能会忽视所需的开创性科学和技术的难度。
我认为,神经假体导致部分和全部替换人脑并最终上传思想是可能的,因为它没有被物理定律明确禁止,因此主要是更先进的科学和技术的问题。
即使这些可能改变人类本质的超人类和未来主义的观点在理论上是可能的,也没有相应的原则说它们是人类可以达到的,也不是在不久的将来,尤其是在我们都死之前。我们不能也不应该只是等待未来,而是必须行动起来创造未来。
“我不想通过我的作品达到不朽;我想通过不死来达到永生。”
—伍迪·艾伦
一个重要的考虑是在通过技术描绘人脑时仿真和模拟之间的相互作用。仿真涉及更多的技术,而模拟涉及更多的科学。
我将探索人脑在头脑上传中的仿真和模拟的差异和效用
仿真和模拟的区别
为了更好地说明人脑仿真和模拟之间的区别,让我们考虑一下鸟类飞行的类比。
鸟类飞行的模拟
作为一个实现飞行的仿真例子,让我们简单地复制一只鸟如何用翅膀飞行。
这将涉及到精确复制鸟类生理学和解剖学的所有方面,我们认为这些方面在没有任何理解的情况下负责飞行。
我们甚至可以进一步复制诸如羽毛的颜色、眼睛的确切大小、内部消化系统的功能和交配模式等特征,但如果我们只关心实现飞行,这将超出范围。
这里的困难是确定鸟的哪些部分有助于它实现飞行,这似乎是直观的,除了翅膀之外的这些其他特征是不相关的。然而,在其他情况下,复制哪些特征可能不一定如此明显。例如,通过简单观察袋鼠如何在澳大利亚炎热的太阳下保持凉爽和不过热就不那么明显了,因为它没有汗腺。(袋鼠的前臂上有一个特殊的血管网络,所以它们舔那些前臂,唾液的蒸发带走热量。)
这种对鸟类飞行的简单模拟可能看起来很原始,但考虑到我们正在复制自然界花了数百万年才进化出来的东西,这是从单细胞生物到鸟类的远祖再到不会飞的鸟类的近亲,最后到达现在的鸟类的进步的顶点。
在不了解其他任何东西的情况下,仅仅通过观察和复制鸟类翅膀的形状就可以取得很大的进步。这种鸟的存在本身就是飞行概念的证明。

人脑的模拟
类似于模仿鸟来实现飞行,我们可以模仿人脑来实现意识,而不一定需要确切理解大脑的所有组成部分是如何工作的,或者意识是如何产生的。我们可以在以下水平上观察和复制大脑:
- 大脑的总体特征
- 神经网络
- 单个神经元
- 轴突和树突
- 突触
- 神经递质
- 单个分子
这里的困难是考虑成功模拟大脑需要什么样的最小抽象层。这可能不一定需要模拟单个分子的运动,但可能需要我们正确复制神经递质及其与突触的相互作用,这本身就可能非常复杂。
此外,我们将需要高度先进的技术工具来成功地探测、复制、复制、实例化,然后运行。
尽管这非常困难,但对于这种仿真过程来说,这更像是一个工程问题,不需要任何更深入的科学原理。
仿真非常有用,因为它不需要理解或发现关于大脑功能和意识的抽象科学概念,它只需要完成,这可以发生在原始人类神经元之外的基底上。
模拟鸟战
现在,假设我们想要“模拟”一只鸟如何飞行。这就需要飞行的原理,这是一种科学的理解。
这需要比简单复制更高层次的抽象。但是一旦我们理解了更高阶的原理,如推力、升力、阻力,以及空气动力学和物理学的原理,我们就可以设计其他的机翼来“模拟”飞行。这种飞行可能是在机翼设计上,不一定看起来像原始鸟类的翅膀形状,而完全忽略了表面的方面,如翅膀的颜色。
此外,因为我们是在抽象的层面上研究飞行原理,所以我们可以将这些原理用于基于这些相同原理的完全不同的工程。例如,我们可以建造一架直升机,其旋翼的功能不同,但基于与鸟类飞行相同的科学原理。
我们甚至可以进一步利用空气动力学和物理学原理来考虑火箭推进,这将允许我们进入一个全新的空间领域。在这里,我们能够超越鸟类在大气中的飞行,进入一个完全不同的领域。
人脑的模拟
同样,如果我们理解大脑解剖学和生理学的更高抽象,我们就不必像在仿真中那样精确地复制有机和天然神经元的工作方式。
这种大脑功能的模拟将更加依赖于科学原理。例如,根据波动力学复制波浪在水上的运动不依赖于特定的水体或者甚至水本身,因为波动力学已经在数学中被抽象掉了。
(题外话:更进一步说,波的概念需要一个概念上的飞跃,因为,例如,当波穿过海洋时,每个单独的水分子并没有被带走,而是波是一种从组成水分子中产生的突发现象。可以这样理解,人类意识是存在于神经元“水分子”基础成分上的“波”,也可能存在于任何其他介质上。)
虽然对大脑原理的理解将允许我们在除了有机神经元之外的其他基底上模拟人类思维,但在可预见的未来,可能很难获得对人类大脑功能的抽象科学理解,因此纯工程模拟方法在近期可能更可行。
就像鸟类的空气动力学原理可以用来建造火箭并到达全新的太空领域一样,也许大脑功能和意识背后的科学原理可以用来建立人工智能,增强我们自己的思维,甚至将它们融合在一起,探索真正新的意识前景,在抽象出鸟类飞行的特定细节之前,我们甚至无法概念化。

同时使用仿真和模拟
因此,似乎当试图在另一个可以产生意识的物理系统中复制人脑时,我们可能需要两种方法的结合,一种是针对我们可能不完全理解的大脑组件的工程“仿真”,另一种是针对我们在更抽象的层次上理解的方面的基于科学的“模拟”。
这样,我们就可以利用它们各自的优势,避免它们在我们当前科技进步水平下的局限性。
例如:
- 如果我们不知道每个神经元的确切内部工作情况,模拟神经元以达到相同的神经放电特征(可能涉及每个神经元内部发生的计算)可能是有用的。
- 如果我们使用更有效的科学和算法原理来运行我们完全科学理解的大体解剖特征的模拟,而不是直接仿真,则可以节省我们的能量或计算需求。
不管仿真或模拟的具体组合,以及这种组合将随着科学和技术的进步而发展到何种程度,在复制人脑时明确区分它们是有用的。
哲学在科学技术中的效用
认知偏差的存在
科学家和技术人员倾向于对哲学持有从明确和积极的厌恶到冷漠的态度。我确实相信通过对科学和技术的理解而增强的好的哲学是非常有益的,但是很容易做出没有科学和技术基础的坏的哲学。
让我们考虑人类大脑中认知偏差的存在。
- 认知偏差所涉及的任何神经架构是否也应该在仿真或模拟的人脑中复制?
- 如果没有这些认知偏差,它“仍然”是人脑吗?如果不是,那么如果我们模仿或模拟一个存在于另一个基底上的复制人脑,它又怎么可能是人类呢?如果是的话,我们可能会盲目地模仿或模拟那些认知偏见,从而盲目地跟随过去几百万年的进化,而那时我们可以更好地设计。
这可能不仅仅是一个哲学练习,因为在仿真或模拟中实现这些认知偏差可能需要对神经元进行更深入的扫描,以获得更精细的细节,更多的数据处理,以及更多的计算或能量。
这是许多例子中的一个,但其他例子可能更抽象,例如:我们应该在多大程度上更倾向于可能需要更多计算来实现相同性能的仿真,而不是可能抽象掉一些不相关部分的模拟。
科学、技术和哲学可以相互借鉴
以前有一个问题,是科学和工程/技术哪个更基础更重要,哪个驱动另一个。但这是一个定义不清的问题。为什么?因为它们存在于一个相互促进的良性循环中:技术可以让更强大的科学得以实现,大型强子对撞机就是一个例子,而大型强子对撞机反过来又可以推动更好的信息技术,这些信息技术可以用来建立更令人印象深刻的技术,这些技术可以用于更好的科学。
同样,哲学有很大的效用,并有希望通过将科学和技术的注意力集中在重要的事情上,或者至少是未来的事情上,以良性循环的方式促进科学和技术的发展。
如上所述的人类大脑中的认知偏差最终会有影响吗?也许吧。也许不是。但考虑这些认知偏差是否是复制人脑所必需的,并真正复制它们是至关重要的。
科学、技术和哲学的进步必须同步进行,这样它们才能相互加强。可以这样理解,哲学可能是最难的,因为问题可能是无限的,甚至是棘手的,所以我们必须满足于足够好。
使用智能手机传感器和机器学习进行身体活动监控

坐着:最危险的活动之一
久坐行为已经成为世界范围内的一个主要公共健康风险。专家告诉我们,最低量的日常体育活动对于保持健康和降低糖尿病、心脏病和癌症等慢性疾病的风险是必要的。一些研究人员提出,除了不活动的总量之外,长时间坐着本身也可能导致这个问题。2017 年发表的一项研究发现,在控制其他因素后,一次静坐超过 30 分钟会增加死亡风险。久坐的总时间是一个独立的危险因素。同一团队的一项后续研究于 2019 年 1 月发表,发现减少久坐的持续时间没有任何好处,除非这些时间被身体活动(任何强度)取代。2019 年 4 月发表的一项研究发现,从 2007 年到 2016 年,坐着的时间增加了一个小时——成年人超过 6.5 小时,青少年接近 8.5 小时。
每个人口袋里的多传感器
随着用于工作和娱乐的各种尺寸的屏幕的激增,许多人发现自己一天中大部分时间都坐着。然而,同样的设备可以提醒人们保持活跃。智能手机包含许多可用于对运动进行分类的传感器,包括加速度计、陀螺仪、GPS 和磁力计(指南针)。GPS 定位只能在户外使用,但其他的在任何地方都可能有效。持续轮询智能手机传感器会消耗大量电力,并会很快耗尽手机电池。因此,一个有用的算法应该能够基于相对不频繁的传感器轮询来对活动进行分类。一些消息来源表明,智能手机陀螺仪比加速度计消耗的功率大得多,因此只有在绝对有必要解决手头问题的情况下,才应使用它们。
人类活动识别的一个问题
关于使用智能手机和可穿戴设备的传感器来检测和分类不同类型的人类活动,有大量的文献。经常使用的 UCI-HAR(人类活动识别)数据集,发表在 UCI 的机器学习知识库上,有来自 30 个对象的加速度计观察结果,这些对象被标记为行走、上楼、下楼、坐着、站着或躺着(原文如此)。
虽然 UCI 数据集和许多其他数据集要求受试者参与许多不同类型的活动,但对于提议的用例(监测和促进身体活动),区分每种活动类型并不是必要的。我们需要能够区分活动和不活动,并且我们还想知道活动的强度,例如步行与跑步的比较。(虽然一些研究表明,坐着可能比站着更糟糕,但我们只能肯定地说,这种风险在于久坐的行为。)这个项目的目标是利用普通智能手机的传感器数据开发一个模型,可以区分 a)活跃和不活跃状态,b)剧烈和不太剧烈的身体活动。
真实世界的数据集
有许多公开可用的人类活动数据集可用于模型开发和测试。我使用了曼海姆大学数据和网络科学研究小组创建的真实世界数据集。除了 UCI-HAR 数据集中相同的六项活动之外,该数据集还包括两项更活跃的活动(跑步和跳跃)。总共 8 个活动类别是:爬下楼梯、爬上楼梯、跳跃、躺、站、坐、跑/慢跑和步行。研究人员为 15 名受试者配备了 7 种身体姿势的智能手机或智能手表:胸部、前臂、头部、胫骨、大腿、上臂和腰部。我决定只使用“大腿”(口袋)位置的数据,假设这是测试中最常用的位置。该数据集包括来自以下传感器的读数:加速度、GPS、陀螺仪、光、磁场和声级。受试者被要求执行每项活动 10 分钟(跳跃除外)。传感器以 50 赫兹(每秒 50 次观察)记录。我的研究仅限于加速度计和陀螺仪的三轴(x、y 和 z 轴)数据。
探索性数据分析
我为执行 8 项活动中每一项活动的 15 名受试者分别创建了加速度计和陀螺仪数据散点图,x、y 和 z 维度以红色、蓝色和绿色显示。例如,这是一个正在行走的受试者的加速度计数据图:

同一个题材的一个情节坐看起来很不一样:

从这些图中可以看出,受试者被要求在开始指定的活动之前等待一小段时间。因此,每个活动周期的开始和结束的标签经常是不正确的。此外,通过检查这些图可以清楚地看出,并非所有的受试者都连续进行了指定的活动。例如,受试者 4 在几次跑步之间站立了几个时间段,这可以从下面看出:

更正标签
因为受试者并不总是在整个时间段内持续执行指定的活动,所以有必要对数据进行过滤,以去除否则会被错误标记的样本。我将原始数据分成两秒钟的窗口,每个窗口有 100 个观察值(50 赫兹)。我在每个主题/活动的每 100 个连续观察值上标注了一个样本号(去掉任何余数)。然后,我计算了每个样本的传感器数据的标准偏差。然后我画出了标准差。例如,以下是受试者 4 跑步的加速度计数据样本的标准偏差图:

可以清楚地看到对象不运行的时间。通过检查所有受试者的该图和类似的图,y 加速度计读数的标准偏差的阈值 5 被选为截止值。y 加速度计的标准偏差小于 5 的所有样本都从数据中删除。同样,为所有其他活动设定了阈值:静止活动(坐、躺)的最大值和运动活动的最小值。没有对站立进行过滤,因为受试者在记录期间没有转换到站立或从站立转换。
从 8 项活动到 3 节课
我最初测试了所有八个活动的模型。尽管机器学习模型可以适用于训练数据,但它们不会以足够的精度对来自未包括在训练集中的对象的数据进行概括。八项活动的最佳分类模型只能对验证数据产生 75%的准确度(当后者完全由新的受试者组成时)。这对于预期的目的来说是不够准确的,因为太多的错误会导致用户不信任、忽略和删除我们潜在的手机应用程序。此外,对于 PA 推广,没有必要知道活动的确切类型。因此,这些活动被分为以下三个 PA 类别:
- 久坐(站、坐、躺)
- 轻中度 PA(行走、上楼梯、下楼梯)
- 精力充沛的 PA(跑、跳)
下面所有的模型结果都是基于将数据分成这三组。
将数据分为定型集、测试集和维持集
来自前 10 名受试者的数据用于开发和训练模型。其余 5 名受试者的数据留待最终测试。训练数据分为训练样本和测试样本。我尝试了两种方法:使用 Keras 中的“验证分割”设置随机选择样本,并手动将样本分为受试者 1 至 7 的训练组和受试者 8 至 10 的测试组。我发现有必要对训练和测试数据进行基于主题的拆分。随机分割意味着在训练数据中存在来自所有 10 个对象的观察结果。虽然最终的测试分数很高,但该模型在来自以前没有见过的受试者的数据上表现不佳,因为受试者之间存在显著差异。因为我们的用例涉及检测来自任何(以前看不见的)用户的 PA 的能力,所以确保该模型可推广到新的主题是至关重要的。“K 倍交叉验证”是训练和测试数据的另一种形式的随机分裂。人类活动识别中创建重叠样本的常见实践通过在训练和测试数据中包括相同的信息而使问题复杂化。HAR 研究中心对这些问题的一项研究发现“k-fold 交叉验证人为地将识别器的性能提高了大约 10%,当使用重叠窗口时甚至提高了 16%。”
使用手工制作功能的模型
我创建了一个汇总数据集,其中受试者进行的活动样本被分组到 100 个观察窗口中(代表 2 秒的测量)。我计算了加速度计和陀螺仪的三轴测量值(x、y 和 z 维度)的平均值、标准偏差和范围。这 18 个(3×3×2)特征被用于各种机器学习模型来预测该类(久坐、轻中度 PA、剧烈 PA)。测试的模型有逻辑回归、KNN、各种决策树,包括 XGBoost、SVM 和朴素贝叶斯。在验证数据上,逻辑回归提供了最高的准确性(0.973)和 F1 得分(0.956),略高于次佳模型。
对维持数据的测试
逻辑回归模型在由其余五名受试者组成的拒绝数据上进行测试。预测的总体准确率为 94%,加权平均 F1 得分为 0.54。这是按类别划分的混淆矩阵图:

对相关系数的检查表明,许多陀螺仪特性与加速度计特性高度相关(r > 0.9)。仅包括加速度计特征的逻辑回归模型与包括加速度计和陀螺仪特征的模型具有相同的精度和 F1 分数。在这个模型中使用陀螺仪数据没有好处。这一结果意义重大:仅使用一个传感器和简单的统计特征就可以非常准确地预测身体活动。
虽然这个简单模型的整体准确性非常高,但大约 600 个轻中度 PA 样本(全班的 11%)被错误地归类为“久坐”。更复杂的模型能做得更好吗?
神经网络模型
神经网络模型使用原始传感器数据,而不是使用“手工制作的特征”,在这种情况下,原始传感器数据被格式化为 100 个观察值(2 秒)x 6 个传感器读数的数组。因为数据有时间维度,所以使用递归图层是合适的。我使用门控递归单元(GRU)技术,因为它比 LSTM 具有更少的参数,并且在较小的数据集上可能具有更好的性能。卷积层可以通过帮助模型学习重要特征、防止过度拟合和减少参数数量来改善结果。批次标准化用于进一步减少过度拟合。最终模型包括:
- 两个卷积层,具有 50 个过滤器、3 个内核和 ReLU 激活,每个卷积层后面都有一个池层和一个批处理规范化层(第一个池大小设置为 4,第二个池大小设置为 2)
- 两个递归(GRU)层,每个层具有 64 个神经元和 tanh 激活(第二层具有 0.2 的递归下降)
- 辍学率为 0.1 的辍学层
- 代表要预测的三个类别的最终密集层。
在这个模型中有超过 55,000 个参数需要训练。虽然这种神经网络比逻辑回归模型的计算强度大得多,但它相对稀疏——一些神经网络有数百万个参数要训练。该模型仅在两个时期内使用 25 个样本的小批量进行训练。计算时间适中。
神经网络模型的性能甚至优于逻辑回归模型,达到 99%的准确性。大约有 100 个实际上是高强度 PA 的样本被错误地归类为轻中度 PA。这是一个非常小的错误率,并且是一个比以前的模型更好的错误类型,因为错误标记涉及身体活动的程度,而不是运动和久坐行为之间的混淆。

进一步的改进
这个模型只在 15 名受试者身上测试过。我想结合其他公共数据集来创建一个更强大的模型,包含更多的受试者、不同的硬件、不同的测试情况和不同的环境。此外,我想看看采样速率从 50 Hz 降至 10–25Hz 是否仍能产生稳定的模型,因为较低的采样速率意味着功耗显著降低。
这项研究的 Python 代码可以在我的 Github 页面上找到。
为线性回归选择合适的 MLA
普通最小二乘法失败的地方,决策树成功了

在我的上一篇文章中,我谈到了选择特定的特征,并在普通的最小二乘公式中使用它们来帮助预测分子相互作用属性的标量耦合常数。我用一个相对较差的预测算法结束了它,并决定这是由于由我所有的 EDA 计算确定的不重要的特征。我将张贴我以前的图片以及只有重要特征的输出视觉效果。


你能看出区别吗?他们使用两种不同的功能。第一个包含权重为 150 或以上的所有特性,而第二个包含权重为 1500 或以上的特性。那时我意识到这只是一个糟糕的工作模式。
鉴于我之前的特征重要性是不正确的,我决定研究一个模型,该模型考虑了多个特征,并且通过主成分分析只保留重要的特征。这让我想到了决策树回归算法。现在,当大多数人谈论决策树时,他们指的是分类,但由于我试图寻找一个连续变量,它必须是一个回归。
首先,我加载了使用 pandas 从大规模数据库中创建的子样本 csv。然后,我使用下面的代码检查所有类别并删除它们。请记住,这些类别很少或没有重要性,并进行太多稀疏计数,它是有效的。
df= pd.read_csv('molecule_subsample.csv')for col in df.select_dtypes(include=[object]):
print(df[col].value_counts(dropna=False), "\n\n")df= df.drop(columns=['molecule_name', 'atom_atom1_structure', 'type', 'type_scc', 'atom'])
现在我有了一个包含我想要的特性的合适的数据框架。我还决定将它保存为 csv 文件,以便将来可以更快地导入它:
df.to_csv('subsample_nocat.csv ',index=False)
然后我创建特征和目标变量,然后进行训练测试分割。我选择了较小的训练规模,因为即使这个子样本也包含了将近 50,000 个数据集。
feature= df.drop(columns=['scalar_coupling_constant'])
target= df[['scalar_coupling_constant']]feature_train, feature_test, target_train, target_test= train_test_split(feature, target, test_size=0.1)total feature training features: 419233
total feature testing features: 46582
total target training features: 419233
total target testing features: 46582
之后,就相对简单了。我加载了决策树回归器,并用我想要的标准填充它。知道我有一个相对较大的数据集,我选择了一个相当大的最大深度
DTR= tree.DecisionTreeRegressor(max_depth=75, min_samples_split=3, min_samples_leaf=5, random_state=1)DR= DTR.fit(feature_train, target_train)DR:
DecisionTreeRegressor(criterion='mse', max_depth=75, max_features=None,
max_leaf_nodes=None, min_impurity_decrease=0.0,
min_impurity_split=None, min_samples_leaf=5,
min_samples_split=3, min_weight_fraction_leaf=0.0,
presort=False, random_state=1, splitter='best')
我试图将它可视化,但我的电脑一直在崩溃,所以我决定采用另一种方法,对我的决策树进行交叉验证。我基本上重做了上面的代码,但考虑到了交叉验证:
drcv= DR.fit(feature_train, target_train)drcv_scores =cv(drcv, feature_train, target_train, cv = 10)drcv_scores:
{'fit_time': array([20.96234488, 20.751436 , 20.6993022 , 20.62980795, 20.80624795,
20.72991371, 20.73874903, 20.65243793, 20.55556297, 20.36065102]),
'score_time': array([0.03302193, 0.0274229 , 0.02751803, 0.02114892, 0.02561307,
0.02700615, 0.02410102, 0.02259707, 0.02510405, 0.02420998]),
'test_score': array([0.99999431, 0.99998765, 0.99999402, 0.99999096, 0.99999444,
0.99999466, 0.99998819, 0.99999362, 0.99999481, 0.99998841])}print("regression score: {}".format(drcv.score(feature_train, target_train)))regression score: 0.9999964614281138
看看结果。它有 99%的预测是正确的。这只能意味着一件事:数据被过度拟合。不管怎样,我都愿意看透这一切。我知道我可以通过降低我的最大深度来调整这种过度拟合,并在稍后更改我的树最小节点和树叶,以及实现随机森林集合方法。
我再次运行了上面的代码,但是做了一点小小的改动:
cv= cross_validate(DR,feature_train,target_train,n_jobs=-1,return_train_score=True)
我还想查看我的训练成绩状态:
{'fit_time': array([19.88309979, 19.68618298, 19.56496 ]),
'score_time': array([0.06965423, 0.08991718, 0.07562518]),
'test_score': array([0.99999126, 0.99998605, 0.99999297]),
'train_score': array([0.99999497, 0.99999486, 0.99999842])}
这表明我的训练和测试几乎没有任何区别。即使在过度配合的情况下,也不应该发生这种情况。我认为这可能是由于发生了一些代码泄漏,或者我在编写变量时犯了某种形式的错误。所以我决定再看看几个结果:
DTR.score(feature_test, target_test)
0.9999870784300612DTR.score(feature_train,target_train)
0.9999964614281138
这显示了两个不同的数字,虽然非常接近。因此,虽然我在名字上没有犯任何错误,但似乎因为我如何建立我的树,一切都被过度拟合了。然后我决定检查最后两个指标。现在,由于它们是两种不同的数据格式,我必须先将其中一种更改为 numpy 数组,然后绘制:
predict=DTR.predict(feature_test)
type(predict):
numpy.ndarraytt_np= target_test.to_numpy()
type(tt_np):
numpy.ndarrayplt.rcParams["figure.figsize"] = (8, 8)
fig, ax = plt.subplots()
ax.scatter(predict, tt_np)
ax.set(title="Predict vs Actual")
ax.set(xlabel="Actual", ylabel="Predict");

Look at how beautiful it is!
哇,我没想到会这样。虽然我的预测错过了一些,但看起来它几乎把所有事情都做对了。这让我得出结论,这个机器学习模型也是不正确的,但我会在尝试随机森林后获得更好的洞察力。
PID 控制器介绍
控制自动驾驶汽车沿着路径行驶
PID 控制器是一种调节应用程序以平滑地跟随某个值或路径的方法。我在这里第一次学到了这个概念,它介绍了如何驾驶汽车沿着特定的路径行驶。虽然一个完整的应用程序解释可能非常复杂,但它以一种超级优雅和简洁的方式总结了背后的数学,并使这一思想可以很容易地扩展到许多现实生活中的问题。在本帖中,让我们跟随关键结构,探索 PID 控制器如何工作。
在本帖中,我们将遵循以下路径:
- P :比例
- PD :比例微分
- PID :比例-微分-积分

基础设置
让我们通过一个问题的实现来学习这个想法。假设我们有一个机器人汽车对象,我们希望汽车能够控制自己沿着一条路径行驶,在这种情况下,这条路径将是一条直线,汽车控制自己的唯一方法是调整它的转向或方向,以便它可以逐渐优雅地接近这条路径,并在到达那里后沿着这条路径行驶。简单地说,问题是:
通过改变方向盘来控制自动驾驶汽车沿着直线行驶
car 对象的完整实现在这里是(整个对象的解释在我们的范围后面)。基本上,机器人对象遵循以下结构:
set功能设置汽车的初始坐标和方向。我们需要知道的是,每次我们都需要给机器人发出steering的指令,这样它就可以调整自己来接近路径。
P
p 代表比例。事情是这样的,我们的机器人坐在坐标为(x, y)的二维世界里。假设目标路径是y = 0,那么当前朝向目标的误差是y — 0 = y,基于该误差,我们应该相应地调整汽车的转向。那么如何调整转向,以便我们可以快速减少误差呢?如果你驾驶的汽车偏离了你的目标路线,你会怎么办?
一个直接的方法是根据误差按比例调整转向,这意味着你离目标越远,你驾驶汽车的角度就越大。考虑到这一点,我们有了第一个解决方案:
我们在n步骤中调整汽车,在每一步中,我们根据误差robot.y成比例地驾驶汽车:
steering = -tau*robot.y
其中tau是我们分配给误差的权重。(注:汽车以speed = 1移动,由于时间戳为 1,所以distance = speed * 1 = speed
让我们画出汽车的轨迹:

P Control
我们设定我们的机器人从(0, 1)开始水平前进。轨迹在目标路径外振荡。看起来我们的转向太过激烈,以至于赛车超过了我们的目标,当它试图转向时,又超过了目标。显然坐在这样的车里会很头疼。那么,我们可以在接近目标时减小转向角吗?
帕金森氏症
问题引出 PD 控制,其中 D 代表导数。这个想法是,随着误差的减少,我们的汽车应该反向转向,以避免超调,而不是不断转向,导致振荡。
因此,我们应该在控制过程中添加的新项是δy/δt,其中δt为 1,因此δy = current_y — last_y。实施将是:
这里的导数项是dev = robot.y — prev_cte,tau_d是它的权重。结果轨迹如下所示:

PD Control
看起来很完美!现在一切看起来都很好,但实际上这个模型并不稳健。我的意思是如果存在固有的系统缺陷怎么办?如果你的车轮在开始时有一点小故障,总是转移到另一个方向,PD 能够克服这一点吗?
答案是否定的。让我们举个例子:
这里我们set_steering_drift给汽车的方向添加了一些噪声,结果变成了:

PD Noise
它远离我们的目标路径振荡。原因是,虽然我们的车正在转向目标,但它不能抵消固有的系统偏差。
PID
为了克服这个问题,我们需要第三项 I,它代表积分,它有助于监控误差,如果在一定时间内误差没有减少,汽车应该增加更多的转向。
在我们的例子中,积分实际上是每步误差的总和,实现方式如下:
在每一步,当前误差robot.y被加到sum_cte上,并且tau_i是该项的权重。因此通过增加这一项,汽车避免了在远离目标的某个地方振荡的机会,因为这一项将不断变大,转向角将相应调整。
这给出了校正后的结果:

PID Control
在同样的转向噪音下,这一次机器人汽车仍然能够到达目的地。
结论
另一个需要注意的是 P、I 和 d 的权重。这些参数需要仔细调整,不同的权重选择会导致显著不同的性能。您可以通过查看的完整实现来测试和演示它。
总而言之,PID 控制器其实挺有意思的,尤其是被这么直观简单的讲解。你可以把三个组成部分 P、I 和 D 想象成负责一项任务的三个不同方面的三个不同的人:
- P :将应用推向目标状态的主要力量
- I :全程监控,遇到困境时帮助跳出困境
- D :监控 P 并在必要时进行计数器测量
最后,PID 控制器的应用绝对不仅限于这个用例,您可以应用和探索更多的乐趣!
参考:
饼图——来饱餐一顿吧!

“Which of these pieces is biggest?” and other problems with pie charts
这是我们的感恩节。我认为这将是一个伟大的时间来谈论,你猜对了,饼状图!我知道,我知道,我怎么敢选择心爱的南瓜饼形象来谈论那些令人厌恶和最讨厌的图表/图形。底线是,尽管几万年来我们人类热爱圆圈,但饼状图与趋势背道而驰,人们完全讨厌它们。这是为什么呢?!我决定看看我们对圆圈的热爱的历史,然后看看饼状图周围的情绪,最后我会给你留下一些使用这个两极分化图的有用指南。
对圈子的热爱永无止境
人们喜欢圈子。这是没有办法的。我们已经这样做了,因为我们有能力把它写下来。有一本巨著概述了人类文化圈的历史(这里见作者关于这本书的媒体博客)。利马提出了三个普遍的理论来解释为什么我们如此热爱圆圈。第一个理论来自进化论,认为圆形和曲线形相比尖锐的边缘让我们放松,尖锐的边缘提醒我们危险,如动物的牙齿(作为参考,我建议看电影《乌鸦》)。
第二个理论是,圈子只是让我们快乐,或者至少我们把圈子等同于积极的情绪。这遵循了第一个基于进化的理论,并走得更远,声称我们认为圆形具有令人愉快的项目,如婴儿的脸。(也许我们现在已经进化到可以用同样的眼光看待南瓜派了。)
利马提出的最后一个理论与我们眼睛的形状有关(眼睛本身是圆的)。因为我们是通过异形镜片来观察世界的,所以与我们眼睛互补的形状比那些边缘粗糙或锯齿状、与我们眼睛不互补的形状更有吸引力。

From Manuel Lima’s post regarding his book. Pleases the eye, but then why do we hate pie charts so much?
令人讨厌的饼图——我们在哪里转错了弯?
因此,随着数千年的进化支持我们对圆的热爱,使用形状来传达数据的意义应该是一件显而易见的事情,对吗?显然不是。问问任何一位数据科学家,或者更好的数据科学讲师,他们对饼状图有什么看法。这是我们在数据科学沉浸式课程中学到的首批数据可视化课程之一。“什么时候使用饼图合适?”响亮、响亮的回答没有留下任何解释的余地。“绝不!”
但是,等等…我们喜欢圆圈。它们补充了我们的眼睛(谁不喜欢这方面的好“赞美”呢)。我们哪里出错了?通过一些研究,你可以看到饼状图从一开始就被讨厌,因为它是一种向数据传达意义的低效和混乱的方式。这些论点是有道理的,我们并不擅长辨别每一块的角度,所以我们可能无法掌握一个馅饼“一块”相对于其他馅饼的大小。这违背了饼图的初衷,因为饼图旨在显示图表中类别作为整体一部分的统计比例。

Marshall Eriksen gets how to diversify his data visualization game.
这些图表的致命一击出现在 1984 年,当时统计学家 William Cleveland 和 Robert McGill 写了一篇论文,将条形图与饼图进行了比较,结果是所有圆圈爱好者都吃了一个大饼。在其他图表中,条形图更容易解释和显示分类值的差异。添加的类别/变量越多,饼状图的使用就失去了有效性,它只会变得一团糟。甚至不要从甜甜圈图开始,它没有圆形那么进化,但也同样受人喜爱。
恰当使用饼状图
到目前为止,我们已经看到了圆形硬币的两面:人们喜欢圆形,但是饼状图却被普遍厌恶。有折中的办法吗?在这个问题上,有什么办法可以得到一半香肠一半蔬菜?我想是的,答案可能和感恩节吃馅饼一样:饼状图可以在适当的情况下使用,但要适度使用。以下是一些使用饼图的规则,基于数据科学社区的建议:
- 饼图只能处理一组数据!南瓜苹果派听起来不错,但是你不能把它们混合在一起并期待好的结果。
- 负值或零值=饼图的负面使用。你不能展示一块已经吃过的馅饼。
- 最多 7 个类别——在一个饼图上放超过 7 个类别会冲淡意义,让浏览者感到困惑。
- 移除图例并使标签易于查看——不要让查看者的眼睛在图例上来回移动,这对于饼图来说效果不好。
- 堆叠饼图以比较大小——也就是说,如果您真的想比较它们。
- 最重要的是,考虑您想用数据讲述的故事。饼图最能显示类别间的不同比例,总计 100%。例如,如果我想展示这个感恩节我吃了多少,我可以用饼图来展示与火鸡和甜点相比,我吃的边数不成比例。
世界将永远喜欢圆圈,但数据可视化社区将永远讨厌饼图。也就是说,饼图有一些合适的用途。不过,当有疑问时,我的建议是坚持一些不那么两极化的东西,除非你想从精通数据的受众中获得增长。
祝大家感恩节快乐!
什么是 PIMCore:赢得 DXP 挑战赛的 PIM
发现 PIMCore,它是一款 MDM、PIM 工具,可以满足您的所有需求(或者几乎是承诺)
在我作为开发人员的职业生涯中,我讨厌很多事情,但是在列表的第一位,有读取和保存数据。我发现花时间在 ORM 上映射字段,创建 CRUD 例程,把大脑放在一个小小的业务逻辑层上是如此无聊。我不仅讨厌它,而且觉得它是对时间的极大浪费。这段时间本应该花在我能带来价值的事情上,比如基础设施、性能或提高可用性。

Pimcore logo from Pimcore press media kit
可能这就是为什么当我遇到 PIMCore 时,我爱上了它。PIMCore 拥有我见过的最聪明、最实用的数据管理。相信我,我尝试了很多数据管理工具和 RAD 框架。PIMCore 背后的想法很简单:创建一个系统,在这个系统中,你可以描述你的数据必须如何编辑和组成,并且可以很容易地使用这些数据。伟大的结果是数据定义由一个简单的 UI 管理,它甚至允许非程序员描述数据以及必须如何编辑数据。这允许创建一个快速简单的过程,分析师或非技术人员只需看一眼就知道系统在做什么。通过这种方式,所有浪费在重复工作上的开发时间都被去除了,我们可以专注于客户真正的需求。好看吗?我喜欢它。
如果您不相信阅读本文并了解 PIMCore 是一个好机会,那么您必须知道,您放入数据库的每一条记录都可以使用 GraphQL 查询进行搜索。
这一切怎么可能?感谢 PIMcore 用来存储数据的强大方法,它定义了关系、字段类型等等。
太好了!让我们详细看看 PIMCore 是如何工作的。
PIMCore 用人类的话来说是什么
名字本身就解释了 PIMCore 是什么:一个 PIM(产品信息管理)。名字没有说出来的才是最重要的部分。PIMCore 是一把多工具的瑞士刀,帮助我们实现数字化转型。它提供了许多功能,结合在一起,让所有的军队赢得 DXP 挑战。仔细想想,将 DAM、PIM、MDM 和 DataHub 放在一个工具中是一个非常好的结果。
这种配置让您无需编写一行代码就可以管理结构化和非结构化数据。

Pimcore in action, image from Pimcore press media kit
Pimcore 如何帮助数字化转型
Pimcore 由几个特性组成,这些特性结合在一起构成了整个 DXP 平台。在第一次安装之后,你给了客户一个强大的生态系统,里面有他需要的一切,甚至更多。根据您的需要,您可以在多种配置中使用它:
- 个人信息管理
- 主数据管理(Master Data Management 的缩写)
- 无头 CMS
- 电子商务
- 客户关系管理
- 羧甲基淀粉钠
这些功能有助于解决 DXP 挑战,推动数字化转型。怎么会?只看下面五点。
数据管理
如今,从技术到制造,数据是每个公司的资产。Pimcore 整合来自多个来源的数据并集中信息。好消息是,所有信息都在一个地方,无需任何额外的努力就可以交付给其他挑战。
数据集成
API 是 2019 年最好的集成,但我们也有遗留系统。Pimcore 通过 CVS export\import 支持遗留系统,可以通过用户界面进行配置,提高了大多数公司的集成度和接受度。对于所有新的东西,API 已经准备好了,开箱即用,没有任何惊喜。
免费开源,社区支持
Pimcore 是开源的,可以免费使用,不需要任何许可费用。这意味着较低的准入门槛和在采用前测试的可能性。社区很大而且很活跃,你也可以要求付费的企业支持。
现成的解决方案
通过安装它,你可以实现门户网站,电子商务没有任何限制。你可以编辑任何内容或任何数据,而无需询问开发者。这有助于专注于内容,而不是被技术问题分散注意力。你可以在不接触内容的情况下改变整个网站的图形,因为 CMS 与内容是分离的。您可以在一个地方编辑所有多语言产品,并在完全遵守公司政策的情况下分发给营销渠道。
上市时间,经济高效
都包括在内了。您可以一次性交付覆盖 DXP 多个地区的解决方案。这降低了集成、维护和开发的成本。省钱本身是好事,但更短的上市时间会带来不同。
利用 Pimcore 打破孤岛
Gartner 和其他大型分析师对孤岛的担忧正在减弱,他们要求我们作为顾问,如果没有任何方法可以让他们一起沟通,就打破孤岛。另一个选择是信任大型供应商,住在一个非常昂贵的大筒仓里。这两种解决方案对每个公司来说都是一个巨大的挑战。大公司有资金实现飞跃,但时间非常有限,小公司总是在寻找经济高效的解决方案。在这种情况下,PIMCore 是上市时间的替代方案,可以帮助您快速实现数字化转型。事实上,该产品是免费的,易于设置,并可以与每一个遗留系统集成,因为它的动态导入\导出系统可以处理 CSV 文件。我们所有的遗留系统的问题,但肯定我们知道,与 CSV 文件的工作是他们可以处理没有任何头痛。对于所有的新系统,都有 API rest 或 graphQL,所以我所期望的是,任何新系统都会乐于与他交互(尊重而不是处理遗留的 CSV 文件😉).在我看来,PIMCore 的方法与大型供应商非常相似。您拥有这个工具,它完成了开箱即用的大部分工作,并为您提供了大多数情况下所需的一切。只有一个能做所有事情的单一工具似乎被供应商锁定了?这就是你每次选择工具的风险。也许有许多工具可以更好地满足单一需求。你可以考虑得到最好的,购买多个垂直解决方案,让大家一起讨论,但我们再次陷入孤岛问题:沟通。所以,问题是没有什么是完美的。对于大多数情况,PIMCore 都很适合,并给出了具体的上市时间解决方案。
带什么回家
PIMCore 是一个非常有趣的帮助管理数据的工具。基本上是因为它的强大,可以用来将最重要的数据带入公司。推出数字化转型的最重要功能,您可以使用它来管理您的网站、门户、PIM、电子商务和 DAM。此外,您可以启用数据中心功能,在整个公司范围内共享黄金记录。
你为什么需要 PIMCore?
- 一致且集中的数据
- 筒仓破坏
- 数字转换加速器
- 全渠道推动者
- 数据中心
PIMCore 的优势
- 全在一个产品中。只需安装到公司,一次性解决多个问题
- 免费和开源。没有饲料,没有限制。由强大的社区支持
- 附带许多功能,您现在并不需要,但当时机到来时,您将做好准备
- 经济高效的定制易于实施。
PIMCore 的局限性
- 基本上是一个单一的应用程序。也可以用在复杂的场景中,但是性能和流程的中断要管理得很好,以避免范围蔓延。
从 Salesforce 到 MySQL 的管道数据,无需外部连接器

我希望这篇文章可以拯救那些需要在没有外部连接器的情况下将数据从 salesforce 传输到 MySQL 的人,因为您的上级要求您这样做。我自己在找到一个简单的方法之前遭受了很长时间,所以现在让我们走吧!
你需要什么
- Salesforce lightning 帐户(以及一些关于 Salesforce 对象的知识)
- MySQL 凭证
- 使用 npm 安装的 jsforce(以及 node.js 的一些知识)
- 一个好的代码编辑器(:
别担心,我会和你一起检查零件!
1。Salesforce lightning 帐户
因此,如果您还不知道,salesforce 将帐户、案例等项目存储到对象中。对象由字段组成,每个字段都有一个类型。我们今天要做的是将所有账户数据导入 MySQL。
获取您的安全令牌

security token from salesforce lightning page
稍后需要安全令牌来使用 node.js 连接到 salesforce
现在这一部分已经完成,让我们转移到 mySQL
2.MySQL 凭证
如果你现在用的是 mySQL,你会知道有用户名和密码以及主机。因此,您将需要这些信息来连接到您的帐户+您想要连接的数据库。
你需要进入 MySQL,点击数据库

然后,从下拉列表中,单击“管理连接”
查看您的连接参数。

现在,让我们使用“创建数据库 test_db”和随后的“创建表 test_table 值”创建一个新的数据库 test_db (我们将在其中存储 test_table 以加载 salesforce 数据。

3.节点 js 脚本
创建一个您选择的目录,并执行 npm init 来获取所有需要设置的文件。如果没有,您需要安装 npm!
现在创建一个 config.js 文件来存放这些凭证。

How the config file should look like
这样,我们将使用‘NPS install jsforce’安装 js force。
现在让我们对 jsforce 做一个测试连接。

基于文档,使用 conn.login,conn.login(用户名、密码+ salesforce 安全令牌)。然后,您应该不会得到任何错误代码(:

是时候把所有东西放在一起了。我们的想法是,首先要连接到 mySQL,并确保它是连接的。接下来,我们将连接到 salesforce 并使用 jsforce wrapper 来查询数据,如使用 SQL langauge,然后将其导入 mySQL(:
var jsforce = require('jsforce')var connection = require('./config.js')connection.connect(function (err) {if (err) {return console.error('error:' + err.message);}//console.log('Connected to MySQL');var conn = new jsforce.Connection({});// password + security tokenconn.login('<login email>', '<password + security token>', function (err, userInfo) {if (err) { return console.error(err); }// Now you can get the access token and instance URL information.// Save them to establish connection next time.//console.log(conn.accessToken);//console.log(conn.instanceUrl);// logged in user property//console.log("User ID: " + userInfo.id);//console.log("Org ID: " + userInfo.organizationId);var records = []var query = conn.query("Select Id,Name FROM Account").on("record", function (record) {records.push(record);console.log(record.Id)let sql = 'INSERT INTO test_table VALUES (?,?)';let data = [record.Id, record.Name]connection.query(sql, data, (err, res, fields) => {if (err) {return console.error(err.message);}console.log('Rows affected:', res.affectedRows);})}).on("end", function () {connection.end(function (err) {if (err) {return console.log('error:' + err.message);}console.log('Close the database connection.');});//console.log(records[0].Id);//console.log("total records is :" + query.totalSize);//console.log("total fetched is :" + query.totalFetched);}).on("error", function (err) {console.log(err);}).run({ autoFetch: true, maxFetch: 4000 });});})//let sql = 'INSERT into test_db VALUES (xxx) '
有了那个,你应该得到这个。

这并不难,对吧!
我希望这在某种程度上帮助了你!如果你也有这个问题,请分享给你的朋友!
数据规范化的陷阱
生命科学的数理统计和机器学习
标准化如何导致单纯形中的虚假相关

这是专栏第四篇生命科学的数理统计与机器学习T5。在本专栏以及 生命科学的深度学习 中,我一直在反复强调,我们在生命科学中处理的数据是高维度的,这一事实我们并不总是意识到并且适当地考虑到。今天,我们将讨论另一个典型的陷阱,即如何执行数据标准化您可能会在单形空间中结束,在那里欧几里德距离不再有效,经典的频率统计数据失效。
为什么要归一化数据?
数据标准化对于分析【NGS】数据,例如 RNA 测序(RNAseq) 和 宏基因组学 非常重要,以避免由于技术人为因素造成的混淆效应。事实上,有时一个基因在患病个体中的表达似乎比健康个体高,这仅仅是因为由于技术噪音,所有基因的总表达,生物信息学术语中的测序深度或文库大小碰巧在患病个体中更高。例如,如果你为我在这一系列文章中一直使用的癌症相关成纤维细胞(CAFs) 数据集绘制所有细胞的库大小,你会发现细胞与细胞之间的巨大差异,这些差异可能具有生物学或技术(噪声)性质。

Library sizes (total expression of all genes) for Cancer Associated Fibroblasts (CAFs)
为了安全起见,通常假设细胞之间的总基因表达应该没有太大差异,观察到的测序深度的巨大差异是由于技术偏差。因此,通过简单地将每个基因的表达除以所有基因的总基因表达(文库大小)来均衡细胞间的“平均”基因表达是非常诱人的。这种方法在 RNAseq 中被称为文库大小归一化,在宏基因组学社区被称为总和缩放(TSS) 。
标准化会导致虚假的相关性
在这里,我们将证明如果我们天真地将文库大小标准化应用于 RNAseq 基因表达数据会发生什么。为了简单起见,让我们模拟两个基因的表达。我们独立地从 负二项分布 中得出它们的表达值(计数),因此基因应该不相关,我们可以通过计算 Spearman 相关性容易地确认它:

我们可以看到,Spearman 相关性ρ= 0.001,p 值= 0.99,所以根本不显著相关。从基因 1 对基因 2 的图中,我们确认两个基因之间没有可见的相关性。现在让我们应用库大小归一化,这意味着每个计数将除以来自相应样品/柱的计数总和。之后,我们再次计算 Spearman 相关系数,并绘制两个基因的表达图。

在这里我们可以看到一些惊人的东西。现在,这些基因似乎完全相关。Spearman 系数ρ=-1,p 值非常显著,该图显示了两个基因之间的完美相关性。
发生了什么事?看起来,通过应用库大小标准化,我们发现了一种新的生物学!
基因在标准化之前是不相关的,但现在它们似乎一起表达(共表达)。等一下!我们知道,我们没有模拟两个基因相关/共表达,我们模拟了数据,所以我们知道真相:基因 不应该相关,而是 它们在文库大小标准化之后。真是自相矛盾!
合成数据和单纯形空间
我们刚刚观察到的效应叫做比率的虚假相关。卡尔·皮尔逊(还记得皮尔逊相关系数吗?)最初是推动库大小规范化的人。然而,在他发现虚假相关性的影响后,他强烈反对通过库大小/总和来进行数据标准化。****
库大小标准化的问题是给定样本的跨基因计数总计为 1 ,即它们不再是独立的,而是受限的,因此它们变成 组成的 。通过构建复合数据,您不再处于传统频率统计所基于的欧几里得空间,因为欧几里得空间/距离是高斯分布的结果。事实上,你最终会进入单形空间,在这里传统的统计方法不再适用,因为两点之间的距离不再是欧几里德距离,而是“带约束的欧几里德距离”,即 艾奇逊距离 。
如果您在单纯形空间中,您不应使用任何传统的统计方法,如线性回归、相关或主成分分析(PCA ),因为它们基于数据点之间的欧几里得距离。而且如果你在做 RNAseq 分析,文库大小归一化可能会彻底毁了 基因网络分析 ,这对基因表达数据的生物学解读非常重要。

Library Size Normalization may severely damage the gene network analysis, image source
如果您仍然想对成分数据使用 PCA,您应该执行对数比转换之一,将数据从单纯形转换回欧几里德空间:加法(alr)、中心(clr)或异构(ilr)对数比转换。
单纯形空间的效果似乎是 众所周知的 在 宏基因组学 区域,这里的社区通常使用对数比变换或 累积和缩放(CSS) 而不是总和缩放(TSS)。令人惊讶的是,我从未在 RNAseq 或 scRNAseq 文献中听到任何关于单形空间偏差的提及。对数比变换(alr,clr,ilr)使用对数函数,我假设这就是为什么 RNAseq 人喜欢对数变换他们的数据,而没有意识到这种操作实际上允许他们放弃单纯形空间。
为什么要 TPM 正常化?
有趣的是,【TPM】最近成了一种流行的归一化策略。 GTEX ,也许是最大的公共 RNAseq 资源,默认提供基因表达数据作为 TPM 标准化计数。

GTEX RNAseq resource provides TPM normalized gene expression by default
根据定义,给定样本的 TPM 计数总计为一百万,这是一个单纯形空间约束。因此 TPM 计数并不比库大小标准化计数更好,因为它们也遭受单形空间偏差,并且不应该天真地对这些计数应用例如 PCA。很多 GTEX 用户似乎并不知道这个陷阱,在 GTEX 门户上没有提到 。因此,我要问所有 GTEX 开发者、用户和 RNAseq 社区的问题是:
你们意识到你们是通过使用 TPM 标准化基因表达计数在单纯形空间中工作的吗?
摘要
在这篇文章中,我们了解到 RNAseq 数据标准化是避免技术偏差的重要一步,然而文库大小标准化可能导致基因之间虚假的相关性。这是因为库大小标准化的表达式值不再是欧几里德空间中的而是单纯形空间中的传统的 频数统计中断和对数比变换必须被应用以避免错误的结果。
在下面的评论中让我知道生命科学中的哪些分析对你来说是特别神秘的,我会在这个专栏中尽力解答。在媒体关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我。我的下一个帖子将是关于神秘的孟德尔随机化,敬请关注。
使用 Python、SQL 或电子表格透视数据

如今,收集数据似乎比以往任何时候都容易,但这并不意味着你总能以一种能提供你所寻找的洞察力的方式来收集数据。有时,您需要以改变数据组织的方式来操作数据,以便可以从一个新的角度来查看数据。透视数据通常是重新组织数据列和行的一种简单方法,可以将它们转换成组、统计数据或摘要。本文将向您展示使用 Google sheets、Microsoft SQL Server Management Studios 和 Pandas Python 库等工具透视数据的三种简单方法。
如果你想跟进,你可以从 kaggle 下载我的期权时间和销售数据:
[## 期权市场交易
下载数千个项目的开放数据集+在一个平台上共享项目。探索热门话题,如政府…
www.kaggle.com](https://www.kaggle.com/bendgame/options-market-trades)
使用熊猫在 Python 中旋转
Pandas 是一个强大而流行的 Python 数据分析库。它允许您创建数据框,以类似于电子表格或数据库表的结构来查看和操作数据。您可以使用熊猫创建数据透视表,只需几行代码!我将向您展示如何透视列或行:
- 导入依赖项和数据:
import pandas as pd
import numpy as np#read your csv data to a Data Frame
data = pd.read_csv(r'C:\Users\Admin\Desktop\Edge\optionsTradeData.csv')#check the data
data.head()

2.使用 Pandas.pivot_table 函数创建类似电子表格的数据透视表:
pd.pivot_table(data, index = ['Sector'], values = ['Spent'], aggfunc = np.sum)

或您可以将扇区设置为列,而不是将其用作索引:
pd.pivot_table(data, columns = ['Sector'], values = ['Spent'], aggfunc = np.sum)

透视您的电子表格

查看我们的数据,假设我们想要按部门合计支出金额。数据透视表使计算变得简单。Microsoft Excel 和 Google Sheets 具有内置的透视功能,可以轻松转换您的电子表格。要在 google sheets 中创建数据透视表,请遵循以下步骤:
1\. Data > Pivot table.
2\. Set the Data range.
3\. Select an insert option data. I recommend New sheet.
4\. Click Create.

您将看到一个空表和透视表编辑器,允许您设置行、列、值和过滤器。如果您对数据透视表完全陌生,Google Sheets 会为您提供建议的数据透视表,让您轻松入门:

要获得部门的总支出,我们需要设置我们的行和值:
1\. Click Add Rows.
2\. Select sector.
3\. Click Add Values.
4\. Select Spent.

您已成功透视了数据,如下所示:

SQL 中的透视
SQL 是一种强大的查询语言,允许你在关系数据库中检索和操作数据。这是一门相当容易学习的语言,如果你不熟悉它,我强烈推荐探索基础。我在工作和家庭中使用微软 SQL Server 和 SQL Server Management Studios ,所以这是我在我的例子中使用的风格,但是有几种不同的数据库/SQL 环境可供您使用:MySQL、PostgreSQL、SQLite 和 Oracle Database,仅举几例。
Microsoft SQL Server 允许您使用 PIVOT 关系运算符来旋转和聚合您的数据,将其转换为数据透视表。这是微软文档中透视语法的一个示例:
SELECT <non-pivoted column>,
[first pivoted column] AS <column name>,
[second pivoted column] AS <column name>,
...
[last pivoted column] AS <column name>
FROM
(<SELECT query that produces the data>)
AS <alias for the source query>
PIVOT
(
<aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
IN ( [first pivoted column], [second pivoted column],
... [last pivoted column])
) AS <alias for the pivot table>
<optional ORDER BY clause>;
从输出您想要透视的数据的 SELECT 语句开始:
(select sector, spent
from v_FlowTotal)

从该数据中,选择要透视的列。在我们的示例中,我们希望旋转扇区:
select [Non-Cyclical Consumer Goods]
,ETF
,Healthcare
,Utilities
,MISC
,[Cyclical Consumer Goods]
,Telecommunication
,WEED
,Financials
,Energy
,[Basic Materials]
,Industrials
,REIT
,Technology
FROM (
select sector, spent FROM v_FlowTotal ft
) as SourceTable
然后应用 PIVOT 运算符以及您的聚合函数:
select [Non-Cyclical Consumer Goods]
,ETF
,Healthcare
,Utilities
,MISC
,[Cyclical Consumer Goods]
,Telecommunication
,WEED
,Financials
,Energy
,[Basic Materials]
,Industrials
,REIT
,Technology
FROM (
select sector, spent FROM v_FlowTotal ft
) as SourceTable
PIVOT (
sum(spent)
for sector IN (
[Non-Cyclical Consumer Goods]
,ETF
,Healthcare
,Utilities
,MISC
,[Cyclical Consumer Goods]
,Telecommunication
,WEED
,Financials
,Energy
,[Basic Materials]
,Industrials
,REIT
,Technology)
)As PivotTable
输出将如下所示:

最后备注:
虽然本文将帮助您开始使用数据透视表,但是了解它们所提供的所有功能的最好方法是通过修补和摆弄这些功能!数据透视表易于创建,并允许您通过更改查看数据的方式来获得对数据的洞察力。
谢谢大家!
- 如果你喜欢这个, 在 Medium 上关注我 了解更多
- 通过订阅 获得对我的内容的完全访问和帮助支持
- 我们连线上LinkedIn
- 用 Python 分析数据?查看我的 网站
Pix2Pix

Shocking result of Edges-to-Photo Image-to-Image translation using the Pix2Pix GAN Algorithm
本文将解释一篇关于使用条件 GANs 进行图像到图像翻译的流行论文 Pix2Pix 的基本机制,以下是该论文的链接:
[## 基于条件对抗网络的图像到图像翻译
我们研究条件对抗网络作为图像到图像翻译问题的通用解决方案…
arxiv.org](https://arxiv.org/abs/1611.07004)
文章大纲
一.导言
二。具有对抗性和 L1 损失的对偶目标函数
三。u 网生成器
四。PatchGAN 鉴别器
动词 (verb 的缩写)估价
介绍
图像到图像的翻译是生成对立网络(GANs)非常适合的任务的另一个例子。在这些任务中,几乎不可能对损失函数进行硬编码。大多数关于 GANs 的研究都与新颖的图像合成有关,将随机向量 z 转换成图像。图像到图像转换将一个图像转换为另一个图像,例如将上面的袋子边缘转换为照片图像。另一个有趣的例子如下所示:

Interesting idea of translating from satellite imagery into a Google Maps-style street view
图像到图像的转换在诸如彩色化和超分辨率的应用中也是有用的。然而,许多特定于 pix2pix 算法的实现思想也与那些研究新颖图像合成的人相关。

上面描述了本文中图像到图像转换架构的非常高级的视图。与许多图像合成模型类似,这使用了条件 GAN 框架。调节图像 x 被用作发生器的输入和鉴别器的输入。
具有对抗性和 L1 损失的对偶目标函数
进行图像到图像翻译的一个天真的方法是完全抛弃对立的框架。源图像将仅通过参数函数传递,并且结果图像和地面实况输出的差异将用于更新网络的权重。然而,用诸如 L1 和 L2 的标准距离度量来设计这个损失函数将不能捕捉这些图像之间的许多重要的区别特征。然而,作者确实发现了 L1 损失函数作为对抗性损失函数的加权助手的一些价值。
条件-对抗损失(生成器对鉴别器)非常普遍地格式化如下:

前面提到的 L1 损失函数如下所示:

结合这些功能会产生:

在实验中,作者报告说他们发现λ参数等于 100 时最成功。
u 网生成器
GAN 发生器中使用的 U-Net 架构是本文中非常有趣的部分。图像合成架构通常采用大小为 100×1 的随机向量,将其投影为具有全连接层的更高维向量,对其进行整形,然后应用一系列去卷积操作,直到实现所需的空间分辨率。相比之下,pix2pix 中的生成器类似于自动编码器。

The Skip Connections in the U-Net differentiate it from a standard Encoder-decoder architecture
生成器接收要翻译的图像,并将其压缩成低维的“瓶颈”向量表示。然后,生成器学习如何将其上采样到输出图像中。如上图所示,考虑标准编码器-解码器结构和 U-Net 之间的差异很有意思。U-Net 类似于 ResNets,其方式是将早期层的信息集成到后面的层中。U-Net skip 连接也很有趣,因为它们不需要任何尺寸调整、投影等。因为被连接的层的空间分辨率已经彼此匹配。
PatchGAN 鉴别器
pix2pix 中使用的 PatchGAN 鉴别器是该设计的另一个独特组件。PatchGAN / Markovian 鉴别器的工作原理是将图像中的单个(N×N)片分类为“真实对虚假”,而不是将整个图像分类为“真实对虚假”。作者认为,这加强了更多的约束,鼓励尖锐的高频细节。此外,PatchGAN 的参数更少,运行速度比分类整个图像更快。下图描述了对要分类的 N x N 个面片的大小 N 进行实验的结果:

The 70 x 70 Patch is found to produce the best results
估价
评估 GAN 输出很困难,有许多不同的方法。pix2pix 的作者使用两种不同的策略来评估他们的结果。
第一种策略是使用人工评分。真实图像和用 pix2pix 创建的图像随机堆叠在一起,人类评分员在观看 1 秒钟后将每张图像标记为真实或虚假。这是使用亚马逊 Mechanical Turk 平台完成的。
我发现的另一个非常有趣的评估策略是在综合生成的网络上使用语义分割网络。这类似于另一种非常流行的 GAN 输出量化评估指标,称为“初始分数”,其中基于预先训练的初始模型的分类能力来对合成图像的质量进行评级。

Far Left: Semantic Segmentation Label, Second: Ground Truth Image, Third: L1 Distance Used, Fourth: cGAN used, Far Right: L1 Distance + cGAN
结论
Pix2Pix 是一种非常有趣的图像到图像转换策略,它结合了 L1 距离和对抗性损失,并在生成器和鉴别器的设计上有所创新。感谢阅读,请查看论文以了解更多实施细节和对实验结果的解释!
TensorFlow 2.0 中的 pix2pix GAN
(查找代码关注本帖 此处 。)

原始代码可以在谷歌联合实验室的这里找到,但我把这当成了一个深入挖掘 TensorFlow 2.0 的好机会,以及快速原型开发的高级 API 的更新范围。
关于新特性的详细讨论,以及如何创建模型模块的子类,请看这里。
在这篇文章中,我将假设对用于训练神经网络(NNs)的方法有一个基本的了解,以及生成式对抗网络(GANs)在实践中是如何工作的。要更深入地介绍有条件——GANS 可以看看我以前的文章这里,或者超分辨率 GANS 这里。
这里有三个主题需要解决:首先是什么是图像到图像翻译以及 Pix2Pix 如何适应这种情况的概述;损失函数如何定义、优化以及如何反馈到网络架构决策中的数学原理;以及在两个数据集上训练的结果——分段立面数据集和卫星/谷歌地图数据集,我还将简要讨论代码和训练。
我法师处理比较辛苦。这些数据是复杂的,高度结构化的,作为人类,我们直观地知道一幅好的图像看起来像什么,即使我们不能容易地量化这意味着什么。然而与此同时,我们正在不断推进我们可以用机器学习算法自动处理的边界,从创建比清晰度高很多倍的超分辨率图像,到动态图像处理,以获得硬件无法单独完成的手机摄像头的性能。但一般来说,所有这些问题都是单独解决的;它们令人印象深刻,但我们距离使用机器学习的图像处理通用解决方案还有很长的路要走。
通常,图像到图像的翻译对于每种情况都需要特定的算法和结构,尽管事实上所有这些情况基本上都是相同的。它们都涉及获取一个像素数组并更新值以映射到另一个空间。而不是需要手握着通过这一过程,为什么缺乏通用性?
伯克利的团队开始设计图像到图像翻译问题的通用解决方案,其中底层方法可以用于几乎任意数据,以在广泛的图像域中执行通用翻译,而无需改变底层算法。他们在 2018 年发表的论文这里,连同 pix2pix 软件。
但是首先,我们在谈论什么是图像到图像的翻译?图像处理通常是指我们获取一幅图像,进行一些处理,然后输出一幅新图像。说起来很琐碎,但第二个图像是第一个图像更新版本,是普通 GAN 方法的不足,甚至是基本的条件 GANS,其中输入定义为随机噪声向量。
这个想法是通过学习一组图像和另一组图像之间的映射,网络可以将相同的映射应用到新数据上。这更符合风格转换的一般情况。我要做一个相当全面的声明,图像可以大致分为两个部分。首先,是图像的内容。内容是指图像中的内容——图像中所有部分的对象、景观和空间关系。另一部分可以认为是图像的风格。风格包括颜色、阴影和每个物体的表现方式。
我们可以把这比作一张照片和一个艺术家的印象之间的区别,或者甚至是同一场景的两个艺术表现。潜在的信息和内容是一样的,尽管他们的风格可能大不相同。诀窍是找到一种方法从一个翻译到另一个。
暗示就在术语中:与其关注图像,不如想想语言,就像翻译学习英语和法语之间的映射一样。给定一个一种语言的句子,他们可以将其翻译成另一种语言,同时尽可能多地保留原始信息(内容),并且仍然可以找到一种自然的方式来表达每种语言的情感(风格)。这就是用 pix2pix 的目的。
损失函数和网络体系结构的相互作用是微妙的,正如我们将看到的,解决一个问题通常有多种方法,但首先我们需要为生成性敌对网络定义一个条件损失。这里的关键原理与任何 GAN 相同,即两个网络之间的生成性对抗性最小最大博弈,以优化损失函数:

其中 G 是发生器,D 是鉴别器,x 是条件输入图像,y 是真实图像,z 是随机噪声向量。
(这里有更详细的讨论,但是基本思想是 G 输出一个图像,使得 D(G)最大化,而 D 被优化以输出真实图像的 1。为什么这个曾经收敛,以及收敛到什么程度,我们是不是找错人了,另一个有趣的讨论是 这里 。)
这与普通的 GAN 案例已经有了一个关键的区别:它是有条件的。这里的“有条件”是指发生器不仅仅接收一个随机噪声向量,还会接收额外的信息。这可能只是 cDCGAN 中的类信息,或者在这种情况下,条件信息是原始图像。凡尼拉 GAN 依赖于 G:z -> y,条件 GAN 则依赖于 G:{x,z} -> y。
有趣的是,这实际上并不是全貌。当网络训练时,它通常学会忽略随机噪声向量,因此为了保持网络的非确定性,使用了丢失来重新引入随机行为。
在许多 GANs 中,基于通过最小化目标和生成图像之间的欧几里德距离(MSE ),生成器将学习图像的结构和颜色,L2 正则化损失被添加到优化中。然而,发现这通常导致模糊的图像,因此为了克服这一点,L1 正则化损失被添加了一些预因子权重,作为替代:

这给出了要优化的总损耗函数:

下一个关键问题是关于网络的结构。
在此分析中,发生器基于 U-Net 结构,是自动编码器的变体,而鉴别器称为 patch-GAN。

Generator Architecture
使用 U-Net 结构的要点是,网络迫使所有信息通过网络中间的一个紧密瓶颈。这迫使找到输入图像的潜在表示,该潜在表示可以被重构为原始图像,其思想是有限量的信息可以通过-网络被迫学习最佳简化映射,而不能简单地记忆训练集。
这有一个明显的局限性;输出和输入图像的很大一部分应该共享相同的描述。图像的内容应该保持不变。U-net 相对于自动编码器的变化是在 U-Net 结构的每个对称层之间增加了一个跳跃连接,如上图所示。这些级联层具有在网络中以适当的规模直接传递更高级信息的效果,并且减少了通过潜在瓶颈层所需的信息量。这里的想法是,传递的信息关注更细微的细节,而不是大规模的结构。
鉴别器更不典型,可能需要更多的上下文。一般来说,L1 和 L2 正则化是对网络的弱约束,不会产生清晰的细节,因为有许多路径可以获得小的 L 值。然而,这并没有取消损失函数的这一部分,因为它鼓励产生高层次的结构,这在鉴别器的选择中被利用。
可以通过多种途径将清晰的细节引入到生成的图像中:
- 调整 L1/L2 损耗上的λ预因子的权重——如上所述,这导致相对模糊的图像,通常是正确的,但没有清晰的细节。
- 增加了量化输出图像性能的额外损失-已经尝试了许多使用预训练网络来评估输出图像质量的方法。特别是在 SRGAN 的情况下,VGG 网络在目标和输出图像上的潜在空间之间的距离被最小化。
- 更新鉴别器以提升清晰的细节——patch-GAN——似乎太明显了,不是吗?

Patch-GAN Discriminator [here]
patch GAN 的工作方式是,不是对整个图像进行分类,而是仅对图像的一部分(NxN)进行真假分类。“补丁”扫描图像,对每个图像进行预测,并优化平均值。
这假设图像可以被视为马尔可夫随机场,其中由 N 个或更多像素分隔的像素是独立的。当考虑高层结构时,这对于整个图像来说是不正确的,但是当查看低层细节时,这是一个合理的假设。
这里的优点是用修补 GAN 保留了细节;为了通过鉴别器测试,鼓励现实的低水平细节,并且使用正则化损失来保存总体结构。与整个图像分类器相比,这种修补方法的另一个重要优点是速度快,因为小的 NxN 修补比单个鉴别器评估和训练更快。它还可以有效地缩放到任意大小的图像。
网络的训练有两个技术点。首先,训练不是最小化(1-log D),而是最大化 log(D)。其次,鉴别器目标函数在训练期间减半,以限制鉴别器相对于发生器的训练速率。
最后,我们来看看我复制论文结果的实验结果。如上所述,我复制结果的代码可以在这里找到,使用 tensorflow 2.0 高级 API 编写。使用单个 GPU 计算节点,在 Amazon AWS EC2 单元上训练了 200 个历元来重现立面结果——每个历元花费了大约 4 分钟。在训练过程中,每 10 个时期拍摄快照图像,并全程记录发生器和鉴别器的损耗。
正如你在这些结果中看到的,建筑的结构已经被发现,并且风格从每一个被映射的部分是一致的。


一些细节显然在分割中丢失了,特别是在每个特征的正面和精细结构的边缘周围,以及窗户和柱柱顶周围的楣梁,正如预期的那样。

很明显,细节没有像论文中描述的那么好。此外,建筑物边缘的模糊性在视觉上不是很吸引人——这是训练 gif 中变化最大的区域,随着时代的增加会稳定下来。

随着训练持续时间的延长,我们可能会期待稍好的结果,因为很明显损失函数还没有完全饱和。
地图数据有一个稍微不同的问题。虽然在这些示例中恢复的一般道路布局和要素相当一致,但生成的结果似乎不如立面输出稳定,并且整个图像更经常出现错误。这些图像看起来也相当暗,可能是因为道路与建筑物或草地之间的对比度比立面上的元素之间的对比度低。

结论是,虽然 pix2pix 方法显然具有令人印象深刻的发电能力和通用性,但它的训练成本仍然相对较高,至于持续恢复最精细的细节,未来几年还需要做更多的工作。
规划数据科学项目?需要做出的最高决策

Source: unsplash — free stock images
6 个问题让你自己不被打败
谁说选择很容易?如果我们能有一个分类模型来告诉我们是否做出了正确的决定,这不是很好吗?可惜,那是不存在的(至少现在是!)在生活的任何方面,从几个选项中选择任何一个,确实是一个挑战。不管我们是在两种不同口味的汽水中做出选择,还是从衣柜里拿出一件 t 恤,去哪里吃晚餐,或者就像在这种情况下,决定在接下来的几周里让自己沉浸在一个数据科学项目中。

毫无疑问,这从来都不容易,但有些决定显然比其他的更容易。我们面临的这种情况可能是最重要的因素,但它也关系到我们拥有的选项数量,以及我们最终评估我们是否做出了正确决定时所考虑的因素。但是还有一个额外的关键因素,那就是决策背后的人:我感兴趣的不一定是你感兴趣的。
这就是为什么我想告诉你我问自己的问题,以选择一些想法来面对一个中等长度的数据科学项目。有句名言是这样说的:
“好的决策来自经验,经验来自坏的决策”
虽然不可能知道这句话是谁第一次说的,因为它被网络上许多不同的人引用,但我可以向你保证,我将详细描述的这些问题来自经验…肯定是一些错误的决定。因此,我希望您能提前发现它对您的特定项目非常有用:
- 应该是哪个题目?你将带着它深入黑暗。在接下来的几个月里,一周几天,所以首先试着选择一些你有激情的事情。不要仅仅因为某个主题的公众相关性或其他有偏见的原因而选择它。
- 我能面对哪一级别的困难?在网上调查在这个领域或类似项目中工作过的其他人,以确保你能够掌握所有必要的工具。
- 哪个应该是我的目标?我将试图发现什么?也许这个话题非常有趣,但是没有真正的意义去研究它。你的目标越具体越好。这将有助于以后评估您的项目结果,不仅使用典型的数据科学指标,而且对照您的目标。
- 你的项目会有观众吗?如果答案是肯定的,那么确保你能同时解决自己和他们的兴趣。否则,你可能会以一个除了你之外没人感兴趣或者没人理解的非常酷的项目结束。
- 我有足够的时间吗?这似乎是显而易见的,但是当我们面对一个有固定期限的项目时(例如,我们必须提交的学校项目),制定一个时间表是必要的。计划我们要做的每一步,确切地知道我们应该在哪个日期完成项目的每个阶段。否则,我们可能会在最后一周结束,甚至没有完成我们的数据集,因为仍然在做网络抓取。
- 有可能获得数据吗?一个需要尽快得到答案的关键问题。如果你不知道如何获取信息,你就一无所有。此外,就这一点向自己提出以下问题也很重要:
- 来源:具体从哪里获取数据?
- 格式:会是哪种格式?你能处理那种格式吗?
- 采取必要的行动:你会废弃它吗?找人要数据集?
如你所见,归根结底,就是做决定。作为我们心爱的决策树,选择数据科学项目是将我们引向终点的小决策的分支:我们的项目。
如果你来到这里,你想知道更多关于我的项目,我邀请你访问我的 GitHub 个人资料,我已经上传了其中的一些:【https://github.com/gonzaferreiro。此外,不要忘记查看我的上一篇文章,关于我在训练测试分裂中犯的 6 个业余错误,以及我的作者简介中的更多内容。如果你喜欢这篇文章,别忘了关注我,如果你想直接在你的邮箱里收到我的最新文章,就订阅我的时事通讯吧:)
感谢阅读!
计划环游世界,用 Python?
如何使用 Python 中的底图和 Networkx 包来规划您的环球旅程

环游世界一直是每个人的梦想,但我们中很少有人准备这样做。老实说,我们甚至不知道环游世界意味着什么。当然,我不能提供你旅行时需要的任何东西,比如钱和假期,这也是我所缺乏的。然而,你可以用从文章中学到的技巧开始计划,或者做白日梦。
本文分为两部分:
- 【第 1 部分】—这篇文章讲的是如何用 Python 中的
Basemap包绘制地图。旅行前和旅行中需要地图,可以用 Python 中的包画出需要的东西。[Jupyter 笔记本中的源代码 - [第二部分] —这篇文章是关于如何用 Python 中的
Networkx包为我们优化环游世界的路线。Networkx是 Python 中一个分析复杂网络的强大软件包,它将有助于我们制定环游世界的计划,有了我们的目标。[Jupyter 笔记本中的源代码]
第一部分
底图简介
matplotlib 底图工具包是一个用 Python 在地图上绘制 2D 数据的库。 正如大家都认同可视化对于人们理解数据真的很重要,地图是我们在处理地理位置时最好的可视化方法(我相信没有人在没有地图的情况下擅长处理经纬度数据。)Basemap是 Python 中一个强大的包,我们将挑选一些重要的特性来详细解释。要对这个包有更多的了解,请查看文档。
投影
众所周知,地球是圆的。因此,要把它做成 2D 地图,我们需要一些投影方法。在basemap包中,支持多种投影方式,这里的列表是。下面是两个如何在basemap中应用不同投影方法的例子。
投影—正投影

Orthographic
投影—瘿体视学

Gall Stereographic
绘画要点
既然我们能画一张地图,我们就想在上面标出一些地方。在baemap包中,我们可以使用之前定义的basemap对象将纬度和经度转换到matplotlib中的坐标系。之后,我们可以应用scatter或annotate函数在地图上放置点。在下面的例子中,我们标记了美国的四个主要城市,纽约、华盛顿、DC、洛杉矶和旧金山。

Marking the four major cities with basemap
绘制路线
除了在地图上放置点之外,我们还需要画线来可视化两个地方之间的路线。我们知道,地图上两点之间最短的路线不是直线,而是大圆路线。在basemap包中,我们可以用gcpoints功能绘制路线,做出大圆路线。以下是使用gcpoints功能从纽约到旧金山的路线示例。

Route between New York and San Francisco
T 这里还有很多有趣的功能在basemap包里,让你的地图更有信息量和吸引力。例如,arcgisimage可以用卫星照片创建地图。然而,我们现在已经足够好,可以继续制定环游世界的计划了。

Satellite map for Taiwan with arcgisimage function in basemap package
玩真实数据
在我们的计划中,我们想乘飞机环游世界。在 OpenFlights 数据库中,我们能够访问世界各地的机场和航线信息。有了这些信息,我们可以制定环游世界的计划。不过,在此之前,我们先来看看数据,先用basemap工具可视化一下。
机场
在 OpenFlights 中,包含了全球所有 6060 个机场,完整的名称、城市、国家、IATA 代码、纬度、经度等所有信息。此外,由于它有经度和纬度信息,我们可以很容易地通过谷歌地图 api 或其他工具查找更多的细节,例如机场属于哪个洲。我们也可以使用basemap工具将不同大洲的机场用不同的颜色放在地图上:北美、南美、亚洲、欧洲、非洲和大洋洲,如下所示。

Airports around the world (Source: OpenFlights)
途径
尽管我们拥有世界各地的所有机场信息,但在考虑如何环游世界时,我们仍然需要航线信息,因为并非所有两个机场都是相连的。在 OpenFlights 中,它确实有航线信息。可惜 2014 年 6 月之后就不更新了。虽然并不完美,但我们仍然可以使用这些数据,并假设这些路线从 2014 年起没有太大变化。
OpenFlights 数据库中有 67,663 条不同的航线。它有源机场和目的地机场的信息,以及航空公司,站的数量和设备的细节。如果我们只考虑来源和目的地机场,数据中有 33,971 条不同的航线。我们可以在basemap中应用工具来可视化这些路线,如下所示。

Airline routes around the world (Source: OpenFlights)
现在我们有了所有的知识:用 Python 在地图上可视化的技能集,以及关于机场和航线的信息。看起来我们已经为下一步做好了准备:优化我们环游世界的路线。
第二部分
环游世界的定义
给问题下一个好的定义总是最重要的。在我们开始分析之前,我们需要为我们定义什么是“环游世界”:如果你在北美、南美、亚洲、欧洲、非洲和大洋洲的每个大洲都至少游览过一个地方,那么你就已经环游了世界。
网络简介 x
etworkx 是一个 Python 包,用于创建、操作和研究复杂网络的结构、动态和功能。我们可以把机场之间的飞行路线看作一个复杂的网络,因此Networkx package 非常有助于我们分析它们。开始之前,我们需要先熟悉一些术语:
- 节点:网络中的点;在我们的例子中,机场是航空网络中的节点。
- 边:两点之间的连接;在我们的例子中,飞行路线是边。由于不是所有两个机场都连接,我们需要使用 OpenFlights 中的路线数据来定义它们。
- 有向/无向图:有向图中,从 A 到 B 的边不等于从 B 到 A 的边;相比之下,在无向图中,边 A 到 B 与 B 到 A 是相同的。在我们的例子中,我们应该使用有向图,因为从 A 到 B 的路线并不表示从 B 到 A 有相应的路线。
- 简单/多图:简单图中,每个点之间最多有一条边;在多图中,它允许两个节点之间有多条边,并且每条边可以有不同的属性。为了简化我们的分析,我们选择使用简单的图模型,这意味着我们不考虑航空公司的差异。
Networkx 包中的函数
下面是一些对我们分析空中飞行网络有用的函数:
- dijkstra_path :用 Dijkstra 的算法从 A 到 B 的最短路径。我们可以在函数中放入“权重”来表示如何计算“距离”。例如,我们要查找从亚特兰大到台北的最短路径是在仁川机场(ICN)停留,代码如下。
nx.dijkstra_path(G, source='ATL', target='TPE', weight='Distance')>> [Output]: ['ATL', 'ICN', 'TPE']
- single_source_dijkstra :当我们没有特定的目的地时,我们可以使用该功能计算到所有可能目的地的距离,并考虑中途停留选项。
使用 OpenFlights 数据
寻找去另一个大陆的最短路线
没有软件包,我们仍然可以对开放航班的路线数据进行一些分析。例如,我们可以通过geopy包中的geodesic函数从机场的纬度和经度计算距离。我们可以使用pandas包查看每个大陆到另一个大陆的最短路线,该包包含已处理的数据集,其中包含计算的里程信息,如下所示。

Top 5 rows for Route_Mileage table
有了这个表格,我们可以找到到另一个大陆的最短距离和相应的机场。以北美为例,到南美的最短路线是从比阿特丽克斯女王国际机场(AUA) 到 Josefa Camejo 国际机场(LSP) 。到大洋洲,最短的路线是从HNL 井上国际机场到CXI 卡西迪国际机场。各大洲之间的最短旅行路线可在地图上显示如下。

The shortest routes from North America to another continents

The shortest routes from North America to another continents

The shortest routes from South America to another continents

The shortest routes from Europe to another continents

The shortest routes from Africa to another continents

The shortest routes from Asia to another continents

The shortest routes from Oceania to another continents
请注意,欧洲和大洋洲之间没有直达航线。然而,这并不意味着欧洲人不能去大洋洲,因为我们总是可以在其他机场转机。在这种复杂的情况下,我们可以使用Networkx包来有效地处理它。
将 OpenFlight 数据导入网络
在 F 或 Networkx 2.3 版本中,我们可以用from_pandas_edgelist函数从pandas导入边,并将其他列作为边的属性。我们可以通过Networkx中的set_node_attributes功能输入所有机场的纬度、经度和大陆信息,并通过以下代码使我们将来的分析更加容易。
Code to import data to Graph and set node attributes
优化我们的旅行
我们可以把我们的目标定义为以最短的距离游遍六大洲。当然,我们可以计算两个机场之间的所有距离,并优化解决方案。然而,由于世界上有 6000 多个机场,可能会有数百万对机场,计算可能会花费很长时间。因此,在给定我们当前所处的位置的情况下,我们可以通过找到离另一个大陆最近的机场来实现目标,而不是计算所有两个机场之间的距离。按照这种逻辑,我们定义一个定制的函数如下。
在函数中,我们可以更改一些输入参数:
- G :全球航线网络图。应该是
Networkx中的图形对象。 - Start_Airport :我们出发的机场。
- 旅行中我们必须去的机场列表。
- 旅行中我们不想去的机场列表。
计划我们的旅行
有了这个工具,我们可以开始环游世界的计划。假设我们从亚特兰大(ATL)开始我们的旅行,我现在住在那里。无需放置任何Must_go和Dont_go站点,我们可以在地图上可视化结果。红色的三角形代表我们计划去的目的地机场,黑色的圆点代表我们中途停留的地方。旅行从亚特兰大开始,第一站是南美洲的BAQ****欧内斯特·科尔蒂索兹国际机场。之后我们将飞往西班牙特内里费岛的 TFN 、西撒哈拉的欧洲的** ) EUN 、土耳其的亚洲的、或帕劳的大洋洲的**,然后返回亚特兰大。如我们所愿,我们走遍了 6 大洲,总距离 39905 公里。****

Our plan for traveling around the world from Atlanta
正如先前的计划所示,有许多机场实际上远离“我们认为”它应该属于的大陆。例如,TFN 机场,尽管它在西班牙之下,是欧洲的一部分,但它实际上更靠近非洲。此外,我想在旅途中回到台北的家。我们可以用Globetrotting函数中的Must_go和Dont_go参数来完成这两个目标。这是我环游世界的最终计划。

Plan for traveling around the world from Atlanta with requirements, total distance: 41,830 km
结论
在这篇文章中,我们应用Networkx软件包分析全球航线网络,并使用 matplotlib 中的basemap软件包将结果可视化。如前所述,Networkx是一个强大的复杂网络分析包,在困难的情况下提供有用的信息。有了这些工具,我们可以计划我们的环球旅行,这意味着在一些定制的要求下,访问所有 6 大洲。此外,我们可以在地图上可视化我们的计划,如图所示。
H 然而,尽管我们现在精通Networkx和basemap套餐,但我们仍然缺乏最重要的资源,时间和金钱,来真正地环游世界。因此,希望我们明天都有一个愉快的工作日!
欢迎通过 LinkedIn 讨论任何关于 Python、R、数据分析或者 AI 的问题。只是不要在我面前炫耀你的旅行照片!
规划您的第一个数据分析项目?
深入分析
科学构建数据分析项目的框架

一个结构良好的项目对帮助你以清晰明确的格式实现项目目标大有帮助。从庞大的数据集合中发现数据驱动的见解有时会令人不知所措,科学地构建这些数据分析项目有助于高效的分析和决策,以及有效地将见解传达给更广泛的受众。
因此,本文作为一个指南,强调了该框架的九个重要阶段及其预期目标,用于根据数据驱动的决策科学地构建项目。
- 概述和动机:在任何需要集思广益的项目中,这都是重要的一步。它强调了项目开始的原因及其预期目标。它最终会对项目的研究领域给出一个清晰的想法,并有效地突出项目最终旨在实现什么样的数据驱动洞察。
- 项目目标:这一步清楚地定义了项目的目标。它进一步有助于基于数据源的初始研究问题的形成。
- 数据源:这一步有助于从各个方面理解项目中使用的数据源。它从所收集数据的来源、数据大小以及数据中要素和实例数量的信息方面概述了数据源。
- 相关工作:这一步给出了项目领域相关工作的背景。它旨在概述您的数据分析项目在目标领域开展的研究,以突出您的项目将做出的重要贡献。在其他相关工作中使用相同数据源的情况下,通过使用相同的数据源与以前的工作进行比较,可以突出项目的不同目标。
- 初始研究问题:该步骤详细说明了在项目初始阶段基于对数据的初步理解而制定的研究问题(rq ),但没有详细的探索性数据分析。
- 数据争论:数据争论由不同的步骤组成,这些步骤将数据从原始格式转换为清晰的格式,这种格式对于数据分析来说是适当且准确的。不同的步骤包括, 输入数据集的检查: 该步骤包括输入数据集的可视化以生成其统计数据和有效摘要, 数据集清理和处理: 该步骤包括输入数据集的清理以消除缺失值、重复行、列重命名&重新排序等。最后,将清理后的数据集写回文件以供进一步分析 ,清理后的数据集的探索: 该步骤包括可视化清理后的数据集以生成统计数据,通过各种数据可视化分析绘制数据集中的不同变量,检查相关特征等。、和 数据准备: 该步骤通过移除不需要的特征、添加新列等,使数据为不同的 rq 做好准备。
- 探索性数据分析:探索性数据分析(EDA)是在对数据进行正式建模之前将数据中的主要特征可视化的过程,以发现数据模式并验证对数据做出的最初的主要假设。这一步进一步有助于有效地重组和重新制定初始 rq。
- 最终研究问题:探索性数据分析对制定的初始 rq 进行可行性检查。EDA 阶段有助于更好地理解与项目目标相关的数据。因此,这会导致新 rq 的修改、删除或添加。因此,这一阶段的结果应该是制定的最终 rq 集,它将通过项目来回答。
- 数据分析和建模:这是数据分析项目中至关重要的一步,在这里我们采用复杂的算法和建模来回答公式化的研究问题。进一步构建数据驱动的见解,并向更多受众清晰有效地传达&;对于每个 RQ,将它们构造成五个信息步骤将是一个很好的实践, 选择的算法,算法选择的原因,分析和建模,观察和应用。
客户行为分析是一所研究型大学开展的数据分析项目的实时示例,该项目根据本文分享的见解构建。
使用 Fastai 的植物病害检测 Web 应用程序
利用 fast.ai 实现最先进的结果

介绍
使用 FastAi 创建一个人工智能 web 应用程序,该应用程序可以检测植物中的疾病,FastAi 构建在脸书深度学习平台 PyTorch 之上。据联合国粮食及农业组织(UN)称,跨境植物害虫和疾病影响粮食作物,给农民造成重大损失,威胁粮食安全。
Resnet34 模型的准确率达到 99.654%
动机
对于这个挑战,我使用了"plan village"数据集。该数据集包含一个开放的植物健康图像库,以支持移动疾病诊断的开发。该数据集包含 54,309 幅图像。这些图像涵盖了 14 种作物:苹果、蓝莓、樱桃、葡萄、橙子、桃子、甜椒、土豆、覆盆子、大豆、南瓜、草莓和番茄。它包含 17 种基础疾病、4 种细菌性疾病、2 种霉菌(卵菌)疾病、2 种病毒性疾病和 1 种由螨虫引起的疾病的图像。12 种作物也有健康叶片的图像,这些叶片没有明显受到疾病的影响。
平台

我用过 谷歌云平台 ,基础平台叫n1-highmem-8,每小时收费 0.12 美元。安装一个 P4 图形处理器每小时要花费 0.26 美元,所以两者加起来总共是每小时 0.38 美元。以及建议的 200GB 标准磁盘存储大小,将会有每月 9.60 美元的额外费用。对于完整的演练,设置访问这里!
培养
我使用的是建立在 Pytorch 之上的 fastai。数据集由来自 PlantVillage 数据集的 38 个疾病类别和来自斯坦福背景图像 DAGS 开放数据集的 1 个背景类别组成。数据集的 80%用于训练,20%用于验证。

Source: Google Images
我们将使用预先培训的 resnet34 模型来解决培训中的问题。
用下面三行开始每个笔记本:
jupyter 笔记本中以' % '开头的线条称为 线条魔法 。这些不是 Python 要执行的指令,而是 Jupyter notebook 的指令。
前两行将确保每当您在库中进行任何更改时自动重新加载。第三行显示笔记本中的图表和图形。
导入所有导入库:
from fastai import *
from fastai.vision import *
from fastai.metrics import error_rate, accuracy
现在,给出数据集的路径(在我的例子中,它在根目录中):
PATH_IMG = Path('PlantVillage/')
批量意味着我们将一次输入 x 张图像,以更新我们深度学习模型的参数。如果较小的 GPU 使用 16 或 32 而不是 64,则将批量大小设置为 64。
bs = 64
ImageDataBunch用于根据图像进行分类。
imagedata bunch . from _ folder自动从文件夹名称中获取标签名称。fastai 库有很棒的文档来浏览他们的库函数,并附有如何使用它们的实例。一旦加载了数据,我们还可以通过使用。标准化为 ImageNet 参数。
img_data = ImageDataBunch.from_folder(path=PATH_IMG, train='train', valid='val', ds_tfms=get_transforms(), size=224, bs=bs)img_data.normalize(imagenet_stats)
path图片目录的路径。ds_tfms图像所需的变换。这包括图像的居中、裁剪和缩放。size图像要调整的大小。这通常是一个正方形的图像。这样做是因为 GPU 中的限制,即 GPU 只有在必须对所有图像进行类似计算(如矩阵乘法、加法等)时才会执行得更快。
为了查看图像的随机样本,我们可以使用。show_batch()函数 ImageDataBunch 类。
img_data.show_batch(rows=3, figsize=(10,8))

让我们打印数据中出现的所有数据类。如上所述,我们总共有 39 个类别的图像!
img_data.classes'''
Output of img_data.classes:
['Apple___Apple_scab',
'Apple___Black_rot',
'Apple___Cedar_apple_rust',
'Apple___healthy',
'Blueberry___healthy',
'Cherry_(including_sour)___Powdery_mildew',
'Cherry_(including_sour)___healthy',
'Corn_(maize)___Cercospora_leaf_spot Gray_leaf_spot',
'Corn_(maize)___Common_rust_',
'Corn_(maize)___Northern_Leaf_Blight',
'Corn_(maize)___healthy',
'Grape___Black_rot',
'Grape___Esca_(Black_Measles)',
'Grape___Leaf_blight_(Isariopsis_Leaf_Spot)',
'Grape___healthy',
'Orange___Haunglongbing_(Citrus_greening)',
'Peach___Bacterial_spot',
'Peach___healthy',
'Pepper,_bell___Bacterial_spot',
'Pepper,_bell___healthy',
'Potato___Early_blight',
'Potato___Late_blight',
'Potato___healthy',
'Raspberry___healthy',
'Soybean___healthy',
'Squash___Powdery_mildew',
'Strawberry___Leaf_scorch',
'Strawberry___healthy',
'Tomato___Bacterial_spot',
'Tomato___Early_blight',
'Tomato___Late_blight',
'Tomato___Leaf_Mold',
'Tomato___Septoria_leaf_spot',
'Tomato___Spider_mites Two-spotted_spider_mite',
'Tomato___Target_Spot',
'Tomato___Tomato_Yellow_Leaf_Curl_Virus',
'Tomato___Tomato_mosaic_virus',
'Tomato___healthy',
'background']
'''
为了创建迁移学习模型,我们需要使用函数cnn_learner,它接受数据、网络和metrics。metrics仅用于打印培训的执行情况。
model = cnn_learner(img_data, models.resnet34, metrics=[accuracy, error_rate])
我们将训练 5 个纪元。
model.fit_one_cycle(5)

正如我们在上面看到的,通过在默认设置下运行五个时期,我们对这个细粒度分类任务的准确率大约是 99.10%。
再训练两个纪元吧。
model.fit_one_cycle(2)

这次准确率 99.2%!
现在用.save()保存模型。
model.save('train_7_cycles')
我们也可以画出混淆矩阵。
interpret = ClassificationInterpretation.from_learner(model)
interpret.plot_confusion_matrix(figsize=(20,20), dpi=60)

我们使用lr_find方法来寻找最佳学习速率。学习率是一个重要的超参数。我们习惯用αα来表示这个参数。如果学习速度太慢,我们需要更多的时间来达到最准确的结果。如果它太高,我们甚至可能无法得到准确的结果。学习率 Finder 的想法是自动获得幻数(接近完美),以获得最佳学习率。这是在去年的快速人工智能课程中介绍的,现在仍然有用。
model.lr_find()
运行 finder 之后,我们绘制了损失和学习率之间的图表。我们看到一个图表,通常选择损失最小的较高学习率。更高的学习速率确保机器最终学习得更快。
model.recorder.plot()

考虑到我们使用的是一个预先训练好的 Resnet34 模型,我们肯定知道这个神经网络的前几层将学习检测边缘,后几层将学习复杂的形状。我们不想破坏早期的图层,这些图层可能在检测边缘方面做得很好。但是希望在缩小图像分类范围方面改进该模型。
因此,我们将为前面的层设置较低的学习速率,为最后的层设置较高的学习速率。

*slice*用于提供学习率,这里我们只提供学习率的范围(它的最小值和最大值)。随着我们从较早的层移动到最新的层,学习速率被设置得逐渐更高。
让我们解冻所有层,以便我们可以使用 unfreeze()函数训练整个模型。
model.unfreeze()
model.fit_one_cycle(3, max_lr=slice(1e-03, 1e-02))

model.fit_one_cycle(5, max_lr=slice(1e-03, 1e-02))

准确率接近 99.63%。保存模型!
model.save('train_lr_8_cycles')
冻结模型,找到学习率,并微调模型:


最后,我们使用 Resnet34 取得了接近 99.654%的准确率。

在 GitHub 上分叉或启动它:
使用 pyTorch 训练和评估植物病害分类任务的最新深度架构。模型…
github.com](https://github.com/imskr/Plant_Disease_Detection)
部署在 AWS 上:

Plantsnap 和 Imagga 利用机器学习把一个植物学家放进你的口袋
在 320,000 个类别和 9,000 万张图像上训练的分类器产生了惊人精确的结果

Credit: Gado Images
人工智能和机器学习通常都与计算机和尖端技术有关。自动驾驶汽车!智能音箱!机器人抢了你的工作!但是,如果有一个人工智能驱动的系统将你带出科技世界,而是将你与你周围的自然世界联系起来,会怎么样呢?
这是 Plantsnap 的承诺,这是一个免费的应用程序,它使用人工智能和机器学习来自动识别几乎任何植物,只需使用智能手机照片。
Plantsnap 很简单。你打开应用程序,立即被带到一个摄像头视图。找一种植物——任何一种都可以,从你上次晚宴的切花到你家后院的一棵树,再到植物园里生长的一些奇异的东西——然后给它拍张照。
应用程序会仔细考虑几秒钟,然后返回你看到的是哪种植物的最佳猜测。对许多植物来说,你可以获得它们生长条件、分类数据、拉丁名称等详细信息。这就像口袋里有一个植物学家。如果你想要一棵自己的,你甚至可以链接到一个苗圃网站,购买这种植物。
Plantsnap 是怎么做到的?他们利用 Imagga 的机器学习服务,Imagga 是一家在云中提供基于 API 的视觉人工智能服务的公司。Plantsnap 使用 Imagga 的系统在 320,000 株植物上训练其模型。为了做到这一点,他们使用了 9000 万张图像的训练集,其中许多图像可能来自公共互联网。
从机器学习的角度来看,这很有趣,因为 Plantsnap 正在处理数量惊人的类。通常情况下,你可能会将照片分类到 ML 系统的几个类别中。也许你会拍摄不同种类的制造缺陷的照片,并使用 ML 驱动系统对它们进行分类。或者你可能会做图像的自动标记,最多需要几千个类。
不过,超过 30 万节课是闻所未闻的。任何许多植物看起来都非常相似——这就是这个系统有价值的原因,因为它们很难区分。这可能是他们需要近 1 亿张图像来正确训练系统的原因。无论你如何切割它,都有大量数据要输入任何机器学习系统。为了训练整个事情,他们使用了英伟达 DGX 站,并为类似的工厂添加了他们自己的消歧过程。其结果是用户友好的植物应用程序,任何人都可以下载。
那么 Plantsnap 有多准确呢?我们在加多图像公司的摄影中经常使用它,我们发现它在许多情况下可靠地提供了正确植物的匹配,或者至少为我们的研究人员指出了正确的方向。Imagga 说,它达到了 90%的准确率,这对于大量的类来说是非常好的。植物也很难识别——即使是人类生物学家,识别植物的准确率也只有 95%。因此,Plantsnap 和 Imagga 已经用机器学习做到了这一点,这一点非常好。
作为测试,这里有一束康乃馨。

Credit: Gado Images
使用 Plantsnap,您可以拍摄花束的照片。

Photographing carnations with Plantsnap Credit: Gado Images
在大约 5 秒钟内,Plantsnap 有一个匹配。答对了-康乃馨!

Credit: Gado Images
值得注意的是,Plantsnap 在这一点上做对了,尽管他们对康乃馨的主要印象是一种完全不同的颜色。切花是一回事,但同样,我们已经看到野生植物,外来物种和更多的成功。
Plantsnap 每天免费拍摄一定数量的快照。除此之外,他们收取象征性的年费来无限制地使用这项服务。
那么 Plantsnap 真的是用机器学习让你更贴近自然吗?使用基于高级机器学习和人工智能的手机应用程序来与自然世界联系,这是一个明显的讽刺。但我发现它真的有用。
看到一株有趣的植物(甚至是一株无聊的植物),拿出手机,拍一张照片,立刻就能知道你在看什么,这是一件神奇的事情。我发现,一旦你用这种技术识别了一株植物,它就开始变得熟悉,即使没有应用程序,你也开始认出它。有奶蓟!啊,这茴香不错!如果你愿意,你可以成为“那个家伙”(是的,通常是一个家伙),在每次徒步旅行中用植物鉴定来烦人们,只是没有经过多年的仔细研究!
除了新奇的因素,还有一些了解植物的具体原因。有没有徒步旅行时想知道“我刚才碰到的是毒藤吗”?Plantsnap 会告诉你!在你的后院种一棵长着不祥红色浆果的植物?Plantsnap 会告诉你它对你的狗是否有毒。
这也是一个关于机器学习可能性的非常有趣的案例研究。你不会认为你可以在 320,000 个类上训练一个系统,并得到任何接近准确的结果。但是 Plantsnap 几乎和专业人员一样准确。教训?如果你正试图解决一个具有大量类别或庞大训练数据集的机器学习问题(同样,他们使用了 9000 万张图像),不要绝望——plant snap 表明这是可能的。
如果你曾经对植物感到好奇——或者只是想看看强大的人工智能的一个非常酷的应用——那么试试 Plantsnap 吧。小心那些长根漆树!
玩 QuickDraw:一个实时应用程序
使用深度学习开发游戏应用的虚拟交互的一步。
什么是 QuickDraw?
猜字谜是一种游戏,一个人在空中画出一个物体的形状或图片,另一个人必须猜出来。就像猜字谜一样,Quickdraw 是一种游戏,你在摄像机前画一个图案,然后让计算机猜你画了什么。
关于快速绘图数据库
这个想法的起源是由谷歌研究院的 Magenta 团队提出的。其实游戏“快,画!”最初是在 2016 年的谷歌 I/O 上推出的,后来该团队训练了基于卷积神经网络(CNN)的模型来预测绘画模式。他们在网上做了这个游戏。用于训练模型数据集由跨越 345 个类别的 5000 万个绘图组成。这 5000 万个模式如图 1 所示。图片取自此处。

Figure 1: Sample of database images
该团队将数据库公开使用,以帮助研究人员训练自己的 CNN 模型。整个数据集分为 4 类:
- 原始文件 (
.ndjson) - 简化图纸文件 (
.ndjson) - 二进制文件 (
.bin) - Numpy 位图文件 (
.npy)
一些研究人员使用这个数据集并成功地开发了训练好的 CNN 模型来预测绘画模式。你可以在这里找到这些实验和教程。他们还在网上制作源代码来帮助其他研究人员。
使用 CNN 开发实时 QuickDraw 应用
在这里,我们开发了一个基于 tensorflow 的 QuickDraw 应用程序,使用了 15 个对象,而不是使用整个数据库图像。我们下载了 15 个班级的图片。npy 格式来自这里的。这 15 类包括苹果、蜡烛、领结、门、信封、吉他、冰淇淋、鱼、山、月亮、牙刷、星星、帐篷和手表。
下载的数据集保存在名为“data”的文件夹中。用这些图像样本训练 CNN 模型。对于小数据集,我们用 3 个卷积层训练 CNN 模型,然后是最大池和丢弃。在拉平网络后,使用两个密集层。可训练参数的数量如图 2 所示。

Figure 2: Summary of CNN model used to train with QuickDraw dataset.
我们在用 OpenCV 库中指定的默认学习速率训练网络时使用了 Adam 优化。然而,我们也使用随机梯度下降(SGD ),但在这种情况下,您必须手动设置这些参数,因为默认学习率 SGD 给出的训练和验证精度较低。我们用 50 个纪元训练它。在 6GB RAM 的 CPU 中训练 CNN 模型需要大约 2 个小时。模型的准确率在 94%左右。实时测试的输出显示在下面的视频中。Bandicam 软件用于记录桌面活动。
该应用程序找到蓝色指针,并围绕它画一个圆,然后找到圆心,用手移动来画直线或曲线。你也可以用其他颜色替换蓝色指针。为此,您需要在代码中给出适当的 RGB 颜色组合。根据绘图模式,模型猜测并显示预测的表情符号作为输出。然而,有时它不能正确地分类物体。明显的原因是实时应用对噪声、背景颜色和用于采集视频流的传感器的质量敏感。然而,系统的准确性可以通过用更多的历元训练它来提高,保持手与蓝色指针足够接近网络摄像头。
这个应用程序的源代码可以在 GitHub 资源库中找到。你可以下载并使用它来训练你自己的 CNN 模型。在 GitHub 页面也提到了所需软件包的安装过程。
感谢 Jayeeta Chakraborty,他帮助我开发了这个项目。我还要感谢阿克谢·巴哈杜尔的精彩教程和代码。然而,我在 CNN 架构上做了一些改进,增加了额外的卷积和丢弃层来提高系统的性能。希望本文和代码能帮助您开发其他类似的实时应用程序。
玩家相似性和插值
简单的数据和计算机科学增强了足球滑板车
分析视频,寻找来自类似球队的球员,在世界各地旅行来侦察球员:scooting 活动可能是漫长而挑剔的。此外,现在有超过 100 个职业联赛和职业球员。
虽然滑板车部门在世界各地都有许多专家,但要跟踪的游戏、玩家和统计数据的数量有时太多,无法仅依靠人类的能力。
这就是足球分析的用武之地。我们将在这篇文章中看到,通过一些数据和基本的计算机科学,滑行过程可以以更高的速度、效率和精度得到增强。
刻画玩家
我们要回答的第一个问题是如何给玩家定性?虽然滑板车部门有专家依靠他们对足球的感觉和经验,但仅凭数据很难收集这些专业知识。
足球分析社区开发了许多很好的指标来捕捉球员特征的复杂模式。在这里,我们将集中我们的探索与 EA 体育 FIFA 20 视频游戏玩家评分。
这些评级是由该领域的一整个小组和专家计算和提炼的。众所周知,甚至体育数据公司也在利用视频游戏的数据来帮助真正的俱乐部招募球员。虽然这可能有点天真,但这种方法可以成为定义真正玩家属性的良好起点。
这个数据库包含了每个球员的三十多种属性:从进攻到防守,甚至是精神属性。
如你所料,我们可以将这些属性视为每个玩家的特征向量。数学开始。

同样,这是非常简单的。对于那些对更复杂的模型和衡量标准感兴趣的人来说,这里有一些值得一看的资源:
- 利用深度学习理解 NBA 球员运动的模式Akhil nista la 和 John Guttag。
- OptaPro Analytics Forum 2019—player 2 vec:球员和球队风格的序列优先方法作者 Ben Tovarney。
计算相似性
现在我们有了描述球员的向量,所以我们能够计算距离。最常见的距离是欧几里德距离,其定义如下:

Euclidean distance.
欧几里得距离就像用尺子来测量距离。然而,选择这个距离很可能不是最佳选择。比如 c 罗和梅西接近,因为他们在射门、速度或者运球方面的评分都很高。但像若昂·费利克斯这样与梅西有着相同轮廓的年轻球员会离得更远,因为他的属性更弱,但比例相同。

With the Euclidean distance, Messi is closer to Ronaldo than J. Felix.
这就是余弦相似度的来源。这是两个向量之间的相似性的度量,看它们之间的角度。
给定属性 x 和 y 的两个向量,余弦相似度 cos(θ)使用点积和幅度表示为:

Cosine similarity.
在我们的例子中,梅西和若昂费利克斯之间的角度小于梅西和 c 罗之间的角度。即使他们离得更远。

Looking at angles between vectors then J. Felix is closer to Messi.
余弦相似性允许我们更好地捕捉“风格”而不是纯粹的“统计”属性。为了更好地理解它,让我们看一些相似性计算的例子:

The five most similar players to James Maddison
詹姆斯·马迪森是英超联赛中最好的年轻中场之一,也是莱斯特队 2019-2020 赛季的关键球员。这里我们计算余弦相似度来得到五个最相似的玩家。
结果可能看起来有点奇怪,但我们必须记住他们是和马迪森有着“相同风格”的球员。因此,他们不会有与英国球员相似的表现,但我们可以相信他们在球场上有相同的技能和能力。值得注意的是,马丁·德加德,最受期待的年轻人之一,是第十个最像詹姆斯·马迪森的球员。

The five most similar players to Valentin Rongier
这个例子很有意思。首先,看到阿尔坎塔拉兄弟(蒂亚戈和拉菲尼亚)彼此靠近是一件好事。虽然拜仁慕尼黑的球员比他的兄弟表现更好,但他们在逻辑上有着相同的比赛风格。
如果你曾经看过瓦伦丁·朗格的比赛,你会感觉到他的风格是多么的接近蒂亚戈。虽然弗拉迪米尔·达里达和卢卡斯·鲁普是更令人惊讶的名字,但在与瓦伦丁·朗格尔最相似的球员名单中,还有巴勃罗·福纳尔斯、阿莱克斯·加西亚(曼城的天才)、阿瑟甚至莫德里奇。没什么好惊讶的。
矢量插值的进一步发展
正如我们所见,相似之处已经很有趣了。例如,像曼联这样想要取代保罗·博格巴的俱乐部可以尝试找到与法国中场最相似的球员。
但是由于矢量表示的性质,我们可以更进一步。
如果曼联的工作人员想找一个像保罗·博格巴一样拥有梅苏特厄齐尔部分技能的球员呢?
这就是矢量插值的用武之地。通过将保罗·博格巴向量表示与梅苏特厄齐尔向量表示混合,我们可以创建一个插值向量,然后从我们的球员数据库中寻找最接近的向量。

所以在我们的例子中,与保罗·博格巴和梅苏特·厄齐尔最相似的球员是罗伯托·菲尔米诺。当你知道巴西前锋在球场上踢得很低时,这是很符合逻辑的。像法国人和德国人一样,他是一名技术非常娴熟的球员,对比赛有着独特的眼光和感觉。请注意,在名单中菲尔米诺之后的球员是莱昂内尔·梅西、保罗·迪巴拉、菲利佩·库蒂尼奥或凯文·德布劳内。
这里我们同样混合了 Pogba 和 Ozil 载体。在计算这个插值向量时,可以改变每个玩家向量的部分。不是取 50%的 Pogba 和 Ozil 载体,我们可以取 80%的 Pogba 和仅 20%的 Ozil 来得到更类似于保罗·博格巴的载体。
通用公式非常简单,α是我们的比例参数,x 和 y 是两个玩家的向量:

另一个有趣的用例:如果一个俱乐部想用一个更好的球员替换他的一个球员怎么办。有点像“升级”。这个俱乐部没有预算去买一个星舰球员,但是真的想找一个有类似经历的球员。
这是西汉姆和塞尔吉奥·拉莫斯的中卫安杰洛·奥博戈的例子。
通过计算许多具有不同α值的插值向量,我们将得到看起来或多或少类似于奥格本纳的不同球员。

结果相当有趣。将计算中向量的比例从奥格本纳移动到拉莫斯会导致不同的“梯度”,这两个球员或多或少地相似。
这是埃里克·拉梅拉和穆罕默德·萨拉赫的另一个例子。

这些例子证明了这种毫不费力的计算的力量:在这些例子中,被发现的球员可能是改善西汉姆或热刺阵容的好目标。利用球员的这种“梯度”,这种方法还根据俱乐部的意愿提供了许多解决方案。
限制和解决方法
正如我们前面提到的,用 FIFA 20 球员数据库做这个实验可能太简单了。虽然这些数据是由人类专家制作的,但它们可能缺乏每个球员更多风格属性的一些特征或细节。仅仅通过在速度、视野或视觉项目上给出好的评分,听起来很难抓住埃登·阿扎尔的比赛风格。
除了寻找好的表示向量的问题之外,这种方法很容易建立。距离很容易在任何现代“数据”软件中实现,插值计算也很直接。我们可以从整支球队中得出这个想法,然后比较每场比赛的阵容。我们甚至可以进一步研究整个游戏:考虑所有的游戏事件,最相似的游戏是什么?回答这个问题,我们可以在一个高的尺度上比较游戏,也许可以尝试在特定的时刻发现最佳选择…
这个实验到此为止。不要犹豫,在 Twitter 上与我联系或在下面留下评论。我希望你喜欢阅读这篇文章。
在 Google Colab 中使用无模型强化学习玩 21 点!
用于解决 21 点等游戏的蒙特卡罗控制和时差控制等算法的比较研究。

Pixabay
我觉得必须写这篇文章,因为我注意到没有多少文章详细解释蒙特卡罗方法,而是直接跳到深度 Q 学习应用。
在本文中,您将了解到
- 强化学习中无模型算法背后的动机
- 这些算法的内部工作,同时应用它们来解决 21 点作为一个例子,在浏览器本身的一个 Colab 笔记本(不需要安装任何东西)!
在我们开始之前,我想让你知道,这篇文章假设你对强化学习的基本概念有基本的了解,如果你不了解,没关系,这里有一个快速回顾:
- 在通常的 RL 设置中,代理在环境中采取行动,并从环境中获得观察和回报。

Illustration (https://i.stack.imgur.com/eoeSq.png)
强化是一种行为模式的强化,这是动物接受与另一种刺激或反应有适当时间关系的刺激的结果。—巴甫洛夫的专著(1927 年)
- 代理执行的这些任务可以是偶发的,也可以是持续的,这里的 21 点是一个偶发的游戏,也就是说,它以你赢或输而结束。
- 代理人期望最大化他们的累积“预期”回报,也称为“预期回报”。在这里,我们对未来可能得到的回报给予的重视程度低于眼前可能得到的回报。即 Gt = Rt+1+ γ Rt+2+…
- 我们假设我们的环境具有马尔可夫性质,即给定当前状态,未来状态或回报独立于过去状态,即 P(St+1|St) = P(St+1|S1,S2,S3…St)。
代理的策略可以被认为是代理使用的策略,它通常从感知的环境状态映射到处于这些状态时要采取的行动。
- 我们定义对应于策略π的状态-值对 V(s ):作为代理如果在该状态下开始并遵循策略π将获得的期望回报。记住 V(s)总是对应于某个策略π。
- 我们还将行动值函数 Q(s,a)定义为在策略π下的状态 s 中采取行动 a 的值。
- V(s) = E [Gt | St = s]和 Q(s,a) = E [Gt | St = s,At=a]也可以按照下图所示的方式书写。这种形式在计算 V(s)和 Q(s,a) 时更有用

Pss’ is a property of the environment, also referred to as P(s’, r|s, a) in the book by Sutton and Barto.
各种基于模型的方法,如动态编程,使用贝尔曼方程(V(St)和 V(St+1) 之间的递归关系)来迭代地寻找最优值函数和 Q 函数。
总结到此结束!
我们所说的无模型方法是什么意思?为什么要使用它们?
要使用基于模型的方法,我们需要对环境有完整的了解,即我们需要知道Pss’(请参考上图):如果代理处于状态 St=1 并在=a 采取行动,我们将结束于状态 St+1 = s’的转移概率。例如,如果一个机器人选择向前移动,它可能会在下面光滑的地板上侧向移动。在像 21 点这样的游戏中,我们的行动空间是有限的,就像我们可以选择“打”或“粘”,但我们可能会在许多可能的状态中的任何一种状态中结束,而你对这些状态的概率一无所知!在 21 点状态下,由您的总和、庄家的总和以及您是否有可用的 a 决定,如下所示:
env = gym.make('Blackjack-v0')
print(env.observation_space)
print(env.action_space)
状态: (32102 阵列)
- 玩家当前总数:[0,31]即 32 个状态
- 庄家面朝上的牌:[1,10]即 10 个州
- 玩家是否有可用的王牌:[0]或[1],即 2 种状态
动作:
- 坚持或击中:[0]或[1],即 0 表示坚持,1 表示击中
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
当我们没有环境模型时该怎么办?你通过一次又一次的互动来获取样本,并从中评估这些信息。无模型基本上是试错法,它不需要任何两个状态之间的环境或转移概率的明确知识。
因此,我们看到,无模型系统甚至不能思考它们的环境将如何响应某个动作而变化。这样,它们比更复杂的方法具有合理的优势,在更复杂的方法中,真正的瓶颈是难以构建足够精确的环境模型。(例如,我们不可能开始列出庄家在 21 点的每种状态下将抽出的下一张牌的概率。)
(旁注)也许第一个简明扼要地将试错学习的本质表述为学习原则的人是爱德华·桑戴克,他在 1911 年发表了《效果法则》,但试错学习的思想可以追溯到 19 世纪 50 年代。
理解了无模型方法背后的动机之后,让我们来看几个算法!
编辑描述
colab.research.google.com](https://colab.research.google.com/drive/1zVdv5KRmWyoYZGt83QTGxPkY1Gm7WjDM)
蒙特卡罗预测算法;
为了制定更好的政策,我们首先需要能够评估任何政策。如果一个代理人在许多事件中遵循一个策略,使用蒙特卡罗预测,我们可以从这些事件的结果中构建 Q 表(即“估计”行动值函数)。
因此,如果 sum 大于 18,我们可以从“坚持”这样的随机策略开始,概率为 80%,因为我们不想超过 21。否则,如果总和小于 18,我们将以 80%的概率“命中”。以下代码使用以下策略生成剧集,稍后我们将评估该策略:
(注意这里的“情节”,即返回的数量是对应于情节中采取的每个动作的(状态、动作、奖励)元组的列表)
现在,我们想得到给定政策的 Q 函数,它需要直接从经验中学习价值函数。请注意,在蒙特卡洛方法中,我们在一集的结尾获得奖励,其中..
插曲= S1 A1 R1,S2 A2 R2,S3 A3 R3…… ST(直到终止状态的步骤顺序)
我们将从 MDP 的样本回报中学习价值函数,从总结中回忆:
Q(s,a) = E [Gt | St = s,At=a]且 Gt = Rt+1+ γ Rt+2+…对于一个策略π 。
样本返回是什么?假设我们使用一个策略玩了 10 集,当我们访问同一个州 10 次中的 4 次时,我们得到了奖励 2,6,5,7,那么样本回报将是(2+6+5+7)/4 = 20/4 = 5v(S)。因此,样本回报是剧集回报(奖励)的平均值。我们以什么顺序结束访问状态在这里并不重要,对每个值的估计是独立计算的!
这样,我们可以构建一个 V 表或 Q 表,为了创建一个 Q 表,我们需要记录我们访问一个(状态,动作)对所获得的奖励,还要记录我们访问状态 N 表的次数。
这取决于在估计 Q 值时选择的收益
- 第一次拜访 MC: 在一集中,我们只对第一次拜访的时间(s,a)进行平均回报。从统计学角度来看,这是一种不偏不倚的方法。
- 每次访问 MC: 在一集中,我们只计算每次访问时间的平均回报。从统计学角度来看,这是一种有偏见的方法。
比如:一集里,S1 A1 R1,S2 A2 R2,S3 A3 R3,S1 A1 R4→结束。然后第一次访问 MC 将考虑奖励,直到 R3 计算回报,而每次访问 MC 将考虑所有奖励,直到剧集结束。
在这里,在 21 点中,我们是使用首次拜访 MC 还是每次拜访 MC 并没有太大影响。以下是初诊 MC 预测的算法

MC pred, pseudocode
但是我们将实现每次访问的 MC 预测,如下所示:
我们首先初始化一个 Q 表和 N 表来记录我们对每个[状态][动作]对的访问。
然后,在生成剧集功能中,我们使用 80–20 随机策略,如上所述。
sum(rewards[i:]*discounts[:-(1+i)]) #is used to calculate Gt
这将估计用于生成剧集的任何策略的 Q 表!一旦我们有了 Q 值,获得 V 值就相当容易了,因为 V(s) = Q (s,π(s))。让我们画出状态值 V(s)!

Plotted V(s) for each of 32102 states, each V(s) has value between [-1,1] as the reward we get is +1,0,-1 for win, draw and loss
现在我们知道了如何估计政策的行动价值函数,我们如何改进它?使用…
蒙特卡罗控制算法
简单来说,计划是这样的。我们从随机策略开始,使用 MC 预测计算 Q 表。所以我们现在知道了哪些状态下的哪些动作比其他的更好,也就是说,它们有更大的 Q 值。因此,我们可以根据我们的知识,即 Q 表,贪婪地选择每个状态下的最佳行动,然后重新计算 Q 表,贪婪地选择下一个策略,以此类推,从而改进我们现有的策略!听起来不错?

From slides based on RL book by Sutton and Barto
- 但是我们有一个问题,如果对于一个状态“St”有两个动作“stick”和“hit ”,如果代理在许多游戏的早期选择“hit”并获胜,而“stick”应该是更好的动作,它永远不会探索这个动作,因为算法继续贪婪地选择它多次。因此,为了解决这种探索-利用困境,我们将使用ε-贪婪策略,即我们将探索,以概率‘ε’(ε)采取随机行动,而不是贪婪地利用学习到的 Q 值。自然,我们希望在开始时将ε值保持在~1,并在学习结束时将它减小到接近 0(总集数)。
- 增量均值:还记得我们在 MC 预测中如何用所有回报的平均值来估计 Q 值吗?但是现在与 MC Pred 不同,在 MC Control 中,我们的政策每个周期都在变化!我们可以这样用之前的 Q 值来写同一个等式:相信我,这不是突然的,如果你看到 N(St,at) * Q(St,at)是这一步之前的 Gt,因此 Gt-Q(St,At)是增量变化,你自己也可以得到同一个等式。
N(St,At) ← N(St,At) + 1
Q(St,At) ← Q(St,At) + (1/N(St,At))*(Gt-Q(St,At))
- 常数α:现在随着 N(St,At)的增加,即我们在互动中多次访问同一个状态-行动对,增量变化项减少,这意味着我们后面的经历对开始的经历的影响越来越小。为了解决这个问题,我们可以用一个常数α代替(1/N)项,它是一个超参数,供我们选择。
Q(St,At) ← Q(St,At) +α *(Gt-Q(St,At))
了解了这些对仅采样返回想法的重要实际变化后,下面是首次访问 MC 控制的算法!

MC control, pseudocode
我们将实现每一次访问的 MC 控制,因为它只是稍微更容易编码
我们只是使用了 3 个函数来使代码看起来更整洁。要像我们对 MC 预测那样生成剧集,我们需要一个策略。但是请注意,我们不是在输入一个随机策略,而是我们的策略相对于之前的策略是ε-贪婪的。get_probs 函数给出了相同的动作概率,即π(a|s)
而 update_Q 函数只是使用增量平均值和常数α来更新 Q 值。最后我们在 MC 控件和 ta-da 中调用所有这些函数!
MC Control
请随意探索笔记本的评论和解释,以获得进一步的澄清!
这样,我们终于有了一个学习玩 21 点的算法,至少是 21 点的一个稍微简化的版本。让我们将学到的策略与萨顿和巴尔托在 RL 书中提到的最优策略进行比较。

RL, Sutton Barto Fig 5.2

Our learnt policy (rotated by 90 deg)

Play using our learnt policy!
沃拉。这就对了,我们有一个人工智能,它玩 21 点的时候大多数时候都会赢!但是还有更多…
时间差分方法
现在 21 点不是学习 TD 方法优势的最佳环境,因为 21 点是一个情节游戏,蒙特卡罗方法假设情节环境。在 MC 控制中,在每集结束时,我们更新 Q 表并更新我们的策略。因此,我们没有办法找出是哪个错误的举动导致了损失,但在像 21 点这样的短期游戏中,这并不重要。如果是像国际象棋这样的较长游戏,使用 TD 控制方法会更有意义,因为它们是引导式的,这意味着它不会等到剧集结束时才更新预期的未来奖励估计值(V),它只会等到下一个时间步来更新价值估计值。
(旁注)TD 方法的独特之处在于,它是由相同量的时间连续估计值之间的差异驱动的。时差学习的起源部分来自动物心理学,特别是次级强化物的概念。次级强化物是一种与初级强化物(来自环境本身的简单奖励)配对的刺激,因此次级强化物具有类似的性质。
例如,在 MC 控制中:
V(s) = E [Gt | St = s]且 Gt = Rt+1+ γ Rt+2+…
但是在 TD 控制中:

就像在动态编程中一样,TD 使用贝尔曼方程在每一步进行更新。
下图有助于解释 DP、MC 和 TD 方法之间的区别。

David Silver’s Slides
因此,我们可以以不同的方式考虑增量均值,就好像 Gt 是目标或我们对代理人将获得的回报的预期,但却获得了回报 Q(St,At),因此通过α *(Gt-Q(St,At))将 Q 值推向 Gt 是有意义的。
类似地,在 TD 方法的情况下,瞬时 TD 目标是 Rt+1 +γQ(St+1,At+1),因此 TD 误差将是(Rt+1 +γQ(St+1,At+1) -Q(St,At))
根据不同的 TD 目标和略有不同的实施方式,这三种 TD 控制方法是:
- SARSA 或 SARSA(0)

SARSA update equation
当用 python 实现时,看起来像这样:
- SARSAMAX 或 Q-learning

SARSAMAX update equation
当用 python 实现时,看起来像这样:
- 预期 SARSA

Expected SARSA update
当用 python 实现时,看起来像这样:
请注意,TD 控制方法中的 Q 表在每集的每个时间步进行更新,而 MC 控制方法中的 Q 表在每集结束时进行更新。
我知道我没有像 MC 方法那样深入地解释 TD 方法,而是以一种比较的方式进行分析,但是对于那些感兴趣的人来说,所有 3 种方法都在笔记本中实现了。欢迎您探索整个笔记本,并尝试各种功能,以便更好地理解!
那都是乡亲们!希望你喜欢!
参考资料:
- 强化学习:导论(安德鲁·巴尔托和理查德·萨顿合著)
- 大卫·西尔弗的幻灯片(RL 上的 UCL 课程)
在火星上玩扑克:人工智能如何掌握游戏
或者,万亿手的边缘
德克·克内梅尔和乔纳森·福利特

Figure 01: Poker, the quintessentially human game of gamblers and dreamers
[Illustration: Le Poker (Poker) by Félix Vallotton, 1896 woodcut, National Gallery of Art, Open Access]
扑克似乎是典型的人类游戏。从表面上看,扑克是一种比象棋或围棋更随意、更社会化、更平易近人的策略游戏。赌博与扑克密不可分,一方面是职业赌徒,另一方面是大量的娱乐梦想家。这也是一种很受欢迎的家庭游戏,朋友和敌人都可以聚在某人的家里——或者在线扑克大厅——进行常规游戏。扑克可以成为美国游戏的一个很好的例子,在世界其他许多地方也同样受欢迎。
扑克需要我们的原始智力,它也需要更软的技能,如阅读其他玩家,虚张声势,并以心理方式强加你的意志。尽管像国际象棋和围棋这样的游戏是完美的信息游戏——在这种游戏中,机器智能可以强行通过——但扑克将简单的数学和百分比与人类特有的各种感知技能结合在一起。或者至少我们认为。事实证明,那些软性的人类技能可能并不重要。
【2017 年 1 月,人工智能增强软件程序 Libratus 在 12 万手扑克比赛中击败了四名职业扑克玩家。这是 apex 战略游戏中软件对顶级人类的最新征服,紧随其后的是1997 年战胜加里·卡斯帕罗夫和alpha go 2016 年战胜李·塞多尔。然而,与许多人的预期相反 Libratus 需要根据每个对手来改变它的玩法——人工智能反而通过完全忽略他们的个体而击败了扑克职业选手。计算机没有考虑到对一些人类专业人士的游戏理论如此核心的心理学——屡试不爽的策略,如寻找“线索”和了解玩家。它只是从大量可能的选择中选择出最好的棋,遵循一种策略,一遍又一遍地无情运用。
看到隐藏的信息
我们采访了 Libratus 的联合创始人诺姆·布朗(Noam Brown),他让我们深入了解了人工智能是如何学习扑克游戏并击败人类职业选手的。“人工智能传统上在处理完美信息游戏方面非常成功,如国际象棋或围棋,其中双方都知道任何时候正在发生什么,”布朗说。“在围棋或象棋比赛中,你需要的所有信息都可供你做出决定。但是在像扑克这样的游戏中,有隐藏的信息。你不知道对手手里拿着什么牌。所以,你总是不得不在不确定的情况下行动,不知道他们的策略是什么,或者你处于什么样的情况。这对 AI 来说尤其具有挑战性。这让事情变得更加困难。这使得计算策略变得更加困难。”
“所以很长一段时间以来,人工智能的研究人员只是忽略了这个问题,”布朗说。“他们专注于这些完美的信息游戏,如国际象棋和围棋。只是假装扑克之类的问题并不存在。这实在令人不满意。所以,我们中的一些人,包括我自己,认为这是一个我们应该解决的问题。因为事实是,大多数真实世界的情况都涉及隐藏的信息。你可以制造一个会下棋的人工智能,但如果涉及到隐藏的信息,它在现实世界中就没那么有用了。”
那么 Libratus 是如何工作的呢?“这些算法试图找到所谓的纳什均衡,”布朗说。“它试图找到一个完美的策略。纳什均衡被证明存在于任何博弈中。特别是,在两人零和游戏中,如果你根据纳什均衡策略进行博弈,那么无论你的对手做什么,你都保证不会输。这就是人工智能试图寻找的东西。它并没有试图适应它的对手。它在努力寻找这个纳什均衡策略,并按照它来博弈。因为它知道,如果它在玩这个纳什均衡,那么无论它的对手做什么,它都不会输。”
“我认为纳什均衡存在的想法,扑克中存在这种完美策略的想法让很多人感到惊讶。但是如果你想一分钟,你可以在更小的游戏中看到这一点。例如,在石头、布、剪刀中。我们都知道纳什均衡策略是什么:扔石头,布,剪刀各三分之一概率。如果你这样做,如果你只是采取这种策略,那么无论你的对手做什么,你都不会在预期中失败。在石头、剪子、布的例子中,你不会在预期中获胜。不,你只是要配合期望。”
“但在像扑克这样复杂的游戏中,如果你能够运用纳什均衡策略,那么你的对手很可能会犯错。但是采用纳什均衡策略,实际上你会赢,因为你采用的是完美策略。所以,我们不是在努力适应对手。事实上,在比赛过程中,我们从来不看对手的牌,比如。我们从来不关心对手是谁。不管对手是谁,我们总是采用相同的策略
不过,为了让纳什均衡策略发挥作用,你需要一个人。扑克界有一句谚语,如果你环顾牌桌,却找不到那个笨蛋,那么他很可能就是你。在人工智能扑克玩家的世界里,人类是吸盘,因为无论我们如何努力,我们永远不会达到完美的纳什均衡。然而,机器可以。

Figure 02: In a world of AI poker players, the humans are the suckers
[Photo: “Gambling” by Chris Liverani on Unsplash]
尽管 AI 在国际象棋和围棋上击败了顶级职业选手,但 Libratus 的优势还是让扑克界感到惊讶。“我认为这对玩家来说可能有点丢人,”本·萨克斯顿说,他是新奥尔良的作家、教师和严肃的扑克玩家,也是一名扑克记者。“他们有点措手不及。”就在两年前,扑克职业选手击败了另一个玩人工智能引擎的扑克玩家克劳迪奥,但显然没有占据主导地位。" Libratus 显然比它的前身改进了许多."
“对于一台计算机来说,打倒一个强大的玩家,打倒一个精英玩家是一回事,”萨克斯顿说。“Libratus 令人信服地击败了世界上最好的四名单挑无限注德州扑克玩家,这是另一回事,这就是所发生的事情。我不认为这是一件令人失望的事情。我认为这可能最初有点令人惊讶和谦卑,但最终我认为它再次肯定了那些强烈相信所谓的 GTO 方法的人。"
GTO 是“博弈论最优”的首字母缩写,是一种更新的、越来越占主导地位的扑克方法。你想成为职业扑克手?在一个以大数据为动力的 GTO 世界里,成为职业扑克玩家的梦想可能已经破灭。Libratus 基本上验证了 GTO 方法是击败顶级职业选手的最佳方法。然而,情况并不完全是这样:“我认为这很大程度上取决于你的对手是谁,水平如何,”萨克斯顿说。“我认为,对于你是否想在最高极限下玩 GTO 平衡策略,没有太多争议。我认为,如果你偏离了最优策略,你将会与真正精明的对手交手,他们会利用你。你真的想避免这种情况。”
这是一个重要的细微差别:Libratus 可能事实上更擅长于优化它对最好的玩家的结果,而不是更差的玩家。当玩家的技能下降时,在那一刻做出反应的开发策略是最好的,这是 Libratus 无法做到的。这为我们提供了指导,让我们了解机器在其他环境中的行为,以及它们的优势和局限性。
从经验中学习
Libratus 由机器学习驱动,软件正在构建自己的策略和游戏概念,而不是由人类指导。“Libratus 这样做的方式是通过自我游戏,”布朗说。“这实际上与人类学习玩游戏的方式非常相似。你从经验中学习。所以人工智能一开始对游戏一无所知。它完全随机地玩,在那场游戏中,它玩自己的副本,进行万亿次迭代——例如,万亿手扑克。”万亿只手是一个可量化的提醒,表明今天的人工智能在理解事物方面比人类有优势。我们可能有自己的优势,如更广泛的上下文和更多样的工具集,但在某种程度上,练习万亿次的能力对机器来说是一个不可思议的优势。
Libratus AI 有三个组成部分。布朗描述了每个组成部分如何有助于 Libratus 在竞争中击败高技能人类选手:“现在第一个组成部分是…试图估计这个纳什均衡。现在我们没有通过这个自我博弈部分找到一个完美的纳什均衡,但是我们得到了纳什均衡的一个粗略的近似值。我们在比赛开始前就离线做了,所以我们带着人工智能认为非常强大的策略参加比赛。但它并不完美。”
Libratus 的第二个组成部分是比赛期间的实时平衡计算。Libratus 能够在实时向前移动时锐化其边缘。“例如,当[Libratus]实际上与一个人玩特定的一手扑克,并且它在第三轮下注时,它会实时计算它在那一刻所处情况的纳什均衡的更接近近似值,”Brown 说。有了这个实时组件,Libratus 可以为特定情况确定一个更好的策略,但这个策略必须符合它为游戏整体计算的蓝图。“我认为这实际上是 Libratus 的重大突破,”布朗说。“以前没有人真正找到在不完全信息博弈中进行实时均衡计算的有效方法。”
Libratus AI 的第三个组件使它能够从对手那里学习——但也许是以一种不寻常的方式。“现在,我想说清楚,这不是适应对手,”布朗说。“它没有试图以任何方式利用对手。…因为人工智能不是完美的,它不能计算完美的纳什均衡。在博弈树中有一些部分…不同的情况下它都处于次优状态。这是一个问题,因为如果它玩得不太好,人类就有机会在这种情况下利用它。”当然,这也是人类对手在整个比赛中不断尝试的:寻找 Libratus 的弱点,以便他们可以利用人工智能。因此,在每天比赛结束时,Libratus 会回顾人类试图利用它的情况,并为这些情况制定更好的策略——更接近纳什均衡。“第二天,[Libratus]在这些情况下会有一个更好的策略,所以这些点的可利用性会小得多,”Brown 说。“所以这导致了一种猫捉老鼠的游戏。因为每天人类都会试图找到弱点并加以利用,每天结束时,人工智能会修复这些弱点,为第二天做准备。随着比赛的进行,人工智能中的这些洞随着时间的推移越来越小。人类利用人工智能的机会就更少了。”
在火星上玩扑克
因为 Libratus 以自己的方式学习扑克,不像任何人可能学习的那样,AI 享受了一些积极的意想不到的后果,重新想象了游戏。“因为[Libratus]是从自我游戏中训练出来的——从零开始,在对游戏一无所知的情况下,与自己的副本进行游戏——它从来不看人类数据。布朗说:“与人类的游戏方式相比,它采用了非常不同的策略。当 Libratus 开始一场比赛时,人类对手感觉他们在玩外星人。“这就像和一个在火星上学会打扑克的人比赛,”布朗说。

Figure 03: It was like playing somebody who learned to play poker on Mars
[Photo: “Poker Night” by Michał Parzuchowski on Unsplash]
例如,Libratus 下注的方式与人类惯例大相径庭。Brown 说:“在人类扑克世界中,你通常只下一小部分赌注。“因此,如果底池中有$300,那么您可能会下注$150。你可能会下注 300 美元。而且,也许在一些非常罕见的情况下,你最多可以下注 500 美元。”但 Libratus 没有将赌注限制在小数金额,而是选择下注 3 倍、5 倍,有时甚至是 100 倍。Brown 说:“[Libratus]在 200 美元的底池中投入 20,000 美元没有问题。”。“这对人类来说是一个巨大的冲击。这对他们来说尤其令人沮丧,因为他们可能有一手非常非常强的牌,可能是第二好的牌。突然,机器人在 500 美元的底池中下注 20,000 美元。这个机器人基本上是在说,“我要么在虚张声势,要么我有最好的牌。”“Libratus 的下注策略阻碍了人类玩家,迫使他们花大量时间考虑自己的反应。“你可以看到人类有时需要五到十分钟来做决定,”布朗说。“这让他们非常沮丧。这让我非常满意。”
“Libratus 是在虚张声势,”布朗说。“它当然学会了虚张声势,因为你必须虚张声势才能玩好扑克。但是它选择虚张声势的情况与人类会做的事情非常不同。…事实上,与[Libratus]对战的人类告诉我们,他们将采取其中一些策略,并开始在自己的游戏中使用。特别是,这些大的过度下注,在某些情况下会对底池下巨额赌注。他们说,这是他们将来与人类比赛时要做的事情。”
Libra tus AI 的进化
一般来说,人工智能只能做很少的事情,然而像开发 Libratus 这样的团队正在试图创造适应性更强的引擎,这些引擎可以处理与多个领域相关的某些类型的因素。即使我们将在狭窄的人工智能世界中待上一段时间,也不奇怪会有一股力量推动创造不仅仅是一技之长的引擎。
Brown 分享了更多关于 Libratus 团队的计划:“我们正在开发的东西并不是专门针对扑克的。它们适用于任何不完美信息博弈。…金融市场是一个不完美的信息游戏,谈判也是,拍卖也是,军事形势也是。因此,我们看到了未来将这些技术应用到其他领域的巨大潜力。”
将 Libratus 扩展到谈判和金融市场等其他领域存在挑战。当然,扑克是一种零和游戏。你赢的任何钱,都是从你的对手那里拿的。“那些游戏(比如扑克)有很好的特性,使得计算均衡解更容易。…但是,如果你正在处理一个谈判,你有双赢的结果。这不是零和游戏。”
“理解这一点很重要。重要的是要明白,在这场游戏中,你和你的对手都有可能赢。所需的技术与零和设置略有不同。所以,弄清楚如何将我们拥有的技术应用到这个总的求和设置中,这是我们短期内要做的事情之一。我认为这是需要克服的较小的障碍,”布朗说。“更大的障碍是,当你从游戏转向现实世界时,你的策略和收益并不明确。”
布朗描述的双赢场景需要背景——也许是大量的背景——才能成功解决。到目前为止,人工智能在上下文方面很糟糕。它以我们无法做到的方式使用数据,但无法应用更广泛的背景,更不用说更细微的个性方面了。
然而,随着不同环境的优化或自动化,我们开始理解我们所认为的创造力确实可以被机器复制。赢得像扑克这样的游戏的方法通常回到数学,而不是人类的判断力。甚至作为我们人性概念核心的东西也越来越显得虚幻。在未来的几十年和几个世纪,由于科学的发现和思维机器的成就,我们对人类的概念可能会经历一次彻底的重新思考。
Creative Next 是一个播客,探索人工智能驱动的自动化对创意工作者,如作家、研究人员、艺术家、设计师、工程师和企业家的生活的影响。本文伴随 第一季第五集——AI 如何解决扑克 和 第一季第六集——扑克、人工智能、学习。
玩物体检测
几个月前,谷歌宣布开放图像数据集(OID)的第五版,以及一些用于物体检测任务的预训练模型。让我们玩其中一个模型,看看它在视觉上的表现如何。

Photo by Icons8 team on Unsplash
介绍
粗略估计, OID 的最新版本由 601 个类别上的 15M 标注包围盒、350 个类别上的 2.5M 实例分割、20k 个类别上的 36M 图像级标签、329 个关系的 391k 关系标注组成。这是一个相当大的数据量,肯定能够实现许多计算机视觉研究的可能性。
将我们的焦点放在对象检测任务上,目标不仅是将图像标记为具有特定对象,而且以高置信度检测对象周围的边界框。一个你可能听说过的著名方法是 YOLO,目前在第三版。

Image from Open Images Dataset V5
如前所述,一些预先在 OID 训练的模型已经发布。最新的是在数据集的第 4 版上训练的,在这篇文章中,我将使用最好的(就地图而言)和较慢的(就速度而言)。这些模型和许多其他模型可以在 Tensorflow 检测模型库中找到。
需要强调的是,这篇文章的目的是玩物体检测,即对输出进行视觉评估。此外,我想为那些对这一领域感兴趣的人提供一个易于理解的代码。然而,讨论研究领域或深入算法细节超出了本文的范围。也许我可以在另一篇文章中这样做。😃
密码
我会按照我的 jupyter 笔记本,使事情更容易显示。您可以随意运行它或者自己实现代码。请记住,一些代码片段使用在前面的代码片段中实现的函数,因此出现的顺序很重要。
这篇文章中提到的所有文件都可以在我的 GitHub 中找到。检查这个回购出!
设置环境
假设你熟悉 Anaconda ,我准备了一个 yml 文件,这样你就可以像我一样快速设置环境,甚至是相同的库版本。这是文件的内容。
name: object-detection-oidv4
channels:
- conda-forge
- anaconda
dependencies:
- python=3.6.9
- numpy=1.16.5
- tensorflow=1.13.1
- matplotlib=3.1.1
- pillow=6.1.0
- pandas=0.25.1
- jupyter=1.0.0
- ipython=7.8.0
一旦有了文件,就执行下面的命令来创建环境并安装所有必需的依赖项。
**$ conda env create -f environment.yml**
安装完成后激活环境。
**$ conda activate object-detection-oidv4**
一切就绪!让我们深入一些 python 代码。
验证 TensorFlow 版本
由于 Tensorflow 2.0 最近的版本,让我们确保您使用的是构建该代码的相同版本——如果没有出现 ImportError ,一切正常。如果您使用我的文件创建了环境,就不必担心这个问题。
Validating TensorFlow version
配置绘图
如果您使用的是 jupyter 笔记本,请配置 matplotlib 以显示内嵌图像。否则,只进行导入。
Configuring plotting
导入一些常用库
我们需要一些库来处理文件,所以这里是导入。我还导入了 numpy ,因为我们将在整个笔记本中使用它。
Importing some common libraries
获取模型文件
在这篇文章中,我将使用一个有趣的模型,命名为:
faster_rcnn_inception_resnet_v2_atrous_oidv4
如果你想检查模型配置,这里有文件。
如果压缩的模型文件还不存在,现在让我们下载它。其他机型只要兼容 OID 就可以玩;否则,它将需要更多的代码更改。
Getting the model file
获取盒子描述文件
我们还需要盒子描述文件。正如您将看到的,推理代码将返回一些对应于类的数字(1 到 601)。因此,我们需要这个描述文件将结果数字与人类可读的类名进行映射。类似于模型文件,如果文件存在就不会下载。注意,每个类都被当作一个对象,比如动物和人。所以,如果我把一个人当作物品,请不要生气。😃
Getting the box description file
如果你对 OID 现有的 601 类物体检测感兴趣,看看这张漂亮的树状图。
获取一些测试图像
我已经为那些只想运行它并查看结果的人准备了笔记本,并可能进行一些定制。事实上,它是基于 Tensorflow models 库中的一些代码,但是我用我的方式修改了它,使它简单易懂。因此,继续简化事情,下面的代码将下载一些测试图像。请随意更改 URL,这样您就可以用其他图像进行测试。
Getting some test images
提取模型文件
既然所有必要的文件都已准备就绪,让我们从压缩的模型文件中提取文件,这样我们就可以访问推理冻结图。这个文件是为推理而优化的,,即所有的训练内容都被删除。
Extracting the model files
将盒子描述加载到字典中
让我们创建一个字典来映射类,其中键只是索引,值是另一个字典,它包含类 id 和名称,分别作为键和值。
Loading the box descriptions into a dictionary
检查一些标签
这就是类的映射方式,但是,我们不会使用 id。相反,我们将使用索引使它更容易。举例来说,如果我们得到类#100,那么它对应于奶酪。
Checking some labels
从文件加载冻结模型
现在我们需要从冻结的模型文件中创建我们的图表。
Loading the frozen model from file
检查原始测试图像
下面的代码片段定义了一个 helper 函数,它显示以文件名为标题的图像。然后,它会检查每一张测试图像并显示出来。
Checking original test images
对我挑选的图片感到好奇吗?他们在这里:



Test images taken from Flicker
狗和猫。我觉得很经典。
定义一些助手代码
我们需要更多的辅助代码。以下是我是如何实现它们的——我添加了一些注释,以便更容易理解。
Font and color constants
ObjectResult class
Converts a PIL image into a numpy array (height x width x channels)
Processes classes, scores, and boxes, gathering in a list of ObjectResult
Draws labeled boxes according to results on the given image
Calculates a suitable font for the image given the text and fraction
Draws the box and label on the given image.
运行推理
一切就绪!让我们对所有测试图像进行推断。
Running the inference
显示结果
现在让我们根据结果画出带标签的包围盒并显示图像。
Showing the results
正如我们在下面的图片中所看到的,使用该模型运行推理在以高置信度检测感兴趣的对象及其相应的边界框方面都提供了相当好的结果。然而,物体的位置和干净的背景很有帮助,这使得这项任务不那么具有挑战性。

Detected objects: Cat

Detected objects: Dog

Detected objects: Cat and Dog
更具挑战性的结果
在现实世界中,您可能会遇到这样的情况:对象与其他对象部分重叠,或者太小,甚至处于不利于识别的位置,等等。因此,让我们对下面的图片进行推理并检查结果。





Photos taken from Unsplash
女人和啤酒:探测到一堆物体。然而,我选择这张图片只是因为啤酒类,不幸的是,还没有检测到。不管怎样,我觉得结果都很好。

Detected objects: Jeans, Clothing, Sunglasses, Tree, Human face, Woman, and Dress
街道:这一条基本上已经检测到车了。我希望得到一个建筑,也许还有交通标志。也许逐渐模糊的背景使它变得更难,但是那些在左边的交通标志肯定可以被一些合理的信心检测到。

Detected objects: Car, Wheel, and Taxi
由于光线条件的原因,我对这张照片没抱太大期望。然而,三顶看得见的软呢帽中有两顶被发现了。其他可能被检测到的是帽子和太阳帽子但是让我们放它一马。

Detected objects: Fedora and Cowboy hat
乡下房子:我所期待的一切都被检测到了。有人可能会说门本可以被检测到,但门是开着的,我们看不到——模型也看不到。印象非常深刻!

Detected objects: Window, Tree, Bench, and House
果实:我所期待的一切都被检测到了。由于不支持这样的类,所以对覆盆子没有操作。草莓则是支持的,但是姑且认为他们的立场并不偏向于检测。

Detected objects: Grapefruit, Juice, Orange, Banana, and Pineapple
结束语
在这篇文章中,我展示了如何使用 Tensorflow 进行物体检测。从许多方法和大量预先训练的模型中,我选择了一个在开放图像数据集(一个巨大的带标签的图像数据集)上训练的特定方法。尽管我们已经看到了非常好的结果,但我还是会让您了解一些关于该模型的要点:
- 如果应用要求实时处理,在 CPU 上运行太慢。如果你在 GPU 上尝试,请让我知道结果。
- 您受限于模型提供的类。包括一个新的类将需要收集标记的图像并重新训练模型。而且,我朋友,在 OID 重新培训这种模式需要大量的时间。
- 如果您想在应用程序中使用这个模型,您需要评估每个感兴趣对象的性能。有些职业肯定比其他职业表现得更好。此外,分数阈值在决定检测或不检测什么时起着重要作用。最后,您还需要考虑忽略一些类,因为可能不是所有的类都与您的应用程序相关。
今天到此为止。感谢阅读!🙂
基于 KNN 和朴素贝叶斯分类器的 Spotify 播放列表分类

by Spencer Imbrock on Unsplash.com
有一天,我觉得如果 Spotify 在我喜欢一首歌的时候帮我挑选一个播放列表会很酷。这个想法是当我的手机锁定时,触摸加号按钮,Spotify 将它添加到我的一个播放列表而不是库中,这样我就不会进入应用程序并试图选择一个合适的播放列表。这样,我就不必在我所有的播放列表中选择一个播放列表,我只需要把它交给算法。然后,我意识到这对机器学习爱好者来说是一个很好的兼职项目。毕竟,我开始这个项目是为了避免解锁我的手机,并思考三秒钟,这对我个人来说不是最佳的解决方案。
你可以在 https://github.com/n0acar/spotify-playlist-selection 找到 Jupyter 笔记本
1-从 Spotify Web API 抓取数据
这实际上是一切开始的地方。我找到了 Spotify Web API 和 Spotipy 框架。通过两者的结合,我们将能够从官方 Spotify 数据中提取有用的功能。
[## 欢迎来到 Spotipy!- spotipy 2.0 文档
编辑描述
spotipy.readthedocs.io](https://spotipy.readthedocs.io/en/latest/) [## Web API |面向开发者的 Spotify
简单来说,你的应用通过 Spotify Web API 接收 Spotify 内容。
developer.spotify.com](https://developer.spotify.com/documentation/web-api/)
在向 Spotify Web API 发送请求之前,您需要安装并导入以下依赖项。
import spotipy
import spotipy.util as util
from spotipy.oauth2 import SpotifyClientCredentials
为了访问 API,你需要为你的应用程序创建特殊的代码。前往 https://developer.spotify.com/dashboard/的,点击“创建客户端 ID”或“创建应用程序”,获取您的“客户端 ID”和“客户端密码”。之后,重定向 URI 必须被更改到你在 Spotify 应用程序的设置中决定的任何页面。
client_id= "YOUR_CLIENT_ID"
client_secret= "YOUR_CLIENT_SECRET"
redirect_uri='[http://google.com/'](http://google.com/')
接下来,从现在开始,从 Spotify 文档中声明你的范围,“sp”将是你访问 Spotify 数据的密钥。
username='n.acar'
client_credentials_manager = SpotifyClientCredentials(client_id=client_id, client_secret=client_secret)
scope = 'user-library-read playlist-read-private'
try:
token = util.prompt_for_user_token(username, scope,client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri)
sp=spotipy.Spotify(auth= token)
except:
print('Token is not accesible for ' + username)
然后,您可以使用 sp 变量做很多事情。它是你的 Spotify 对象。例如,你可以提取你的曲库,获取一个播放列表的数据,或者更好,一个用户的所有播放列表。
songLibrary = sp.current_user_saved_tracks()playlist = sp.user_playlist(username, playlist_id='6TXpoloL4A7u7kgqqZk6Lb')playlists = sp.user_playlists(username)
要找到播放列表或曲目的 ID 号,基本上使用共享链接并从中获取代码。API 提供的所有东西都是 JSON 格式的,你可以从中提取你需要的信息。

对于 Spotify 数据的各种用法,你可以浏览 Spotify 和 Spotify Web API 的文档。有许多方法和数据类型可能很适合用于项目。
2-特性和洞察力的可视化
当人类听一首歌时,他们会感知到它的几个方面。一个人可能没有困难确定一个轨道是声学的还是电子的。大多数有某种音乐感觉的人可以说很多关于音乐的东西,这些东西在电脑上很难找到。然而,Spotify API 提供了一些关于平台上任何曲目的有用的功能。它们中的大部分都在 0 到 1 之间标准化,所以很容易处理。
下面是两个 Spotify 播放列表的对比,蓝色代表“古典精华”,红色代表“Get Turnt”(Spotify 的一个说唱播放列表)。

Get Turnt (RED) — Classical Essentials (BLUE)

从这些情节中,你可以看到这两个流派根据音乐的性质表现出截然不同的模式。虽然 rap 播放列表在可跳舞性、能量、响度、语速、效价和节奏方面具有更高的值,但是古典音乐播放列表具有预期的更高的声学和乐器价值。


3-应用机器学习算法
我使用了两种算法,即 K-最近邻和朴素贝叶斯分类。事实上,我只想从头开始实现几个基本算法,我意识到这些是最方便的开始。
K-最近邻分类:K-最近邻(KNN)算法是一种基于距离概念利用现有数据和新数据之间的特征相似性的分类技术。
KNN
朴素贝叶斯分类:朴素贝叶斯分类是一种计算每个特征的概率,就好像它们是独立的,并根据贝叶斯定理确定概率最高的结果的方法。
Naive Bayes Classification
两者之间的统计差异非常明显。我从我的功能列表中排除了速度和响度,因为它们不包含太多关于流派的信息,而且非常分散。我使用播放列表中的前 35 首歌曲进行训练,其余歌曲进行测试。由于音乐类型的主观性,测试集可能不是最客观的。由于这两个播放列表都是由 Spotify 创建的,我认为这是测试算法最安全的方式。幸运的是,我们的目标是对说唱和古典音乐进行分类,统计数据没有任何错误,我们获得了 100%的准确率。
即使所有的歌曲都被归入了正确的类别,有一首古典歌曲在使用 KNN 时也几乎被归入了错误的类别。被称为E 大调小提琴协奏曲 BWV 1042:I .快板。当使用朴素贝叶斯分类时,它甚至不接近说唱歌曲,但它仍然是古典音乐播放列表中最接近说唱的东西。起初,我试图找到距离最近的说唱歌曲,这是由未来粉碎的,所以这有点令人惊讶。然后,我检查了这首古典歌曲的特点,它在我看来确实有点问题。(听一听吧!至少“仪器性”特征应该大于零。)

然而,这并不是一个困难的任务。为了让事情变得更复杂,更好地评估算法,我决定增加一个播放列表,即“摇滚拯救女王”。


Accuracy Rates (%)
NBC 看起来比 KNN 更稳定。上面提到的那首古典歌曲已经不在古典音乐播放列表中了,因为有新的摇滚歌曲。尽管如此,NBC 没有犯任何错误。然而,在 Get Turnt 播放列表中,事情有点不同。这次只根据最近的邻居来决定流派对于说唱歌曲来说不是一个好主意。最混乱的说唱歌曲是“Wifisfuneral”和“Robb Bank$”的“EA”,最后在 rock bucket 结束了。
为了让事情更具挑战性,我添加了一个新的播放列表,“咖啡桌爵士乐”,由 Spotify 创建。

在向您展示结果之前,我想添加算法在最终模型中使用的四个播放列表的更新可视化。当我调查结果时,我将在最后一步参考这些。(摇滚拯救女王和茶几爵士分别用黄色和橙色表示。)

Get Turnt (RED) — Classical Essentials (BLUE) — Rock Save the Queen (YELLOW) — Coffee Table Jazz (ORANGE)


Accuracy Rates (%)
从这些,我们清楚地看到,古典音乐和爵士音乐流派在统计上彼此接近,而摇滚和说唱流派也是如此。这就是为什么当摇滚乐被引入时,新出现的歌曲没有引起古典歌曲的混乱,而是损害了 Get Turnt 的准确率。当咖啡桌爵士乐被引入系统时,也发生了同样的情况。这一次,KNN 的古典音乐分类法受到了严重的伤害。要知道古典音乐只是和爵士乐混淆,51%的准确率几乎是随机的。这表明,只考虑最近的邻居对分类流派不起作用。至少,可以肯定地说 NBC 要好得多。
NBC 不适合古典和爵士音乐,也不适合说唱和摇滚。我认为这是因为人们区分古典歌曲和爵士乐的方式是对传统上使用的不同乐器进行分类。即使他们之间在可跳性方面有一点差异,对一些歌曲来说这也是不够的。其他功能与预期的几乎相同。工具性;然而,几乎完全相同,因为它可以看到在蜘蛛图。是的,他们显然都是工具性的,然而这就是这个特征缺乏人类感觉的地方。也许,Spotify 提供另一种数据类型,说明歌曲中具体使用了哪些乐器或哪些类型的乐器,也可能解决这个问题。我认为 98.44%的准确率对于其他两个播放列表来说是很棒的。NBC 只错过了每个播放列表中的一首歌曲。他们是 Travis Scott 的观星, Travis Scott 认为是摇滚,而遥远的过去被所有人认为是说唱。(如果你听了他们的话,请分享你的评论。)
4-结论
这个项目的目的是创建给定播放列表的未确定流派,并相应地对新歌进行分类。它应该适用于任何包含相同类型歌曲的播放列表。我选择使用 Spotify 创建的四个带有明显流派标签的播放列表。我使用了两种不同的算法,如帖子中提到的 KNN 和 NBC。NBC 的整体表现优于 KNN。该系统可能遇到的挑战是区分两个具有非常接近的风格的不同播放列表。我还意识到,Spotify 提供的功能在某些情况下可能还不够。
不要犹豫提出问题并给出反馈。音乐,流派,总体来说,这部作品是主观的。我很感激任何关于结果的评论,因为这个话题是公开讨论的。
干杯,
Nev
给我更多的内容和支持关注!
请注意性别工资差距

TL;DR:性别薪酬差距是一个真实的现象,来自英国性别薪酬差距服务的数据告诉我们一个重要的故事,男女是多么不平等,尤其是在职业发展方面。
性别平等是一个备受争议的重要话题,它理应如此。随着我们社会的发展,妇女在我们社会中的角色也在发展。然而,并不是我们社会的所有时间规范都跟得上变化。在本文中,我们将关注性别(不)平等问题的一个子树,即男女之间的性别薪酬差距。我们会问一些重要的问题,并根据英国政府提供的性别薪酬差距数据来回答。
性别薪酬差距到底是什么?
在继续讨论之前,我们应该就性别薪酬差距的定义达成一致。也许你心目中对性别工资差距的定义与实际情况有所不同。如果员工的性别不同,你可能会认为向做同样工作、有同样表现的不同员工支付不同的工资/奖金是性别工资差距。然而,这就是不平等报酬的定义,不平等报酬不一定是性别之间的。
报酬不平等是指从事相同(或相似)工作的男女报酬不同。自 1970 年以来,不平等报酬在英国一直是非法的。 【参考】
根据英国性别薪酬差距网站 性别薪酬差距 是整个劳动力大军中所有男女平均时薪的差异 。例如,如果女性在一个组织中比男性做更多的低收入工作,那么性别工资差距通常会更大。
性别薪酬差距 是劳动力中所有男性和女性的平均时薪之差
就这一定义达成一致非常重要,因为这篇文章恰恰将关注上述性别薪酬差距的定义。薪酬不平等是一个大问题,大多数时候,人们笼统地提到性别薪酬差距。他们大多指的是不平等的报酬。
那么,问题是什么呢?
既然我们对性别薪酬差距有了明确的定义,现在我们需要回答几个好问题。毕竟,这可能是你读这篇文章的原因,对吗?
- 男性的平均收入/中位数比女性高吗?
- 如果是,他们多赚多少?
- 哪些部门雇佣女性较多?
- 这些行业是否为女性提供了足够多的晋升和加薪机会?
- 哪些部门是女性最难晋升的?
- 对于一家我们不太了解的公司,有没有办法预测性别薪酬差距?我们能建立一个统计模型吗?
现在我们有几个问题,现在我们的目标是有条不紊地回答,有条不紊意味着我们需要数据!
给我看看数据!
在英国,自 2017 年以来,拥有 250 名以上员工的大型雇主必须在自己的网站上公布性别薪酬差距数据,并提交给政府性别薪酬差距网站。这一数据公布在性别薪酬差距网站上。截至 2019 年 10 月,数据集中有超过 21000 家公司。
解释性别薪酬差距数据
数据本身值得一个快速的解释,因为我们将在我们的帖子的其余部分使用它。
工资/奖金数据:
- 所有男性全薪相关雇员的平均时薪为 15.25 英镑,所有女性全薪相关雇员的平均时薪为 13.42 英镑的雇主,其平均性别薪酬差距为 12.0%(
*DiffMeanHourlyPercent*)。
四分位数计算:
- 雇主有 322 名全薪相关员工,按照最低小时工资率到最高小时工资率对他们进行了安排,将名单分成四个四分位数,并确保相同小时工资率的员工在跨越四分位数界限时按性别均匀分布
- 在下四分位数的 81 名雇员中,48 名是男性,33 名是女性。这意味着 59.3%是男性,40.7%是女性。
- 在中下四分位数的 80 名雇员中,28 名是男性,52 名是女性。这意味着 35%是男性,65%是女性。
- 在中上四分位数的 81 名雇员中,40 名是男性,41 名是女性。这意味着 49.4%是男性,50.6%是女性。
- 在上四分位数的 80 名雇员中,58 名是男性,22 名是女性。这意味着 72.5%是男性,27.5%是女性。
如果您需要更多详细信息,请参阅第 11 页 上的 管理性别薪酬报告、计算部分。
薪资对比
在这 21000 家公司中,男性的平均收入高于女性,这可能并不令人惊讶。下图显示了所有公司中性别间平均和中位数工资差异的分布情况。

Distribution of Hourly Mean and Median salaries
两张图的重心都是正的,这告诉我们,在大多数公司里,男性比女性挣得多。图形的形状略有不同;中位数工资分布有一个右偏。在大多数公司中,平均工资差距略大于零。在数据集中 87%的英国公司中,男性的平均工资高于女性。同样,在 77%的英国公司中,男性的平均工资高于女性。
在 87%的英国公司中,男性的平均工资高于女性。
下图的下半部分显示了平均工资差异远大于平均工资差异的公司(每个点代表一家公司)。平均值和中位数之间的差异告诉我们,典型的员工(男性或女性)获得相似的工资,即典型员工的性别差异较小,但很少有男性员工挣得这么多,并扭曲了平均工资差异。

Companies with higher mean salary and smaller median salary difference
当我们按行业来看这些公司时,我们看到体育俱乐部脱颖而出。这些体育俱乐部付给少数男性雇员(可能是运动员)的工资比女雇员高。

Sport Clubs has a few very big salary earner men and many regular earner men and women
女性在事业上往上爬有多容易?
我们可能都有一种直觉,认为女性在职业发展方面的机会通常较少。可能有很多原因,包括女性对她们年幼的孩子承担了更多的责任,产假,提前退休..等等。确保制定政策,让妇女加入各个领域的劳动力队伍,并给予足够的晋升机会,对于缩小男女薪酬差距非常重要。
让我们来看看女性在四个不同收入区间的比例分布,低收入、中低收入、中高收入和高收入。他们每个人都被平均分配,这意味着他们在每个桶中有相同数量的员工。
这个数据讲述了一个有趣的故事。下图显示了收入最低的 25%的员工的性别分布情况:

在大约 21000 家公司中,收入最低的**女性在超过 57%的公司中占大多数。我们大概可以推测几个原因:
- 女性职业生涯的起点可能是收入较低的工作。记住这张图是同一家公司中收入最低的人的分布。它没有说他们是否做同样的工作得到不同的报酬,它只是告诉了最低收入者的比例。
- 女性可能无法晋升到收入更高的工作岗位,并且收入一直低于男性。这里不讨论原因,因为我们这里的数据对此一无所知。
- 女性可能会在同一家公司更长时间地从事低收入工作。
上面的每一个要点都是有效的研究方向,但是,我们没有数据来回答这些问题。因此,我们在这一点上停止进一步的推测。
下面是剩余的三个图表:
- 中下四分位数的性别分布

- 中上四分位数的性别分布

- 最高四分位数的性别分布

趋势很明显,从底部四分之一到顶部四分之一,男性雇员的比例增加了。在收入最高的四分之一人群中,超过 65%的公司男性员工的比例高于女性员工。
超过 65%的公司在高薪员工中男性多于女性。
不同部门的细分情况如何?
上一节的图表显示了男女百分比的总体分布情况。在这里,我们将把它分成不同的部分。我们可能都有一个想法,男人是大多数劳动力。建筑业和采矿业就是两个例子。
让我们检查一下我们的直觉

Women workforce percentage in different sectors
在上图中,有一些观察结果符合我们的预期。正如预期的那样,女性雇员在需要较少体力的类别中占大多数,更多地集中在服务部门。采矿、运输和建筑等需要更多体力的行业较少由妇女从事。
真正有趣的观察是,在所有类别中,女性都不太可能晋升到高收入职位,无论是教育还是建筑。这一趋势在所有部门都是一致的。这是一个强有力的论点,即女性处于劣势,从事高薪工作的女性较少直接造成了性别薪酬差距。这可能意味着女性不容易获得晋升,或者即使获得晋升,她们的工资也比做同样工作的男性同事低(不平等的工资),或者两者兼而有之。
为了了解上述现象的影响,让我们从最低收入阶段到最高收入阶段,按女性劳动力百分比的下降对行业进行排序。

Construction, Mining and Finacial Services are the most difficult for women to gain promotion.
在某些行业,女性比例下降幅度几乎高达 45%。即使在女性明显占多数的教育领域,下降幅度也在 23%左右。
在所有行业中,女性获得最高职位的机会始终较少。从最低到最高铲斗的落差在 14%到 45%之间。
根据图表,建筑和采矿部门的妇女处于最不利的地位,因为她们爬到更高收入阶层的机会更少。
一个有趣的观察结果是,金融和保险活动等软领域也出现大幅下降。再次纯粹猜测原因,金融部门的压力有可能因为怀孕等事情而惩罚妇女,因为工作时间损失直接影响收入,但其他部门如人类健康和社会工作活动更宽容和灵活。
然而,这种持续下降是一个严重的问题,解决这一问题将是解决性别薪酬差距问题的一大步。
预测未知
现在,我们有了大约 21000 家公司的所有数据,包括他们的工资和奖金分配以及其他元数据。如果有一个统计模型可以预测一家公司是否有或大或小的性别差距,即使他们还没有提交性别薪酬差距数据,那将是非常棒的。
为了建立一个模型,我们需要一些具有预测能力的特征,这些特征的组合决定了模型的成功。
让我们来看看性别薪酬差距数据中的三个不同变量:
- 英格兰、威尔士、苏格兰和北爱尔兰。
sic_code:不同的行业有不同的女性比例EmployerSize:大公司有没有消除性别薪酬差距的政策?
我们排除了使用与工资或奖金相关的变量作为预测因素,因为这些变量对于尚未提交数据的公司来说是不可用的。但所有其他信息,如公司规模、部门、地址..etc 是公开的。
State Code
大不列颠由四个不同的国家组成,每个国家都有所不同。因为每个国家都有权力下放的政府,而这些权力下放的政府在其行政区域内有不同的法规和法律。这可能会对女性的工作参与率产生一些影响(不同的儿童保育计划、孕产规则、工作场所的规章制度..等等)。让我们看看是否有任何明显的区别:

% of Women emplyees in top and bottom salary buckets
英格兰、苏格兰和威尔士的两个工资区的女性雇员比例都非常相似,北爱尔兰的女性雇员比例要低得多。state_code单独看起来不像一个强有力的预测者,但有一些预测能力,所以我们保留它。
SIC 代码
正如我们在本文前面看到的,不同的部门有不同的女性员工参与;例如,从事建筑业的女性人数(百分比)比从事教育的女性人数少。因此,SIC 代码本身可能是一个很好的预测器:

Construction has the least % of women in high earners bucket, Human health has the highest
雇主规模
有人可能会认为,与小公司相比,大公司可能会有一个内部政策来调节性别薪酬差距。下面的统计数据不一定告诉我们这是事实,因此这个变量的预测能力可能相当小

Biggest companies (>20000) do not have the highest % of top women earners
模型结构
我没有准确预测最高级别女性员工的百分比,而是决定将其分为三个同等大小的级别,也就是说在各自的级别中#低= #中= #高。截止点是:
- “低”是<22.5%
- “Middle” is between > 22.5%和<50.6%
- “High” is between > 50.6%
请注意,这是我根据数据集任意定义的,将高、中、低设置为其他值(如 66%和 33%)并相应地构建模型完全没问题。我只是想保持标签数量相等。
基于特征建立决策树模型,模型准确率为 54%。**
该模型的准确度分数不是特别高,但这在某种程度上是意料之中的,因为我们的预测因子只是分类(离散)变量,并不像我们之前探索的那样具有很大的预测能力。尽管如此,这仍然比基线值 0.33 有适度的提高,基线值 0.33 只是一个随机的猜测。
当调查个体群体时,我们的模型具有以下成功率:**
- 预测低/不低标签,成功率为%63
- 预测中等/非中等标签,成功率为%64
- 预测高/不高标签,成功率为%82
尽管这个模型有一些预测能力,比随机猜测要好,但在实践中,它还不是很强大。如果我们有更多具有更强预测能力的特征(预测器),那么我们的模型的准确性将会提高。
结论
不幸的是,性别薪酬差距是一个现实问题,我们需要做更多的工作来解决这个问题。联合王国政府了解和量化性别薪酬差距的举措是一个很好的举措。我们发现,总体而言,男性员工的工资水平处于中等偏上水平(然而,这并不意味着做同样工作的男性,比女性挣得多,这就是不平等工资的定义)。我们还发现,女性比男性更难爬上薪酬阶梯,因为公司中的高薪者大多是男性,这是所有行业的一致趋势。没有一个部门的女性雇员看不到她们的代表性从最低工资水平上升到最高工资水平。******
我们还试图建立一个机器学习分类器,根据公开可用的元数据,如行业、公司规模和位置,预测公司的女性员工比例是低、中还是高。
如果你对实际分析感兴趣,jupyter 笔记本在这里有售
感谢阅读…
matplotlib 中的绘图组织—您的一站式指南
本教程的 Jupyter 笔记本可以在我的 Github 页面找到。
如果你正在读这篇文章,可能是因为你同意我的观点,大多数 matplotlib 教程都缺少绘图和数据可视化的一个关键方面。
虽然可能有成千上万的关于如何改变线条粗细或标题大小的教程,但他们似乎都忘记了支线剧情的组织在传达数据所讲述的故事中起着巨大的作用。有时,您可能希望通过放大某个地块来强调其重要性,或者通过在其上添加另一个插图来补充该地块。
为了进行这些编辑,我见过(太多)许多人保存用 Python 完成的单个图,然后在其他图像编辑软件上重新排列。这不仅效率低下,而且无法扩展到大容量绘图或需要多次调整和/或重做的绘图。
对于所有那些朋友和所有仍在为这些问题而奋斗的人,这里是你的一站式指南,教你如何只用 Python 上的 matplotlib 来组织你的情节和支线剧情。
一张图胜过千言万语,所以对于那些想要快速预览的人,那些正在重新阅读这篇文章的人,或者那些患有 TLDR 综合症的人,这里有一个我们将在本教程中完成的示例代码:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as nptime = np.linspace(0, 10, 1000)
height = np.sin(time)
weight = time*0.3 + 2
score = time**2 + height
distribution = np.random.normal(0, 1, len(time))fig = plt.figure(figsize=(10, 5))
gs = GridSpec(nrows=2, ncols=2)ax0 = fig.add_subplot(gs[0, 0])
ax0.plot(time, height)ax1 = fig.add_subplot(gs[1, 0])
ax1.plot(time, weight)ax2 = fig.add_subplot(gs[:, 1])
ax2.plot(time, score)ax3 = fig.add_axes([0.6, 0.6, 0.2, 0.2])
ax3.hist(distribution)plt.show()

Overview of subplot organization elements
下面是我们将如何处理支线剧情组织问题的示意图:

Schematic of subplot organization elements
如果这激起了你的兴趣,或者如果你回来回顾你以前读过的内容,欢迎来到关于使用 matplotlib 在 Python 中组织你的情节的综合初学者教程!
我们处理组织问题的方法是澄清图形、支线剧情和轴之间的区别,以及我们如何用 matplotlib 的 gridSpec 优化它。事不宜迟,让我们从第一个也是最基本的开始。
身材——你的框架
一个 matplotlib 图的最基本元素是图形。该图形对象应被视为您的帧。把任何可视化想象成潜在的图形拼贴,把图形想象成所有这些图形被粘贴和移动的地方。
就像一个相框一样,这个图形本身就是一个等待内容的空结构。这是我们初始原理图的第一个元素:

Schematic — Figure
在 matplotlib 中,我们在使用线fig = plt.figure()时创建图形对象。我们也可以省略放fig =。这很简单,所以我们将 figure 对象存储在某个变量中,以防我们以后需要使用它。例如,如果您想用fig.save()将图形保存为图像。如果您要保存新创建的图形,您将看到新创建的漂亮的空白图像。
如果你想要的只是绘制一个图形,那么你就不需要考虑坐标轴或者支线剧情。你可以简单地这样做:
# Synthetic Data
time = np.linspace(0, 10, 1000)
height = np.sin(time)# Plotting on a figure
fig = plt.figure()
plt.plot(time, height)
fig.savefig(‘figures/basicFigure.png’)
plt.show()

我们可以解构前面的代码块:
plt.figure()创建一个图形对象plt.plot(time, height)会取可用空间,画出时间(x 轴)和高度(y 轴)的关系plt.show()会简单渲染剧情。这在笔记本中是不必要的,但是如果你在一个文本编辑器或者一个交互较少的编辑器上编码,这是一个很好的实践。
如果你有更多的数据,你可以在这个简单的图中添加更多的线条。但是如果我们想在不同的图上画第二个变量呢?这就是轴和支线剧情变得有用的地方。
轴-绘图画布
当图形对象是您的绘图框架时,您将在其上绘图的画布是轴对象。轴本身是自由浮动的,这意味着它们可以放在图形的任何位置。我们可以将它们添加到原始原理图中:

Schematic — Axes
在代码中,这很简单,如下所示:
# Original plot
fig = plt.figure()
plt.plot(time, height)# Adding new empty axes
fig.add_axes([0.43, 0.6, 0.15, 0.15]) #[lowerCorner_x, lowerCorner_y, width, height]
fig.add_axes([0.2, 0.4, 0.1, 0.1]) #[lowerCorner_x, lowerCorner_y, width, height]fig.savefig('figures/figureAxes')
plt.show()

这里我们看到add_axes([left, bottom, width, height])函数在原始绘图上添加了两个新的绘图区域。新轴的具体位置和大小在相应的函数输入数组中指定。
然而,添加空轴并没有多大作用,所以让我们看看如何使用它们来绘图:
# Original plot
fig = plt.figure()
plt.plot(time, height)# First new axes
ax1 = fig.add_axes([0.43, 0.6, 0.15, 0.15])
ax1.hist(height)# Second new axes
ax2 = fig.add_axes([0.2, 0.3, 0.1, 0.1])
ax2.plot(time, time**2)fig.savefig('figures/figureAxes2.png')
plt.show()

在这个例子中,我们看到add_axes()实际上给出或者返回我们可以在其上绘图的实际轴对象,我们将它存储在变量 ax1 和 ax2 中。然后,我们可以使用这些变量来绘制不同的有意义的数据,以类似于我们之前所做的方式为我们的图增加价值。
在这种情况下,我们添加了高度值的直方图,并绘制了时间的平方(除了显示如何在轴上绘制之外,这不是非常有意义的信息)。
子图—组织多个轴
当一个人第一次学习用 matplotlib 绘图时,支线剧情和轴线之间的区别不是特别明显。这是因为,通常情况下,支线剧情会在引入轴线之前引入。正如你现在将看到的,那个微小的细节可以改变你对一个人如何组织他们的视觉化的整体感知。
简而言之,支线剧情只是一种将你自由浮动的坐标轴组织成一个刚性网格的方式。你可以想象支线剧情创建了一个表格,表格的每一个单独的单元格都是一个新的坐标轴供你填充数据。就像这个对原始示意图的新诠释一样:

Schematic — Subplots
这就是如何使用支线剧情:
# Synthetic Data
time = np.linspace(0, 10, 1000)
height = np.sin(time)
weight = np.cos(time)# Plotting all the subplots
fig, axes = plt.subplots(2, 3)axes[0, 0].plot(time, height)
axes[0, 1].plot(time, time**2)
axes[0, 2].hist(height)axes[1, 0].plot(time, weight, color='green')
axes[1, 1].plot(time, 1/(time+1), color='green')
axes[1, 2].hist(weight, color='green')plt.tight_layout()
fig.savefig('figures/figureAxesSubplots.png')
plt.show()

同样,让我们一次看一行:
plt.subplots(),创建了一个网格(2 行 3 列)。该函数返回图形对象(存储在fig中),该对象将保存所有支线剧情,以及所有单独的轴(存储在axes)。- 这些轴包含在一个 2D 数字阵列中(如果你喜欢的话,可以是一个矩阵或表格),可以根据它们的位置单独访问。比如带
axes[0, 0]的左上轴。如果我们有大量的绘图要做,这里我们可以遍历这些位置来分别绘制它们。 plt.tight_layout()通常在使用plt.subplots()时使用,以确保轴刻度和标签不会跨支线剧情重叠。请随意删除它,看看这意味着什么。
我强烈推荐查看官方文档以进一步定制子情节网格,例如行/列之间的间距或者轴是否共享。
因此,我们知道如何在特定的、自由浮动的位置和结构良好的网格下绘制多个图形。那么在组织方面还有什么要做的呢?那么,如果我们想通过把一个情节放大一倍来强调另一个情节呢?如果我们想创建某种类型的平铺式图库呢?这就是 GridSpec 有用的地方。
GridSpec——一个灵活的组织
GridSpec 的工作方式是,和支线剧情一样,定义一个网格。但是,可以选择每个单独的绘图区域将占用多少个单元,而不是自动将轴与网格的每个单元相关联。因此,如果我们希望一个图占据另一个图的两倍面积,我们可以将它与网格中两倍数量的单元相关联。
让我们看看代码:
# A new set of data
time = np.linspace(0, 10, 1000)
height = np.sin(time)
weight = time*0.3 + 2
distribution = np.random.normal(0, 1, len(time))# Setting up the plot surface
fig = plt.figure(figsize=(10, 5))
gs = GridSpec(nrows=2, ncols=2)# First axes
ax0 = fig.add_subplot(gs[0, 0])
ax0.plot(time, height)# Second axes
ax1 = fig.add_subplot(gs[1, 0])
ax1.plot(time, weight)# Third axes
ax2 = fig.add_subplot(gs[:, 1])
ax2.hist(distribution)fig.savefig('figures/gridspec.png')
plt.show()

- 我们首先用
gs = gridspec.GridSpec(nrows=2, ncols=2)创建(2 乘 2)的网格。 - 在这种情况下,
fig.add_subplot(gs[0, 0])将通过索引[0, 0]访问这些单元格来获取它们的子集。 - 这个子集可以包含 gridSpec 网格的多个单元格,只需像使用
ax2 = fig.add_subplot(gs[:, 1])那样访问多个单元格,它选择所有行和第二列。
我们可以通过设置每行或每列的大小来进一步定制我们的绘图。这意味着一些行可能比其他行更窄或更宽。例如:
# Setting up the plot surface
fig = plt.figure(figsize=(10, 5))
gs = gridspec.GridSpec(nrows=2, ncols=2, width_ratios=[3, 1], height_ratios=[3, 1])# First axes
ax0 = fig.add_subplot(gs[0, 0])
ax0.plot(time, height)# Second axes
ax1 = fig.add_subplot(gs[1, 0])
ax1.plot(time, weight)# Third axes
ax2 = fig.add_subplot(gs[:, 1])
ax2.hist(distribution)plt.show()

有了这个,我们终于可以得到我们最初的原理图:

Schematic of subplot organization elements
从我们到这里所学到的一切,我们可以完全解构这个示意图:我们在这里看到的是,我们在(5,5)的网格上应用了 gridspec,但是一些行比其他的小。然后我们添加了覆盖整个第一行的支线剧情,一个覆盖第三行一半的支线剧情,两个覆盖最后一行的支线剧情和一个覆盖最后一列和最后三行的支线剧情。在最后一个支线剧情的顶部,我们添加了两个自由浮动的轴。
和以前一样,我强烈建议查看官方文档来进一步定制 gridSpec,比如行/列之间的间距或者如何创建嵌套的 grid spec。
至此,希望你现在知道如何使用人物、轴、支线剧情和 GridSpec 来构建你的剧情。如果您有任何问题或意见,请在下面留下您的评论。
Plotly 实验—柱形图和线图

条形图
在我的上一篇文章中,我已经解释了如何使用 Plotly 从 King County 住房数据集中创建散点图。另一种流行的绘图类型是柱形图或条形图。与散点图不同,散点图用于比较两个数值变量并检查它们之间的关系,条形图/柱形图用于调查不同类别的一个或多个数值变量。有不同类型的条形图—单独的、聚集的、堆积的等等。
数据集
在这篇文章中,我将使用来自 Kaggle 的湾区自行车共享数据集。湾区自行车共享区让人们可以在旧金山湾区快速、轻松、经济地骑自行车旅行。他们使定期开放数据发布(这个数据集是来自这个链接的数据的转换版本),加上维护一个实时 API。数据集包含各种文件,这些文件包含湾区不同站点的信息、每天的行程以及天气信息。在这个数据探索练习中,我将使用车站和行程数据集。
站
让我们看一下台站数据集:

该数据集包含湾区每个站的名称、位置和容量。哪些城市拥有最多的车站和容量(码头)?
这也可以通过“瀑布”图来描述。让我们看看如何。
让我们看一个“聚集”柱形图,每个城市的车站和码头的数量都聚集在一起。
旅行
现在让我们检查 trips 数据集。这包含了我们在上面看到的各个站点的自行车往返信息。

开始和结束日期列包含对我们的分析有用的日期和时间信息。让我们从这些专栏中提取更多信息。
让我们问一些问题,通过我们的视觉化来回答。
- 旅行持续时间的分布是怎样的?
- 自行车租赁者中最受欢迎的月份/日期/时间是什么?
- 哪些自行车站最受欢迎?
- 订阅类型如何影响这些参数?
我们将看到回答上述每个问题的图表,以及因订阅类型不同而导致的变化。
持续时间分布
让我们通过直方图来研究行程长度的分布。
现在,让我们按照订阅类型来划分直方图,看看客户和订阅者之间的旅行持续时间是否不同。订阅者是经常使用自行车共享的用户,并且是湾区自行车共享的会员。另一方面,顾客没有会员资格,按需使用自行车。
很明显,顾客倾向于比用户使用自行车的时间更长!
自行车旅行的流行时间
人们通常什么时候骑自行车旅行?一天中的哪几个小时,一周中的哪几天,一年中的哪几个月最受骑车人欢迎?客户和订户之间有什么不同?让我们一起探索。
一年中受欢迎的月份
让我们从绘制每月的旅行次数开始。
订阅类型是否因月份而异?让我们看看。
总的来说,对出行次数的逐月分析表明,冬季的乘客量较低,从春季到夏季和秋季,乘客量逐渐增加。客户与用户的比例似乎不会因月份而有太大变化。现在让我们看看星期几是如何影响乘客量的。
一周中受欢迎的日子
一周中的哪几天骑自行车的次数多?趋势是否因工作日和周末而异?让我们一起探索。由于数据集更大,我将只关注 2013 年最后三个月的分析。
显然,周末的使用率比工作日低。这些天订阅类型有什么不同?
看起来用户的使用率在工作日较高,在周末较低。我认为,如果我们用堆积柱形图绘制百分比数字,会比绘制绝对值数字更好。我们可以这样做:
我们可以从对一周中不同日子的出行次数的分析中得出以下结论:
- 订户大多倾向于在工作日使用自行车。这表明他们可能使用它上下班(我们可以在以后按小时进行分析时确认这一点)
- 客户大多倾向于在周末和节假日使用自行车(在上图中,您可以看到客户在圣诞节的使用率高于用户,即使是在工作日)
一天中受欢迎的时间
现在让我们看看在一天中的哪些时段自行车使用率很高。由于这是大量的数据点,我将只检查一周的数据,比如 2013 年 12 月的第一周。
工作日期间,尤其是早上和晚上(上午 8 点和下午 5 点),出行次数往往会更多。我们已经看到用户大多在工作日使用自行车。这证实了我们的假设,即用户主要在日常上下班时使用它。
这篇文章中用来生成情节的所有代码都可以在 GitHub 上获得。
线形图
线形图通常有助于研究数值变量随时间变化的趋势。没有折线图,任何时间序列分析都是不完整的。折线图有各种各样的风格——有或没有标记、面积图、步进折线图、线性和平滑线等。让我们在这本笔记本中探索这些。
在 Plotly 中,折线图只是散点图的一种变体,只是用一条线将点连接起来。因此,我们将使用 scatter(或 scattergl)函数进行绘图。
让我们使用相同的数据集,并使用线图来研究它。首先,让我们按日期画出旅行的次数。
不可否认,这个图表看起来太拥挤了,因为我们试图在一个图表中塞进一个很宽的时间段。令人欣慰的是,Plotly 提供了一个非常方便的工具,叫做 rangeslider,它将使用户能够非常容易地选择一个特定的时间框架。让我们看看怎么做。
你可以在 x 轴下方看到的“较小的”图形称为范围滑块。您可以单击并拖动滑块来放大特定的时间范围。
人们可以看到,冬天的乘客量往往很低,尤其是在接近年底的时候。让我们画一些移动平均线来平滑曲线,看看模式。
移动平均曲线清楚地显示了在冬季的几个月里乘客量是如何下降的。
标记+线条
现在让我们放大到一个特定的时间窗口,并做一些分析。我将关注 Q2 青奥会。
我们可以看到数据有一个每周循环的模式。在时间序列分析中,这被称为“季节性”。让我们根据星期几给标记涂上颜色。
这张图表清楚地表明周末乘客量下降了。然而,为什么 2014 年 5 月 26 日的客流量很低呢?这里的答案是这里的。
另一种突出周末的方法是用 Plotly 通过形状画出盒子。让我们看看怎么做。
面积图
面积图与折线图的不同之处在于,线下的区域会变得有阴影,这让查看者感觉到所绘制的数字的大小。例如,通过折线图绘制股价可能更合适,但通过面积图绘制市值可能更合适。面积图在堆叠模型中使用时也很有用,它在阴影区域中显示两个数值之间的差异。
首先让我们看一下乘客数量的面积图。
让我们查看客户和订户之间的差异,并在面积图上画出差异。
我们也可以将这些类别堆叠在一起,这样我们就可以比较这些数字。
阶梯式折线图
阶梯折线图通过垂直线和水平线连接各点,而不是用直线连接两点。这让用户看到哪里有急剧的增加和减少,哪里的数字保持稳定。
我希望这篇文章能帮助你学会如何用 Plotly 绘制不同类型的柱形图、条形图和折线图。
绘图实验——散点图
让我以一个有点不受欢迎的观点开始这篇文章:Python 中的数据可视化绝对是一团糟。不像 R,gg plot在绘图方面几乎是独占鳌头,Python 有太多的选项可供选择。这张图片很好地概括了这一点:

Courtesy: Jake VanderPlas (@jakevdp on Twitter)
毫无疑问,matplotlib 是最受欢迎的,因为它提供了一系列可用的绘图和面向对象的绘图方法。有些软件包是基于 matplotlib 构建的,以使绘图更容易,例如 Seaborn。然而 matplotlib 的主要缺点是陡峭的学习曲线和缺乏交互性。
还有另一种基于 JavaScript 的绘图包,比如 Bokeh 和 Plotly。我最近一直在玩 Plotly 包,它肯定是我最喜欢的 Python 数据可视化包之一。事实上,它是下载量第二大的可视化软件包,仅次于 matplotlib:
Plotly 有各种各样的图,并为用户提供了对各种参数的高度控制,以自定义图。随着我对这个包了解得越来越多,我想在这里介绍一下我的一些实验,作为我练习的一种方式,也作为任何想学习的人的一个教程。
关于 Plotly 的注释
Plotly 图表有两个主要部分:数据和布局。
数据—这代表我们试图绘制的数据。这将通知 Plotly 的绘图功能需要绘制的绘图类型。它基本上是一个应该是图表的一部分的图的列表。图表中的每个图都被称为“轨迹”。
布局—这表示图表中除数据以外的所有内容。这意味着背景、网格、轴、标题、字体等。我们甚至可以在图表顶部添加形状和注释,以便向用户突出显示某些点。
然后,数据和布局被传递给“图形”对象,该对象又被传递给 Plotly 中的绘图函数。

How objects are structured for a Plotly graph
散点图
在 Plotly 中,散点图函数用于散点图、折线图和气泡图。我们将在这里探索散点图。散点图是检查两个变量之间关系的好方法,通常两个变量都是连续的。它可以告诉我们这两个变量之间是否有明显的相关性。
在这个实验中,我将使用 Kaggle 上的金县房屋销售数据集。直观来看,房价确实取决于房子有多大,有几个卫生间,房子有多旧等等。让我们通过一系列散点图来研究这些关系。
数据集包含分类属性和连续属性的良好组合。房子的价格是目标变量,我们可以在这篇文章中看到这些属性如何影响价格。
Plotly 有一个散点函数,还有一个 scattergl 函数,当涉及大量数据点时,它可以提供更好的性能。在这篇文章中,我将使用 scattergl 函数。
客厅面积和价格有关系吗?
这看起来像一个很好的图表,显示了客厅面积和价格之间的关系。我认为,如果我们用对数(价格)来代替,这种关系会得到更好的证明。
通常,显示线性关系的散点图伴随着“最佳拟合线”。如果您熟悉 Seaborn visualization 软件包,您可能会意识到它提供了一种绘制最佳拟合线的简单方法,如下所示:

我们如何在 Plotly 中做到这一点?通过向数据组件添加另一个“跟踪”,如下所示:
现在让我们看看散点图的变化。如何通过颜色显示散点图中的类别?例如,数据是否因房屋中楼层、卧室和浴室的数量而不同?我们可以通过将颜色参数传递给 Scatter 函数中的标记来检查这一点,如下所示:
请注意,这给出了一个“色阶”,而不是用不同颜色表示不同“等级”的图例。当有多条迹线时,Plotly 会指定单独的颜色。我们可以这样做:
我们可以清楚的看到,除了客厅面积,房子的档次(景郡分配)也影响着房子的价格。但是,如果我们想同时看到这个图表中等级、条件和其他变量的影响呢?答案:支线剧情。
让我们画一个上图的堆积子图,但是以卧室数量、浴室数量、条件、等级和滨水区作为参数。
在 Plotly 中,我们通过将轨迹添加到图形中并指定它们在图中的位置来制作支线剧情。让我们看看怎么做。
“plotly.tools”下提供了支线剧情功能。
Click on the picture to see the interactive plot
等级看起来像是一个非常明显的区别因素,与客厅面积一起决定价格。其他变量似乎有一些影响,但我们可能需要进行回归分析来检验这一点。
我希望这能让你对如何在 Plotly 中使用散点图有所了解。我将在随后的文章中练习柱形图/条形图。
这篇文章使用的代码可以在 GitHub 上找到。
好的、坏的和丑陋的
它可能更新,但它更好吗?
从数据科学过程的开始到结束,创建有效的数据可视化是数据科学的一个非常重要的部分。在探索性数据分析过程中使用可视化是了解数据内容的好方法。在项目结束时创建可视化效果是以一种易于理解的方式传达您的发现的好方法。Python 中有很多不同的数据可视化工具,从像 Matplotlib 和 Seaborn 这样的狂热爱好者,到新发布的 Plotly Express。这三个工具都很容易使用,并且不需要很多深入的编程知识,但是如何决定使用哪一个呢?
什么是 Plotly Express?
如果您曾经使用过 Plotly,或者只是查看过使用 Plotly 编写的代码,您就会知道它绝对不是用于可视化的最简单的库。这就是 Plotly Express 的用武之地。Plotly Express 是 Plotly 的一个高级包装器,本质上意味着它可以用更简单的语法做很多你可以用 Plotly 做的事情。它非常容易使用,并且不需要连接你的文件到 Plotly 或者指定你想要离线使用 Plotly。安装 Plotly Express 后,只需一个简单的import plotly_express as px就可以开始用 Python 创建简单的交互式可视化。
好人
使用 Plotly Express 创建可视化效果有几个优点。
- 整个可视化可以用一行代码创建(某种程度上)。
px.scatter(df, x='ShareWomen', y = 'Median',
color = 'Major_category',
size = 'Total', size_max = 40,
title = 'Median Salary vs Share of Women in a Major',
color_discrete_sequence = px.colors.colorbrewer.Paired,
hover_name = 'Major'

虽然从技术上来说用了 6 行代码来创建它,但它仍然只用了一个命令。在创建 Plotly Express 可视化时,所有事情都可以在同一命令中完成,从调整图形的大小,到它使用的颜色,再到轴标签。在我看来,Plotly Express 是快速创建和修改可视化的最简单的方法。此外,可视化是自动交互的,这就引出了我的下一个观点。
- 它是互动的。

将鼠标悬停在特定点上会弹出一个框,其中包含用于创建图表的任何信息,以及您想要包含的任何额外信息。在这个特殊的图表中,包括hover_name = 'Major'在内的特定专业指的是每个框的标题。这使得我们能够从图形中获取大量信息,而这些信息是我们无法通过其他方式获得的。此外,我们还可以看到两个最大的专业是什么,这是我们在使用 Seaborn 创建类似的情节时无法做到的。
- 你可以分离出某些信息。

单击可视化图例中的某个类别两次将会隔离该类别,因此它是我们在图形中可以看到的唯一类别。单击一次将会删除该类别,因此我们可以看到除该类别之外的所有类别。如果你想放大某个区域,你所要做的就是点击并拖动来创建一个矩形,它包含了你想要更仔细检查的较小区域。
- 你可以激发变化。
Plotly Express 提供的最酷的功能之一是添加动画帧的能力。通过这样做,你允许自己观察某个变量的变化。大多数情况下,动画帧是基于年份的,因此您可以直观地看到事物如何随着时间的推移而变化。这不仅看起来很酷,因为你正在为自己创建可视化效果,而且能够创建一个动画和交互式的可视化效果会让你看起来很清楚自己在做什么。
坏事
- 它没有吨的特点。
不要误会我的意思,你可以用 Plotly express 做很多事情。只是在调整图形外观时,它没有太多的选项。例如,在 Seaborn 中,您可以通过改变jitter = False和kind = 'swarm'来改变分类散点图上的点排列的原因。据我所知,使用 Plotly Express 这两种方法都是不可能的。这真的不是世界末日,特别是考虑到 Plotly Express 的主要目标之一是允许用户在执行探索性数据分析时快速轻松地创建交互式可视化。我猜想,出于这个目的使用它的大多数人并不太关心他们的点在散点图上是如何排列的。
- 每次创建新图形时,都需要设置颜色。
# Seaborn
sns.catplot(x = 'Major_category', y = 'Median', kind = 'box', data = df)
plt.xticks(rotation = 90)
plt.show()# Plotly Express
px.box(df, x = "Major_category", y = 'Median', hover_name = 'Major')
您可能希望这两种方法能够创建非常相似的可视化效果,事实也确实如此(在很大程度上)。


Boxplots created with Seaborn (left) and Plotly Express (right)
在使用 Seaborn 运行代码之前,配色方案被设置为“Paired ”,这贯穿于笔记本的其余部分,除非后来有所改变。在 Plotly Express 中,图形使用的配色方案需要包含在每个单独的绘图中。此外,Seaborn 会自动为不同的类别分配不同的颜色,而 Plotly Express 不会。这可能是也可能不是一件好事,取决于你的数据。一个好的经验法则是,如果没有理由在你的图中给类别不同的颜色,就不要这样做。在创建可视化效果的过程中,您可能希望每个类别都有自己的颜色,但也可能不希望这样。如果这是您想要的,只需将color = 'Major_category'添加到您的px.box()通话中。这可能有点不方便,但也没什么大不了的。
但是,当将不同的颜色指定给不同的类别时,Plotly Express 中会出现一些问题。
px.box(df, x = "Major_category", y = 'Median',
color = 'Major_category',
color_discrete_sequence = px.colors.colorbrewer.Paired,
hover_name = 'Major')

当类别被指定一种颜色时,Plotly Express 会创建明显更小的箱线图。因为这个图是交互式的,你可以将鼠标放在这些点上来查看对应的数字,这并不是一个大问题。然而,一件小事就能让图形的实际格式发生如此大的变化,这仍然很烦人。我找不到关于这个问题的任何信息,我也不相信有一个好的解决办法,这就引出了我的下一个观点。
- Plotly Express 仍然相对较新,所以没有太多的在线帮助。
Plotly Express 是今年 3 月发布的,所以截至目前,它只有 3 个月的历史。因此,在网上还没有很多关于它的问答。另一个问题是,任何时候你在谷歌上搜索“如何在 plotly express 中做 x ,所有出现的信息都与 plotly 有关。我想随着时间的推移,这将不再是一个问题。我也想象这些关于创造情节的问题只是程序中的一些错误,希望随着时间的推移会得到解决。
丑陋的
Plotly Express 仍然是相当新的,是为了探索性的数据分析,所以它的一些问题是彻头彻尾的丑陋。
- 这个线形图
#Seabornsns.relplot(x= 'ShareWomen', y = 'Women', kind = 'line', data = df)
plt.title('Share of Women vs Total Women in a Particular Major')
plt.show()#Plotly Express
px.line(df, x ='ShareWomen', y = 'Women',
title = 'Median Salary vs Share of Women in a Major',
color_discrete_sequence = px.colors.colorbrewer.Paired,
hover_name = 'Major')


Line Graphs created with Seaborn (left) & Plotly Express (right)
默认情况下,Seaborn 按照 x 和 y 值的顺序对点进行排序,以避免图表看起来像右边的图表。Plotly Express 只是按数据帧中出现的顺序绘制点。Plotly Express line()函数中没有sort 参数,所以最好的办法是根据感兴趣的变量对数据进行排序,然后相应地绘制数据。这不是最复杂的修复,但绝对不方便。
- 这个小提琴的情节
px.violin(df, x = "Major_category", y = 'Median',
color = 'Gender Majority',
color_discrete_sequence =px.colors.colorbrewer.Paired,
hover_name = 'Major')

我们再一次遇到了这样一个问题,分配不同的类别用不同的颜色来表示,这使得它们在可视化上看起来比类别被分类之前要小得多。如果你没有构建这个图,你甚至不会知道你在看一个小提琴图,而且绝对没有办法通过这个图得到这些东西的密度。当然,您仍然可以将鼠标悬停在图形上以获得相关的数值,但是创建可视化的主要原因之一是为了直观地感受您的数据,而这种可视化并不能做到这一点。
- 没有一种好的方法可以将视觉效果融入到演示文稿中。
同样,因为 Plotly Express 主要用于探索性数据分析,所以这不是 Plotly Express 提供的功能也就不足为奇了。我对普通版本的 Plotly 不太熟悉,但我相信你需要通过他们的网站创建演示文稿,以便在你的演示文稿中包含交互式可视化。这可能是 PowerPoint/Google 幻灯片没有嵌入交互式可视化功能的结果,而不是 Plotly 没有制作可视化功能,因此它们可以嵌入到演示文稿中。
结论
plotly Express真的很酷。我 100%推荐使用它进行探索性数据分析。图形的交互性让你可以轻松地对你的数据进行更彻底的调查。将鼠标悬停在某个点上并获取与其相关的所有信息,这比你只需查看图表并猜测这些点是什么更能让你得出更好的结论。然而,它可能无法像你希望的那样做你想让它做的所有事情。除了 Matplotlib 和 Seaborn 之外,我建议使用 Plotly Express 来创建最佳的可视化数组。
我强烈推荐查看这个代码,以便查看 Plotly Express 提供的更多不同选项。
巧妙地表达自己
使用 Python 中的 Plotly Express 快速浏览基本可视化。
我最近发现了 Plotly Express ,我非常兴奋地将它添加到我的工具箱中,因为它的潜力。
对于那些不熟悉 Plotly 的人(或者更困惑为什么会有一个 express 版本的 need ),让我们让你了解一下。
Plotly 是一个可视化库,可以在许多平台上使用,包括 Java、Python 和 R(这是我个人过去使用 Plotly 的经验,尽管有限)以及 Plotly(组织)的商业产品。Plotly 很棒,因为你可以创建高度互动的可视化,而不是通过matplotlib或seaborn ( matplotlib更酷的兄弟)的静态可视化。例如:

https://mangrobang.shinyapps.io/Project_Draft_AV/
但是不利的一面(至少对我来说)是,在 Plotly 中完成任何事情总让人感觉有很多代码行。例如,前面绘图的代码如下所示(为了简洁起见,没有包括一些数据设置):
p <- plot_ly(draft
, x = ~Rnd
, y = ~First4AV
, type = "scatter"
, mode = "markers", color = ~draft$Position.Standard
, colors = brewer.pal(8, "Spectral")
, marker = list(size = 10, opacity = .25 )
) %>%
# Plot the average for each draft round
layout(title = "Accumulated AV over First 4 Years of Player Careers by Draft Round, Yrs 1994-2012") %>%
layout(xaxis = xx, yaxis = yy) %>%
add_trace( data=avg.4.AV, x = ~Rnd, y = ~avg.F4.AV, type = "scatter"
, name = "Avg 4-Yr AV", color=I("lightpink"), mode="lines" ) %>%
# Plot the predicted value
add_trace(x = input.rnd, y = mod.draft.pred(), type = "scatter"
, mode = "markers"
, marker = list(symbol='x',size=15, color='black')) %>%
layout(annotations = jj) %>%
layout(annotations = pr)p
注意上面是用 R 写的,不是 Python,因为我过去用 Plotly 的大部分经验都是用 R 写的。但是我在 Python 中看到和使用的似乎在语法复杂性上是相似的。
Plotly Express 所做的是围绕基本 Plotly 代码创建一个更高级别的包装器,因此语法更简单。对我来说,这意味着更容易学习,更容易建立编码肌肉记忆。减少从旧项目中复制和粘贴代码或堆栈溢出!
为了测试它,我认为这将是有用的,通过一些标准的勘探地块运行。当我们完成时,我们希望最终能为 Plotly Express 提供一个方便的备忘单!
准备!(和前言思想)
- 对于我们的测试数据,我在 Kaggle 上找到了这个关于超级英雄的有趣数据集(嘿,我刚看了复仇者联盟 4:终局之战!):

Multiple Spider-Men and Captains America? Yes, the multiverse exists!
2.获取和清理数据的代码以及下面的片段可以在这个 jupyter 笔记本这里找到。
3.如果您尚未安装或导入它:
# Install
pip install plotly_express# Import
import plotly_express as px
4.我们假设您对下面显示的图有一些概念上的熟悉,所以我们不会深究每个图的优缺点。但是我们也会在可能的时候添加一些想法和参考。
5.以下情节的外观变化很大,因为我很好奇各种内置模板(即主题)的外观和感觉。所以,如果这很烦人,我很抱歉,但就像我提到的,我正在努力弯曲所有的肌肉。
绘制单变量数据
让我们来看几个经典的方法来对连续变量进行单变量探索:直方图和箱线图。
直方图(单变量):
px.histogram(data_frame=heroes_clean
, x="Strength"
, title="Strength Distribution : Count of Heroes"
, template='plotly'
)

Not the prettiest histogram.
你可以在这里阅读更多关于直方图有多酷的信息。
箱线图(单变量):
…以及箱线图。简单中透着优雅。
px.box(data_frame=heroes
, y="Speed"
, title="Distribution of Heroes' Speed Ratings"
, template='presentation'
)

Boxplot.
更多信息可在箱线图此处、箱线图此处和箱线图此处中找到。但是如果你觉得盒子情节有点方,也许小提琴情节也可以?
小提琴剧情
px.violin(data_frame=heroes
, y="Speed"
, box=True
, title="Distribution of Heroes' Speed Ratings"
, template='presentation'
)

More of an upright bass plot.
小提琴情节越来越受欢迎。我喜欢把它们看作 boxplot 更酷、更帅的兄弟姐妹。哎哟。
但是,如果您想要探究的变量或特征是分类的,而不是连续的,该怎么办呢?在这种情况下,您可能希望从一个条形图开始,以获得对数值计数的感觉。
条形图(单变量)
px.bar(data_frame=heroes_publisher
, x='publisher'
, y='counts'
, template='plotly_white'
, title='Count of Heroes by Publisher'
)

Remember when NBC’s Heroes was cool?
这里有一个关于条形图的快速入门。
单变量分析很好,但实际上,我们通常希望将变量与其他变量进行比较,试图梳理出有趣的关系,这样我们就可以建立模型。因此,让我们继续在一些二元技术的例子上建立我们的plotly-express超能力。
绘制二元数据
让我们从比较连续变量和连续变量开始。
散点图
px.scatter(data_frame=heroes
, x="Strength"
, y="Intelligence"
, trendline='ols'
, title='Heroes Comparison: Strength vs Intelligence'
, hover_name='Name'
, template='plotly_dark'
)

If a theoretical character has 0 Strength, they at least rate 57 in Intelligence. Hmm.
散点图是比较两个连续(数字)变量的可靠方法。这是快速评估两个变量之间是否存在关系的好方法。
在上面的例子中,我们通过添加一条趋势线来进一步帮助自己确定关系。看来在Strength和Intelligence之间存在微弱的正相关。
线条图
px.line(data_frame=heroes_first_appear_year
,x='Year'
,y='Num_Heroes'
,template='ggplot2'
,title="Number of Heroes by Year of First Appearance"
,labels={"Num_Heroes":"Number of Heroes"}
)

The early ’60s was a big turning point in comic superheroes.
连续与连续比较的一个特例是时间序列。经典的方法是用线图。几乎总是日期/时间变量沿着 x 轴,而另一个连续变量沿着 y 轴测量。现在你可以看到它是如何随着时间的推移而变化的!
如果我们想要比较分类变量和连续变量呢?事实证明,我们可以只使用单变量技术,但只是“重复”它们!我最喜欢的方法之一是使用堆叠直方图。我们可以为连续变量,分类变量的每个值做一个直方图,然后把它们叠加起来!
例如,让我们再看一下之前在Strength上的直方图,但是这次我们想看看由Gender分离出来的数据。
堆积直方图
px.histogram(data_frame=heroes[~heroes.Gender.isna()]
, x="Strength"
, color='Gender'
, labels={'count':'Count of Heroes'}
, title="Strength Distribution : Count of Heroes"
, template='plotly'
)

I’m guessing the big bar for 10–19 is non-superpowered characters, like Batman. Nerd.
可能堆栈让您感到困惑,您只想看到按媒体夹分组的条形:
堆积直方图(分组仓)
px.histogram(data_frame=heroes[~heroes.Gender.isna()]
, x="Strength"
, color='Gender'
, barmode = 'group'
, labels={'count':'Count of Heroes'}
, title="Strength Distribution : Count of Heroes"
, template='plotly'
)

…或者,如果这两种外观中的任何一种在视觉上对您来说都太过繁忙,那么您可能只想为每个类别值绘制一个图。你会看到这有时被称为刻面(或者至少我是这样称呼它的)。
多面直方图
px.histogram(data_frame=heroes[~heroes.Gender.isna()]
, x="Strength"
, color='Gender'
, facet_row='Gender'
, labels={'count':'Count of Heroes'}
, title="Strength Distribution"
, template='plotly'
)

哇,我被历史淘汰了。让我们看看如何将相同的分面/分割概念应用于盒状图。
分割方框图
px.box(data_frame=heroes[~heroes.Gender.isna()]
, y="Speed"
, color="Gender"
, title="Distribution of Heroes' Speed Ratings"
, template='presentation'
)

无论盒子情节可以做什么,小提琴情节也可以!
分裂小提琴剧情
px.violin(heroes[~heroes.Gender.isna()]
, y="Speed"
, color="Gender"
, box=True
, title="Distribution of Heroes' Speed Ratings"
, template='presentation'
)

‘Agender’ characters have higher median (and likely mean) Speed.
那么,如果你只想比较分类值和分类值呢?如果是这种情况,你通常想看看相对计数。所以堆积条形图是个不错的选择:
堆积条形图(分类对比分类)
px.histogram(data_frame=heroes
,x="Publisher"
,y="Name"
,color="Alignment"
,histfunc="count"
,title="Distribution of Heroes, by Publisher | Good-Bad-Neutral"
,labels={'Name':'Characters'}
,template='plotly_white'
)

Marvel and DC Comics are pretty top heavy with ‘Good’ characters.
题外话:事实证明,使用.histogram的堆叠条形图更容易,因为它提供了对histfunc的访问,这允许您对直方图应用函数。这省去了必须先进行汇总的步骤(您可能已经注意到,上面的条形图就是这样做的)。
绘制三个或更多变量
我们可能感觉到了一种模式。通过使用另一种视觉元素,比如颜色,我们可以将任何一元可视化转换成二元(或多元)可视化;或者通过沿着类别值分面/分割。
让我们探索添加一个第三变量。一种常见的技术是使用颜色将分类变量添加到散点图中。
彩色散点图
px.scatter(data_frame=heroes[~heroes.Gender.isna()]
, x="Strength"
, y="Intelligence"
, color="Alignment"
, trendline='ols'
, title='Heroes Comparison: Strength vs Intelligence'
, hover_name='Name'
, opacity=0.5
, template='plotly_dark'
)

Similar relationships across Alignments.
也许这些数据对于添加的类别来说并不那么有趣,但是当你找到正确的模式时,类别真的会脱颖而出,例如经典的虹膜数据集…就像这样:

credit: https://www.plotly.express/
但是回到我们最初的带颜色的散点图,如果我们想添加一个第三连续变量会怎么样呢?如果我们把它绑在我们的马克笔的尺寸上怎么样?
散点图,带颜色和大小
下面我们添加连续的Power变量作为标记的大小。
px.scatter(data_frame=heroes[~heroes.Gender.isna()]
, x="Strength"
, y="Intelligence"
, color="Alignment"
, size="Power"
, trendline='ols'
, title='Heroes Comparison: Strength vs Intelligence'
, hover_name='Name'
, opacity=0.5
, template='plotly_dark'
)

Wow, Galactus is tops in Strength, Intelligence, and Power!
我注意到的一件事是,图例不会自动为尺寸添加图例。这有点烦人。我能说什么呢,Plotly Express 已经在这篇文章中宠坏了我!
根据我在文档中所看到的,我们仅仅开始触及可能的表面。我们可以继续下去,但是让我们以更多的例子来结束我们的探索。
散布矩阵
散点图矩阵在一组连续变量上执行成对散点图,然后您可以用颜色、符号等对其进行定制。代表分类变量。
px.scatter_matrix(data_frame=heroes[~heroes['Gender'].isna()]
, dimensions=["Strength", "Speed", "Power"]
, color="Alignment"
, symbol="Gender"
, title='Heroes Attributes Comparison'
, hover_name='Name'
, template='seaborn'
)

Maybe a future release will have the option to toggle the diagonal plots into a histogram (or some other univariate plot).
带边际图的散点图
这很简洁,但是下一个我真的很喜欢它的简洁。这个想法是,你可以将我们已经讨论过的单变量图添加到散点图的边缘。
px.scatter(data_frame=heroes[~heroes.Gender.isna()]
, x="Strength"
, y="Speed"
, color="Alignment"
, title='Strength vs Speed | by Alignment'
, marginal_x='histogram'
, marginal_y='box'
, hover_name='Name'
, opacity=0.2
, template='seaborn'
)

咻!那…太多了。但是我认为这是一个很好的开始,可以创建一个更常见的绘图技术的快速参考,全部使用 plotly express 。我真的很挖掘我到目前为止所看到的(我们在这个帖子中所做的一切从技术上来说都是一句话!)并期待他们未来的更新!感谢阅读。
请随意伸手!|LinkedIn|GitHub
来源:
普洛特利。介绍 Plotly Express,2019 年 3 月 20 日,https://medium . com/@ Plotly graphs/introducing-Plotly-Express-808 df 010143d。2019 年 5 月 11 日访问。
H-1B 签证分析-R 闪亮的情节视觉效果
国际学生在美国找工作指南

Photo by Andrew Neel on Unsplash
大约一个月前,我在某一周安排了三次电话面试。由于我的美国签证身份,他们都以我的申请“没有进一步发展”而告终。其中一次对话持续了不到 60 秒,因为他们问的第一个问题是我现在或将来是否需要签证担保,仅此而已。采访到此结束。
是的,臭名昭著的 H-1B 签证,它让美国公司可以临时雇佣外国人才长达 6 年。雇主必须代表雇员向 USCIS 提出申请,USCIS 的批准使雇员有资格在美国工作。可以理解的是,由于频繁的法律变更和不确定性,雇主们正变得不愿意雇佣将来需要签证担保的人。
很多和我一样的留学生都因为这个原因觉得找工作很难。作为一名有抱负的数据科学家,我决定分析美国公民及移民服务局数据中心的公开数据。数据的详细描述可在这里获得。
我使用 R 中的‘plotly’包来创建交互式图形,并最终使用‘R Shiny’开发了一个 web 应用。本文末尾详细描述了如何使用该应用程序并以交互方式浏览这些数据。目的是让求职更有针对性,这个应用程序对所有和我处境相同的国际学生都有用。
注意:我想把这篇文章的重点放在描述性报告上,因此我省略了帖子中的一些代码。然而,所有这些都可以在 Github 上找到。本帖中的图表是静态的,但你可以在网络应用中与它们互动。
以下是我发现的一些见解:
1B 签证拒签率上升了吗?
是的,他们有。当我们在下图中查看批准和拒签的总数时,我们意识到,自 2015 年以来,签证批准数量在减少,而拒签数量在增加。在接下来的章节中,我们将更深入地研究批准和拒绝的数量,并从地理、行业、组织等不同角度进行分析。
## Code for plotly chart of total approvals and denialsrequire(tidyverse)
require(plotly)
output$plot1 <- renderPlotly({
Data %>%
group_by(Year) %>%
summarize(Approvals = sum(Initial_Approvals), Denials = sum(Initial_Denials), C_Approvals = sum(Continuing_Approvals), C_Denials = sum(Continuing_Denials)) %>%
plot_ly(x = ~Year, y = ~Approvals, type = “scatter”, mode = “lines”, color = I(‘dark green’), name = “Approvals”) %>%
add_trace(x = ~Year, y = ~Denials, type = “scatter”, mode = “lines”, color = I(‘red’), name = “Denials”) %>%
layout(title = “H-1B Visas by Year”,
xaxis = list(title = “Year”),
yaxis = list(title = “Count”))
})

Total Visa Approvals And Denials Since 2009
地理分析
作为国际学生,你最有可能在哪些城市/州找到职业机会?
我想考察一下这个国家哪些地区的签证批准率最高,这也意味着国际学生有更多的机会。此外,由于许多公司更喜欢“本地”候选人,我正在考虑是否应该搬到这些城市中的任何一个。所以,我调查了过去两年签证批准数量最多的州和城市;特别是最近两年,我们目睹了这一时期的急剧下降。您可以在应用程序中选择想要查看的年份,并进行更深入的分析。
加利福尼亚州是签证批准数量最多的州。这并不奇怪,因为加州是美国大陆最大的州之一,硅谷和几个主要科技中心城市经常招聘人才。以不到加州一半的支持率,纽约、新泽西和德克萨斯分别位居第二、第三和第四。

Top states with highest H-1B visa approvals in the last two years
就城市而言,我发现纽约市是过去两年中签证批准数量最多的热点城市。紧随纽约之后的分别是芝加哥、旧金山和波士顿。
# Plot of top cities for shiny serverrequire(plotly)
require(ggmap)#geo styling for plot
g <- list(
scope = ‘usa’,
showland = TRUE,
landcolor = toRGB(‘light gray’),
projection = list(type = ‘albers usa’),
showlakes = TRUE,
lakecolor = toRGB(‘white’)
)
output$cities <- renderPlotly({
Data %>%
filter(Year %in% input$Year) %>%
group_by(City) %>%
summarize(Approvals = sum(Initial_Approvals), Denials = sum(Initial_Denials),
C_Approvals = sum(Continuing_Approvals), C_Denials = sum(Continuing_Denials)) %>%
arrange(desc(Approvals)) %>%
top_n(50, Approvals) %>%
left_join(coords_cities, by=”City”) %>%
plot_geo(lat = ~lat, lon = ~lon, color = ~Approvals, size=~(Approvals)) %>%
add_markers(hovertext = ~(paste(“City:”, City, “\nNo. of Approvals:”, Approvals))) %>%
layout(title = ‘Top cities with H-1B Visa approvals in the selected Years’, geo=g)
})

Top cities with highest H-1B approvals in the last two years
哪些行业受影响最大?
当我们在下图中查看拒签率时,我们注意到 2018 年签证拒签率全面上升。拒签率是以拒签占全部申请的百分比来计算的。
教育服务行业受影响最小,总体拒绝率最低。教育行业免除每年 85,000 份 H-1B 认证或许是一种解释。
高等教育机构、与高等教育机构相关或附属的非营利组织、非营利研究组织和政府研究组织不受 H-1B 年度配额的限制。
# Code for denial rate plot
output$dept_denial <- renderPlotly({
plot_ly(Dept_Data, x = ~Year, y=~Denial_Rate, color =~Dept_Name, type=’scatter’, mode = ‘line’) %>%
layout(title = “H-1B Visas Denial Rate by Department”,
xaxis = list(title = “Year”),
yaxis = list(range = c(0,50), title = “% Denials”))
})

Denial Rates For The Departments
当考虑批准总数时,“专业和技术服务”行业占主导地位,占批准总数的 50%以上。虽然,自 2015 年以来,这个行业的批准已经下降,并稳步下降。

Number of approvals per department
瞄准哪些雇主?
这是最有趣的部分。平均而言,我个人在每份工作申请上花费大约 5-10 分钟。我不想在向一家雇主申请工作时,却听到他们告诉我,他们不考虑国际学生或“现在或将来需要担保”的申请人。
你可以在应用程序中查找任何雇主,以查看这些年来他们赞助了多少候选人的详细信息。对于分析部分,我决定寻找排名靠前的雇主,比如在过去两年中支持率最高的雇主。
#Code for employer wordcloudrequire(wordcloud2)
employer_wc <- Data %>%
filter(Year > 2017) %>%
group_by(Employer) %>%
summarize(Approvals = sum(Initial_Approvals), Denials = sum(Initial_Denials), C_Approvals = sum(Continuing_Approvals), C_Denials = sum(Continuing_Denials)) %>%
mutate(Denial_Rate = Denials/(Approvals+Denials)*100) %>%
select(Employer, Approvals) %>%
arrange(desc(Approvals)) %>%
top_n(15, Approvals)wordcloud2(employer_wc, size = .25, minRotation = 0, maxRotation = 0, color=rep_len(c(“blueviolet”,”blue”,”darkgreen”,”purple”,”deeppink”,”darkorchid”,”darkviolet”,”brown”,”darkred”), nrow(demoFreq) ), backgroundColor = “white”)

Top employers with H-1B approvals in the last two years
正如我们所见,所有的科技公司在签证审批方面都处于领先地位。在过去两年中,苹果公司以大约 1700 项批准位居榜首。德勤、英特尔、IBM、微软、脸书、谷歌和其他公司也是顶级竞争者。
这是我用我发现的一些见解完成的分析。我知道人们可以利用这些数据做更多的事情。如果你想在应用程序中看到任何变化,请在评论中提出建议或直接在 Github 上合作。
App 怎么用?
对使用这些数据并与之互动感到兴奋吗?您可以使用以下任一选项来完成此操作:
1)使用 R 在本地运行:如果您的系统上安装了 R 和 R studio/任何其他 IDE,请在 R 控制台中运行以下命令。该应用程序将直接从 GitHub repo 加载。首次加载应用程序可能需要一些时间。
runGitHub("Shiny_H1b","SurajMalpani")
2)虚拟主机:https://surajmalpani.shinyapps.io/Visas/
您可以通过访问上述地址来访问 web 应用程序。然而,我个人建议使用第一种方法(runGitHub ),因为 Shinyapps 不支持其中的一些图,因此我必须删除它们。
关于该应用程序的详细信息:
如您在以下视频中所见,共有 4 个选项卡。视频下面提到了他们的描述:
Demonstration video of the shiny web app
I)所有数据:该标签包含来自 USCIS 的所有数据。您可以查找感兴趣的任何或多个雇主、年份、州、城市。根据您的输入过滤数据后,您可以下载. csv 格式的过滤文件以供进一步分析。
ii)批准与拒绝:您可以查看自 2009 年以来的批准和拒绝总数。
三)地域:你可以看看批准人数最多的前 10 个州和前 50 个城市。您可以选择您希望查看的年份,这些图将针对所选年份进行更新。
iv)部门:您可以查看一段时间内每个部门的审批数量。此外,您可以查看这些部门的拒绝率。
请随时在评论区提问/给出建议,或者随时在我的网站或 LinkedIn 与我联系。
感谢您的阅读。希望这个分析和 web app 对你有帮助!
参考文献:
- Plotly 网站
- 闪亮的 RStudio
- Plotly-R
- 美国移民局
使用 Python 绘图和数据表示。使用 Coursera 问题的引导式演练

这篇文章基于 Coursera 课程的一个作业,“Python 中的应用绘图、图表和数据表示”。这是密歇根大学 Python 专业应用数据科学的第 2 部分。这是为了向您介绍我对其中一个作业的一个解决方案,这种解释方式对于 Python 经验有限的人来说更容易理解。请注意,如果你正在学习这门课程,你可以从我的解决方案中学习,但不要抄袭它对你最有利,因为从长远来看,这只会伤害你。此外,还有一个更好的解决方案,可以让你更好地理解作业的目的。
场景
假设您正在某个城市或地区的气象站测量温度。根据我的研究,尽管有限,这种天气经常被测量到十分之一度(不管什么原因)。你开始收集超过十年的数据,测量每日和每月的最高和最低温度,并将这些数据编辑成 excel 电子表格。
新的一年过去了,假设现在是 2015 年,你继续像往常一样测量温度。2015 年,你一直在观察全国各地创纪录的高点、低点和热浪。你开始想知道今年的气温与过去 10 年的气温相比如何。你想回答问题;如何将今年每个月的历史高点和低点与过去 10 年每个月的历史高点进行对比?
你决定呈现这些发现的最佳方式是通过可视化的折线图,该图将显示过去 10 年中每个月的创纪录高点和低点。为了回答您当前的问题,您还需要叠加一个散点图,显示 2015 年以来的创纪录高点和低点,这些高点和低点要么超过了过去 10 年创下的创纪录低点或高点。
代码
所以现在你有一个问题,你知道你需要发展什么来回答你的问题。你决定用 Python 写出代码。为了有助于你的可视化,你需要导入某些库,这将帮助你执行许多复杂的动作,而无需编写冗长复杂的代码。
导入库并读取数据
import datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#read data from a csv
df = pd.read_csv('data/C2A2_data/BinnedCsvs_d400/fb441e62df2d58994928907a91895ec62c2c42e6cd075c2700843b89.csv')
对于这种可视化,我们希望使用日期时间库来规定截止日期,以便我们可以将数据分成两个表;2005 年至 2014 年 12 月 31 日之间的温度测量值;ii .)在截止日期之后进行的测量。
Numpy 是数字 python 的缩写,它将允许我们在多维矩阵和数组上执行数学函数。
熊猫和图书馆将允许我们对将要与之互动的数据框架进行操作和计算。而 matplotlib 将用于绘制我们的图形。为了便于引用,我们使用别名 pd 和 plt(以及 numpy 的 np)与这些库进行交互。
然后,下一行代码使用 pandas 库中包含的 read_csv 模块从 csv 中读取我们想要的数据。这被自动保存为熊猫数据帧——我们将该数据帧命名为“df”。
此时,我喜欢使用 head 方法来读取数据帧的前 5 行。这有助于我理解数据帧有哪些列,并对数据的外观有更好的感觉
df.head()

Output
现在我们知道数据帧有 4 列;ID、日期、元素和数据值。还有一个索引列,列出从零开始的行号。数据帧总共包含 165 085 行
您会注意到,测量结果分为 TMAX(最高温度)和 TMIN(最低温度)。ID 显示了站点识别代码,该代码大概显示了哪个站点进行了测量。
删除闰年数据
由于 2015 年不是闰年,我们希望删除所有闰年数据。如果闰年在 10 年内记录了高/低温,因为这可能会对高/低温测量记录产生影响。由于 2015 年将比闰年(2008 年和 2012 年)少一天,这不是一个准确的比较。
#converting date to date timestamp
df['Date'] = pd.DatetimeIndex(df['Date']).date
lis = ['2922008','2922012'] #list of 29 Febs appearing in each leap year
dates = [datetime.datetime.strptime(i, '%d%m%Y').date() for i in lis] #converting into list of datetime.date objects
df = df[~df['Date'].isin(dates)] #remove all 29 February dates
在这段代码中,我们使用 DatetimeIndex 模块转换整个日期列。这将日期列转换为时间戳对象。然后我们用。我们希望返回 datetime.date 对象的日期(“不带时区信息的时间戳的日期时间”)。
使用 datetime.date 库,我们将闰年日期列表转换为 datetime.date 对象。然后,我们只选择没有列为闰年日期的日期(在列表“lis”下),并从我们的数据框架中过滤出所有闰年日期。
日期、截止日期和将数据帧分成两个表格
接下来,因为我们已经确保了日期列中的所有日期都是相同的数据类型(例如,它们都是日期而不是字符串),所以我们希望根据测量是发生在截止日期之前还是之后,将数据划分到两个表中。
#creating a separate column for the month
df['Month'] = pd.DatetimeIndex(df['Date']).month
#we are colleting dates between 2005 - 2014, so want to remove any data from dates after 31 Dec 2014
#creating datetime.date format of cutoff date
a= '31122014'
#converting a string into datetime.date object
cutoff_date = datetime.datetime.strptime(a,'%d%m%Y').date()
#dataframe for values after 31 Dec 2014
df2 = df[df['Date'] <= cutoff_date]
#returning dates before cutoff date
df3 = df[df['Date'] > cutoff_date]
因为我们希望表格显示十年期间的月度高点,所以我们还希望创建一个月列。仅将日期列的月份作为整数列出。
对于截止日期,我们添加了一个整数形式的日期,并使用 datetime.date 模块将字符串转换为 datetime.date 对象(类似于我们的 date 列),格式为“日期/月/年”。
然后,我们继续创建 2 个新的数据帧;df2-包含截止日期之前或之时的所有测量值,df3-包含截止日期之后的所有测量值。
发现历史高点和低点
现在我们需要回答这个问题,我们如何按月找到 2005 年到 2014 年这十年间创纪录的高温和低温。
通过使用 head 方法查看我们的数据框架,我们已经知道有一列指定了测量值是否记录为最高温度的最小值。此外,由于我们已经创建了一个 month 列,我们知道它包含了记录测量的月份。为了帮助我们完成这个计算,我们将使用 groupby 。这是一个 pandas 模块,允许我们根据一个或多个标准将数据分组。这可以与聚合方法“aggregate”结合使用,该方法允许我们对数据进行聚合,例如返回最大值或最小值。结合 groupby 我们可以确定我们创建的按月分组的组之间的最大值和最小值。
df_min = df2[df2['Element'] =='TMIN'].groupby('Month').aggregate({'Data_Value':np.min})
df_max = df2[df2['Element'] == 'TMAX'].groupby('Month').aggregate({'Data_Value':np.max})
在这段代码中,我们创建了 2 个数据帧;df_min-在这里,我们过滤了分类为最低温度的测量值,按月分组,找到了十年期间每个月的最低温度,而 df_max-我们过滤了分类为最高温度的测量值,按月分组,找到了每个月的最高温度。
可视化过程
现在我们想开始利用 matplotlib 库来可视化我们的数据。
plt.plot(df_max, '-o',df_min,'-o')

plt.show() output
我们首先将过去十年间的历史高点和低点绘制成一个线形图。这两个将自动以不同的颜色绘制。我们使用“-o”来标识该图表上绘制的每个数据值,这在我们的折线图上表示为一个圆圈。
为了可视化的目的,我们想在最高点和最低点之间的区域加阴影。我相信这将使数据看起来更容易,帮助你关注最高和最低温度以及十年期间记录温度的可变性。
x = df_max.index.values
plt.fill_between(x,df_min.values.flatten(),df_max.values.flatten(), color='grey',alpha='0.5')

plt.show() output
我们使用 fill_between 模块来填充两条线之间的区域。对于 fill_between 函数,您需要有要填充的 x 坐标的长度以及来自 df_min 和 df_max 的坐标/值。
为了找到 x 坐标的长度,我们创建了一个包含 df_max 索引值的 x 变量。这将返回一个 numpy 数组,其中包含与一年中的月份相对应的数字 1-12。
因为 df_min 和 df_max 是数据帧,我们只需要 df_min 和 df_max 的数据帧(温度)中的值。然而,这将返回一个数组的数组,而我们需要一个一维数组来使用 fill_between 模块。然后我们选择将数组展平成一维数组。颜色和 alpha 只允许我们指定填充之间的颜色和色调。
超级气势散点图
为了回答我们的中心问题,我们需要开始查看截止日期之后的记录。为了做到这一点,我们需要按月对数据进行分组,并在我们的折线图上寻找超过最低和最高记录温度的测量值。
#superimpose scatterplot
df3_max = df3[df3['Data_Value'] > df3['Month'].map(df_max['Data_Value'])]
df4_min = df3[df3['Data_Value'] < df3['Month'].map(df_min['Data_Value'])]
plt.scatter(df3_max.Month.tolist(),df3_max['Data_Value'],s=10,c='red') #values over max
plt.scatter(df4_min.Month.tolist(),df4_min['Data_Value'],s=10,c='black') #values under min

plt.show() output
为了将我所在月份的每个温度分组与每个月的最低和最高温度进行比较,我们使用映射函数遍历 df3 数据库,确定每列的月份,并将 Data_Value 列下列出的温度与 df_min 和 df_max 数据帧中该特定月份的值进行比较(取决于我们查看的是最低还是最高温度)。如果该值高于或低于十年期间记录的最高和最低温度,我们将该行添加到新的数据帧中;df3_max 和 df4_min。
现在我们想把我们的散点图叠加到我们的折线图上。对于散点图,我们需要指定 x 轴(月)和 y 轴(数据值)。我们还想指定代表每个值的圆圈的大小和颜色,以区别于折线图圆圈。
让图表更容易解读
如果你打算把这个图表展示给其他人,重要的是要确保这个图表易于理解,并显示你想要它显示的内容,而不误导你的观众。
图表的第一个问题是 x 轴在 2s 内增加,跳过奇数个月。
plt.xticks(list(df_max.index),['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec']) #we want to show all the x values and rename according to the month
为了纠正这一点,我指定了 xticks,并将它们重命名为它们所代表的月份的缩写。
下一个问题是如何记录温度。我们希望以摄氏度显示温度,而不是十分之一度。
df_max['Data_Value'] = df_max['Data_Value']/10
df_min['Data_Value'] = df_min['Data_Value']/10
df4_min['Data_Value'] = df4_min['Data_Value']/10
df3_max['Data_Value'] = df3_max['Data_Value']/10

plt.show() output — I mistakenly left out April bur corrected this error
然后,我将所有温度值除以 10,将十分之一度转换为摄氏度。
最后一个问题是图表没有标注,而且图表的大小使我们很难知道记录的超过每月高温或低温的天数。
plt.title('Min and Max temperatures in Ann Arbor Michigan United States between 2005-2015', fontsize=20)
plt.xlabel('Years', fontsize=20)
plt.ylabel('Temperatures', fontsize=20)
fig = plt.gcf()
fig.set_size_inches(20,10)
plt.tick_params(labelsize=20)
plt.show()

plt.show() output
然后,我开始放大图表,首先获取当前数字(gcf),将其保存为 fig,并以英寸为单位设置 fig 的大小。由于这只会放大数字,我需要同时放大 x 和 y 刻度参数以及 y 和 x 标签的字体大小。
从这张图表中,我们可以看出,2 月份大约有 6 天的最低温度低于 2005 年至 2014 年期间记录的最低温度,而 11 月和 12 月的另外两天的温度超过了 10 年来记录的最高温度。这种解释的意义很大程度上取决于你试图用这种想象回答的问题。
这不是表示该数据的唯一方式,因为您可以选择显示十年期间每天的最高记录温度,并使用它来确定 2015 年后超过先前设定的最高和最低记录的个别日期。如果这样做的话,可能会让你对 2015 年的气温记录有一个更清晰的了解(答案应该不会太不同)。
使用 Python 中的多个绘图库在地图上绘制商业位置

Photo by Andrew Stutesman on Unsplash
我在浏览 Kaggle 时,发现了一个数据集,其中包括经度和纬度的位置。我没有在地图上绘制过地图,所以我决定使用这个数据集并探索各种可用的选项来处理它们。这是一个关于我所做的事情和我对这些库的推断的基本指南。
我们的目标是寻找一个非常容易使用的库,并且可以无缝地在地图上绘制。另一个目标是找到一个可以一次打印数据集中所有点(190,000+点)的库。
在这里,我探索了四个库,gmplot、geopandas、plotly和bokeh。我会在需要的时候导入这些库,而不是一开始就全部导入。完整代码可从 GitHub repo 获得:
一个数据可视化项目,包括多种在地图上绘制位置的方法。-kb22/Python 绘图地图
github.com](https://github.com/kb22/Plot-Maps-in-Python)
我们开始吧!!
资料组
我从 Kaggle 中取出数据集,并将其保存在data文件夹中,命名为dataset.csv。它包括一个完整的地址,州,位置和更多的企业列表。我在不同的数组中提取了纬度、经度、州、unique_states 和名称。我还提取了最小和最大的纬度和经度值,这将有助于我放大世界地图上的特定区域,我们将在下面看到。
对于每个库,我将绘制前 1000 个位置,然后尝试绘制所有的点。
gmplot
gmplot是一个库,它通过创建一个可以在浏览器中加载的 html 文件来生成绘图。这是开始在地图上绘制数据的最简单快捷的方法之一。它在谷歌地图上绘制信息,因此看起来真的很好。
首先,我们创建一个GoogleMapPlotter,它采用描述地图中心的前两个值(在我们的例子中是美国),第三个参数是缩放级别。你可以在这里选择任何数字,但我发现 5 是适合这种情况。数据文件夹中会生成一个文件gmplot.html。让我们在浏览器中打开它。

gmplot without API Key
哎呀!整个地图变暗,被文字For development purposes only覆盖。这是因为现在对谷歌地图 API 的访问是付费的,我们需要一个 API 密钥。为此,你必须在https://console.cloud.google.com上创建一个账户。默认情况下,你可以免费试用 12 个月,有足够的学分来完成这个项目和更多。转到 Google Maps Javascript API 并生成一个 API 密钥。让我们现在把它填上。
只需用 API 键替换文本<API-KEY>。让我们看看新的 HTML 文档是什么样子的。

gmplot with API key
现在,地图已正确生成。由于这是一个实际的谷歌地图,我们可以将左上角的设置改为卫星,放大并根据需要移动地图。接下来,让我们试着在地图上标出所有的位置。
我试图加载 HTML 页面,但它加载了几秒钟,过了一段时间后就没有反应了。我尝试了多次,但无法得到一个工作页面。

Unresponsive page
地质公园
GeoPandas是一个建立在熊猫数据结构上的库,并使用它们在地图上绘制空间数据。
图书馆期望所有的位置都被描述为点。因此,第一步是创建一个点数组,我将它们存储在data_points中。我发现这个库的惊人之处在于,它使得给每个位置添加颜色变得非常简单。如果你熟悉熊猫和 matplotlib,你会觉得很舒服。我在colormap创建了一个基于独特州的颜色列表,并用它来生成所有地点的颜色数组,只需一个循环。
首先,我们需要一张地图来标出这些点。图书馆提供了一些地图,我用过naturalearth_lowres。我简单地用浅灰色的背景和黑色的边界来绘制它。一旦我们准备好地图,我们就可以使用GeoDataFrame来绘制数据。这就像熊猫数据帧,所以我们首先传递数据集,限制为 1000,以便在开始时只绘制 1000 个位置。使用crs定义所需的坐标系,我们在geometry中定义数据。我关闭了轴,并根据纬度和经度值设置了 x-极限和 y-极限。

GeoPandas with 1000 locations
如你所见,每个位置都是蓝色的略微不同的变体。让我们也画出所有的点。

GeoPandas will all points
瞧啊。GeoPandas 能够绘制出所有的点。然而,我在这里发现了两个主要的缺点,包括没有一个交互式地图,以及如何正确使用它的文档非常有限。
Plotly
Plotly是另一个绘制高度交互式图表的库,也允许用户与图表交互。但是,它需要用户名和 API 密钥才能工作。你必须在 https://plot.ly/feed/#/注册并获得一个 API 密匙。
你必须用你自己的价值观取代<USERNAME>和<API-KEY>。
我们使用ScatterGeo来定义我们想要绘制的点,也可以在text参数中传递它们的名称。设置标记也很容易,可以修改其大小,不透明度,颜色等。layout用于定义图的标题、基色等。最后,Figure()方法创建了我们可以使用iplot绘制的图形。

Plotly plot for 1000 locations
我们能够用相同的颜色绘制 1000 个点,但是要用不同的颜色绘制它们,我们必须用不同的颜色绘制不同的ScatterGeo方法。让我们试着画出所有的点。
过了一段时间后生成了这个图,但是所有的位置都是可见的,尽管它非常滞后。甚至会出现一个警告,说对于绘制大量的点,我们应该使用其他方法。这很烦人,因为当我试图将它作为图像返回时,它需要库plotly-ocra,而库plotly-ocra不能与pip一起安装,只能与conda一起工作。

Plotly map for all locations
散景
Bokeh是一个惊人的可视化库,它依赖于 Google Maps 的地图,因此,也需要我们上面使用的 API 键来工作。鉴于它使用谷歌地图,我非常怀疑它能否一次标绘所有的点。
我们将输出保存为bokeh.html。GMapOptions帮助我们定义地图的中心,并根据需要进行缩放。然后我们用这些选项生成一个gmap,并传入 API 密钥。我们将数据定义为 ColumnDataSource,如上面的要点所示。然后,我们在图上将所有点标为circle,并用show显示出来。它加载 html,我们可以看到结果,这是一个交互式地图。

Bokeh plot for 1000 points
所有位置都已正确绘制。让我们试着画出所有的点。
不出所料,该页面打开时显示了地图,但无法工作,也没有显示任何数据点。
结果
从上面的图中,我发现所有的库对于绘制数据点都非常有用,我的观察如下:
- 最简单的入门方法是从
gmplot开始。它需要最少的代码来启动,并且能够产生良好的结果。 - 在
GeoPandas中,根据状态用不同的颜色绘制点是最容易的。很容易画出所有的点,可能是因为这个图不是交互式的,是静态的。 - 最具互动性的是
Plotly的剧情。他们能够绘制出包含所有点的图,但反应较慢,并提出了其他绘制方法。 Bokeh能够用最少的代码生成非常好的谷歌地图。然而,它也没有画出所有的点。
希望你喜欢读这篇文章。这里还有一些你可能会喜欢的文章:
虚拟快速指南
towardsdatascience.com](/python-virtual-environments-made-easy-fe0c603fe601) [## 在 Kaggle 上使用 CNN 进行深度学习以识别疟疾细胞
医学领域的深度学习
towardsdatascience.com](/deep-learning-to-identify-malaria-cells-using-cnn-on-kaggle-b9a987f55ea5) [## 使用 Flask、Flask RESTPlus 和 Swagger UI 处理 API
Flask 和 Flask-RESTPlus 简介
towardsdatascience.com](/working-with-apis-using-flask-flask-restplus-and-swagger-ui-7cf447deda7f) [## 使用 React 和 Flask 创建一个完整的机器学习 web 应用程序
用 UI 和后端构建完整的 ML 应用程序
towardsdatascience.com](/create-a-complete-machine-learning-web-application-using-react-and-flask-859340bddb33)
请随时分享你的想法,想法和建议。我很乐意收到你的来信。
绘制离散和连续随机变量的概率
通过用代码绘制概率来学习分布

理解统计学可以帮助我们看到随机数据中的模式。
让我们讨论两种主要类型的随机变量,以及如何为每一种绘制概率图。
什么是随机变量
据 investopedia 报道。
随机变量是一个值未知的变量,或者是一个为每个实验结果赋值的函数。
与我们对随机性的直觉相反,这个函数的可能值并不是同样可能的。
维基百科言简意赅。一个随机变量…
被非正式地描述为一个变量,其值取决于随机现象的结果。
随机变量依赖于使用随机性的函数,但不一定输出均匀的随机性。
重要提示:当我们谈论一个随机变量时,通常用 X 表示,它的最终值仍然未知。随机变量是“X”。不是 x 的输出不是random.random()的输出。只有 X,有可能的结果和相关的概率。
酷毙了。现在随机变量通常分为两类:
1)离散随机变量
2)连续随机变量
让我们从离散开始,因为它更符合我们人类看待世界的方式。
离散随机变量
据维基百科。
当 X 的图像(或范围)是可数的时,该随机变量被称为离散随机变量,并且其分布可以由概率质量函数来描述,该概率质量函数将概率分配给 X 的图像中的每个值。
如果随机变量只能取有限数量的值,那么它就是离散的。是 PMF(概率质量函数)给每个可能的值分配一个概率。请注意,离散随机变量有 PMF,而连续随机变量没有。
如果您事先不知道 PMF(我们通常不知道),您可以根据与您的随机变量来自相同分布的样本来估计它。
步骤:
1 .从总体中收集一个样本。计算每个值的频率
3。将频率转换成概率
对于一些任意数据,我们可以遵循这个逻辑,其中sample = [0,1,1,1,1,1,2,2,2,2]。
def frequencies(values):
frequencies = {}
for v in values:
if v in frequencies:
frequencies[v] += 1
else:
frequencies[v] = 1
return frequenciesdef probabilities(sample, freqs):
probs = []
for k,v in freqs.items():
probs.append(round(v/len(sample),1))
return probssample = [0,1,1,1,1,1,2,2,2,2]freqs = frequencies(sample)
probs = probabilities(sample, freqs)
x_axis = list(set(sample))plt.bar(x_axis, probs)


PMF (left). Probability distribution (right).
继续我们上面的代码,PMF 计算如下。
dict(zip(x_axis, probs))
#=> {0: 0.1, 1: 0.5, 2: 0.4}
直觉上,所有可能性的概率总是加 1。
经验法则:假设一个随机变量是离散的,如果你能事先列出它可能的所有可能值。这不是定义,而是一个有用的启发。
许多分布是基于离散随机变量的。这些包括伯努利,二项式和泊松分布。
在我们深入研究连续随机变量之前,让我们再看几个离散随机变量的例子。
示例 1:抛硬币(离散)
抛硬币是离散的,因为结果只能是正面或反面。这符合伯努利分布,只有两种可能的结果,一次只能抛一次硬币。
让我们用 numpy 生成数据来对此建模。
import numpy as np
import matplotlib.pyplot as pltn = 1 # number of trials
p = 0.5 # probability of success
sample = np.random.binomial(n, p, 100)plt.hist(sample, bins=10)
并绘制出结果的频率。以及概率。
# using the functions we wrote above
freqs = frequencies(sample)
probs = probabilities(sample, freqs)
x_axis = list(set(sample))plt.bar(x_axis, probs)


请注意值落入的桶的离散数量。
例 2:一个人的四舍五入体重(精确到磅)(离散)
四舍五入的砝码(精确到磅)是不连续的,因为砝码可以落入间隔为 1 磅的不连续桶中。
未舍入的权重是连续的,因此我们将在讨论连续随机变量时再次回到这个例子。
从 Kaggle 下载数据集,并保存在与此笔记本相同的目录下。
import pandas as pddf = pd.read_csv('weight-height.csv')# convert values to integer to round them
df['Height'] = df['Height'].astype(int)
df['Weight'] = df['Weight'].astype(int)df.head(3)
我们将对数据进行采样(如上)并绘制图表。


现在添加density=True将我们的图转换成概率。
plt.hist(weights, bins=20, density=True)

再次注意离散的桶。现在让我们继续讨论连续随机变量。
连续随机变量
据维基百科。
如果像是不可数无穷的,那么 X 称为连续随机变量。
输出可以是一个范围内的无限个值。因此,虽然曲线下的组合概率等于 1,但不可能计算任何单个点的概率——它非常小。可以计算曲线下 x 值范围的概率,但我们不会在这里讨论。
连续随机变量有一个 PDF(概率密度函数),不是 PMF。与 PMF 不同,该函数定义了随分布变化的曲线,而不是列出每个可能输出的概率。
让我们回到我们的重量例子。这一次,权重没有四舍五入。
为什么重量是连续的?因为真正的体重不会整齐地落在 1 磅的区间内。一个人站在磅秤上可以重达 150 磅。但是如果我们放大到分子水平,它们实际上可能有 150.0000001 磅重。
步骤:
1 .在直方图上绘制样本数据
2。找出一个众所周知的样本服从的分布
3。计算从样本
4 生成分布所需的参数。生成 PDF 并绘制在直方图的顶部
让我们用上面的重量例子来做这件事。
如果您还没有从 Kaggle 下载数据集,并将其保存在与笔记本相同的目录中。
检查数据的形状。
import pandas as pd
df = pd.read_csv('weight-height.csv')
df.head(3)

我们将移除男性,让我们的生活更轻松。包括男性和女性将导致双峰分布(2 个峰而不是 1 个),这使我们的计算变得复杂。
df = df[df['Gender']=='Female']
df.head(3)
绘制剩余的重量数据。
weights = df['Weight'].tolist()
plt.hist(weights, bins=20)

这看起来像什么一般的分布?一个正态分布,呵呵。
计算均值和标准差,因为我们需要它们来生成正态分布。
from numpy import mean,stdmean = mean(weights)
std = std(weights)print(mean, std)
#=> 135.86009300746835 19.020565463416645
使用这些参数生成正态分布。
from scipy.stats import norm
distribution = norm(mean, std)
选择我们将绘制 PDF 的范围。
min_weight = min(weights)
max_weight = max(weights)
values = list(range(int(min_weight), int(max_weight)))
生成该范围的概率。
probabilities = [distribution.pdf(v) for v in values]
绘制我们的样本分布和我们生成的 PDF。
from matplotlib import pyplot
pyplot.hist(weights, bins=20, density=True) # ,
pyplot.plot(values, probabilities)

现在我们有了。
我们有意选择了一个服从正态分布的样本来简化这个过程。但是最好了解不同的常见分布类型和生成它们所需的参数。
R 中基于位置的数据可视化
在旧金山城市地图上标出商业高度集中的街区

Photo by Anastasia Petrova on Unsplash
我最近得到了一个处理空间数据的机会,想分享我对一个这样的数据集的分析。
这些数据包括旧金山湾区的各种注册企业,可以在这里找到。更新版本可以在这里找到。
空间数据属于与位置相关联的数据。通常用坐标参考系统(纬度和经度)来描述。
本练习的目标是找到旧金山商业高度集中的街区。你需要从谷歌的地理定位 API 获得一个密钥来使用他们的地图。我使用 R 中的 ggmap 包来绘制这些数据。然后,我将分析范围缩小到一个特定的高度集中的社区,看看企业是如何在该区域内分散的。
首先…快速扫描数据集
str(biz)head(biz, 25)summary(biz)
在这个练习中,我只关心邻居、地址、日期,最重要的是包含每个企业的纬度和经度数据的位置列。企业名称及其代码(由城市为注册企业分配)暂时没有考虑。
在进行了基本的数据清理活动(比如消除重复和空值)之后,我只提取了与旧金山有关的信息,并消除了与海湾地区相邻城市有关的记录。
识别仅与旧金山相关的数据
我有几种方法可以实现这个目标;基于 c 城市或 b 业务地点或邮政编码过滤数据集。我选择使用邮政编码逻辑,因为其他两个字段具有不一致的旧金山城市名称模式,很容易被遗漏。但是,我已经包含了过滤这些数据的所有三种方法的命令。
按邮编
sf_biz_zip <- biz %>% filter(grepl(pattern = "94016|94105|94110|94115|94119|94123|94127|94132|94139|94143|94147|94156|94161|94171|94102|94107|94108|94109|94111|94112|94114|94116|94117|94118|94120|94121|94122|94124|94125|94126|94129|94130|94131|94133|94134|94137|94140|94141|94142|94144|94145|94146|94151|94153|94154|94158|94159|94160|94162|94163|94164|94172|94177|94188", Business.Location))
按城市
sf_biz_city <- biz %>% filter((grepl(".*San Francisco.*|.*SAN FRANCISCO.*|.*SF.*|.*S SAN FRAN.*|.*Sf.*|.*San+francisco.*|.*S+san+fran.*", City)))
通过业务。位置
sf_biz_loc <- biz %>% filter((grepl(".*San Francisco.*|.*SAN FRANCISCO.*|.*SF.*|.*S SAN FRAN.*|.*Sf.*|.*San+francisco.*|.*S+san+fran.*", Business.Location)))
转换日期对象
接下来,我想消除已经不复存在的企业。为此,我对每个地点使用了结束日期。然而,日期字段存储为转换为 posixct 的因子,这通常有助于进一步分析日期。
sf_biz_zip$Business.Start.Date <- as.POSIXct(sf_biz_zip$Business.Start.Date, format = "%m/%d/%Y")sf_biz_zip$Business.End.Date <- as.POSIXct(sf_biz_zip$Business.End.Date, format = "%m/%d/%Y")sf_biz_zip$Location.Start.Date <- as.POSIXct(sf_biz_zip$Location.Start.Date, format = "%m/%d/%Y")sf_biz_zip$Location.End.Date <- as.POSIXct(sf_biz_zip$Location.End.Date, format = "%m/%d/%Y")
过滤掉不活跃的业务
2018 年 12 月 1 日后查封存在的企业予以剔除。
sf_biz_active_zip <- sf_biz_zip %>% filter(is.na(Location.End.Date))sf_biz_active_zip <- sf_biz_zip %>% filter(Location.Start.Date < "2018-12-01")
从业务位置字段中剥离坐标
业务位置列包含地址和坐标信息。因此需要提取纬度和经度信息。
sf_biz_active_zip <- sf_biz_active_zip %>% separate(Business.Location, c("Address", "Location"), sep = "[(]")sf_biz_active_zip <- sf_biz_active_zip %>% filter(!(is.na(Location)))sf_biz_active_zip <- separate(data = sf_biz_active_zip, col = Location, into = c("Latitude", "Longitude"), sep = ",")
其他字符也需要清除。
sf_biz_active_zip$Longitude <- gsub(sf_biz_active_zip$Longitude, pattern = "[)]", replacement = "")
然后,我将纬度和经度变量从离散转换为连续,并将它们存储为数字变量,因为这有助于绘制/可视化数据,并避免错误。
sf_biz_active_zip$Latitude <- as.numeric(sf_biz_active_zip$Latitude)sf_biz_active_zip$Longitude <- as.numeric(sf_biz_active_zip$Longitude)
现在有趣的部分…
可视化数据
产生的数据集有 88,785 条记录,需要绘制在 Google 地图上。至少可以说,在地图上解释这么多的记录是一件非常困难的事情。尽管取样是一种方法,但我还是试图找出拥有最多企业的前 10 个街区,并在地图上画出一个这样的街区。
viz <- sf_biz_active_zip %>% group_by(Neighborhoods...Analysis.Boundaries) %>% tally() %>% arrange(desc(n))col.names(viz)[2] <- “Total_businesses”viz <- viz[1:10, ]
然后我创建了一个前 10 个街区的直方图。
fin_plot <- ggplot(viz, aes(x = Neighborhood, y = Total_Businesses)) + geom_bar(stat = "identity", fill = "#00bc6c")fin_plot <- fin_plot + geom_text(aes(label = Total_Businesses), vjust = -0.2) + theme(axis.text.x = element_text(angle = 45, size = 9, hjust = 1), plot.title = element_text(hjust = 0.5))fin_plot <- fin_plot + ggtitle("Top 10 neighborhoods by business count", size = 2)

让我们更详细地看看金融区/南海滩社区,因为它拥有最大数量的活跃企业。
注册谷歌地图密钥
我安装了“ggmap”、“digest”和“glue”包,然后向 Google API 注册以获得地理位置 API 密钥。
install.packages("ggmap","digest","glue")register_google(key = "<google maps key>")
谷歌提供地形、卫星、混合以及其他类型的地图。我选择使用地形图。简单的谷歌搜索就能给你旧金山的城市坐标。
sf <- c(lon = -122.3999, lat = 37.7846)map <- get_map(location = sf, zoom = 14, scale = 2)
通过调节变焦,你可以看得更清楚。下面两张图片的缩放比例不同
fin_map <- ggmap(map) + geom_point(aes(Longitude, Latitude), data = fin_dis) fin_map <- fin_map + ggtitle("Concentration of businesses in Fin. District and South Beach") + xlab("Longitude") + ylab("Latitude") + theme(plot.title = element_text(hjust = 0.5))

Zoom out view

Zoom in view
更好的可视化
热图可能会使可视化更加直观。为了做到这一点,除了 x 和 y 坐标之外,我们还需要增加一种美感;z .这种美学是特定纬度的商业总数。-朗。组合。
fin_heatmap <- ggmap(map, extent = "device") + stat_summary_2d(data = u, aes(x = Longitude, y = Latitude, z = n), fun = mean, alpha = 0.6, bins = 30) + scale_fill_gradient(name = "Price", low = "green", high = "red")fin_heatmap <- fin_heatmap + ggtitle("Concentration of businesses in Fin. District and South Beach") + xlab("Longitude") + ylab("Latitude") + theme(plot.title = element_text(hjust = 0.5))")

结论
鲍威尔街地铁站、联合广场和恩巴卡德罗地铁站周围的区域有相对较多的商业,而南海滩和林肯山周围的区域人口稀少。
类似地,可以绘制其他单个社区的地图,以了解那里的商业分布。
这是一种非常直观的可视化空间数据的方式。我欢迎任何反馈和建设性的批评。
感谢您的阅读!
使用 t-SNE 绘制文本和图像向量
如何在二维空间中绘制使用 Infersent 和 Resnet 创建的多维向量。

Power to your eye - Happy Visualizing
用可视化创造奇迹
约翰·米特尔南正确地指出,“展示才是真正的娱乐”,如果你能以另一个人希望的方式展示你的数据,他将处于一种幸福的状态。想象一个所有数据的交流都可以可视化的世界,我的大脑和我的老板都会感谢我的这个姿态。维克拉姆·瓦什尼 阿尤什·古普塔。考虑到人脑对于图像内容的舒适性,并且考虑到通过人脑传输的 90%的信息是视觉的,我们已经尝试在 2-D 空间中绘制句子和图像向量,其中每个空间点代表一个句子,并且相似的句子向量将被放置在空间邻近处。
问题陈述:
绘制句子和图像向量,其中句子向量是使用 Infersent 或 Google 的通用句子编码器得出的,图像向量是在 Resnet 50 模型中提取的特征。
我们的句子只不过是产品名称、描述和规格的组合,而图像基本上就是产品图像。要了解产品在 Indiamart 中的含义,请阅读此处。
理解文本和图像向量
文本向量(单词向量或句子向量)是通过使用嵌入技术(如 word2vec、fasttext、Infersent、Google universal sent encoder 等)将文本数据转换为数字形式来创建的。你的向量的维度可能取决于你准备它的技术。例如,Infesent 为每个句子创建 4096 维的向量,而 GUSE(Google 通用句子编码器)为每个句子创建 512 维的向量。
图像向量是使用一些图像特征提取技术创建的图像的特征向量。在我们的例子中,对于每个图像,我们有 512 维的 Resnet 图像向量。
注意:在这篇文章中,我们不会详细讨论一个句子或单词的嵌入(它们将在另一篇博客中单独讨论),而是关注我们如何在散点图上描绘它们。
去多维化再回来
可视化文本和图像特征向量的分布式表示的困难在于它们是多维的(例如,一些向量是 512 维的,而一些是 4096 维的),这比人类可以看到和解释的通常的 2 维或 3 维矢量图高得多。
由于没有直接的方法将这些数据点投影到易于人类理解的图上,我们需要一些强大的方法将这些高维数据映射到 2 或 3 维空间。在这里,SNE 霸王龙为我们开了一个玩笑。
下图显示了如何将每个矢量转换为二维数组,以便于绘图。

Multidimensional Vectors converted to a two-dimensional vector using t-SNE.
什么是 SNE 霸王龙?
t-SNE 代表 t 分布随机邻居嵌入。这是一种降维技术,最适合于高维数据集的可视化。t-SNE 是一种随机化算法,I;e .每次我们运行该算法时,它都会在相同的数据集上返回稍微不同的结果。为了控制这一点,我们用任意值设置一个随机状态。这里使用随机状态来播种算法的成本函数。
给思想上色——散点图
以下步骤将指导您在散点图上描绘您的矢量。
步骤 1: 导入必要的库:
第二步:加载使用不同嵌入技术创建的向量:在这一步中,我们已经加载了使用 GUSE、Infersent 或 Resnet(在图像向量的情况下)创建的大维向量,并使用 t-SNE 算法拟合到 2 d 空间中。
步骤 3:创建一个用户定义的函数,在二维空间中绘制上述向量:我们已经使用了 t-SNE、matplotlib 和 seaborn 的组合来创建散点图。
步骤 4:可视化并保存图形
基于上述句子向量的 K-Means 聚类的可视化
我们使用 K-Means 聚类对相似的句子向量进行分组。形成了 18 个这样的集群。下面的散点图显示了产品在每个集群中的分布。

可视化基于已有的句子向量的聚类。
在这里,句子向量是基于其现有的簇绘制的。每个产品(句子向量)属于某个聚类或类别,并且每个类别都有唯一的颜色。

We can see how the products(sentences) from Earthmoving Bucket are spread across the plot suggesting their nearness to other related clusters.
添加一个轴到你的二维空间:我们可以简单地在轴上设置 x-limit(在我们的例子中从-60 到+ 60)和 y-limit(从-60 到+60)
解码情节
散点图中的每个点代表单个产品中的文本。文本语义相似的产品放在一起。每个点的颜色代表它所属的类别,即类别 0 中的所有句子用红色表示,类别 1 中的所有句子用橙色表示,依此类推。
图像向量的可视化
图中的每个点代表单个图像的特征。相似的图像落在紧密结合的空间中。使用 Resnet 将每个图像转换为特征向量。

Image Vectors plotted in a 2-D Space. On close observation, it is seen that similar images are occupying close spaces while different image vectors are scattered in the plot.
将东西投入使用
散点图对我们有多种应用——它告诉我们现有的图像或句子有多相似,以及当前的分类对于其中映射的产品是否合适。它还帮助我们对一组新的产品或图片进行分类。
在我的 Github 库上查看的完整 python 代码。
了解如何使用 Python 创建漂亮且有洞察力的图表——快速、漂亮且令人惊叹
PYTHON 指南的最终绘图
用 Python 可视化的综合代码指南,解释用 Pandas、Seaborn 和 Plotly 绘图。在那里我们想象,除了别的以外,金钱可以买到幸福。

2018: Regplot showing how Life Ladder (Happiness) is positively correlated with Log GDP per capita (Money)
在今天的文章中,我们将探讨用 Python 绘制数据的三种不同方式。我们将利用 2019 年世界幸福报告中的数据来完成这项工作。我用 Gapminder 和维基百科上的信息丰富了《世界幸福报告》的数据,以便探索新的关系和可视化。
《世界幸福报告》试图回答哪些因素影响着全世界的幸福。
在报告中,幸福被定义为对“坎特里尔阶梯问题”的回答,该问题要求受访者在 0 到 10 的范围内评估他们今天的生活,最糟糕的生活为 0,最好的生活为 10。
在整篇文章中,我将使用**Life Ladder**作为目标变量。每当我们谈论人生阶梯时,想想幸福。
文章的结构

Photo by Nik MacMillan on Unsplash
这篇文章旨在作为一个代码指南和一个参考点,供您在需要查找特定类型的情节时参考。为了节省空间,我有时会将多个图表合并成一个图像。不过放心,你可以在这个回购或者对应的 Jupyter 笔记本里找到所有的底层代码。
目录
我用超链接连接了文章的不同部分,所以如果你不关心介绍,可以直接跳到绘图部分。我不做评判。
我的 Python 绘图史

Photo by Krys Amon on Unsplash
我大概两年前开始比较认真的学习 Python。从那以后,几乎没有一个星期我不惊叹于 Python 本身的简单和易用性,或者生态系统中许多令人惊叹的开源库之一。我熟悉的命令、模式和概念越多,一切就越有意义。
Matplotlib
用 Python 绘图则正好相反。最初,我用 Matplotlib 创建的几乎每个图表看起来都像是 80 年代逃脱的犯罪。更糟糕的是,为了创造这些令人厌恶的东西,我通常要在 Stackoverflow 上花上几个小时。例如,研究一些具体的命令来改变 x 轴的倾斜度,或者做一些类似的傻事。别让我从多图表开始。结果看起来令人印象深刻,以编程方式创建这些图表是一种奇妙的感觉。例如,一次为不同的变量生成 50 个图表。然而,这是如此多的工作,需要你记住一大堆无用的命令。
海生的
了解到 Seaborn 是一种解脱。Seaborn 抽象掉了许多微调。毫无疑问,由此产生的图表的美感是一个巨大的飞跃。但是,它也是建立在 Matplotlib 之上的。通常,对于非标准调整,仍然有必要深入到类似于机器级 matplotlib 代码的地方。
散景
时间匆匆一瞥,我以为散景会成为我的 goto 解决方案。我在研究地理空间可视化的时候遇到了散景。然而,我很快意识到,Bokeh 虽然不同,但和 matplotlib 一样愚蠢地复杂。
Plotly
不久前,我确实尝试过 plot.ly (从现在开始称为 plotly)。同时,再次致力于地理空间数据的可视化。那时候,它似乎比前面提到的图书馆更荒谬。你需要一个帐户,必须通过你的笔记本登录,然后 plotly 会在网上渲染一切。然后,您可以下载生成的图表。我迅速丢弃 plotly。然而,最近,我偶然发现了一个关于 plotly express 和 plotly 4.0 的 Youtube 视频,其中最重要的是,他们摆脱了所有这些在线废话。我摆弄了一下,这篇文章就是它的成果。我想,迟到总比不到好。
Kepler.gl(地理空间数据荣誉奖)
虽然不是 Python 库,但 Kepler.gl 是一个强大的基于网络的地理空间数据可视化工具。您所需要的只是 CSV 文件,您可以使用 Python 轻松创建这些文件。试试看!
我当前的工作流程
最终,我决定使用 Pandas native plotting 进行快速检查,使用 Seaborn 制作我想在报告和演示中使用的图表(视觉效果很重要)。
分布的重要性

Photo by Jonny Caspari on Unsplash
我在圣地亚哥学习时教统计学(Stats 119)。统计 119 是统计学的入门课程。课程包括统计基础知识,如数据聚合(视觉和定量),几率和概率的概念,回归,抽样,以及最重要的分布。这一次,我对数量和现象的理解几乎完全转移到了基于分布(大部分时间是高斯分布)的表述上。
时至今日,我发现这两个量的意义相差如此之远令人惊讶,标准差可以让你抓住一个现象。仅仅知道这两个数字,就可以简单地得出一个特定结果的可能性有多大。人们马上就知道大部分结果会在哪里。它给你一个参考框架来快速区分轶事事件和具有统计意义的事件,而不必通过过于复杂的计算。
一般来说,当面对新数据时,我的第一步是试图可视化它的分布,以便很好地理解数据。
加载数据和包导入

Photo by Kelli Tungay on Unsplash
让我们加载我们将在整篇文章中使用的数据。我对数据做了一些预处理。我在有意义的地方进行内插和外推。
**# Load the data**
data = pd.read_csv('[https://raw.githubusercontent.com/FBosler/AdvancedPlotting/master/combined_set.csv'](https://raw.githubusercontent.com/FBosler/AdvancedPlotting/master/combined_set.csv'))**# this assigns labels per year**
data['Mean Log GDP per capita'] = data.groupby('Year')['Log GDP per capita'].transform(
pd.qcut,
q=5,
labels=(['Lowest','Low','Medium','High','Highest'])
)
数据集包含以下列的值:
- 年份:计量年份(从 2007 年到 2018 年)
- 人生阶梯:受访者根据坎特里尔阶梯,用 0 到 10 分(10 分最好)来衡量他们今天的生活价值
- 对数人均国内生产总值:人均国内生产总值按购买力平价(PPP)计算,按 2011 年不变国际美元进行调整,取自世界银行于 2018 年 11 月 14 日发布的《世界发展指标(WDI)》
- 社会支持:问题回答:“如果你遇到了麻烦,你有亲戚或朋友可以在你需要的时候帮你吗?”
- 出生时的健康预期寿命:出生时的预期寿命是根据世界卫生组织(世卫组织)全球卫生观察数据库的数据构建的,其中包含 2005 年、2010 年、2015 年和 2016 年的数据。
- 做出生活选择的自由:对问题的回答:“你对选择如何生活的自由感到满意还是不满意?”
- 慷慨:对“在过去的一个月里,你有没有向慈善机构捐过钱?”与人均国内生产总值相比
- 对腐败的看法:对“腐败在政府中是否普遍存在?”以及“企业内部的腐败是否普遍?”
- 积极影响:包括前一天快乐、欢笑和享受的平均频率。
- 负面影响:包括前一天焦虑、悲伤和愤怒的平均频率。
- 对国家政府的信心:不言自明
- 民主品质:一个国家有多民主
- 交付质量:一个国家的政策交付情况如何
- Gapminder 预期寿命:来自 Gapminder 的预期寿命
- Gapminder 人口:一国人口
进口
import plotly
import pandas as pd
import numpy as np
import seaborn as sns
import plotly.express as pximport matplotlib%matplotlib inlineassert matplotlib.__version__ == "3.1.0","""
Please install matplotlib version 3.1.0 by running:
1) !pip uninstall matplotlib
2) !pip install matplotlib==3.1.0
"""
快速:熊猫的基本绘图

Photo by Marvin Meyer on Unsplash
andas 有内置的绘图功能,可以在系列或数据帧上调用。我喜欢这些绘图功能的原因是它们简洁,使用了相当智能的默认设置,并且能够快速给出正在发生的事情的想法。
要创建一个图,在数据上调用.plot(kind=<TYPE OF PLOT>),如下所示:
np.exp(data[data['Year']==2018]['Log GDP per capita']).plot(
**kind='hist'**
)
运行上面的命令将产生下面的图表。

2018: Histogram of the number of countries per GDP per Capita bucket. Not surprisingly, most countries are poor!
在绘制熊猫图时,我使用了五个主要参数:
**kind**: 熊猫已经知道你要创造什么样的剧情,以下选项可用hist, bar, barh, scatter, area, kde, line, box, hexbin, pie。**figsize**: 允许覆盖 6 英寸宽、4 英寸高的默认输出尺寸。figsize期望一个元组(例如,我经常使用的figsize=(12,8))**title**: 给图表添加标题。大多数时候,我用这个来澄清图表中显示的任何内容,这样当我回到图表中时,我可以很快确定发生了什么。title需要一个字符串。**bins**: 允许覆盖直方图的框宽度。bins需要一个列表或类似列表的值序列(例如,bins=np.arange(2,8,0.25))**xlim/ylim**: 允许覆盖轴的最大值和最小值的默认值。xlim和ylim都需要一个元组(例如xlim=(0,5))
让我们快速浏览不同类型的可用图。
垂直条形图
data[
data['Year'] == 2018
].set_index('Country name')['Life Ladder'].nlargest(15).plot(
kind='bar',
figsize=(12,8)
)

2018: List of 15 happiest countries is led by Finnland
水平条形图
np.exp(data[
data['Year'] == 2018
].groupby('Continent')['Log GDP per capita']\
.mean()).sort_values().plot(
kind='barh',
figsize=(12,8)
)

Average GDP per capita by continent in 2011 USD Dollars clearly led by Australia and New Zealand
箱形图
data['Life Ladder'].plot(
kind='box',
figsize=(12,8)
)

Box plot of the distribution of Life Ladder shows that the median is somewhere around 5.5 ranging from values below 3 to up 8.
散点图
data[['Healthy life expectancy at birth','Gapminder Life Expectancy']].plot(
kind='scatter',
x='Healthy life expectancy at birth',
y='Gapminder Life Expectancy',
figsize=(12,8)
)

Scatter plot of the World Happiness Report life expectation against the Gapminder life expectation shows a high correlation between the two (to be expected)
赫克宾图表
data[data['Year'] == 2018].plot(
kind='hexbin',
x='Healthy life expectancy at birth',
y='Generosity',
C='Life Ladder',
gridsize=20,
figsize=(12,8),
cmap="Blues", # defaults to greenish
sharex=False # required to get rid of a bug
)

2018: Hexbin plot, plotting life expectancy against generosity. The color of bins indicates the average of life ladder in the respective bin.
圆形分格统计图表
data[data['Year'] == 2018].groupby(
['Continent']
)['Gapminder Population'].sum().plot(
kind='pie',
figsize=(12,8),
cmap="Blues_r", # defaults to orangish
)

2018: Pie chart showing the total population by continent
堆积面积图
data.groupby(
['Year','Continent']
)['Gapminder Population'].sum().unstack().plot(
kind='area',
figsize=(12,8),
cmap="Blues", # defaults to orangish
)

Population numbers accross the globe are on the rise.
折线图
data[
data['Country name'] == 'Germany'
].set_index('Year')['Life Ladder'].plot(
kind='line',
figsize=(12,8)
)

Line chart depicting the development of happiness in Germany.
关于用熊猫绘图的结论
用熊猫绘图很方便。它很容易访问,而且速度很快。情节相当丑陋。偏离缺省值几乎是不可能的,这没关系,因为我们有其他工具来制作更美观的图表。继续前往锡伯恩。
漂亮:与 Seaborn 的高级绘图

Photo by Pavel Nekoranec on Unsplash
eaborn 利用绘图默认值。要确保您的结果与我的匹配,请运行以下命令。
sns.reset_defaults()
sns.set(
rc={'figure.figsize':(7,5)},
style="white" # nicer layout
)
绘制单变量分布
如前所述,我是发行版的忠实粉丝。直方图和核密度分布都是可视化特定变量关键特征的有效方法。让我们看看如何在一个图表中生成单个变量的分布或多个变量的分布。

Left chart: Histogram and kernel density estimation of “Life Ladder” for Asian countries in 2018; Right chart: Kernel density estimation of “Life Ladder” for five buckets of GDP per Capita — Money can buy happiness
绘制二元分布
每当我想直观地探索两个或多个变量之间的关系时,通常会归结为某种形式的散点图和对分布的评估。概念上相似的图有三种变化。在这些图中,中心图(散点图、双变量 KDE 图和 hexbin 图)有助于理解两个变量之间的联合频率分布。此外,在中心图的右上方边界,描绘了相应变量的边际单变量分布(作为 KDE 或直方图)。
sns.jointplot(
x='Log GDP per capita',
y='Life Ladder',
data=data,
**kind='scatter' # or 'kde' or 'hex'**
)

Seaborn jointplot with scatter, bivariate kde, and hexbin in the center graph and marginal distributions left and on top of the center graph.
散点图
散点图是一种可视化两个变量的联合密度分布的方法。我们可以通过添加色调在混合中添加第三个变量,通过添加大小参数添加第四个变量。
sns.scatterplot(
x='Log GDP per capita',
y='Life Ladder',
data=data[data['Year'] == 2018],
hue='Continent',size='Gapminder Population'
)**# both, hue and size are optional**
sns.despine() **# prettier layout**

Log GDP per capita against Life Ladder, colors based on the continent and size on population
小提琴情节
小提琴图是箱线图和核密度估计的组合。它的作用类似于一个方框图。它显示了定量数据在分类变量中的分布,以便对这些分布进行比较。
sns.set(
rc={'figure.figsize':(18,6)},
style="white"
)sns.violinplot(
x='Continent',
y='Life Ladder',
hue='Mean Log GDP per capita',
data=data
)sns.despine()

Violin plot where we plot continents against Life Ladder, we use the Mean Log GDP per capita to group the data. It looks like a higher GDP per capita makes for higher happiness
配对图
Seaborn pair 图在一个大网格中绘制两变量散点图的所有组合。我通常觉得这有点信息超载,但它有助于发现模式。
sns.set(
style="white",
palette="muted",
color_codes=True
)sns.pairplot(
data[data.Year == 2018][[
'Life Ladder','Log GDP per capita',
'Social support','Healthy life expectancy at birth',
'Freedom to make life choices','Generosity',
'Perceptions of corruption', 'Positive affect',
'Negative affect','Confidence in national government',
'Mean Log GDP per capita'
]].dropna(),
hue='Mean Log GDP per capita'
)

Seaborn scatterplot grid where all selected variables a scattered against every other variable in the lower and upper part of the grid, the diagonal contains a kde plot.
小平面网格
对我来说,Seaborn 的 FacetGrid 是使用 Seaborn 的最有说服力的理由之一,因为它使创建多情节变得轻而易举。在 pair 图中,我们已经看到了一个 FacetGrid 的例子。FacetGrid 允许创建多个由变量分割的图表。例如,行可以是一个变量(人均 GDP 类别),列可以是另一个变量(大陆)。
它确实需要比我个人喜欢的多一点的定制(例如,使用 matplotlib),但它仍然是令人信服的。
面网格—线状图
g = sns.FacetGrid(
data.groupby(['Mean Log GDP per capita','Year','Continent'])['Life Ladder'].mean().reset_index(),
row='Mean Log GDP per capita',
col='Continent',
margin_titles=True
)
g = (g.map(plt.plot, 'Year','Life Ladder'))

Life Ladder on the Y-axis, Year on the X-axis. The grid’s columns are the continent, and the grid’s rows are the different levels of Mean Log GDP per capita. Overall things seem to be getting better for the countries with a Low Mean Log GDP per Capita in North America and the countries with a Medium or High Mean Log GDP per Capita in Europe
FacetGrid —直方图
g = sns.FacetGrid(data, col="Continent", col_wrap=3,height=4)
g = (g.map(plt.hist, "Life Ladder",bins=np.arange(2,9,0.5)))

FacetGrid with a histogram of LifeLadder by continent
FacetGrid —带注释的 KDE 图
还可以向网格中的每个图表添加特定于方面的符号。在下面的示例中,我们添加了平均值和标准偏差,并在平均值处画了一条垂直线(代码如下)。

Life Ladder kernel density estimation based on the continent, annotated with a mean and standard deviation
Draw a vertical mean line and annotation
FacetGrid —热图图
我最喜欢的绘图类型之一是热点图 FacetGrid,即网格中每个方面的热点图。这种类型的绘图对于在一个单独的绘图中可视化四个维度和一个指标非常有用。代码有点麻烦,但可以根据您的需要快速调整。值得注意的是,这种图表需要相对大量的数据或适当的分段,因为它不能很好地处理缺失值。

Facet heatmap, visualizing on the outer rows a year range, outer columns the GDP per Capita, on the inner rows the level of perceived corruption and the inner columns the continents. We see that happiness increases towards the top right (i.e., high GDP per Capita and low perceived corruption). The effect of time is not definite, and some continents (Europe and North America) seem to be happier than others (Africa).
heatmap_facetgrid.py
牛逼:用 plotly 创造牛逼的互动情节

Photo by Chris Leipelt on Unsplash
终于,再也没有 Matplotlib 了!Plotly 有三个重要特征:
- 悬停:当悬停在图表上时,会弹出注释
- 交互性:无需任何额外的设置,图表可以是交互式的(即穿越时间的旅程)
- 漂亮的地理空间图表: Plotly 已经内置了一些基本的制图功能,但是另外,人们可以使用 mapbox 集成来制作令人惊叹的图表。
散点图
我们通过运行fig = x.<PLOT TYPE>(PARAMS)和fig.show()来调用 plotly plots,如下所示:
fig = px.scatter(
data_frame=data[data['Year'] == 2018],
x="Log GDP per capita",
y="Life Ladder",
size="Gapminder Population",
color="Continent",
hover_name="Country name",
size_max=60
)
fig.show()

Plotly scatter plot, plotting Log GDP per capita against Life Ladder, where color indicates continent and size of the marker the population
散点图——漫步时光
fig = px.scatter(
data_frame=data,
x="Log GDP per capita",
y="Life Ladder",
animation_frame="Year",
animation_group="Country name",
size="Gapminder Population",
color="Continent",
hover_name="Country name",
facet_col="Continent",
size_max=45,
category_orders={'Year':list(range(2007,2019))}
)fig.show()

Visualization of how the plotted data changes over the years
平行类别——可视化类别的有趣方式
def q_bin_in_3(col):
return pd.qcut(
col,
q=3,
labels=['Low','Medium','High']
)_ = data.copy()
_['Social support'] = _.groupby('Year')['Social support'].transform(q_bin_in_3)_['Life Expectancy'] = _.groupby('Year')['Healthy life expectancy at birth'].transform(q_bin_in_3)_['Generosity'] = _.groupby('Year')['Generosity'].transform(q_bin_in_3)_['Perceptions of corruption'] = _.groupby('Year')['Perceptions of corruption'].transform(q_bin_in_3)_ = _.groupby(['Social support','Life Expectancy','Generosity','Perceptions of corruption'])['Life Ladder'].mean().reset_index()fig = px.parallel_categories(_, color="Life Ladder", color_continuous_scale=px.colors.sequential.Inferno)
fig.show()

Seems like not all countries with high life expectations are happy!
条形图—交互式过滤器的一个示例
fig = px.bar(
data,
x="Continent",
y="Gapminder Population",
color="Mean Log GDP per capita",
barmode="stack",
facet_col="Year",
category_orders={"Year": range(2007,2019)},
hover_name='Country name',
hover_data=[
"Mean Log GDP per capita",
"Gapminder Population",
"Life Ladder"
]
)
fig.show()

Filtering a bar chart is easy. Not surprisingly, South Korea is among the wealthy countries in Asia.
Choropleth 图——幸福如何随时间变化
fig = px.choropleth(
data,
locations="ISO3",
color="Life Ladder",
hover_name="Country name",
animation_frame="Year")fig.show()

Map visualization of how happiness evolves over the years. Syria and Afghanistan are at the very end of the Life Ladder range (unsurprisingly)
总结和结束语
今天到此为止。在本文中,您学习了如何成为一名真正的 Python 可视化忍者。你学会了如何更有效地进行快速探索,以及如何在该死的董事会开会时制作更漂亮的图表。您学习了如何创建交互式 plotly 图表,这在绘制地理空间数据时尤其有用。
如果你发现了一些奇妙的新的可视化效果,想要给出反馈,或者只是简单地聊聊天,请在 LinkedIn 上联系我。
如果你喜欢你所读的,看看我在 Medium 上写的其他文章。
用 5 个简单的步骤制作武器探测器

目标检测
在自定义数据集上使用 mask-RCNN 进行目标检测
对象检测是编码库中的一个有用工具。
它构成了许多奇妙工业应用的基础。其中一些是自动驾驶汽车、医学成像和人脸检测。
在我上一篇关于物体检测的文章中,我谈到了物体检测模型是如何发展的。
但是如果我们不能实现它,理论有什么用呢?
这篇文章是关于在我们定制的武器数据集上实现并获得一个物体探测器。
我们今天要特别解决的问题是使用 Mask-RCNN 的实例分割。
实例分割
我们可以为图像中的每个物体创建 蒙版 吗?具体来说就是:

解决这个问题最常见的方法是使用 Mask-RCNN。Mask-RCNN 的架构如下所示:

基本上,它包括:
- 像 resnet50/resnet101 这样的主干网络
- 区域提案网络
- ROI-对齐层
- 两个输出图层-一个用于预测掩膜,一个用于预测类和边界框。
还有很多事情要做。如果你想了解更多的理论,请阅读我的上一篇文章。
简单解释!!!我试过了
towardsdatascience.com](/a-hitchhikers-guide-to-object-detection-and-instance-segmentation-ac0146fe8e11)
这篇文章主要是关于代码。
1.为实例分段创建自定义数据集

Our Dataset
我们将要研究的用例是一个武器探测器。武器探测器是一种可以与街头摄像机和闭路电视一起使用来打击犯罪的东西。所以它非常漂亮。
因此,我开始从开放图像数据集下载 40 张枪和剑的图像,并使用 VIA 工具对它们进行注释。现在在 VIA 中设置注释项目是非常重要的,所以我将尝试一步一步地解释它。
1.通过设置
VIA 是一个注释工具,使用它你可以注释图像的边界框和遮罩。我发现它是最好的注释工具之一,因为它是在线的,可以在浏览器中运行。
要使用它,打开http://www.robots.ox.ac.uk/~vgg/software/via/via.html
您将看到如下页面:

接下来我们要做的是在 region_attributes 中添加不同的类名。在这里,我根据我们的用例添加了“枪”和“剑”,因为这是我想要注释的两个不同的目标。

2.注释图像
我把所有的文件都放在了文件夹data里。下一步是添加我们想要注释的文件。我们可以使用 VIA 工具中的“添加文件”按钮在data文件夹中添加文件。并在选择折线工具后开始标注标签,如下所示。

Click, Click, Enter, Escape, Select
3.下载注释文件
点击 VIA 工具顶部菜单上的save project。

通过更改项目名称字段,将文件另存为via_region_data.json。这将把注释保存为 COCO 格式。
4.设置数据目录结构
我们将需要首先设置数据目录,以便我们可以进行对象检测。在下面的代码中,我创建了一个我们将要使用的模型所需要的目录结构。
运行上述代码后,我们将获得以下文件夹结构中的数据:
- procdata
- train
- img1.jpg
- img2.jpg
- via_region_data.json
- val
- img3.jpg
- img4.jpg
- via_region_data.json
2.设置编码环境
我们将使用来自[matterport/Mask_RCNN](https://github.com/matterport/Mask_RCNN) GitHub 库的代码。您可以从克隆存储库和安装所需的库开始。
git clone [https://github.com/matterport/Mask_RCNN](https://github.com/matterport/Mask_RCNN)
cd Mask_RCNN
pip install -r requirements.txt
一旦我们完成了依赖项的安装和 repo 的克隆,我们就可以开始实现我们的项目了。
我们在Mask_RCNN文件夹中复制了一份samples/balloon目录,并创建了一个***samples/guns_and_swords*** 目录,我们将在这里继续我们的工作:
cp -r samples/balloon ***samples/guns_and_swords***
设置代码

Yes. We are doing AI
我们从重命名开始,将***samples/guns_and_swords*** 目录中的 balloon.py 改为 ***gns*.py**。balloon.py文件现在只训练一个目标。我已经扩展到使用多个目标。在这个文件中,我们更改了:
balloonconfig至gnsConfigBalloonDataset到gnsDataset:我们在这里修改了一些代码,以便从我们的注释数据中获取目标名称,并给出多个目标。- 以及
train功能的一些变化
此处仅显示更改后的gnsConfig以让您有所了解。你可以在这里看一下整个[gns.py](https://github.com/MLWhiz/data_science_blogs/blob/master/object_detection/guns_and_swords/gns.py)代码。
3.可视化图像和遮罩
一旦我们完成了对gns.py文件的修改,我们就可以可视化我们的遮罩和图像了。简单按照这个[Visualize Dataset.ipynb](https://github.com/MLWhiz/data_science_blogs/blob/master/object_detection/guns_and_swords/1.%20Visualize%20Dataset.ipynb) 笔记本就可以了。




4.用迁移学习训练 MaskRCNN 模型
为了训练 maskRCNN 模型,在 Guns and Swords 数据集上,我们需要在命令行上运行以下命令之一,这取决于我们是否要用 COCO 权重或 imagenet 权重初始化我们的模型:
# Train a new model starting from pre-trained COCO weights
python3 gns.py train — dataset=/path/to/dataset — weights=coco# Resume training a model that you had trained earlier
python3 gns.py train — dataset=/path/to/dataset — weights=last# Train a new model starting from ImageNet weights
python3 gns.py train — dataset=/path/to/dataset — weights=imagenet
与weights=last的命令将从最后一个纪元恢复训练。重量将保存在Mask_RCNN文件夹的logs目录中。
这就是我们最后一个纪元后的损失。

使用 Tensorboard 可视化损失
你可以利用 tensorboard 来可视化你的网络是如何运行的。只需运行:
tensorboard --logdir ~/objectDetection/Mask_RCNN/logs/gns20191010T1234
你可以在
[https://localhost:6006](https://localhost:6006)
这是我们面具损失的样子:

我们可以看到,验证损失表现得相当突然。这是意料之中的,因为我们在验证集中只保留了 20 张图像。
5.新图像预测
预测一个新的图像也很容易。使用我们训练过的模型,按照[prediction.ipynb](https://github.com/MLWhiz/data_science_blogs/blob/master/object_detection/guns_and_swords/2.%20predict.ipynb)笔记本来看一个最小的例子。下面是代码的主要部分。
这是验证集中一些图像的结果:


丰富
结果看起来不是很有希望,还有很多需要改进的地方,但这是意料之中的,因为训练数据非常少(60 张图像)。人们可以尝试做以下事情来提高这种武器探测器的模型性能。
- 由于时间限制,我们只对 60 幅图像进行了训练。当我们使用迁移学习时,数据仍然太少——注释更多的数据。
- 训练更多的时代和更长的时间。看看验证损失和培训损失是什么样子的。
- 更改
Mask_RCNN目录下mrcnn/config文件中的超参数。关于这些超参数意味着什么的信息,看看我以前的帖子。你可以看看主要的几个:
# if you want to provide different weights to different losses
LOSS_WEIGHTS ={'rpn_class_loss': 1.0, 'rpn_bbox_loss': 1.0, 'mrcnn_class_loss': 1.0, 'mrcnn_bbox_loss': 1.0, 'mrcnn_mask_loss': 1.0}# Length of square anchor side in pixels
RPN_ANCHOR_SCALES = (32, 64, 128, 256, 512)# Ratios of anchors at each cell (width/height)
# A value of 1 represents a square anchor, and 0.5 is a wide anchor
RPN_ANCHOR_RATIOS = [0.5, 1, 2]
结论

Photo by Christopher Gower on Unsplash
在这篇文章中,我谈到了如何使用 Mask-RCNN 为自定义数据集实现实例分割。
我试图使编码部分尽可能简单,并希望你发现代码有用。在本文的下一部分,我将使用 web 应用程序部署这个模型。敬请关注。
你可以在 Github 下载带注释的武器数据和代码。
如果你想了解更多关于各种 物体检测技术,运动估计,视频中的物体跟踪等 。,我想推荐这个关于计算机视觉深度学习的很棒的课程
谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 媒体 关注我,或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系。
此外,一个小小的免责声明——在这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
Google BigQuery 中的加号代码(开放位置代码)和脚本
我们不是已经有街道地址了吗?
几周前,我参加了 2019 年谷歌的 Geo For Good 峰会。这是一次令人惊叹的经历,对于我见过的人、每个人正在做的工作以及会议本身,我说不出有多好。
当我参加会议时,我会随机选择至少一个我不熟悉的主题。我们都花了太多的时间专注于巩固我们的专业领域,有时会错过很多其他重要的话题。这就是我拓展业务的方式。
我选择了“带加号代码的数字寻址”,这是最让我难忘的一次会议。
那么什么是加代码?它是由开放位置代码地理编码系统生成的代码的名称。这个代码给你一个四位数的区号(只有在 25 公里内没有最近的城镇时才需要),和一个六位数的本地代码(注意,即使它们是字母数字,在加号代码中术语称每个字符为一个“数字”)。在最后两位数字之前有一个“+”符号,它实际上并不传达任何数据,只是为了更容易记住(很像电话号码中的破折号和空格)。十位数格式看起来像“8FVC9G8F+6W”(这是谷歌在苏黎世的办公室所在地)。如果包括城镇名称,还可以使用最后六位数字,因此这将是“9G8F+6W 苏黎世,瑞士”如果你在谷歌地图中输入任何一种格式,它会带你去那里。
谷歌地图多年来一直在搜索结果中包含加号代码。如果你曾经在地图上看到一个随机的字符串,并想知道那是什么,那么,这就是它。

It’s been hiding there the whole time!
如果你想全面了解 Plus Codes(它的动机、优点、缺点等等。),强烈推荐在线阅读开放位置代码定义。
简而言之,它实际上是将整个地球转换成一个 20 度乘 20 度的网格。第一对数字定位该网格中的单元格。从那里开始,每一个后续对都表示该单元格内网格中的一个位置,直到到达最后一对。可以把它想象成地图上不同级别的缩放。

Needs more zoom!
真正酷的事情是,一个十位数的代码(或一个六位数的城市名称代码)提供足够的精度来识别一个 14x14 米的正方形。考虑到使用它需要记住的信息是如此之少,这已经很不错了。
我想在我位于纽约的谷歌办公室尝试一下。地址是纽约州纽约市第八大道 111 号,邮编 10011。该坐标的加号代码是纽约 PXRX+86。

PXRX+86 New York points to 16th St. and 8th Ave… I think.
完整代码是 87G7PXRX+86。我们提到的前两位数字给出了网格的最高级别。那看起来像什么?

Here’s 87. That’s a big grid.
如果我们看看这个网格,我们可以看到额外的 G7 加入了这个网格。

87G7. Enhance.
你添加的数字对越多,你能得到的细节就越多。

87G7PX. Enhance
最终,你可以钻到我们快乐的小角落。你可以在地图下方的红框中看到这一点。

87G7PXRX+86, in red. Enhance.
如果你想加入 Plus Code fun,可以在 Plus Codes 网站上找到这个地图工具!
你可以看到这些代码可以变得多么精确。这比普通的街道地址要准确得多,也更有意义。大多数时候,街道地址甚至不会带你去你想去的地方。例如,如果你想联系约翰霍普金斯大学,街道地址是 3400 N Charles Street,Baltimore,MD 21218 。尽管查尔斯街北 3400 号实际上并不在查尔斯街北,但校园的其他部分都有相同的街道地址,不管它是哪栋建筑。
Plus 代码可以提供足够的精度来指引某人到建筑物的确切位置。让我们回头看看更早的 Google NYC 的例子。

So… where am I supposed to go?
这栋楼的地址有些问题。第一,它没有告诉你确切的方向。这座大楼有三个访客入口。一个在第 16 街和第 9 大道的建筑西北侧,一个在第 16 街和第 8 大道的东北侧,一个在第 15 街和第 8 大道的东南侧。
但是在你用访客入口玩“三号门后面是什么”之前,你需要确保你有正确的地址。原来这栋楼有两个有效的街道地址。第八大道 111 号和第九大道 76 号。
有了加号代码,我们可以得到粒度。NW 入口为 PXRW+R7 ,NE 入口为 PXRX+C8 ,SE 入口为 PXRX+75 。
你可能认为这都是微不足道的。你可以很容易地给一个朋友一个街道地址,然后说“去东北入口”,这很容易找到。然而,在世界上的许多地方,地址精确度的相同问题有非常真实的后果。
在大多数工业化国家,物理地址——当然还有电子地址——是日常生活的一部分。就像道路、自来水和卫生服务一样,物理地址通常被视为理所当然。然而,在发展中国家,实际地址往往只存在于大城市中心。在这些国家,许多街道没有名字,房产也没有编号。因此,公共服务和企业很难或不可能接触到它们的目标客户……在大多数工业化国家,物理地址被认为是理所当然的。但是对于数十亿人来说,地址实际上并不存在。(面向世界——万国邮政联盟)
在峰会的 Plus Codes 会议上,我们听取了纳瓦霍部落成员分享他们在犹他州/新墨西哥州/亚利桑那州地区面临的挑战,以及 Plus Codes 如何帮助解决这些挑战。
在像这样的区域,你会得到很多远离街道地址所在道路的位置,你会得到几英里不正确的地址,并且通常会有很多定位问题。

The officially listed address, and where the building actually is. When it’s this far off, it creates a lot of issues.
这是另一个例子,有人讲述了他们将包裹运送到这个地区的经历。
大约在 2005 年,我想在网上购买一台新的笔记本电脑,并试图让联邦快递将包裹送到我在 AZ dil kon 的 hogan,因为我住的地方距离主要公路(纳瓦霍 15 号公路)大约 0.5 英里。我提交给联邦快递的地址如下:
名称
迪尔康分会办公室以北 1 英里
灰色屋顶的蓝色霍根
亚利桑那州温斯洛,邮编 86047在命令的进一步指示部分,我写了如下内容:从分会办公室向北,经过警察局和一座大教堂。经过教堂建筑,你会穿过一个大水池。这条路有点弯曲,但一直向北走。你会看到一片大树。朝那些树走。他们应该是这个地区唯一的一片树林。我的霍根会在那些树附近,蓝色的霍根有灰色的屋顶。我也留下了我的手机号码,以防有什么问题。
我通过联邦快递网站跟踪递送过程,并确保在递送当天我可以通过电话联系到。我暗暗希望我的指示足以让送货员找到我的霍根,把包裹送出去。然而,下午 4 点左右,我接到了联邦快递送货员的电话。他迷路了。他曾试图按照指示走,但最终却来到了一所没有人的房子里。在请他描述了这个地区之后,我断定他走得还不够远。事实上,他还在去我家的半路上,在大教堂附近,还没有去洗澡。所以当他在打电话和开车的时候,我把他带到了我的家,当他描述他所看到的时候,我给了他一个接一个的方向。
最终他到了我家,但这次经历并不理想。(司机不知道什么是霍根。)
从我在韩国长大的经历中,听到这样的事情让我产生了共鸣。直到 2000 年中期,我们还没有传统意义上的建筑地址。有明确的区域和街区,但建筑物没有街道号码。指路的时候,你只需要知道东西在哪里。告诉别人类似“从地铁站的第六个出口出去,在全家超市后左转进入小巷,继续走,它就在 LG 手机店上面的第二层”这样的话很常见。街道地址通常不会被使用,现在也不会。
首尔相当发达,所以对我的影响可能是见朋友晚了一点。然而在其他领域,这是一个严重的问题。
Finding addresses in rural Utah and Arizona.
Finding addresses in Kolkata, India.
没有明确的位置不仅会影响包裹的递送,还会影响急救人员的效率、获得出生证明、投票等等。这种情况发生在纳瓦霍部落、加尔各答,无疑还有世界上无数其他地区。加码可以帮助,影响的机会是有意义的。
BigQuery 脚本和过程
我喜欢 BigQuery 的原因有很多。在使用了许多不同的遗留数据技术之后,BigQuery 是最容易维护的,并且最擅长立即让我对数据做一些很酷的事情,而不是坐在这里探索基础设施,想知道为什么无论是什么都不能与无论是什么都不能工作。
话虽如此,没有任何事物和任何人是完美的。BigQuery 缺少一些我正在寻找的东西。脚本和存储过程曾经是一个很好的例子。
不久前,这些功能公开发布。
这让我非常非常开心。
Plus 代码和 BigQuery 脚本。
Open Location Code 有一个不同语言的开源代码库,可以生成和读取 Plus 代码。当 BigQuery 脚本发布时,我认为创建编码 Plus 代码的东西可能是引入这个新特性的一个好方法。它允许轻松地批量编码坐标,BigQuery 在大规模处理中带来了许多好处。
让我们看看编码逻辑。根据规范,这是一个非常简单的编码程序:
总结:在纬度上加 90,在经度上加 180,强制它们进入正范围。使用上面的符号将纬度和经度编码为基数 20,每个数字五位,即位值为 0.000125。从纬度开始,交错数字。
下面提供了一种从最低有效位到最高有效位对值进行编码的算法:
1 —在纬度上加 90 度,在经度上加 180 度,两者都乘以 8000,将整数部分分别作为纬度和经度
2 —用具有经度模数 20 的整数部分的符号作为现有代码的前缀
3—用具有纬度模数 20 的整数部分的符号作为现有代码的前缀
4 —将经度和纬度除以 20
5 —从步骤 2 开始重复四次以上。
看起来像是一个简单的循环,我们可以在脚本中完成。
让我们使用来自不同谷歌办公室的经度和纬度来看看它是如何工作的。办公地点来自六大洲的多个国家,因此我们可以对两个半球进行现场测试。这些位置将作为脚本中的输入表。
# Create array of encoding values.
DECLARE OLC_DIGITS_ ARRAY<STRING> DEFAULT ['2','3','4','5','6',
'7','8','9','C','F','G','H','J','M','P','Q','R','V','W','X'];# Control variable.
DECLARE loopControl INT64 DEFAULT 0;# Create example source data.
CREATE OR REPLACE TEMP TABLE
pc_data (
latitude float64,
longitude float64,
expectedCode STRING);INSERT
pc_data (
latitude,
longitude,
expectedCode)
VALUES
(40.740737, -74.002047,'87G7PXRX+75'),
(37.500172, 127.036404,'8Q99G22P+3H'),
(35.660797, 139.729442,'8Q7XMP6H+8Q'),
(-23.586436, -46.681907,'588MC879+C6'),
(12.993711, 77.660769,'7J4VXMV6+F8'),
(-26.073533, 28.032052,'5G5CW2GJ+HR'),
(-33.864971, 151.195854,'4RRH45PW+28'),
(59.333260, 18.054152,'9FFW83M3+8M');# STEP 1 - Normalize coordinates.
CREATE temp table olc_table AS (
SELECT
*,
FLOOR((latitude + 90) * 8000) AS workingLatitude,
FLOOR((longitude + 180) * 8000) AS workingLongitude,
'' AS plusCode
FROM
pc_data);
SELECT
*
FROM
olc_table;
# STEP 2 - Loop through the encoding loops.
SET
loopControl = 0;
WHILE
loopControl < 5 DO CREATE OR REPLACE TEMP TABLE olc_table AS(
SELECT
* EXCEPT (
workingLatitude,
workingLongitude,
plusCode),
workingLongitude / 20 AS workingLongitude,
workingLatitude / 20 AS workingLatitude,
CONCAT(
OLC_DIGITS_[
OFFSET(MOD(CAST(FLOOR(workingLatitude) AS INT64), 20))],
OLC_DIGITS_[
OFFSET(MOD(CAST(FLOOR(workingLongitude) AS INT64), 20))],
plusCode) AS plusCode
FROM
olc_table );
SET
loopControl = loopControl + 1;
SELECT
*
FROM
olc_table;
END WHILE;# STEP 3 - Add the Plus!
CREATE OR REPLACE temp table olc_table AS(
SELECT
* EXCEPT (plusCode,
workingLongitude,
workingLatitude),
CONCAT( SUBSTR(plusCode, 0, 8), '+', SUBSTR(plusCode, -2)) AS plusCode
FROM
olc_table );# Look at the results!
SELECT
*
FROM
olc_table;
当我们运行它时,通常显示结果数据的底部面板看起来像这样。

A script’s output.
这是新的输出信息,显示了 BigQuery 运行。这对于调试很方便,因为您可以看到中间数据进入每个执行的语句。如果您在最后一步单击“查看结果”按钮,您将找到脚本结果。

Much success!
我希望代码易于阅读(请留下带问题的评论),但只是对我们在这里做的一些事情做一些注释。
- 在步骤 2 的循环中,我们创建和/或替换一个临时表。这是与脚本一起发布的新特性,我们将使用它来对整个列执行操作。
- 在脚本中,我们手动创建一个临时表作为源表。如果您想在另一个有坐标数据的表上运行这个,您可以去掉那个特定的部分,让创建的
olc_table指向您的源表。 - 如果您想将输出写到另一个表,只需在最后一个
SELECT语句的末尾加上一个CREATE TABLE [dataset.table] AS,就可以了。
从这里你可以把这个脚本放到一个存储过程中,并把它合并到其他工作流中!
现在,您可以快速浏览 BigQuery 脚本和编码坐标,并批量添加代码!
要了解更多关于 Plus Codes 的信息,请务必查看公开发布的 2019 年 Geo for Good Summit Plus Codes 会议的幻灯片内容。这是纳瓦霍部落的人们出席的会议。另外,请查看议程页面,了解幻灯片内容以及任何适用的培训材料发布到的所有其他会议!谷歌地球上有很棒的东西,地球引擎就在那里!
此外,请务必查看为解决未解决的问题所做的工作,这些工作有助于让世界更容易了解这些位置。
基于深度学习的胸片肺炎检测

肺炎占全球 5 岁以下儿童死亡总数的 15%以上。2015 年,有 92 万名 5 岁以下儿童死于该疾病。虽然肺炎很常见,但准确诊断却很困难。它需要由训练有素的专家审查胸片(CXR ),并通过临床病史、生命体征和实验室检查进行确认。
胸片基础知识
在拍摄图像的过程中, X 射线穿过身体,到达另一侧的探测器。具有稀疏物质的组织,如充满空气的肺,不吸收 X 射线,在图像中显示为黑色。骨骼等致密组织吸收 X 射线,在图像中显示为白色。简而言之
*黑色=空气
*白色=骨头
*灰色=组织或液体
按照惯例,主体的左侧在屏幕的右侧。还可以看到右上角的小 L。我们在正常图像中看到的肺部是黑色的,但它们有不同的投影——主要是胸腔骨骼、主气道、血管和心脏。
胸部 x 光照片示例如下:

肺炎通常表现为 CXR 上一个或多个区域的肺部阴影增加。

然而,在 CXR 上肺炎的诊断是复杂的,因为肺部有许多其他情况,如液体超载(肺水肿)、出血、容量损失(肺不张或虚脱)、肺癌、或放疗后或手术后的变化。在肺外,胸膜腔内的液体(胸腔积液)在 CXR 上也表现为阴影增加。如果可以的话,比较患者在不同时间点的 CXRs 以及与临床症状和病史的相关性有助于做出诊断。
此外,临床医生每次轮班都要阅读大量图像。疲劳或分心的临床医生可能会错过图像中的重要细节。在这里,自动图像分析工具可以提供帮助。例如,人们可以使用机器学习来自动进行潜在肺炎病例的初步检测(成像筛查),以便优先考虑和加快他们的审查。因此,我们决定开发一种从胸片中检测肺炎的模型。
资料组
我们使用了来自 kaggle 的 RSNA 肺炎检测挑战的数据集。这是一个带有注释的胸部 x 光数据集,它显示了肺部的哪一部分有肺炎的症状。
安装机器学习工具
我们将使用 Intelec AI 训练一个检测肺炎的模型。你可以从这里免费下载并安装。
数据准备
我们从 kaggle 下载了训练图像(stage_2_train_images.zip)和注释(stage_2_train_labels.csv) 。注释文件如下所示:
import pandas as pdann = pd.read_csv('stage_2_train_labels.csv')
ann.head()

上图中的第一行对应于编号为“0004 CFA b-14fd-4e 49–80ba-63 a80 b 6 bddd 6”的患者。此行的“目标”列为 0。意味着这个病人没有肺炎。另一方面,最后一排的患者患有肺炎,因为相应的胸片上的区域(xmin = 264,ymin = 152,宽度= 213,高度= 379)具有不透明。
我们决定用 SSD 物体探测器。Intelec AI 要求注释文件包含 image_name、xmin、ymin、xmax、ymax 和 class_name 列。因此,我们将数据转换成这种格式:
ann['image_name'] = ann.patientId + '.dcm'
ann = ann.rename(columns = {'x': 'xmin', 'y': 'ymin'})
ann['xmax'] = ann.xmin + ann.width
ann['ymax'] = ann.ymin + ann.heightann['class_name'] = np.NaN
ann['class_name'][pd.notna(ann.xmin)] = 'pneumania'
ann = ann[['image_name', 'xmin', 'ymin', 'xmax', 'ymax', 'class_name']]ann.head(10)

上图中的前 4 幅图像(第 0-3 行)没有肺炎注释。另一方面,图像“00436515–870 c-4b 36-a041-de 91049 b 9 ab 4 . DCM”有两个注释(第 4 行和第 5 行)。我们将其保存在“annotations.csv”文件中。
然后,我们创建一个“images”文件夹,并从 stage_2_train_images.zip 中提取所有图像。所有提供的图像都是 DICOM 格式。dcm)。
import os
images = os.listdir('images')
images[:5]

最后,我们的数据集看起来像这样:

然后我们创建了一个训练来训练我们的 SSD 对象检测器。事情是这样简单明了的:

我们开始训练,它跑了一天。当它不能再提高训练精度时,它就自动停止训练。

点击训练总结显示地图得分 0.2725 。我们部署它是为了检查它的性能。使用新的胸部 x 光照片测试展开的模型给出了以下结果:

预测看起来不错。但是它有多好,我们真的不能说,因为我们的团队中没有任何临床医生。
改进建议
从图像中检测肺炎仍然是一项需要使用深度学习来解决的困难任务。我们获得的地图分数 0.2725 很低。对于大多数对象检测任务,它通常高于 0.5。问题是,对象检测器擅长检测具有预定义形状和外观的对象。另一方面,肺部阴影没有精确的形状。这使得这个问题变得如此困难。我们将对如何进一步提高精度给出一些想法。
招数一:检测前分类
我们观察到,我们的算法有很高的假阳性率,即它在图像中检测到肺炎,而它不应该检测到肺炎。因此,如果我们将给定的胸片分类为“有无肺炎”,并且仅在它有肺炎的情况下用目标检测器检测肺炎症状,这将大大提高检测准确性。
招数二:先检测肺部
另一个问题是,在胸片中,不透明度(“白色像素”)也存在于肺的外部。但是我们总是检测肺部阴影,因为我们知道肺炎与肺部问题有关。我们可以让我们的对象检测器从训练数据中学习这一点,或者我们可以帮助它单独检测肺部,并在下一步检测肺部的肺炎。

参考
Intel EC AI:https://www . Intel EC . AI
RSNA 肺炎检测挑战:https://www . ka ggle . com/c/rsna-Pneumonia-Detection-Challenge/
什么是肺部阴影?https://www.kaggle.com/zahaviguy/what-are-lung-opacities
诗意神经网络
教神经网络如何写阿拉伯诗歌

Source: artfire.com
如果明天诗歌消失了,股票市场不会崩盘,桥梁不会倒塌,电脑仍会运转。
然而,诗歌具有独特的价值,因为它表达了我们内心无法量化或测量的东西。在这篇文章中,我们将尝试使用神经网络生成诗歌,但有一个额外的警告:它将使用阿拉伯语。
总之,这篇文章跨越了以下几点:
- 我们如何创建自定义数据集
- 如何预处理数据
- RNN 的超参数调谐
- 阿拉伯语诗歌输出(和英语翻译)
请随意跳过技术部分,直接跳到输出。底部有一个 GitHub 库的链接。
来自大马士革的诗人

Nizar Qabbani (source: Edarabia.com)
尼扎尔·卡巴尼是一位叙利亚诗人,他以探索爱情、民族主义、色情和宗教的诗歌而闻名。此外,他是一位多产的作家,这意味着他的工作为我们的神经网络提供了潜在的大量数据。
以下是他作品的样本:
你是谁
像匕首一样闯入我生命的女人
温和如兔眼
柔软如梅皮
纯洁如茉莉花串
天真如儿童围兜
贪婪如言语?
作为第一步,我们需要创建一个包含大部分(如果不是全部)他已知作品的文本语料库。幸运的是,我们可以找到专门保存 Qabbani 作品的网站。
使用像 BeautifulSoup、这样的软件包,人们可以收集数据并创建一个包含我们能找到的所有可用作品的语料库。收集了所有的诗歌,数据量略低于 1MB,约为 100 万个字符,约有 32,000 个独特的单词。有关需要多少数据的更多信息,请参考下面参考资料中的 Andrej Karpathy 帖子。
尽管看起来像是大量的文本,但实际上,它被认为是一个非常小的数据集,这可能会限制我们的目的。
论阿拉伯语的特殊性

Source: Whyseen on pinterest.com
与拉丁字符不同,阿拉伯语是从右向左读的。此外,没有大写或小写字符之类的东西。此外,元音和辅音的概念不同于英语。
关于这种语言和其他语言与英语的不同还有更多方面。已经有了用英语以外的语言创作诗歌的成功例子,比如中文(见底部的参考文献)。
准备数据
这一步包括创建一个返回两个字典的查找表:
- 整数到 vocab
- vocab 到整数
接下来,我们使用空格作为分隔符将脚本分割成一个单词数组。然而,像句点和感叹号这样的标点符号可以为同一个单词创建多个 id。比如“拜拜”“拜拜!”会生成两个不同的单词 id。
我们实现了一个函数来返回一个字典,该字典将用于标记像“!”这样的符号变成“||感叹号| |”,我们的列表看起来像:
- 句号(。)
- 逗号(,)
- 返回(\n)
- 回车符(\r)
该字典将用于标记符号并在其周围添加分隔符(空格)。这将每个符号作为自己的单词分开,使神经网络更容易预测下一个单词。
超参数和调整
一般来说,我们可以用更大的隐藏和n_layer维度得到更好的结果,但是更大的模型需要更长的训练时间。下面是要调整的参数列表:
- sequence_length :序列的长度。
- batch_size :批量大小。
- 次数:训练的次数。
- learning _ rate:Adam 优化器的学习速率。
- vocab_size :我们词汇表中惟一记号的数量。
- 输出尺寸:输出所需的尺寸。
- 嵌入 _ 尺寸:嵌入尺寸;小于 vocab_size。
- hidden_dim :我们 RNN 隐藏的维度。
- n _ layers:RNN 的层数/单元数。
模仿诗人
参数设置好了,模型训练好了,我们可以进入有趣的部分了:生成诗歌!
要做到这一点,我们必须定义prime_word,它是开始的单词。在下面的小节中,我们将展示一些结果。
” أنا (我/我):
أنا كهذا الماء ؟.
وظلت شيخ حارتنا..
وتشاهد أسماكٌ وبحار.
وتشاهد فهم أنفسنا
الخائفون من الخشب
ويضاجعون تطرف يعنيها.
تنقرض الأمة من الأصل ذكورا..
تهاجر أسماكٌ وبحار..
وعدت..
بإلغاء
粗略翻译:
我像这水吗?我成了我们街区的酋长..你会看到鱼和海洋。
你看《了解我们自己》
害怕木头
他们带着极端主义睡觉。这个民族是从男性起源开始灭绝的。鱼和海洋会迁徙..我答应了..
取消
“يا”
يا قطتي الخليفة
وباركنا في جبيني
خليفةٌ في خاصرة التعذيب الأوسط
هل سأقرأ حـيٍ ، ينسـى القط؟
والزنبق الأسود ، والشذا ومستواه
فقصها..
وبقينا نسأل أنفسنا
وفي غرف الإنعاش ،
وألوان للعشاء
قطعاً في
粗略翻译:
我的继任者阿猫
保佑我们在我的额头上
一个哈里发在中间一边折磨我会活着读书吗,猫会忘记吗?
黑色百合,香味和它的等级
所以切吧..
我们不停地问自己
在恢复室里,晚餐的
和颜色
绝对在
《نحن》(我们)
نحن عشته
لا تحسبي أن أحبك في البيادر
في أخبار التاريخ,.
تنقرض الأمة يعنيها.
تنقرض الأمة من عارٍ فيها– الحداد..
عيونها على ذراعيها..
ومذيع الدولة في أجساد الأميره ؟
يا رب أيـن ملتفٌ نسبي
粗略翻译:
我们经历了它
不要以为我爱你在历史新闻的花园里
。
这个民族灭绝了。这个国家因为它的耻辱——哀悼而灭绝了..
她的眼睛在她的胳膊上..
公主的尸体和国家广播电台?主啊,相对的缠绕在哪里?
“امرأة”(女人)
امرأة كلها...
يا كل عامٍ في الطبيعة..
ومذيع الدولة في جرحنا
نتفاءل جميله..
ووجدنا جسداً مغتصباً..
ومذيع الدولة ؟؟
من هؤلاء هؤلاء الهدبـا
من هؤلاء سقيت أعماقي وإرهاقي برأس أدبي؟
粗略翻译:
一个完整的女人……
哦,大自然中的每一年..
国家广播公司是在伤害我们
美好的乐观主义..我们发现了一具被强奸的尸体。
还有国家广播公司??
这些都是纤毛
这些被水浇得很深、筋疲力尽却顶着文艺脑袋的人是谁?
结论
我们可以看到,我们对诗歌的尝试不如原作者那样连贯,当然也不如原作者那样雄辩。在某些方面,写作是滑稽的,打破了所有的语法和逻辑规则。
我们的缺点的一个可能的原因可能是训练数据不足,因为理想情况下,我们需要至少 3MB 价值的文本。此外,可能需要考虑语言本身的独特方面。然而,请记住,RNN 人不得不从头开始学习最难的语言之一。
我希望您喜欢阅读这篇文章,并对文本生成的可能性有所了解。我也希望非英语母语的深度学习社区成员能够在他们自己的母语社区中预见潜在的有益应用。
[## NadimKawwa/poeticnurealnetworks
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/NadimKawwa/PoeticNeuralNetworks)
参考
نزار قباني دبلوماسي و شاعر عربي. ولد في دمشق (سوريا) عام 1923 من عائلة دمشقية عريقة هي أسرة قباني ، حصل على البكالوريا…
www.nizariat.com](https://www.nizariat.com/) [## 递归神经网络的不合理有效性
递归神经网络(rnn)有一些神奇的东西。我仍然记得当我训练我的第一个循环…
karpathy.github.io](http://karpathy.github.io/2015/05/21/rnn-effectiveness/) [## 1 MB 中有多少个字符?txt 文件?
问这个问题已经有了答案:我正在开发一个 Android 应用程序,它使用意图字符串,从一个…
stackoverflow.com](https://stackoverflow.com/questions/48698786/how-many-character-in-1-mb-txt-file) [## 用 RNN 编解码器生成中国古典诗歌
我们把中国古典诗词诗句的生成看作是一个序列到序列的学习问题,并建立了一个新颖的…
arxiv.org](https://arxiv.org/abs/1604.01537)
与 Python 的点双列相关
线性回归是确定数据文件的两个或多个连续特征之间相关性的经典技术。当然,只有当特征具有几乎线性的关系时,这才是理想的。

Linear Regression from Towards Data Science article by Lorraine Li.
但是,如果我们需要确定二分数据(即二进制数据)和连续数据之间的相关性,该怎么办呢?那不能得到我们上面看到的直线。这就是双列杂交对我们有帮助的地方。

Photo by Providence Doucet on Unsplash
点双列相关及其计算方法。
点双列相关系数与线性回归中使用的皮尔逊相关系数相同(从-1 到 1 测量)。唯一的区别是我们比较的是二分数据和连续数据,而不是连续数据和连续数据。从这一点开始,让我们假设我们的二分数据由两组(组 0 和组 1)的项目组成,连续数据为“y”。该系数通过以下公式计算:

Modified Point Biserial Correlation Formula from Wikipedia.
其中:
M0 =第 0 组数据的平均值。
M1 =第 1 组数据的平均值。
Sy =连续数据的标准偏差。
n0 =组 0 中的项目数。
n1 =组 1 中的项目数。
n =两组中的项目总数(也称为数据集中的总行数)。
应用点双列相关
PBC 经常用于分析多项选择测试问题,以确定它们是否是充分的问题。成为一个充分的问题意味着回答这个问题的人群(比如一类学生)不会觉得这个问题太容易(接近 1)或者太混乱(接近 0 或者负相关)。
ZipGrade ,一个从你的手机上扫描多项选择答题卡的应用程序,为我们扫描的测试数据提供了方便的 CSV 文件。我将从我给学生的代数测验中抽取一个。下面是我给学生的多项选择测验的部分数据。

Multiple-Choice Quiz data with the scores (Percent Correct) and points earned from the question in each question column.
“正确率”是学生获得的分数。每个“Q#”栏是学生回答正确(2 分)或错误(0 分)的分数。该数据集还表明,二分法数据不一定需要严格采用 0 或 1 格式,因为您会为机器学习模型编码数据。因为公式只关心每组中的项目数,所以它们是如何标记的并不重要。
SciPy 方便的有一个叫做pointbiserialr的点双列相关函数。我们将把这个函数应用于第一个问题“Q1”。
这给了我们以下结果:
PointbiserialrResult(correlation=0.555989931309585, pvalue=1.5230357095184431e-06)
这导致了约 0.56 的相关性和相关的 p 值,以说明相关性是否具有统计学显著性。
对于我们的情况,这种相关性意味着这个测试问题对于我的学生来说是一个公平的问题。如果相关性接近 1,那就意味着这个问题太简单了,也许不需要包括在内。更接近于 0 或负值的相关性将表明问题可能做得不好或对学生不公平。这个主题的扩展可以在以后的文章中讨论。
从头开始点双列相关
正如我们在我写的关于超几何分布的文章中所做的,我想借此机会展示我们如何用 Python 从头开始创建这个公式。
这个版本将二分数据列、连续数据列和相关数据框的名称作为参数。为了重新创建我们的例子,我们将调用函数为pbc_scratch(‘Q1’, ‘Percent Correct’, data)。我不认为这是创建函数的最佳方式,所以我欢迎任何有更好实现的人在下面的评论中分享。
额外资源
感谢阅读!我希望这篇教程对你有所帮助。如果你想要一个视频版本来巩固你对点双列相关的理解,你可以看看下面的视频。它有一个更一般的例子和另一个选择题的例子,以及进一步的解释意义。
你可以在 Linkedin 和 Twitter 上关注或联系我。我对 Twitter 上的 DM 持开放态度。如果你喜欢这篇文章,可以看看下面我写的关于超几何分布的类似文章:
数学课上的概率问题,你需要的概率要么给你,要么相对容易…
towardsdatascience.com](/hypergeometric-distribution-explained-with-python-2c80bc613bf4)
如果你喜欢阅读 Medium,并愿意进一步支持我,你可以使用我的推荐链接注册 Medium 会员。这样做可以用你的会费的一部分在经济上支持我,我将不胜感激。
直到下一次,
约翰·德杰苏斯
点云数据:简单方法
电力线探测的激光雷达数据

介绍
近年来,激光雷达探测器的发展取得了巨大的进步,产生了大量的原始数据。激光雷达现在更加精确,分辨率甚至比 10 年前高得多。空基激光雷达已经成为接收环境信息的有效方法。然而,您获得的数据实际上只是稀疏点的集合,这些点可能提供一些不错的可视化效果,但在涉及到更实际的目的时,需要进行大量的处理。不幸的是,截至目前,计算机视觉的进展大多涉及结构化的二维数据(照片、视频)。当前的方法不能推广到多维稀疏数据,如我们在基本的激光雷达数据预处理后接收的点云。该领域正在进行广泛的研究。我们

应该特别提到 PCL——一个伟大的国际社区开发的库的嫁接,为各种应用提供 2D 和 3D 数据的工具。不幸的是,目前要将这个库应用于感兴趣的解决方案并不容易。这通常意味着你必须深入图书馆。这对于需要高可伸缩性的生产级产品来说是有意义的。但是对于 PoC 开发来说,成本可能太高。因此,我决定使用一种简单的方法和非常标准的 Python 库来尝试对点云数据做些什么(可以从 Python 中使用 PCL,但仅限于此,因为只有很小的子集可以无缝集成)。
实验数据
作为一个例子,让我们使用基于航空的激光雷达生成的数据来检测电力线。电力线通常在点云可视化中清晰可见。然而,映射属于电力线的点需要大量的手动工作。另一方面,简单的几何考虑可以为我们提供大大简化甚至自动化这种处理的方法。
图上的电力线实际上是形成某种几何图形的点的集合。为了简化进一步的分类,我决定检查我们如何从这些点形成集群。

用于实验的软件
我将使用 NumPy、Sklearn、Laspy 和 SciPy 库来形成集群,并使用 matplotlib 来实现可视化。
import laspyimport scipyimport numpy as npimport matplotlib.pyplot as pltfrom sklearn.cluster import DBSCANfrom sklearn import metricsfrom sklearn import preprocessingfrom mpl_toolkits.mplot3d import Axes3Dfrom matplotlib import path
Laspy 非常适合在 Python 中处理点云数据。我们从 las 文件中读取点云数据,并检查实际数据集的形状。
# Open a file in read mode:inFile = laspy.file.File(“./LAS/simple.las”)# Grab a numpy dataset of our clustering dimensions:dataset = np.vstack([inFile.x, inFile.y, inFile.z]).transpose()dataset.shape
(5942479, 3) —我们的点云由 5942479 个点组成。如果你想了解细节,这是不够的。但是如果你试图把这个数据帧转换成一个三维的 NumPy 数组,这个数字就太大了,就像在这个例子中,我们会得到一个巨大的 5942479 = 2.09*10 的⁰数组。它将使用大量的 RAM 来存储非常稀疏的数据。显而易见的是使用 NumPy 稀疏数组。但事实上,稀疏阵列对 2D 很有效,但对 3D 数据无效。矩阵操作函数与稀疏 3D 矩阵不完全兼容。由于内存需求,我们必须坚持使用 DataFrame 而不是 NumPy 数组。
消除范围外的点
我们需要找到一种方法来消除那些不是电力线的点。出于安全原因并为了确保其最佳性能,电力线被放置在离地面很高的地方。但对于崎岖的地形,我们必须考虑到,由于地面倾斜,一些地面点在图像的不同部分可能高于电力线。为了避免这种情况,让我们把点云分成小的垂直部分。
%%timedef frange(start, stop, step): i = start while i < stop: yield i i += step#ground points grid filtern = 100 #grid stepdataset_Z_filtered = dataset[[0]]zfiltered = (dataset[:, 2].max() — dataset[:, 2].min())/10 #setting height filtered from groundprint(‘zfiltered =’, zfiltered)xstep = (dataset[:, 0].max() — dataset[:, 0].min())/nystep = (dataset[:, 1].max() — dataset[:, 1].min())/nfor x in frange (dataset[:, 0].min(), dataset[:, 0].max(), xstep): for y in frange (dataset[:, 1].min(), dataset[:, 1].max(), ystep): datasetfiltered = dataset[(dataset[:,0] > x)
&(dataset[:, 0] < x+xstep)
&(dataset[:, 1] > y)
&(dataset[:, 1] < y+ystep)] if datasetfiltered.shape[0] > 0: datasetfiltered = datasetfiltered[datasetfiltered[:, 2]
>(datasetfiltered[:, 2].min()+ zfiltered)] if datasetfiltered.shape[0] > 0: dataset_Z_filtered = np.concatenate((dataset_Z_filtered,
datasetfiltered))print(‘dataset_Z_filtered shape’, dataset_Z_filtered.shape)
在这种简单方法的帮助下,即使使用中等的计算能力,我们也可以立即大大减少云中的点数。在我们的例子中,这是在 3 分钟内将点数减少了一个数量级——对于几行代码来说还不错,因为我们没有为任何优化付出真正的努力。
dataset_Z_filtered shape (169862, 3)CPU times: user 3min 16s, sys: 7.14 ms, total: 3min 16sWall time: 3min 16s
现在我们将使用一个更小的过滤数据集。
探索我们的数据
让我们来探索我们的数据:
print(“Examining Point Format: “)pointformat = inFile.point_formatfor spec in inFile.point_format:print(spec.name)Examining Point Format:XYZintensityflag_byteraw_classificationscan_angle_rankuser_datapt_src_idgps_time
在我的实验中,我尝试使用数据的 4D 表示(X,Y,Z 和强度),但结果并没有在 3D (X,Y,Z)上有所改善,所以让我们坚持使用后一个数据子集。
print(‘Z range =’, dataset[:, 2].max() — dataset[:, 2].min())print(‘Z max =’, dataset[:, 2].max(), ‘Z min =’, dataset[:, 2].min())print(‘Y range =’, dataset[:, 1].max() — dataset[:, 1].min())print(‘Y max =’, dataset[:, 1].max(), ‘Y min =’, dataset[:, 1].min())print(‘X range =’, dataset[:, 0].max() — dataset[:, 0].min())print(‘X max =’, dataset[:, 0].max(), ‘X min =’, dataset[:, 0].min())Z range = 149.81Z max = 181.78999908447264 Z min = 31.979999084472652Y range = 622.9700000002049Y max = 2576396.509974365 Y min = 2575773.539974365X range = 556.4400000000605X max = 711882.7199987792 X min = 711326.2799987792
数据标准化
如您所见,这些值在不同的范围内。为了获得更好的结果,我们应该对数据集进行规范化。
dataset = preprocessing.normalize(dataset)
对几何上接近的点进行聚类
现在我们准备处理我们的数据。我们的电力线实际上是点的空间群集,因此尝试群集算法是很自然的。经过短暂的调查,我发现 Sklearn 库提供的 DBSCAN 开箱即用效果最好。
clustering = DBSCAN(eps=2, min_samples=5, leaf_size=30).fit(dataset)
现在让我们来看看我们的结果。
core_samples_mask = np.zeros_like(clustering.labels_, dtype=bool)core_samples_mask[clustering.core_sample_indices_] = Truelabels = clustering.labels_# Number of clusters in labels, ignoring noise if present.n_clusters_ = len(set(labels)) — (1 if -1 in labels else 0)n_noise_ = list(labels).count(-1)print(‘Estimated number of clusters: %d’ % n_clusters_)print(‘Estimated number of noise points: %d’ % n_noise_)Estimated number of clusters: 501Estimated number of noise points: 1065
形象化
我们的大部分点被分成了几组。让我们看看它在实践中是什么样子的:
# Black removed and is used for noise instead.fig = plt.figure(figsize=[100, 50])ax = fig.add_subplot(111, projection=’3d’)unique_labels = set(labels)colors = [plt.cm.Spectral(each) for each in np.linspace(0, 1, len(unique_labels))]for k, col in zip(unique_labels, colors): if k == -1: # Black used for noise. col = [0, 0, 0, 1] class_member_mask = (labels == k) xyz = dataset[class_member_mask & core_samples_mask] ax.scatter(xyz[:, 0], xyz[:, 1], xyz[:, 2], c=col, marker=”.”)plt.title(‘Estimated number of cluster: %d’ % n_clusters_)plt.show()

现在很清楚,简单的几何考虑和相当标准的聚类方法帮助我们使用适度的计算资源来简化点分类。
如果需要,每个点簇可以单独分类。

结果
我们的实验表明,几何考虑和标准 Python 库的结合可以显著减少为进一步使用而对原始点云数据进行分类所需的工作。
感谢
我要感谢我的同事们安迪·博西、米科拉·科兹连科、沃洛季米尔·森德茨基的讨论、合作和有益的建议,以及整个 MindCraft.ai 团队的持续支持。
观点— Luis,Watson,信息提取的开源—半结构化文档

提出使用认知工具从半结构化模板中提取元素的观点
我们已经看到了 AI/ML 在从非结构化文档中提取元素方面的能力,比如说客户对产品的评论。
像脸书、亚马逊、谷歌这样的公司已经使用它很多年了。银行部门、保险部门、物流部门的公司需要大量的文书工作来支持他们的业务。以保单发行为例。对于给定的保险公司,会有许多经纪人代表公司向客户提供保险。经纪人会将这些报价文件发送给保险公司。后台管理人员将对信息进行数字化处理,即将报价信息输入到客户保单管理应用程序中,在保险公司进行尽职调查后,将签发保单。考虑到所涉及的人工劳动,保险公司正在寻找通过应用智能自动化来自动化事件序列的选项。智能自动化解决方案有两个组件— AI(信息提取),RPA(信息接收)。我们将专注于人工智能部分。
可以使用认知工具(如 Watson、LUIS、Amazon Textract)和开源工具(如 CRFSUITE、Spacy)来构建信息提取的人工智能模型。在我参与的信息抽取 POC 中,我使用了 Watson、Luis、CRFSUITE,并将对这些工具提出自己的观点,作为参考指南。信息抽取包括三个主要任务——标注、模型训练和模型输出解析。
在注释阶段:-要提取的字段将被定义为实体。这些文件必须转换成文本格式并加以注释。
模型训练阶段:-发布注释阶段,AI 模型需要在带注释的文档上进行训练。然后,通过 rest api 调用部署和调用经过训练的模型进行预测。
模型输出解析阶段:-从模型接收的预测是 json 格式的,需要编写一个定制服务,将 json 输出格式化为所需的格式,以便进一步处理。
Watson、LUIS 等认知工具为上传语料库、定义实体、标注语料库、训练模型和模型部署提供了非常好的 UI 界面。对于上一句中提到的任务,绝对不需要任何编码工作,全部精力都放在训练模型上。下面,我将强调每种认知工具的优缺点。
沃森概述
Watson 拥有一系列文本分析服务,包括自然语言理解、自然语言分类器和发现服务。还有一个服务——沃森知识工作室。这个 Watson Knowledge Studio 充当一个存储库,在其中加载语料库集(文本格式— utf-8 编码)、定义实体、标注语料库集、训练模型。然后,对照自然语言理解服务来部署经训练的模型。Watson Knowledge Studio 就像一个容纳多个工作空间的容器。在工作空间内,执行语料库集、实体定义、注释和模型训练任务。这个工作空间也是可导出的。因此,如果您想将经过训练的人工智能模型导出到其他工作空间,您可以简单地导出上传的语料库和实体定义。导出的语料库和实体定义需要重新导入到新的工作空间中。先前完成注释将被自动导入。剩下要做的唯一任务就是在带注释的文档上训练模型并部署模型。Watson 的人故意将这项任务留给了手动,以防新模型需要做一些注释更改,用户可以查看它并相应地进行更改,训练模型,然后根据 NLU 服务部署它。
上传语料库和定义实体的 UI 非常好,并且易于解释。除了实体之外,您还可以定义将两个实体链接起来的关系。例如在文本行“权利要求号:456789”中。定义了两个实体——索赔号标签和索赔号值。在 Watson 中,关系也被定义为“什么是索赔号”,其中这两个实体将被链接——索赔号标签(父实体)、索赔号值(子实体)。在注释步骤中,实体和关系都被注释。
Watson Knowledge Studio 提供了一个优秀的注释框架接口。这里,对于注释过程,创建一个或几个任务。对于创建的任务,文档与其相关联。例如,有 100 个文档和 10 个人,主管将创建 10 个任务,并将 10 个文档与每个任务相关联。然后,任务将被分配给每个人,主管将检查注释,提交注释,并将任务标记为已完成。
所有任务完成后,将开始模型训练。在模型的训练过程中,Watson 将只显示那些注释已经完成的文档,供用户选择。模型的训练非常简单。用户将选择测试集、训练集、盲集的百分比(默认-70%、23%、7%)并继续进行训练。发布模型训练,在模型摘要中,我们还会得到一个报告,指示模型性能(每个定义的实体/关系的 F1 分数)。如果所有语料库都被视为训练集,则此报告将不可用。
模型部署也是一个非常简单的步骤。只需创建一个新的 NLU 服务实例,并针对 NLU 服务实例部署在 WKS 培训的模型。NLU 服务提供用户 id 和模型 id 凭证,这些凭证可以在 rest api 调用期间作为参数传递。更多详情请点击。
“get_relations”方法在分析过程中使用。输出是 json 格式的,它由标识关系的数组以及父实体和子实体组成。解析相对容易得多,因为我们只需检查关系并选择子实体。子实体是我们感兴趣的值。如果 json 只包含实体,那么就必须进行额外的验证来检查被识别的行是否也有标签实体。例如,对于行-参考号:456789,模型将 456789 识别为索赔号值实体,但它不会将“参考号:”识别为索赔号标签实体,因此对于行的关系“什么是索赔号”没有输出。
翻转一边的沃森
1)对于定义的自定义实体,所有这些实体都被假定为 Watson 中的字符串类型。例如,对于实体“策略开始日期值”,我们不能在任何地方将此实体的类型指定为“日期”。另一方面是将日期格式转换成标准格式,比如说 YYYY-MM-DD,这必须在解析代码本身中处理。同样的例子也可以延伸到“数字”。
2)没有 dictionary 类型的实体。例如,实体客户名称标签可以由出现在语料库中的几个术语组成,如“企业所有者”、“提议人姓名”、“客户姓名”、“法定姓名”。人工智能模型必须在训练集本身中看到所有这些术语。例如,我们有一个在 50 个文档上训练的模型。这 50 份文件都有客户名称、法定名称和申请人名称作为客户名称标签,但没有一份包含“企业所有者”。因此,对于一个测试文档,如果文档包含“企业所有者”,模型不会将其识别为“客户名称标签”实体。
3)在概念验证中,我们在 120 个文档(每个文档平均有 10-15 页)上训练了一个 Watson 模型。120 个文档的集合基本上有 12 个变体(每个变体— 10 个文档)。被训练的模型变得特定于格式。在这种意义上,如果我们让模型预测另一个变体,即使新的变体包含模型在训练集中看到的术语,模型也不会给出任何输出。测试集或预测集在数据表示方面必须与训练集非常相似。例如,我们有一个国家不同州的驾驶执照。例如,一个国家的 50 个州,我们将有 50 个变体。现在,沃森模型训练了 25 个变量,如果给定第 26 个变量,就不能预测所有的场。
沃森的优势
——沃森模型在对大量页面的文档进行预测时速度相当快。
-预处理,内部完成标记化。文档必须通过 rest api 调用以文本格式呈现给模型。这里涉及的唯一预处理是转换成文本。随着新版本的 WKS,PDF 文档也被接受,但文本转换输出不是很好的扫描图像 PDF。Watson 中的解析服务要简单得多。关系提取使它变得非常容易。这可以用 Java、Python 或者你选择的任何其他语言编写。它支持多种语言,更多关于这个, 可用这里的 。
-高超的注释框架。
-模型培训(后期注释),部署非常简单。
沃森的缺点
——模型的再训练和训练的努力是一样的。没有可用的记录机制来跟踪低置信度得分预测。目前,这个过程基本上是手动的。
-训练集和测试集必须非常相似。
-在解析逻辑中必须注意日期值、数值的标准化,但如果我们知道语料库中出现的所有可能的格式,这只是一个小障碍。
-标注过程非常耗时。使用字典,这可以加快速度,但如果有超过 100 个文档,这仍然有点乏味。按照惯例,训练至少需要 50 个文档(每个变体 50 个样本)
-模型训练算法是一种黑盒。我们不能在这里选择梯度优化算法(例如在 CRSFUITE 中有— lbfgs,l2fgd,arow,pa)。如果模型在某些文档上表现不佳,解决方法是在训练集中包含更多这样的文档。
-发送到客户端网络外部进行预测的数据。
什么时候去找沃森?
-适用于包含大量页面的文件(第> 10 页)。
-文件的预测集和训练集相似
-模型的再训练需要手动处理。在 Watson 中,再训练是不能自动进行的,因为如果再训练的模型在新的一组文档的同时在另一组文档上给出期望的输出,则需要检查额外的注意/验证。
-客户端可以将数据发送到云(客户端网络之外)进行预测。
LUIS 概述
Luis 或者说语言理解服务不是为信息提取而建立的。它实际上是用来构建聊天机器人的。在我们客户的一个 POC 中,我们研究了 LUIS 的信息提取,因为客户在 Azure 平台上有其基础架构。就像我们在沃森知识工作室有 workspace,在 LUIS 有‘App’(应用)。该应用程序将包含所有的实体,意图和定义。
在 LUIS 中,预测需要逐行进行,因为有 500 个字符的字符限制。因此,这里的方法是在需要提取信息的行上训练模型,并且还需要在测试/预测文档上逐行进行预测。
要提取的字段被定义为实体,非常类似于 Watson 中的实体。然而在 LUIS 中,我们可以将类型与实体联系起来,例如字典、简单、复合(仅举几个例子)。它也支持正则表达式和模式。还提供了预构建的实体—日期、号码、人员、位置。
由于文档是半结构化类型,要提取的信息以键值格式呈现(字段标签:字段值),因此字段标签被定义为字典类型的实体,语料库中表示字段标签的术语被定义为其值。例如,创建字典类型的“字段标签”实体。创建“字段标签”类型的实体“客户名称”,并向其添加术语—“企业所有者”、“建议人名称”、“客户名称”、“法定名称”。因此,在包含术语“建议者姓名”的行的文档中,模型将给出输出—类型“字段标签”,文本“建议者姓名”,规范化值“客户姓名”。以这种方式,如果训练集不包含术语“提议者姓名”,那么模型也可以用术语“提议者姓名”来预测测试文档上的客户姓名(不像在 Watson 中)。
这里定义的不是关系,而是意图。例如,对于行“客户名称:ABC 有限公司”,将定义一个意图,“什么是客户名称?”这一行将添加实体“客户名称标签”——“客户名称:”和“客户名称值”——“ABC 有限公司”,并相应地进行注释。
这里有一点要注意,意图和实体之间绝对没有关系。我们可以在文档中的其他行给出相同的意图,在这些行上模型没有被训练——“什么是客户名称?”(置信度较低)但是该行不包含实体——客户名称标签和客户名称值。对于每个定义的意图,我们包括来自训练集的 5-10 行实例。
LUIS 中预先构建的实体具有很大的价值,如果使用得当,它将免去标准化日期和数字值的麻烦。我们使用“日期”和“数字”预先构建的实体。路易斯的日期预建实体非常强大。它能够识别任何日期格式(YYYY-MM-DD,YYYY-MON-DD,DD/MM/YY,DD/MM/YYYY),日期范围(2019 年 12 月 28 日至 2019 年 12 月 30 日)。即使是格式中指定的日期— 2019 年 12 月 28 日,LUIS 也能识别序数值、月份和年份,并相应地给出标准值—2019–12–28。对于日期范围,它给出开始日期和结束日期。在我们进行的概念验证中,日期格式不是固定的,我们有日期范围,日期以顺序格式出现在语料库中。LUIS——日期预建实体像魔咒一样处理这些。用于日期类型实体的斯坦福 NER 标记器模型非常简单,它只识别少数日期格式。
与 Watson 相比,LUIS 的培训过程耗时更少。从一个总共有 100 行(10 页,每页 10 行)的文档中,假设我们对从 10 行(20 个字段)中提取内容感兴趣。每行 2 个字段),并且如果我们有 50 个文档作为训练集,那么我们定义 10 个意图、20 个实体,并且对于 10 个意图中的每一个,在训练集行的任何这样的 10 个实例(每个意图的可用 50 个实例)中键入。正如我们在这里看到的,我们不需要将 50 个文件上传到 LUIS。一旦定义了意图和相关注释,只需点击 LUIS 屏幕上的 train 按钮,即可启动培训流程。还提供了一个测试按钮,可以测试输入行的模型输出。
LUIS 的部署过程非常简单。通过 LUIS 应用程序屏幕上的发布按钮部署训练好的模型。该模型将根据订阅密钥进行部署。必须在 azure 门户上创建订阅密钥。
这里需要注意的一点是,在重新发布模型的情况下,rest api url 没有变化,因为 model_id 保持不变。如果有新的订阅密钥,url 将会改变。
翻转身边的路易斯
请注意,如上所述,LUIS 不是用于信息提取的目的,但在这里提到了它,以防 LUIS 被考虑用于信息提取。
1)解析代码会变得非常复杂。下面解释一些复杂情况(a,b,c)
a)实体-实体链接
与 Watson 不同,LUIS 不支持关系提取。由于实体&的意图是一个不连贯的集合,因此在进行关联时,必须在解析中加入适当的逻辑。例如,如果 LUIS 为一行输出实体索赔号值,我们还必须检查同一行是否也包含实体-索赔号字段。必须小心使用模式类型实体,因为模型往往会将不正确的线识别为已定义模式的线。因此,如果使用了模式实体,并且模式出现了某些变体,那么必须应用识别变体的逻辑,并相应地考虑模式输出。
b)一个字段实体的多个字段值
如果我们使用了像‘数字’这样的预建实体,考虑一行‘支付总额:490 收回金额:400’。对于“已付总额”字段,将有两个数字 490 & 400,如模型所标识的,必须编写逻辑来选取具有最小开始标记(但大于已付总额的结束标记)的数字
c)一个字段值的多个字段实体(标签)。
假设我们在单据中为行“挂失日期:2019 年 6 月 20 日”定义了两个字段标签实体
“挂失—字段标签(类型字典)—‘挂失日期’
客户挂失日期—字段标签(类型字典)—‘挂失日期’
,模型将给出两个字段标签实体——“挂失”(期限—挂失日期)和“客户挂失日期”(期限—挂失日期),日期值为 2019 年 6 月 20 日。这里,必须编写逻辑来考虑具有最少开始标记值的字段标签。
我们在 Watson 中花费的训练时间,与我们在 LUIS 中设计解析逻辑的时间一样多。
2)逐行预测:-
逐行预测会导致尽可能多的 api 调用,从而减慢进程。对于超过 10-15 页的文档,LUIS 可能不是理想的方法。
3)意向和实体数量的限制
我们可以在 LUIS 应用程序中创建的意向数量有限。从 POC 的角度来看,这可能不是一个问题,因为范围内的文档数量约占总数量的 5–10%。对于生产运行来说,这可能是一个问题,因为生产运行中的字段数量可能远远大于 POC 所考虑的字段数量。进一步阅读, 此处可用
LUIS
的优势——与 Watson 相比,模型的再训练要容易得多。LUIS 确实保持着低得分意向记录。这也可以在解析层中处理,其中生成低得分意图的 excel 文件。使用 excel 文件,用户可以通过 UI 界面继续训练模型。
-由于预测是逐行进行的,并且给定了字典类型的实体,与 Watson 相比,LUIS 在与训练集不相似的测试集上做得更好。
——预建实体,尤其是日期是 LUIS 的一个得分点。LUIS 本身输出日期值的标准格式。所以日期&数字类型的标准化是自动进行的。
-预处理、标记化在内部完成。文档必须通过 rest api 调用以文本格式呈现给模型。这里涉及的唯一预处理是转换成文本并逐行调用 rest api 调用 LUIS。
LUIS
的缺点——逐行预测不适用于具有大量行的文档
——解析代码(格式化模型输出)是大部分工作将转移到的领域。这里的方法必须保持解析代码尽可能的通用,并且必须尽可能的与变体无关,否则在新的变体上重新训练模型可能会导致代码变化。
-模型训练算法是一种黑箱。我们不能在这里选择梯度优化算法(例如在 CRSFUITE 中有— lbfgs,l2fgd,arow,pa)
—将数据发送到客户端网络外部进行预测。
什么时候去找路易斯?
-适用于页数较少的文件(第< 5 页)。
-文档的预测集和训练集不同
-如果需要提取日期类型、数字类型的字段值。可以使用 LUIS 预建实体。
-客户端可以将数据发送到云端(客户端网络之外)进行预测。
开源概述
目前有几个开源选项可用——CRF suite、SPACY、RASA NLU。我已经尝试过使用 CRFSUITE 构建信息抽取原型。在这篇文章之后,我将详细介绍如何使用 CRFSUITE 设计端到端的信息提取工具。
像 Watson & LUIS 这样的认知工具相对于开源解决方案的一个明显优势是,这些工具只是消除了预处理工作——标记化、词性标注。后处理工作也得到了关注,即原始 AI 模型输出已经被格式化为 json 结构,该结构由实体以及文本属性(如开始标记、结束标记、规范化值、文本值)组成。
预处理和后处理将占据 50%-60%的工作量。有大量工作要做注释,如果不使用合适的接口,手工注释会导致很多错误。我将在下一篇文章中讨论这个问题。
开源解决方案的缺点
——主要缺点是精力过于集中。在这里,工作转移到开发用于标记化、词性标注、手动注释的代码,将注释的数据转换成模型所需的格式,格式化模型输出。当我们忙于将我们的努力引向错误的方向时,模型训练和模型调优步骤就变得很麻烦了。
开源解决方案的优势
——开源解决方案将是内部/内部解决方案。在 LUIS 的情况下,一行一行的调用非常慢。但在 CRFSUITE 的情况下,它非常快,因为整个解决方案都在内部,不涉及网络延迟
-在 Watson & Luis 的情况下,模型的重新训练有点问题。如果再训练的目的是提高信心分数,那么在开源解决方案中,我们可以使用两个模型方法来自动化它,已经写了帖子, 此处可用 。
何时使用开源解决方案?
-如果需要非商业解决方案
-保留通过云发送数据的权利,协议提供内部解决方案
skimage-Python 在图像处理中的点操作
当我们用手机拍照时,我们在社交媒体上发布之前会做什么?当然,我们调整亮度、对比度等等。你有没有想过我们在屏幕后面的形象发生了什么变化?
这一切都可以通过一个叫做点操作的简单技巧来实现。有很多点操作,也有很多点操作的用例。有些是,
- 修改亮度或对比度
- 应用任意强度变换
- 量化图像
- 全局阈值
- 非线性校正
- 颜色变换等等。
什么是点操作?
我们都知道图像只是简单地用 2D 有序矩阵来表示。用于修改像素值而不影响相邻像素的操作称为点操作。点操作将、
- 不改变图像的大小
- 不改变图像的几何形状
- 不改变图像的局部结构
- 不影响相邻像素
例如,简单的点操作可以表示为,
a' <------- f(a) #here "a" is the intensity value of a pixelORI'(u,v) <------- f(I(u,v)) #here "u,v"are the coordinates of the image I
存在两种类型的点操作。同质或全局点运算,其中函数 f()与图像坐标无关。如上式所示,v 仅用于获取特定像素的亮度值。
另一种类型的点操作是非齐次点操作,其中使用了映射函数 g()。
a' <------- g(a,u,v)
#here "a" is the current pixel value and u,v are the coordinates of the pixel which should be considered in calculationsORI' <-------g(I(u,v), u,v)
非同质点操作的一个示例用例是对比度或亮度的局部调整。
同质点操作的示例
对比度修改
让我们将图像的对比度增加 50%。你能猜到我们应该对每个像素执行什么操作吗?
f_contrast(a) = a x (1.5)#We should iteratively get pixel values in image one by one modify them and we have to put the result into the same location.
让我们在 skimage 库的帮助下尝试一下。
from skimage import io
import matplotlib.pyplot as pltdef f(x):
return x * 1.5def point_operation(image, method):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
image[row][col] = method(image[row][col]) return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy(), f) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show() Output: Figure-1

Figure-1 (Before and After contrast modification)
亮度修改
让我们将图像的对比度增加 50 个单位。你能猜到我们应该对每个像素执行什么操作吗?
f_contrast(a) = a + 50#We should iteratively get pixel values in image one by one modify them and we have to put the result into the same location.
让我们在 skimage 库的帮助下尝试一下。
from skimage import io
import matplotlib.pyplot as pltdef f(x):
return x + 50def point_operation(image, method):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
image[row][col] = method(image[row][col])
return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy(), f) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show()Output: Figure-2

Figure-2 (Before and After Brightness Modification
但是你有没有注意到在我们修改过的图像中有一种连线行为?是的,它们没有我们想象的那么干净。图像中的某些区域破坏了图像的结构。有什么问题吗?
当我们使用算术运算修改像素值时,我们应该记住修改后的值应该在可接受的亮度值范围内。在我们的例子中,它应该在[0–255]之间,因为我们使用的是 8 位表示。
为了解决这个问题,我们可以使用一种叫做夹紧的技术。实现夹紧非常容易。
a' <------- f(a)if a' > 255 then a' <------- 255
if a' < 0 then a' <------- 0
else a' <------- a'
我们可以简单地在代码中实现箝位,只需修改如下的点操作函数,
def point_operation(image, method):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
temp = method(image[row][col])
if temp > 255:
image[row][col] = 255
elif temp < 0:
image[row][col] = 0
else:
image[row][col] = temp return image

Figure-3 (Contrast modification after Clamping)

Figure-4 (Brightness modification after Clamping)
反转图像
反转图像只是反转像素值的顺序。这是通过,
a' = (a x -1) + a_maxHere we multiply the pixel value by -1 and then add it with the constant of maximum intensity value of that image, to map the result with in the acceptable range.
Python 代码反转,
from skimage import io
import matplotlib.pyplot as pltdef point_operation(image):
a_max = image.max()
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
image[row][col] = (image[row][col] * -1) + a_max return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy()) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show()
阈值化图像
阈值处理是一种特殊的量化操作,用于将图像修改为其二进制版本。这项技术非常简单,
if a >= threshold then a' = 255
else a' = 0#Here threshold can be any value within the acceptable intensity range of a perticular image, in our case within [0-255]
Python 代码反转,
from skimage import io
import matplotlib.pyplot as pltdef point_operation(image, threshold):
for row in range(0, image.shape[0]):
for col in range(0, image.shape[1]):
if image[row][col] >= threshold:
image[row][col] = 255
else:
image[row][col] = 0 return imageif __name__ == '__main__':
image = io.imread('~/Desktop/Lenna_gray.png')
image_modified = point_operation(image.copy(), 100) plt.figure()
_ = io.imshow(image) plt.figure()
_ = io.imshow(image_modified) plt.show()
自己尝试上述反相和阈值函数的代码,以可视化输出。在 skimage 中有 API 函数可以用来执行上述操作,你可以自由探索。
希望大家对点算符及其一些应用有更好的了解!!!
对机器学习的毒害攻击
机器学习的安全性
一个持续了 15 年的安全问题卷土重来

我真的为机器学习感到兴奋。这是一项令人着迷的技术,真正将科幻变为现实。然而,就像我之前写的,机器学习不会没有自己的问题(以安全漏洞的形式)——我们开始思考它们是非常重要的。
今天我们将讨论中毒攻击——一种在训练中利用你的 ML 模型的攻击类型(与推断相对)。
让我们开始吧。
垃圾邮件还是火腿?
尽管大肆宣传可能会让你相信,投毒攻击并不是什么新鲜事。事实上,一旦机器学习开始被认真用于安全领域,网络黑客就开始寻找规避它的方法。因此,第一个中毒攻击的例子可以追溯到 2004 年的和 2005 年的和,它们是为了躲避垃圾邮件分类器。
但是什么是中毒袭击呢?
当对手能够将坏数据注入到你的模型的训练池中,从而让它学习一些它不应该学习的东西时,中毒攻击就发生了。中毒攻击最常见的结果是模型的边界以某种方式移动,如下所示:

中毒攻击有两种类型——一种针对您的 ML 的可用性,另一种针对其完整性(也称为“后门”攻击)。
第一次攻击是可用性类型的。这种攻击的目的是将如此多的坏数据注入到您的系统中,以至于您的模型学习的任何边界基本上都变得无用。先前的工作已经在贝叶斯网络、支持向量机和神经网络上完成。例如, Steinhardt 报道说,即使在强大的防御下,3%的训练数据集中毒也会导致 11%的准确率下降。其他人提出了反向梯度方法来产生毒素,甚至使用 eutoencoder 作为攻击发生器。
接下来来了 后门 攻击。这些要复杂得多,事实上它们想让你的分类器完全像它应该的那样运行——只有一个例外:后门。后门是一种模型设计者不知道的输入类型,但是攻击者可以利用它让 ML 系统做他们想做的事情。例如,假设攻击者教导恶意软件分类器,如果某个字符串出现在文件中,则该文件应该总是被分类为良性的。现在,攻击者可以编写他们想要的任何恶意软件,只要他们将该字符串插入到文件中的某个位置,他们就可以开始工作了。你可以开始想象这样的攻击可能会有什么后果。
最后,随着迁移学习成为一种用有限数据集训练模型的流行方法,攻击者意识到他们可以将毒药与学习的其余部分一起迁移。例如,这篇是一篇非常有趣的论文,它研究了预训练模型的中毒问题,包括在现实世界中,美国街道标志分类器学会了将停止标志识别为限速标志。

到今天为止,已经针对情绪分析、恶意软件聚类、恶意软件检测、蠕虫特征检测、 DoS 攻击检测、入侵检测、更多入侵检测、更多入侵检测,以及我个人最喜欢的社交媒体聊天机器人。

攻击者能力和中毒攻击
攻击者在发动攻击之前对您的系统了解多少很重要。当考虑信息访问时,通常区分两种类型的对手:白盒(知道模型的内部)和黑盒(不知道)。
当我们谈论规避攻击时,这种知识主要定义了对手可以发动的攻击有多强大。
然而,在投毒攻击中,还有第二个同样重要的方面来定义攻击者的能力— 对抗性访问 —或者说他们可以深入您的系统多深。就像信息访问一样,对抗性访问也分等级(从最危险到最不危险):
- 逻辑腐败
- 数据操作
- 数据注入
- 迁移学习
我们一个一个来。
逻辑腐败是最危险的场景。当攻击者可以改变算法及其学习方式时,就会发生逻辑破坏。在这个阶段,机器学习部分不再重要,因为攻击者可以简单地编码他们想要的任何逻辑。你还不如用一堆 if 语句。
为了帮助你想象逻辑破坏是什么样子,这里有一篇论文的摘录,他们在神经网络中添加了一个“后门检测器”:

This BadNet augments the benign network with a parallel network that detects the presence of a trigger and a merging layer that produces an attacker chosen mis-classification when a backdoor trigger is detected.
接下来是数据修改。在这种情况下,攻击者无法访问算法本身,但可以更改/添加/删除训练数据。
他们能做的一件事是操纵标签。例如,他们可以为训练池的一部分随机抽取新标签,或者试图优化它们以造成最大程度的破坏。如果目标是损害可用性,这种攻击非常适合,但是如果攻击者想要安装后门,这种攻击就变得更具挑战性。此外,由于其“有限”的扰动空间(您只能将标签更改为固定数量的其他标签),该攻击要求攻击者能够更改所有训练数据的大部分(例如 40% 这里是,随机标签翻转,30%是启发式)。
一个更老练的攻击者可能会操纵输入本身——例如,干扰它以移动分类边界,改变聚类距离,或者添加一个不可见的水印,该水印稍后可用于“后门”进入模型。扰动法应该让你想起规避攻击,事实上,它经常以类似的方式通过优化另一个方向(梯度上升)的损失函数来完成。这种攻击的相似之处在于它们从攻击者可获得的额外信息中获益(白盒、黑盒等)。
操纵输入是一种更复杂的攻击,不仅因为它更强大,还因为它背后有一个更现实的威胁模型。想象一下这样一个场景:一个反病毒产品位于多个端点上,并持续收集数据以供将来重新训练。对手很容易插入他们喜欢的任何文件,但他们无法控制标记过程,这是由另一端的人自动或手动完成的。因此,如果他们能够插入看似良性的恶意文件,他们只是安装了一个后门。

数据注入类似于数据操作,除了,顾名思义,它仅限于加法。如果攻击者能够向训练池中注入新的数据,这仍然使他们成为非常强大的对手。例如, Perdisci 通过在蠕虫通信流中插入扰动,阻止了蠕虫签名生成工具 Polygraph 学习有意义的签名。
最后,对抗性学习中最弱的是迁移学习。不重复我们上面已经讨论过的,迁移学习是四个中最弱的,因为自然地,当网络经历第二次训练时,它的原始记忆(包括毒素)被稀释了。
关于能力的最后一点,当我们谈到毒化训练数据池时,一个自然想到的问题是,我们能毒化多少?因为可以说,如果我们可以修改所有这些——我们基本上可以让机器学习模型学习我们想要的任何东西。至少从研究文献来看,答案似乎是目标是低于 20% 。除此之外,威胁模型开始听起来不切实际。
防御数据中毒
类似于闪避攻击,说到防御,我们处于困境。方法是存在的,但没有一种方法能保证 100%的稳健性。
最常见的防御类型是异常检测,也称为“数据净化”和“异常检测”。这个想法很简单——当毒害一个机器学习系统时,根据定义,攻击者将一些与它应该包含的内容非常不同的东西注入训练池——因此我们应该 能够 检测到 和。
挑战在于量化“非常”。有时注入的毒素确实来自不同的数据分布,并且很容易被隔离:

在其他情况下,攻击者可能会生成与真实数据分布非常相似的中毒点(称为“内联者”),但仍然成功地误导了模型。
离群点检测失败的另一种情况是在创建过滤规则之前注入了病毒。在这种情况下,离群值不再是离群值:)
异常检测的一个有趣变化是微模型。微模型防御是为了清理网络入侵检测器的训练数据而提出的。防御在训练集(微模型)的非重叠时期上训练分类器,并在训练集上评估它们。通过使用微模型的多数投票,训练实例被标记为安全或可疑。直觉告诉我们,攻击持续时间相对较短,一次只能影响几个微模型。
第二种最常见的防御类型是分析新添加的训练样本对模型的准确性的影响。其思想是,如果收集的输入是有害的,它将破坏测试集上模型的准确性,通过在将新样本添加到生产训练池之前对其进行沙盒运行,我们可以发现这一点。例如,拒绝负面影响(RONI) 和它的表亲目标感知 RONI (tRONI) 就是依靠这种特性的检测方法。
我见过的其他一些有趣的防御包括:
- 剥离 =有意扰动输入,观察预测值与未扰动值的差异。如果不够方差=恶意。
- 循环中的人类 =众所周知,毒害离群值会导致更大的边界移动,那么为什么不将人类(大概是安全分析师)的注意力集中在这种情况上呢?
- 修剪 =模型在迭代中从 MSEs(回归)的“修剪”版本中学习。
逃避和中毒——一枚硬币的两面
还记得我在开始时说过,大多数中毒攻击都是通过移动分类器的边界来实现的吗?你认为另一种人会怎么做?
答案是他们“拉”有毒的例子越过现有的边界,而不是移动它。这正是这篇论文的作者们想要达到的目标:
- 他们想给分类器下毒,把 A 归类为 B
- 所以他们拿了一堆 Bs,扰乱它们直到分类器认为它们是 As,然后仍然将它们标记为 Bs,并插入训练池
- 因此,他们用对立的 Bs 例子来毒害分类器

如作者所料,结果是分类器开始将 as 识别为 Bs。但这里是最有趣的部分:上面第(3)步的另一个名字是什么?
…
如果你读过我关于逃避攻击的帖子,那么你将有希望认识到这是对抗训练,这是对抗例子中保护模特最常见的方式。这给我们留下了一个令人不安的结论:通过很好地保护自己免受躲避攻击——你实际上可能会打开中毒的大门。
对一个惊险刺激的人来说怎么样?
泊松分布——直觉、例子和推导
何时使用泊松分布
在设置参数 λ 并将其代入公式之前,我们先暂停一秒钟,问一个问题。
为什么泊松非要发明泊松分布?
为什么会有这种分布?
什么时候应该使用泊松进行建模?
1.泊松为什么会发明泊松分布?
预测未来发生的#事件!
更正式地说,预测给定数量的事件在固定时间间隔内发生的概率。
如果你曾经卖过东西,这个"事件"可以定义为,例如,一个顾客从你这里买了东西(关键时刻,而不仅仅是浏览)。它可以是你的网站一天有多少访客,下个月你的广告有多少点击,你轮班时接到多少电话,甚至是明年有多少人会死于致命疾病,等等。
下面是我在现实生活中如何使用泊松的例子。
Every week, on average, 17 people clap for my blog post. I’d like to predict the # of ppl who would clap next week because I get paid weekly by those numbers.**What is the probability that** **exactly 20 people** (or 10, 30, 50, etc.) **will clap for the blog post next week?**
2.现在,让我们假设我们对泊松分布一无所知。那我们怎么解决这个问题?
解决这个问题的一个方法是从读取次数开始。每个阅读博客的人都有可能真的会喜欢它并鼓掌。
这是二项式分布的经典工作,因为我们正在计算成功事件(拍手)数量的概率。
一个二项随机变量是在 n 次重复试验中成功的次数 x 。我们假设成功的概率 p 在每次试验中都是不变的。
然而,在这里我们只得到一条信息——17 ppl/周,这是一个“比率”(每周成功的平均次数,或 x 的期望值)。我们对鼓掌概率 p 和博客访问量 n 一无所知。
因此,我们需要更多的信息来解决这个问题。我们还需要什么来把这个概率框定为一个二项式问题?我们需要两样东西:成功的概率(鼓掌) p &试验次数(访客) n 。
让我们从过去的数据中得到它们。

The stat of my Medium blog post about Gradient Descent
这些是一年的统计数据。总共有 59k 人阅读了我的博客。59,000 人中,有 888 人鼓掌。
所以每周阅读我博客的人数( n )是 59k/52 = 1134。每周鼓掌的人数( x )是 888/52 =17。
# of **people who read** per week (**n**) = 59k/52 = **1134**# of **people who clap** per week (**x**) = 888/52 = **17**Success probability (**p**) : 888/59k = 0.015 = **1.5%**
使用二项式 PMF ,下周 我将获得恰好 20 次成功(20 人鼓掌)的概率是多少?

<Binomial Probability for different **x**’s>╔══════╦════════════════╗
║ **x** ║ Binomial P(X=**x**)║
╠══════╬════════════════╣
║ 10 ║ 0.02250 ║
║ **17** ║ **0.09701** ║ 🡒 The average rate has the highest P!
║ 20 ║ 0.06962 ║ 🡒 Nice. 20 is also quite Likely!
║ 30 ║ 0.00121 ║
║ 40 ║ < 0.000001 ║ 🡒 Well, I guess I won’t get 40 claps..
╚══════╩════════════════╝
我们刚刚用二项分布解决了这个问题。
那么,泊松是为了什么? 有哪些事只有泊松能做,二项式做不到?
3.二项分布的缺点
a)二项随机变量是“二进制”——0 或 1。
在上面的例子中,我们每周有 17 个人鼓掌。这意味着 17/7 =每天 2.4 人鼓掌,17/(7*24) =每小时 0.1 人鼓掌。
如果我们使用二项式随机变量按小时(0.1 人/小时)对成功概率建模,这意味着大多数小时得到零拍手但是一些小时将得到恰好 1 拍手。然而,也很有可能某些小时会得到不止一次掌声(2 次,3 次,5 次,等等。)
二项式的问题是在单位时间内不能包含 1 个以上的事件(在本例中,1 hr 是单位时间)。时间单位只能有 0 或 1 个事件。
那么,把 1 小时分成 60 分钟,把单位时间变小,比如一分钟,怎么样?那么 1 小时可以包含多个事件。(尽管如此,一分钟将包含一个或零个事件。)
我们的问题现在解决了吗?
算是吧。但是如果在那一分钟里,我们得到了多次鼓掌呢?(也就是说,有人在 Twitter 上分享了你的博文,流量在那一分钟激增。)然后呢?我们可以把一分钟分成几秒钟。那么我们的时间单位就变成了秒,同样,一分钟可以包含多个事件。但是这个二进制容器的问题将永远存在于更小的时间单位中。
这个想法是,我们可以通过将一个单位时间分成更小的单元,使二项随机变量处理多个事件。通过使用更小的划分,我们可以使原始单位时间包含多个事件。
数学上,这意味着 n → ∞ 。
因为我们假设速率是固定的,我们必须有 p → 0。因为不然的话, n*p,也就是事件数,会炸。
使用极限,单位时间现在是无穷小。我们不再需要担心在同一单位时间内会发生不止一个事件。这就是我们如何得到泊松分布。
b)在二项分布中,试验次数(n)应该事先知道。
如果使用二项式,则不能仅用比率(即 17 ppl/周)来计算成功概率。你需要“更多信息”( n & p )来使用二项式 PMF。
另一方面,泊松分布不需要你知道 n 或者 p,我们假设 n 无限大,p 无限小。泊松分布的唯一参数是率λ (期望值 x )。在现实生活中,只知道费率(即在下午 2 点~ 4 点期间,我接到了 3 个电话)比知道两个 n & p 要常见得多。
4.让我们从二项式 PMF 数学推导泊松公式。

Deriving Poisson from Binomial
现在你知道λ^k 各个组件在哪里了,k!还有 e^-λ是从哪里来的!
最后我们只需要证明前两项的相乘 n!/((n-k)!当 n 接近无穷大时,为 1。

是 1。
我们得到了泊松公式!

From https://en.wikipedia.org/wiki/Poisson_distribution
现在维基百科的解释开始有意义了。
把自己的数据代入公式,看看 P(x)对你有没有意义!
下面是我的。
**< Comparison between Binomial & Poisson >**╔══════╦═══════════════════╦═══════════════════════╗
║ **k** ║ Binomial P(X=**k**) ║ Poisson P(X=**k;**λ=**17**) ║
╠══════╬═══════════════════╬═══════════════════════╣
║ 10 ║ 0.02250 ║ 0.02300 ║
║ **17** ║ **0.09701** ║ **0.09628** ║
║ 20 ║ 0.06962 ║ 0.07595 ║
║ 30 ║ 0.00121 ║ 0.00340 ║
║ 40 ║ < 0.000001 ║ < 0.000001 ║
╚══════╩═══════════════════╩═══════════════════════╝* You can calculate both easily here:
Binomial: [https://stattrek.com/online-calculator/binomial.aspx](https://stattrek.com/online-calculator/binomial.aspx)
Poisson : [https://stattrek.com/online-calculator/poisson.aspx](https://stattrek.com/online-calculator/poisson.aspx)
需要注意一些事情:
- 即使泊松分布模拟罕见事件,比率 λ 可以是任何数字。它不一定总是很小。
- 泊松分布是不对称的,它总是向右倾斜。因为它在左边被零发生壁垒(没有“负一”clap 这种东西)抑制,在另一边是无限的。
- 随着 λ 变大,图表看起来更像正态分布。

https://en.wikipedia.org/wiki/Poisson_distribution
4.泊松模型假设
a .单位时间内事件的平均发生率不变。
这意味着每小时访问你博客的人数可能不遵循泊松分布,因为每小时的比率不是恒定的(白天的比率较高,晚上的比率较低)。对消费者/生物数据使用月利率也只是一个近似值,因为季节性效应在该领域并非微不足道。
b .事件是独立的。你的博客访客的到来可能并不总是独立的。例如,有时大量的访问者成群结队而来,因为某个受欢迎的人提到了你的博客,或者你的博客出现在 Medium 的首页,等等。如果一次大地震增加了余震的可能性,一个国家每年的地震次数也可能不遵循泊松分布。
5.泊松分布和指数分布之间的关系
如果单位时间内的事件数量遵循泊松分布,则事件之间的时间量遵循指数分布。泊松分布是离散的,而指数分布是连续的,然而这两种分布密切相关。
基于策略的强化学习,简单的方法
逐步理解强化学习中基于策略的方法

更新 1 :学习和练习强化学习的最好方式是去 http://rl-lab.com
更新 2 :如果你是这个主题的新手,从开发人员强化学习政策文章开始可能更容易。
介绍
假设你在一个新城镇,既没有地图也没有 GPS,你需要到达市中心。你可以试着评估你相对于目的地的当前位置,以及你所采取的每个方向的有效性(价值)。你可以认为这是计算价值函数。或者你可以问一个当地人,他会告诉你直走,当你看到一个喷泉时,你向左走,一直走到市中心。他给了你一个可以遵循的政策。
自然,在这种情况下,遵循给定的策略比自己计算价值函数要简单得多。
在另一个例子中,假设您正在管理库存,并且您决定当每件商品的数量低于某个限制时,您发出一个购买订单来补充您的库存。这是一个比研究客户的活动、他们的购买习惯和偏好要简单得多的策略,以便预测对你的股票的影响…
毫无疑问,值函数将导致确定策略,如前几篇文章中所见,但是还有其他方法可以学习使用参数选择操作的策略,而无需咨询值函数(这不太正确,因为需要值函数来提高准确性)。
因此,主要思想是能够确定在状态(【s】)下采取哪种行动,以使回报最大化。
实现这一目标的方法是微调𝜽指出的参数向量,以便为𝜋.政策选择最佳行动
策略记为𝜋(a|s,𝜽) = Pr{At = a | St = s,𝜽t = 𝜽},这意味着策略𝜋是在状态时采取行动 a 的概率,参数为𝜽.
优势
- 更好的收敛特性
- 在高维或连续动作空间有效
当空间很大时,内存的使用和计算消耗增长很快。基于策略的 RL 避免了这一点,因为目标是学习一组远小于空间计数的参数。 - 可以学习随机策略
随机策略比确定性策略更好,尤其是在两人游戏中,如果一个玩家确定性地行动,另一个玩家会制定对策以获胜。
不足之处
- 通常收敛于局部最优而不是全局最优
- 评估策略通常效率低下且差异大
基于策略的 RL 差异很大,但有一些技术可以降低这种差异。
随机政策
首先需要注意的是,随机并不意味着在所有状态下都是随机的,但在某些有意义的状态下,它可能是随机的。
通常报酬最大化会导致确定性政策。但是在某些情况下,确定性策略并不适合这个问题,例如在任何两个玩家的游戏中,确定性的参与意味着另一个玩家将能够采取反制措施以便一直获胜。例如,在石头剪刀布游戏中,如果我们每次都玩确定性的相同形状,那么其他玩家可以很容易地对抗我们的策略并赢得每场游戏。

所以在这个博弈中,最优策略是随机的,比确定性策略好。
蓝图
在深入研究数学和算法的细节之前,了解一下如何进行是很有用的,这是一种蓝图:
- 找出一个可以用来评估政策有效性的目标函数。换句话说,告诉我们政策的效果有多好。
- 定义策略。
我们的意思是列出一些可以在学习过程中使用的有用政策。 - 幼稚的算法。
提出一种直接利用策略来学习参数的算法。 - 改进的算法
寻找改进目标函数的算法,以最大化策略的有效性。
蓝图的第 1 部分:寻找目标函数
请记住,在上面的蓝图中,我们谈到了寻找一个目标函数来评估政策的有效性。在这一节中,我们将定义目标函数及其一些有用的推导。
(关于政策梯度的更多细节可以在文章政策梯度一步一步中找到)。
目标函数
当谈到函数的最大化时,一个突出的方法是梯度。
但是我们如何根据𝜽最大化回报呢?一种方法是找到一个目标函数 J(𝜽)这样

其中,V𝜋𝜽是策略𝜋𝜽的值函数, s0 是起始状态。
简而言之,J(𝜽最大化意味着 V𝜋𝜽(s).最大化由此可见

根据政策梯度定理

其中𝝻(s)是𝜋下的分布(意为遵循政策𝜋时处于状态 s 的概率),q(s,a)是𝜋下的动作值函数,∇𝜋(a|s,𝜽)是𝜋给定 s 和𝜽.的梯度
最后𝝰的意思是成比例的。
所以这个定理说∇J(𝜽)正比于 q 函数的和乘以我们可能处于的状态下所有行为的策略梯度。然而我们不知道𝜋(a|s、𝜽),我们怎么能找到它的梯度呢?
事实证明,如以下演示所示,这是可能的:

提醒: ∫ dx/x = Log(x) 意思是 dx/x = (Log(x))' = ∇Log(x)
∇Log 𝜋𝜃(s,a) 被称为得分函数。
注意,政策的梯度可以表示为预期。如果你问自己为什么?查看维基百科关于期望值的文章。
参数更新
由于这是一个梯度方法,参数的更新(我们正试图优化)将按通常的方式进行。

蓝图的第 2 部分:定义策略
本节介绍了几种标准梯度策略,如 Softmax 和 Guassian。我们在 RL 算法中使用这些策略来学习参数𝜽.
实际上,每当在 RL 算法中我们看到对 ∇Log 𝜋𝜃(s,a) 的引用时,我们就插入所选策略的公式。
Softmax 策略
softmax 策略由一个将输出转换为概率分布的 softmax 函数组成。这意味着它影响每个可能动作的概率。
Softmax 主要用于离散动作的情况:

因此

在哪里

这里可以查看推导的完整演示。
高斯策略
高斯策略用于连续动作空间的情况,例如当你驾驶一辆汽车时,你转动方向盘或踩下油门踏板,这些都是连续动作,因为这些都不是你做的少数动作,因为你可以(理论上)决定旋转角度或气体流量。

推导变成了

蓝图的第 3 部分:朴素算法
本节将给出一些算法,这些算法将考虑策略和它们的目标函数,以便学习给出最佳代理行为的参数。
强化(蒙特卡罗策略梯度)
该算法使用蒙特卡罗根据策略𝜋𝜃创建剧集,然后对于每个剧集,它迭代该剧集的状态并计算总回报 G(t)。它使用 G(t)和∇Log 𝜋𝜃(s,a(可以是 Softmax 策略或其他)来学习参数𝜃.

from Sutton Barto book: Introduction to Reinforcement Learning
蓝图的第 4 部分:改进的算法
我们已经说过基于策略的 RL 具有很高的方差。然而,有几个算法可以帮助减少这种差异,其中一些是加强基线和演员的批评。
用基线算法增强
基线的概念是从 G(t)中减去称为基线的 b(s ),目的是减少结果的大范围变化。
假设 b(s)不依赖于动作 a,可以证明 ∇J( 𝜽)的方程仍然成立。
所以现在的问题是如何选择 b(s)?
基线的一个选择是计算状态值的估计值,( St,w ),其中 w 是通过蒙特卡罗等方法学习的参数向量。
所以 b(s)=③(St,w)
用基线增强算法变成

演员评论家算法
(详细解释见演员评论家简介篇)
演员评论家算法使用 TD 来计算用作评论家的价值函数。批评家是一个状态值函数。评估事情的进展是很有用的。在每个行动之后,批评家计算新的状态来确定是否有任何改进。这个评估就是 TD 误差:

然后,δ(t)用于调整参数𝜽和 w 。
简而言之,𝜽和 w 都以修正该误差的方式进行调整。

from Sutton Barto book: Introduction to Reinforcement Learning
相关文章
政策梯度渐进
政策梯度方程推导的综合分析

Photo by Kyle Glenn on Unsplash
更新 1 :学习和练习强化学习的最好方式是去 http://rl-lab.com
更新 2 :如果你是这个主题的新手,从开发人员强化学习政策文章开始可能更容易。
概观
在上一篇文章中,我们了解了基于策略的学习,并探讨了这种方法的优缺点。在本文中,我们将深入研究数学细节,以推导出政策梯度的正确方程。
但是在我们开始之前,让我们快速回顾一下为什么政策学习是有用的:
- 策略学习在高维或连续的动作空间中是有效的。
大的状态和动作空间消耗巨大的内存和计算能力。另一方面,学习策略消耗较少,因为我们学习定义策略的一组可管理的参数。 - 可以学习随机政策。
随机策略比确定性策略更好,尤其是在两人游戏中,因为任何确定性行为都将很容易受到反措施的威胁。
数学
我们需要时刻提醒自己我们的目标是什么,因为我们很容易忘记它。
不断对自己说,目标是在每一个强化学习问题中获得最大的回报。
所以从这个基本原则出发,我们定义 J(𝜃为我们在从 0 到 t 集结束的每个时间步中得到的预期回报 R(s,a ),遵循一个𝛑(𝜃).政策
如果我们将这一集定义为一条从 t=0 到 t 的𝞽轨迹,那么预期报酬就是所有可能轨迹的总和,即𝞽根据𝜃被选中的概率,乘以这条轨迹的回报率 R(𝞽).
这使我们得出以下等式:

同样,我们的目标是找到𝜃的一组参数,使 J(𝜃)(which 的预期回报最大化。

从微积分我们知道,为了找到一个函数的极值(最大值或最小值),我们要计算它的导数。
我们将在下面的一组等式中完成:

注意上面的技巧,我们用𝜃的 P(𝞽来乘除(这是合法的,因为𝜃的 p(𝞽= 1)。这个技巧的目的是达到一个术语𝛻P(𝞽,𝜃)/P(𝞽,𝜃),因为∫df/f .dx= log(f(x)),因此 log(f(x))的导数是 df/f。由此我们发现𝛻log P(𝞽,𝜃 ) = 𝛻P(𝞽,𝜃)/P(𝞽,𝜃).我们神奇地得到了下面的等式。

然而,由于我们实际上不能计算每一个可能的轨迹,𝞽,we 将后退以获得数量减少的(m)个轨迹。在这里我们也可以看到𝜃的 P(𝞽(日志前的那个)神奇地消失了。原因是,当我们选择了(m)个轨迹时,就不再有选择它们的可能性,因为它们已经被选择了。
我们以这个等式结束:

请注意,我们从期望值(所有可能的轨迹)移动到(m)个选定轨迹的平均值。
现在让我们单独关注𝛻logp(𝞽(𝜃)。轨迹被遵循的概率等于当遵循某个策略𝛑(𝜃).时,该轨迹的每一步到达的概率的乘积
下面一行显示 log P(𝞽,𝜃)转化为 P(S(t+1)|St,at)的乘积。𝛑(𝜃)

直觉上,我们说我们遵循了一个轨迹,当我们通过了它的所有步骤。所以沿着它走到底的概率,等于我们经过这条轨迹每一步的概率的乘积。
我们知道乘积的对数等于对数之和:

上述方程组的第二行显示,我们将σlog(p.𝛑进一步分解为σ(log(p)+log(𝛑),然后是σlog(p)+σlog(𝛑).
当对𝜃应用这两项的导数时,𝛻σlog(p 消失,因为 p 不依赖于𝜃.
既然我们已经发现𝛻log P(𝞽,𝜃)=𝛻σlog(𝛑(a|s,𝜃,我们可以把它放回初始方程,并检索最终结果,如下所示:

结论
正如我们已经看到的,推导政策梯度是非常优雅和智能的程序。完全掌握这种推导的本质是至关重要的,因为它是许多众所周知的基于政策的方法的基础,例如演员-评论家。
相关文章
人工智能社会契约的政策处方

The Leader of the Luddites, Published in May 1812 by Walker & Knight, Sweetings Alley, Royal Exchange, Copyright Expired
在我最近的故事“人工智能经济的新世界秩序”中,我提出未来人工智能经济的成功将在很大程度上取决于每个国家人工智能劳动力、人工智能基础设施以及最终人工智能社会契约的创造。在这个原始故事中,我提出了一些高层次的政策想法作为讨论的开始,但很明显,人们渴望更深入地研究人工智能社会结构的想法以及实现它的具体政策想法。
为什么是人工智能社会契约?
人工智能和工作场所自动化的采用已经在改变我们的工作方式和我们需要的劳动力。根据《2019 年世界发展报告》(图 1),美国近一半的工作岗位预计将实现自动化。

Figure 1: WDR 2019: The Changing Nature of Work© World Bank http://www.worldbank.org/en/publication/wdr2019 License: Creative Commons Attribution license (CC BY 3.0 IGO)
工人的流离失所导致了技术抵制,19 世纪英格兰卢德运动的经典例子就是工人摧毁纺织机械,这被视为夺走工人的生计。根据 Forrester 的 Craig Le Clair 在最近的金融时报文章中的预测,随着人工智能在“立方体”中自动处理白领工作的应用迅速增长,预计到 2022 年将达到 480 亿美元以上,我们可以预计未来将出现勒德派式的动荡。
虽然自动化的历史告诉我们,每一次新的技术进步都会增加总就业,但新的工作岗位往往会流向不同的工人阶层、技能水平等。据估计,到 2030 年,7500 万到 3 . 75 亿工人需要改变就业类别或重新获得技能(来源:麦肯锡全球研究所)。
这种从人到“数字工人”、物理机器人和 RPA 机器人的工作快速转移,使得新的“人工智能社会契约”成为必要,以保持创新,同时为受到工作场所演变影响的工人提供服务。
人工智能社会契约政策
第一要务:再培训、技能提升和教育:自动化的早期影响将主要集中在中低技能劳动力身上。包括送货、分拣、驾驶、提升和检查在内的工作将是自动化的早期目标。新美洲的工作为自动化换挡表明,居住在城市以外的工人在面对自动化时面临更大的困难。
- 人工智能优化的职业教育:人工智能教育的大部分重点是高等数学和科学。人工智能社会契约将需要职业教育,为新兴领域的工人提供装备:先进制造、机电一体化、机器人服务和维修等。非政府组织和社区大学需要资金来继续和发展像西雅图友好社的青年航天计划这样的项目。
- 将年轻工人从衰退的行业和工作职能中分流出来至关重要。对衰落行业中完全就业的个人进行再培训和工作变动激励,将节省政府用于再培训年龄较大、灵活性较差的工人的长期支出。政策规定雇主雇用职业生涯晚期的工人来填补目前的空缺,这在这一领域有所帮助,并解决一些被解雇的老年工人的就业需求。
- 中年工人的再培训和技能提升面临着最大的挑战,因为他们已经工作了很多年,不太能灵活地改变行业,而且经常会选择与他们过去的就业相关的培训选项,从而增加了裁员的风险。一个关键的成功因素是接受职业评估,以帮助失业工人做出再培训决定(来源:大西洋)。
优先事项 2: 如今,只有 48%的美国人支持为自动化失业工人提供基本收入的想法,而几乎相同比例的工人面临机器人失业的风险(来源:东北大学/盖洛普调查,2017 年 9 月 15 日至 10 月 10 日)。随着自动化对工人的影响越来越清晰,有理由假设公众支持将达到多元化。随着公众支持的增加,决策者在执行这些政策时需要考虑以下因素:
- 有保障的收入计划将需要保留给那些无法重新获得技能的工人
- 保证收入计划的参与者应该优先作为被分流的年轻工人的替补
- 这些计划应该有时间限制,以使该计划对接近社会保障和医疗保险资格的工人更有吸引力
- 这些计划将需要提供医疗补助资格作为权宜之计,直到工人及其家人达到标准的医疗保险资格
政治巨蟒
用 Scrapy 刮国会文件

No, not that kind of python.
2020 年民主党总统候选人将在周三开始的辩论中交锋。他们中的许多人是现任或前任国会议员。他们都在竞争领导这个国家。
作为选民,如果我们有他们在政治生涯中在参议院或众议院所说的每一句话的记录,那不是很棒吗?
作为数据科学家,难道我们不想提取他们的话,分析他们,并用他们来判断或预测这些国会议员作为总统候选人吗?
用 Scrapy 造一只蜘蛛
是的,我们可以。宪法要求国会记录其议事过程。因此,政府出版办公室印刷(并以数字形式发布)国会记录,其中包含立法机构两院的日常正式会议记录,包括:
- 住房部分
- 参议院部分
- 评论的延伸(演讲、颂词、证词和立法史)
- 每日文摘

Congressional Record, November 17, 2008.
尽管这些数据存在于公共领域,但将它们从网站上取出并转换成可用的格式却是一个挑战。在无法访问昂贵的法律数据库的情况下,网络搜集是有事业心的公众的最佳选择,Scrapy 使得快速获得大量信息变得相对容易。
蜘蛛去了华盛顿
Scrapy 允许使用 python 进行异步 web 抓取。您可以使用它通过 API 提取数据,将它与 BeautifulSoup 和 Selenium 集成,并像蜘蛛网有细丝一样扩展它的功能。
Scrapy 的核心想法是复制 Django 一个“不要重复自己”的框架,这意味着它提供了一种重用代码和轻松将项目扩大到更大范围的方法。爬行器的组成部分,如项目、中间件、管道和设置,存在于单独的脚本中,爬行不同网站的多个“蜘蛛”可以在同一个“项目”中使用它们。
蜘蛛本身自然依赖于面向对象的编程(每个都属于“蜘蛛”类):

A Scrapy spider.
重要组件包括:
- 开始 URL
- 你从回答中提取了什么,如何提取
- 返回给解析函数的内容
在 Python 中使用 XPath
Scrapy 最有用的功能之一是 Scrapy shell,它允许你实时探索你正在抓取的网站,以测试你的假设。本质上,在部署代码之前,您可以在沙盒模式下尝试您的代码(并发现它不起作用)。
当您在处理像 XPath 这样复杂的东西时,这个附加功能极大地减少了深入网站结构提取所需内容的时间和挫折感。例如,我需要从 Congress.gov 网站的 HTML 中的特定元素获取部分 URL 和文本。Scrapy shell 允许我在将结果语法复制并粘贴到代码中之前,确保我的 XPath 语法不会返回空列表。

Is it worth auto-generating XPath?
关于从 DevTools 中“复制 XPath”的说明:虽然您可能觉得在 DevTools 中右键单击 HTML 元素并从出现的菜单中选择“复制 XPath”很吸引人,但是不要屈服于诱惑。如果像国会一样,你的网站是用表格组织的,你将无法检索到你想要的一切。就我而言,我想要的 pdf 文件位于页面的最右边一栏:

Congress.gov website
单击“复制 XPath”会显示以下信息:
//*[@id="browse_expanded_panel_1"]/div/table/tbody/tr[1]/td[6]/a
这只是描述一个表内的位置。
下面是 shell 中返回的内容:

An empty list
下面是我的实际的 XPath 表达式:
response.xpath('//td/a[@target="_blank"]/@href').extract()
这是它返回给定页面的内容:

PDFs that exist
您必须学会使用 XPath 来选择有意义的内容,而不是依赖于自动生成的表达式,后者——很像正在讨论的政府机构——通常表功能有利于形式。
使用 Tika 提取文本
一旦我写了这个蜘蛛,它很快就把 25 年的国会记录下载到了我的硬盘上。后来没有太多额外的代码,我已经用处理 pdf 的 python 库 Tika 提取了文本。
我创建了一个正则表达式来拆分个人发言:

Preliminary NLP pre-processing
尝试了一些非常初步的情感分析:

Preliminary NLP with VADER
议长先生,交出你的秘密
既然他们的话已经暴露无遗,国会下一步该怎么办?分析与数据挖掘,自然(语言处理)ly。尽管人们对 Twitter 争论不休,但几乎没有人对参议员和众议员在国会发言时所说的话提出质疑,无论是因为获取数据的难度、训练模型的难度、缺乏法律素养,还是上述所有原因。但借助适当的工具和领域知识,我希望在 2020 年选举季之前提供一些有价值的见解。
原载于https://www.espritdecorpus.com。
民意测验和新闻
探索新闻报道与民意测验表现之间的关系
候选人投票是政治竞选过程的重要组成部分。投票结果受到选民、记者,当然还有候选人及其竞选团队的密切关注。民调排名的上升有助于竞选造势,巩固支持,刺激筹款。减少会产生相反的效果。
在 2019 年 5 月撰写这篇文章时,美国正处于 2020 年总统大选的早期阶段,已经有大约 20 名民主党候选人争夺本党提名,并面对可能的共和党提名人唐纳德·特朗普。理解投票驱动因素是这些候选人、他们的支持者和反对者以及中立观察者的浓厚兴趣。
一个驱动因素是媒体报道,在这篇文章中,我通过研究 2016 年共和党总统初选来探索新闻报道和民调结果之间的关系。这场初选与今天的民主党竞选有一些相似之处,特别是在候选人众多的领域,它可能会成为有用见解的来源。
这种方法是建立一个由 2015 年和 2016 年的民调结果和关键新闻报道指标和主题组成的数据集,在这段时间里,初选过程将一个巨大的候选人场削减到一个获胜者唐纳德·特朗普(Donald Trump)。利用这些数据,我们可以探索关系。
数据
为了进行这种分析,需要民意测验和新闻报道数据。这些数据来自两个不同的来源,FiveThirtyEight 和全球事件、语言和语调数据或 GDELT 项目。
FiveThirtyEight 是一项分析服务,拥有一个受欢迎的网站,提供政治、体育、科学和健康、经济和文化的定量和统计分析。
[## 五三八
内特·西尔弗的《FiveThirtyEight》使用统计分析——硬数字——讲述关于选举的引人入胜的故事…
fivethirtyeight.com](https://fivethirtyeight.com/)
2015/2016 年共和党总统初选的民调数据是使用基于 Selenium 和 Beautiful Soup 工具构建的网络抓取对象从一个特定的 538页面获得的。它们涵盖了 2015 年 1 月 25 日至 2016 年 5 月 3 日之间的 670 项民意调查,结果涉及 11 位候选人(唐纳德·川普、约翰·卡西奇、特德·克鲁兹、马尔科·卢比奥、本·卡森、杰布·布什、克里斯·克里斯蒂、卡莉·菲奥莉娜、里克·桑托勒姆、兰德·保罗和迈克·哈克比)。
GDELT 由 Google Jigsaw 支持,是一个监控、存储和提供“来自几乎每个国家每个角落的世界广播、印刷和网络新闻”的项目。GDELT 管理几个存储库,但是对于这个项目,数据是从全球知识图第二版(GKG V2)数据库中提取的。具体而言,针对与主要候选人相关的文章,获取了文件计数、语气、正负分数、极性、活动参考密度和自我参考密度的 GDELT CKG 值以及文章主题汇编。
GDELT 项目
哥德尔 Projectwww.gdeltproject.org](https://www.gdeltproject.org/)
GDELT 数据可以通过文本 API 获得,并作为谷歌 BigQuery 大数据服务上的公共数据集。对于这项工作,我使用 BigQuery 并通过 Google 提供的 Python API 访问数据库。CKG V2 非常大,有 9.85 万亿字节(TB)和大约 9.35 亿行。处理如此大的数据集意味着我首先必须将相关的新闻数据写入另一个小得多的 BigQuery 表,然后查询它以在 Python 的 pandas 库中构建建模数据。这个较小的表大约有 540 万行,其中的行代表在投票期间与上述候选人相关的 CKG·V2 条目。
形象化
有 11 个候选人和一系列功能,有许多可视化图表可能会揭示有趣的关系。从对投票结果的纵向观察和对单个候选人的媒体报道度量开始可能是最有用的。例如,我们可以查看特德·克鲁兹和川普的投票结果和文章数量。


文章计数显示了一个有趣的模式,在活动的早期覆盖率相对较低,然后在最后几个月迅速增加。民意测验结果有趋势成分,似乎显示出变化。实际上,在相当长的时间内,它们大多围绕趋势线波动(换句话说,它们围绕同一个值波动)。
这也有助于横向比较候选人之间的相似度量。在接下来的两个图表中,我们看到了所有候选人的平均投票结果与文章数量的对比,以及平均投票结果与文章语气平均值的对比。
第一张图中各点的排列证实了一个直观的观察结果,即较高的民调数字与较大的媒体报道相关联。

GDELT 为每份 CKG·V2 文件获取了几个指标,包括对其平均“语气”的测量声调被计算为正分数和负分数之间的差,正分数和负分数分别代表正单词和负单词的百分比。在下图中,我们检查了平均投票结果和平均音调之间的关系。

这里没有明显的模式,但有趣的是,特朗普在这个尺度上是一个值得注意的异数,平均语气最低,但平均民调最高。
GDELT 还跟踪每个文档的极性。它测量音调词典中匹配单词的百分比,作为文本“情绪极化或充满激情”程度的指标更高的值与更大的极化相关联。相对于极性平均值的平均值绘制平均投票结果揭示了沿极性维度的有趣分组。保罗、克鲁兹、哈克比和川普属于媒体高度分化群体。

建模
一个自然的问题是,这些新闻数据能预测未来的民意测验表现吗?为了解决这个问题,我构建了一个分类模型框架,其中目标变量是未来民意测验中的表现,解释特征是从 GDELT 收集的新闻数据。
目标变量包括三类,下降、持平和上升,对应于特定候选人的投票结果在两次感兴趣的投票之间是下降、保持持平还是上升。在我的分析中,我对下一轮投票和第三轮投票的性能进行了建模,但是可以检查任意数量的投票周期。解释性特征包括 GDELT 的文档计数、度量以及文档主题列表。
事实证明,使用这些数据来创建未来投票表现的预测模型具有挑战性。总的来说,这项分析发现,在一个广泛的候选人领域中平均的广泛媒体指标在预测即将到来的民意调查中的表现方面没有什么力量。这些模型的精度并不比下行、平坦和上行观测的基线分布好多少。
研究了几种模型配置,包括预测未来投票(在这种情况下是第三次投票)中的表现,以及将数据集限制为单个候选人。这些模型显示了改进的准确性,并表明这种方法可以产生结果。需要做更多的工作。
更多细节和未来的工作
对于那些感兴趣的人,你可以在我的 GitHub 库中找到更多信息,包括这个项目的所有建模工作。
如果读者有兴趣,我可以将分析扩展到 2019/2020 民主党总统初选,看看从这场比赛的早期观察中可以收集到什么观察结果。让我知道。
基于时间序列和 LSTM 的 MXnet 污染预测

Photo by Jake Hills on Unsplash
时间序列
时间序列分析是一种处理时间序列数据或趋势分析的统计技术。时间序列数据是指数据在一系列特定的时间段或间隔内。
TSA(时间序列分析)应用:
- 模式识别
- 地震预报
- 天气预报
- 金融统计
还有更多…
MXnet

Apache MXNet(孵化)是一个深度学习框架,旨在提高效率和灵活性。它允许你混合符号和命令式编程来最大化效率和生产力。MXNet 的核心包含一个动态依赖调度器,它可以动态地自动并行化符号操作和命令操作。在此之上的图形优化层使得符号执行速度更快,内存效率更高。MXNet 是可移植的、轻量级的,可以有效地扩展到多个 GPU 和多台机器。
问题

在中国这个世界上人口最多的国家,随着新兴精英阶层的崛起和超过 10%的经济增长率,环境问题已经退居其次。日益严重的污染导致了无法使用的水道,出生缺陷的增加,以及一些地球上最脏的空气。它是如此的令人讨厌,以至于现在有一个词来形容它:“smogpocalypse”。
我们开始吧!
密码
这里用到的所有代码都可以作为笔记本使用。可以直接在 google colab 上运行。
我们从安装基本组件和依赖项开始我们的笔记本。
在本教程中,我们将使用北京 PM2.5 数据集来训练和预测污染水平( PM2.5 )
使用 wget 直接下载数据集:
导入所需模块:
用熊猫读数据:
小数据处理 —
大量的可视化和特征工程超出了本教程的范围,但是我们仍然需要做一些事情来继续。
- 下降 Nan 值。
- 标签编码风向( cbwd )

我们数据集的快速可视化:

我们定义了一个将数据转换为数据序列的函数:
多一点数据预处理
- 将数据集值中的值缩放至[0,1]
- 通过将我们的数据传递给上面定义的
make_dataset_many_to_one来创建新的数据集。在这里,我们选择每 24 小时做一次记录。
按如下方式重塑数据:
- 样品。一个序列就是一个样本。一个批次由一个或多个样本组成。
- 时间步长。一个时间步长是样本中的一个观察点。
- 特点。一个特征是一个时间步长的一个观察值。
将 numpy 数组转换成mxnet.nd.array:
定义 LSTM 神经网络:
接下来,我们需要将模型初始化为一个设备。(CPU 或 GPU)
现在,我们需要定义训练参数。更多
这里,loss_function用于训练期间,mse用于计算每个历元后的损失。
主训练循环:
损失应该随着每个时期变得更低,我们为 15 个时期训练我们的模型。
对整个数据集进行预测:
将缩放后的预测值转换为实际大小,并将其存储在数据框中:
将预测可视化:

正如你所看到的,我们的模型在预测明天的污染水平方面做得很好。
希望这篇文章对你的探索有所帮助。
新的 Jupyter?
围绕网飞的多语言笔记本 Polynote 有很多议论。但这是木星杀手,还是所有的炒作?

Source: https://polynote.org/
如果你在过去几年中使用 Python 做过任何类型的分析工作,你很有可能会遇到 Jupyter notebooks。无需重新运行整个脚本即可执行新代码的能力使笔记本成为分析工作流程中无处不在的一部分,但 Jupyter 笔记本并非没有问题。例如,不支持代码编辑、提示和林挺;这可能导致代码混乱、难以调试,并导致一些开发人员完全避免使用该工具。2019 年,科技巨头网飞开源了他们的笔记本工具, Polynote,,许多人将其称为 Jupyter-killer。
像 Jupyter 一样,Polynote 也支持多语言,但是 Scala 已经作为“一级语言”包含在 Polynote 中,另外还支持 Python (+ Spark)、SQL 和 Vega。虽然 Jupyter 允许您用一种受支持的语言(JUlia、PYThon、R)创建一个笔记本,但是 Polynote 在一个笔记本中提供了多语言支持,甚至允许不同语言执行的代码之间的互操作性。
装置
与 Jupyter 不同,Polynote 的核心是基于 JVM 的语言,这使得安装过程比pip install polynote稍微复杂一些。问题是,Polynote 目前只能在 Linux 和 OSX 上使用;有一些关于包含 Windows 的讨论,但是这不是开发团队的目标。鉴于像 Swift 这样的流行工具的开源 Windows 移植已经失败,期待 Polynote 在 Windows 上的无缝体验是愚蠢的。
也就是说,让我们假设您有一台 Linux 或 OSX 机器。我将概述我让 Polynote 在 Ubuntu 18.04 上工作的步骤。
- 转到版本并在最新版本的“资产”下下载最新的
polynote-dist.tar.gz - 用
tar -zxvpf polynote-dist.tar.gz提取多项式 - 安装 Java:
sudo apt install default-jre和sudo apt install default-jdk - 用
export JAVA_HOME=/usr/lib/jvm/default-java/设置 JAVA_HOME pip3 install jep jedi virtualenv
火花支持需要额外安装。你可以在这里阅读更多关于安装 Polynote 的信息。
进入 polynote 目录并执行polynote.py,然后在浏览器中导航到提供的本地地址(我已经在 Firefox 和 Chrome 上测试过,没有问题)。
多语言笔记本
Polynote 的同名特性是语言互操作性:如果用一种语言定义了一个变量,就可以在另一种语言中使用它。这显然有局限性,因为不是所有的语言都有相同的类型和对象概念,但是最重要的类型,包括 numpy 数组和 pandas 数据帧,都是受支持的。

我必须承认,我对 Scala 只有一知半解,所以我不确定需要它与 Python 协同工作的具体用例,但很明显,消除在两种语言之间转换数据的需要将使这样一个项目运行得更加顺畅。
我参与的大多数项目都只需要一种语言(大部分是 Python,一些是 R ),所以很容易相信,在大型项目之外,语言混合对于大多数数据科学家来说是多余的工具。当然,作为一个开源项目,社区很可能会扩展所支持的语言套件(已经有人对 R、 Julia 和 Java 感兴趣),这将使 Polynote 成为对大量开发人员更有吸引力的工具;就我个人而言,我希望能够在 R 中对 Python 数据进行统计测试,而不是花费时间去寻找一个有效的、可维护的 Python 测试端口。
(英)可视化(= visualization)
数据可视化是数据科学的重要组成部分,但它是一项经常被忽视的技能。Polynote 通过从 Scala 数据生成 Vega 规范来强调高质量的可视化。
Vega 是一种可视化语法,一种用于创建、保存和共享交互式可视化设计的声明性语言。使用 Vega,您可以用 JSON 格式描述可视化的视觉外观和交互行为,并使用 Canvas 或 SVG 生成基于 web 的视图。—https://vega.github.io/vega/
织女星可以很容易地产生美观、复杂的视觉效果。流行的 Python 可视化库牛郎星建立在织女星的统计可视化部分 Vega-lite 之上(你可以在这里阅读关于在牛郎星中生成交互式地图的内容)。
不幸的是,没有对 Python 数据可视化的直接支持,所以这对 Python 原生程序没有什么好处。当然,您仍然可以直接进入模式,但是在大多数情况下,使用 Altair 这样的工具会容易得多。

An interactive bar chart? Try producing that in matplotlib
代码编辑
Jupyter 笔记本的一个很大的缺点是代码非常不透明:没有错误代码的可视提示,这在任何像样的 IDE 中都可以找到,也没有函数的代码补全(这导致我的 API 使用量显著增加)。然而,Polynote 在脚本和笔记本之间架起了一座桥梁。

With Polynote, there is no need to memorise swathes of functions
Scala 中的类型错误会加下划线,但是 Polynote 不关心 Python 类型提示(很像 Python 社区)。

Polynote 不与最好的代码编辑器竞争,但在这方面,它是 Jupyter 笔记本的巨大改进。
摘要
我对 Polynote 的可能性很感兴趣。在数据科学社区中有很多讨论,内置的 Vega 绘图生成和跨语言通信等功能听起来像是游戏改变者。然而,我不认为 Polynote 提供的东西足以吸引大多数数据科学家离开 Jupyter。首先,缺乏对 Windows 的支持使许多人无法使用它;Windows 是最流行的操作系统,许多企业和组织都强制要求使用它。其次,尽管 Scala 有它的好处,但它并不是日常数据科学中广泛使用的语言。大多数分析项目可以并且已经完全可以单独用 Python 实现,所以语言混合和内置 Vega 没有任何好处。
Polynote 的代码编辑是 Jupyter 的一大改进,我希望 Jupyter 在这方面有所改进。然而,作为一个更成熟和更积极开发的项目,我发现 Jupyter 是两者中更强的一个。Polynote 当然值得一看。只是需要多一点时间。
美国有线电视新闻网预测狗是不是狮子狗
用 Keras 构建二进制卷积神经网络分类器

我想把我的迷你狮子狗放在柱子上。因此,我们将建立一个 CNN 来检测照片中是否包含狮子狗(而不是另一种狗),而不是一个对数字图像进行分类的无聊教程。
我称之为:狮子狗不是狮子狗。
我发现了一个 [Kaggle](# https://www.kaggle.com/c/dog-breed-identification/data) 数据集,包含大约 10k 张狗的照片,按品种分类。下载并保存在与笔记本相同的目录中。
附带的 CSV 包含从品种到照片名称的映射。用熊猫进口。
import pandas as pd
df = pd.read_csv('dog-breed-identification/labels.csv')
df.head(3)

现在,通过计算每个品种的数量,并按降序排列,了解不同品种之间的分布情况。熊猫使这变得容易。
grouped_df = df.groupby('breed').count()
grouped_df.sort_values('id', ascending=False, inplace=True)
grouped_df.head(3)

太好了。但是我对狮子狗感兴趣。里面有多少只贵宾犬的例子?我从 Kaggle 上的数据集了解到,这里有 3 个不同的狮子狗品种。
POODLES = ['miniature_poodle', 'standard_poodle', 'toy_poodle']# count examples per breed
poodle_grouped_df = grouped_df.loc[POODLES]# add total for all poodles
poodle_grouped_df.loc['Total',:]= poodle_grouped_df.sum(axis=0)
poodle_grouped_df

很好。接下来,我们将构建一个分类器来检测微型狮子狗。我们还将挑选一种其他特定品种的狗(而不是混合品种),以方便 CNN 的采访。我们必须训练的每个品种的数据量并不多!
创建 2 个数据框,贵宾犬和非贵宾犬。
poodle_df = df.loc[df['breed'].isin(['miniature_poodle'])]non_poodle_df = df.loc[df['breed'].isin(['bernese_mountain_dog'])]
poodle_df = >包含迷你贵宾犬。
non_poodle_df = >包含伯恩山犬。
定义一个函数来构建下载数据集中图像的路径。
import osdef build_path_for_dataset(dframe):
train_file_names = os.listdir('dog-breed-identification/train') DIR = 'dog-breed-identification/train/' paths = []
for idx, row in dframe.iterrows():
file_name = row['id']+ '.jpg' if file_name in train_file_names:
paths.append(DIR + file_name) return paths
为每个数据集生成路径。
poodle_df['path'] = build_path_for_dataset(poodle_df)
poodle_df.head(3)

non_poodle_df['path'] = build_path_for_dataset(non_poodle_df)
non_poodle_df.head(3)

完美。现在让我们为每个表添加一个布尔列,指定图片是否是狮子狗的。
poodle_df['is_poodle'] = 1
poodle_df.head(3)

non_poodle_df['is_poodle'] = 0
non_poodle_df.head(3)

组合两个数据帧(现在我们有一个布尔列来区分例子)。
combined_df = pd.concat([poodle_df, non_poodle_df], ignore_index=True)
把它们排好,这样所有的狮子狗就不会在最上面或者最下面。通过使用frac=1,我们实际上是在抽取一个包括全部人口的样本,并且在这个过程中,改变顺序。
shuffled_combined_df = combined_df.sample(frac=1)
再次将数据帧分割成 2 个数据帧。但是这一次,一个是训练集,另一个是测试集。这里我们使用的是 70/30 分割。
example_count = len(shuffled_combined_df)
break_point = int(0.7 * example_count)train_df = shuffled_combined_df[:break_point]
test_df = shuffled_combined_df[break_point:]
差不多到了有趣的部分。
这些图像的大小都不一样,所以让我们看看图像的平均大小。
from PIL import Imagewidths = []
heights = []for idx, row in shuffled_combined_df.iterrows():
path = row['path']
im = Image.open(path)
width, height = im.size
widths.append(width)
heights.append(height)avg_width = int(sum(widths) / len(widths))
avg_height = int(sum(heights) / len(heights))print(avg_width, avg_height)#=> 448 385
平均宽度为448,平均高度为385。
使宽度和高度相等会使以后的变换更简单。因此,我们将图像的大小调整为 300x300。
# we'll use this in a minute
WIDTH = 300
HEIGHT = 300
编写一个函数来:
-加载一个图像
-转换为灰度
-调整大小为 300x300
from keras.preprocessing.image import load_imgdef load_resize_color_image(path): # load image
image = load_img(path) # convert to greyscale
image = image.convert('L') # resize
# 'Image.ANTIALIAS' minimizes distortion when resizing
image = image.resize((WIDTH,HEIGHT), Image.ANTIALIAS) return image
另一个函数为每个传入的图像创建 2 个转换。
from keras.preprocessing.image import ImageDataGenerator
from numpy import expand_dimsdef random_transform_image(image):
augmentations = []
samples = expand_dims(image, 0)
datagen = ImageDataGenerator(rotation_range=90)
it = datagen.flow(samples, batch_size=1) for i in range(2):
batch = it.next() # convert to unsigned integers
augmentations.append(batch[0].astype('uint8')) return augmentations
上面的函数获取一幅图像,并通过随机旋转创建两幅图像。然后我们将完全忽略传入的图像,只训练转换。
训练图像的变换在使用 CNN 时很重要,因为默认情况下它们不是旋转不变的。也就是说,一个直立的图像和同一个图像翻转 90 度,对你的模型来说可能看起来完全不同。所以这两方面的训练都很有帮助。
生成我们的训练数据。
X_train = []
y_train = []from keras.preprocessing.image import img_to_arrayfor idx, row in train_df.iterrows():
path = row['path']
is_poodle = row['is_poodle'] image = load_resize_color_image(path) image = img_to_array(image)
images = random_transform_image(image) for i in images:
image = i.tolist() X_train.append(i)
y_train.append(is_poodle)
生成测试数据,我们不会为此生成额外的转换。
X_test = []
y_test = []for idx, row in test_df.iterrows():
path = row['path']
is_poodle = row['is_poodle'] image = load_resize_color_image(path) image = img_to_array(image) image = image.tolist()
X_test.append(image)
y_test.append(is_poodle)
将我们的特征转换为 numpy 数组。
X_train = np.array(X_train, dtype=np.uint8)
X_test = np.array(X_test, dtype=np.uint8)
二进制化我们的标签。
y_train = label_binarize(y_train, neg_label=0, pos_label=1, classes=[0,1])
y_train = np.hstack((1 - y_train, y_train))y_test = label_binarize(y_test, neg_label=0, pos_label=1, classes=[0,1])
y_test = np.hstack((1 - y_test, y_test))
构建模型。
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.layers. normalization import BatchNormalizationmodel = Sequential()model.add(Conv2D(32, kernel_size = (3, 3), activation='relu', input_shape=(300, 300, 1)))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())model.add(Conv2D(64, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())model.add(Conv2D(128, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(BatchNormalization())model.add(Dropout(0.2))
model.add(Flatten())model.add(Dense(128, activation='relu'))
model.add(Dense(2, activation = 'softmax'))
这是一个非常典型的模型,卷积层的大小会增加。
编译模型。
model.compile(loss='binary_crossentropy', optimizer='adam', metrics = ['accuracy'])
跑起来!
for i in range(10): model.fit(X_train, y_train, batch_size=30, epochs=1)
loss, acc = model.evaluate(X_test, y_test, verbose = 0)
print(acc * 100)

考虑到少量的训练数据,这实际上比我预期的要好。虽然我们只选择了两个品种的狗,这让我们自己变得容易多了。
为了好玩,我们会把我给你看的狮子狗的照片分类放在最上面。
X_me = []
y_me = []path = 'my_poodle.jpg'image = load_resize_color_image(path)
image = img_to_array(image)
image = image.tolist()X_me.append(image)
y_me.append(1)X_me = np.array(X_me, dtype=np.uint8)y_me = label_binarize(y_me, neg_label=0, pos_label=1, classes=[0,1])
y_me = np.hstack((1 - y_me, y_me))loss, acc = model.evaluate(X_me, y_me, verbose = 0)
print(acc * 100)

成功!!
我们能做得更好吗?当然啦!更多不同类型的变换,如翻转、亮度和缩放可能会有所帮助。但最重要的是,更多的数据。
尽管我们应该对目前的结果感到满意。
人口金字塔动画
RStudio 中 ganimate 的简短入门

Photo by CHUTTERSNAP on Unsplash
可视化是展示在数据中发现的洞察力的一种极好的方式。如果一张图片能表达一千个单词,那么一部动画就能表达一百万个单词。如果使用得当,动画可以非常清晰地展示我们从数据中提取的智慧财富。为了让决策者注意到它的真正价值,他们必须能够将提供给他们的信息内在化。我们可以通过讲述更具视觉吸引力的故事来帮助他们建立联系。
在这个简短的教程中,我将向您展示如何在 RStudio 中制作加拿大 1960 年至 2018 年的动画人口金字塔。你可以把你学到的东西应用到其他动画项目中。我们开始吧!
让我们从安装和加载必要的包开始。
- 吃豆人:一次装载所有的包裹
- tidyverse :准备数据帧( tidyr ),轻松操作数据帧( dplyr , stringr ),生成绘图( ggplot2 )
- gg pol:gg plot 2 的附加特性
- gganimate :图形动画(这就是你来这里的目的!)
- gifski :将文件渲染成 GIF 格式
- kableExtra:创建视觉上吸引人的表格
if(!require('pacman')) install.packages('pacman')
pacman::p_load(tidyverse, ggpol, gganimate, gifski, kableExtra)
加拿大的人口数据来自世界银行网站。时间跨度从 1960 年到 2018 年。数据帧最初是在宽格式** t 中,由代表每个年龄组和性别的 34 条记录构成。数据保存为 csv 文件并导入到 RStudio IDE 中。要跟进,你可以在这里 下载数据集 。**
CAPop <- read.csv('CAPop.csv')
大部分工作都是准备数据,以便在动画中使用。步骤如下:
- 移除数据框底部的 NA 值
- 为年龄组创建列
- 更改最后一个年龄组的值
- 为性别创建一列
- 对因子等级重新排序(对显示的顺序数据很重要)
- 移除未使用的柱****
- 从宽格式变为长格式****
- 从年份中删除前缀
- 将人口金字塔的所有男性流行值更改为负****
CAPop <- CAPop %>%
na.omit %>%
mutate(Age = substr(Series.Name, 17, 21),
Age = as.factor(case_when(Age == '80 an' ~ '80+', TRUE ~ Age)),
Gender = as.factor(ifelse(str_detect(Series.Name, 'female$') == TRUE, 'Female', 'Male')),
Gender = factor(Gender, levels = c('Male', 'Female'))) %>%
select(-Country.Name, - Series.Name) %>%
gather(Year, Pop, X1960:X2018) %>%
mutate(Year = as.integer(substr(Year, 2, 5)),
Pop = ifelse(Gender == 'Male', as.integer(Pop * -1), as.integer(Pop)))
处理完数据后,我们剩下 2006 行和 4 列。让我们看一看我们在做什么。
dim(CAPop)kable(head(CAPop)) %>%
kable_styling(bootstrap_options = 'striped', font_size = 12, full_width = FALSE)

接下来,我们在 3 个不同的步骤中创建动画:
- 创建静态图****
- 应用动画参数
- ****渲染到文件
静态情节
每个动画都是从一张图片开始的。首先,通过将变量映射到美学来确定情节将显示什么。一个人口金字塔是一种特殊的条形图。它在背靠背的 x 轴上显示男性和女性人口。如果你还记得的话,男性人口数值是负数。这允许沿着共享 x 轴显示两个图形的“镜像】。 facet_share 函数允许条形图背靠背放置。插入参数 reverse_num = TRUE 以获得正确的比例,因为左边的图本质上是一个反向图。
PopPyramid <- CAPop %>%
ggplot(aes(
x = Age,
y = Pop/1000,
fill = Gender
)
) +
geom_bar(stat = ‘identity’) +
scale_fill_manual(values = c(‘#4682b4’, ‘#ee7989’)) +
coord_flip() +
facet_share(
~ Gender,
dir = ‘h’,
scales = ‘free’,
reverse_num = TRUE
)
设置主题
为了操纵情节的整体外观,你可以使用主题。有九个内置主题可供选择,这可能是一个很好的开始。我已经选择创建我自己的自定义主题来操作背景、面板、边框、网格线、刻度线和所有元素文本。主题( )中有90 多个参数** 进行最大化定制!正如你所看到的,这给了很大的灵活性来决定你的动画的整体外观。我只用了其中的 17 个论点。**
PopPyramid <- PopPyramid +
theme(
plot.background = element_blank(),
axis.ticks = element_blank(),
axis.title.y = element_blank(),
legend.title = element_blank(),
panel.background = element_blank(),
panel.border = element_blank(),
strip.background = element_blank(),
strip.text.x = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
axis.text = element_text(size = 14),
legend.key.size = unit(0.75, ‘cm’),
legend.text = element_text(
size = 15,
face = ‘bold’
),
plot.title = element_text(
size = 22,
hjust = 0.5,
face = ‘bold’
),
plot.subtitle = element_text(
size = 14,
hjust = 0.5,
face = ‘bold’
),
axis.title.x = element_text(
size = 16,
face = ‘bold’
),
plot.caption = element_text(
size = 12,
hjust = 0.5,
face = ‘italic’,
color = ‘gray’
)
)
标签
最后,我添加了有意义的标签让观众更容易理解。我希望动画随着时间的变化而变化,所以我希望这种变化能反映在标题中。使用** ,将显示与画面最近的状态的年份。**
PopPyramid <- PopPyramid +
labs(
title = ‘Canada Population Estimate 1960 - 2018\n\n{closest_state}’,
subtitle = ‘\n\nAge Group’,
y = ‘\n\nPopulation (in thousands)’,
caption = ‘\n\nData Source: [https://databank.worldbank.org’](https://databank.worldbank.org%27/)
)
动画
接下来,使用 transition_states 来确定动画的行为。这种转变应该跨时间发生,在本例中是年。过渡 _ 长度和状态 _ 长度分别给出了过渡和状态的相对长度。在本例中,这产生了 1:2 的比率。 ease_aes 函数描述了补间如何在状态之间发生。
PopPyramid <- PopPyramid +
transition_states(
Year,
transition_length = 1,
state_length = 2
) +
enter_fade() +
exit_fade() +
ease_aes('cubic-in-out')
渲染到文件
在这种情况下,产生了一个 gif 文件。使用 gifski ,你可以微调文件的渲染方式。我选择了每秒 24 帧平滑运动, 30 秒总时长和 500x500 大小。
animate(
PopPyramid,
fps = 24,
duration = 30,
width = 500,
height = 500,
renderer = gifski_renderer(‘PopPyramid.gif’)
)
这可能是 休息一下 的好时机。根据您为动画选择的参数,此步骤可能需要很长时间进行渲染。如果我打算在 GitHub 上展示所有的 gif 图片,我会尽量保持在 30 秒或更短的时间内,因为在这个平台上 gif 图片似乎有时间限制。
瞧啊。
现在你有了:加拿大人口金字塔动画从 1960 年到 2018 年!借助动画,我们可以更有说服力地看到加拿大人口正在老龄化 T21。

谢谢你抽空过来。我希望你在创建自己的动画形象时有信心。完整代码可以在我的 GitHub 库上查看。请随时在 LinkedIn 上与我联系。
脚注
- 2019 年 12 月 12 日检索数据。根据检索日期的不同,结果可能略有不同。
便携式计算机视觉:树莓 Pi 上的 TensorFlow 2.0

微小、低成本的物体检测和分类。
第 1 部分—简介
只需大约 100 美元,你就可以将深度学习添加到嵌入式系统或你的下一个物联网项目中。
你是刚入门机器/深度学习,TensorFlow,还是 Raspberry Pi?完美,这个博客系列是给你的!
在这个系列中,我将向您展示如何:
- 使用 TensorFlow 2.0 和 Keras 部署预训练的图像分类模型( MobileNetV2 )。
- 将模型转换为 TensorFlow Lite,一种针对嵌入式和移动设备优化的模型格式。
- 使用 Coral 的 USB Edge TPU 加速器和 Edge TPU 编译器,加速任何 TensorFlow Lite 模型的推理。
- 使用转移学习用自定义图像分类器重新训练 MobileNetV2。
本系列第一篇(你现在正在看!)将带您完成构建材料、安装以及将 MobileNetV2 部署到您 Raspberry Pi。
术语和参考📚
树莓派——一款受教育者、硬件爱好者和机器人专家欢迎的小型廉价电脑。🤖
TensorFlow —机器学习的开源平台。
tensor flow Lite—用于在移动和嵌入式设备上部署 TensorFlow 模型的轻量级库。
卷积神经网络——一种深度学习模型,非常适合图像分类和对象检测应用。
MobileNetV2—一种先进的图像识别模型,针对普通手机处理器的性能进行了优化。

Comparison of general-purpose computer vision neural networks. Image Credit: MobileNetV2: The Next Generation of On-Device Computer Vision Networks
边缘 TPU —张量处理单元(TPU)是一个集成电路,用于加速 TensorFlow 执行的计算。****边缘 TPU 是为“在边缘”的移动和嵌入式设备开发的,占地面积小



TPUv1, TPUv2 (left, middle) at Cloud Next ‘18. Edge TPUs on a United States penny (right). Image credit: Google)
第 2 部分—✅构建列表
启动工具包
如果你刚刚入门树莓 Pi,我推荐 Arrow 的 Pi 相机套装(90 美元)。它包括您需要的一切,立即开始:
- 5V 2.4A MicroUSB 电源
- 320x240 2.8 英寸 TFT 型号 PiTFT 电阻式触摸屏
- 树莓 Pi 3 型号 B
- Raspberry Pi 摄像机 v2
- 塑料盒
- 预装了 NOOBS 安装管理器的 8GB MicroSD 卡
Coral USB Edge TPU 加速器(可选)
您可以编译 TensorFlow Lite 模型,在 Coral 的 USB 加速器( Link )上运行,以便更快地进行模型预测。
实时应用程序从这种加速中受益匪浅。自动驾驶机器人的决策模块就是一个例子。
一些应用程序可以容忍更高的预测速度,可能不需要 TPU 加速。例如,你不需要 TPU 加速来建立一个智能狗门,为你的狗开门(但不让浣熊进来)。
如果你刚刚开始,跳过购买这个组件。
你不确定你是否需要 USB 加速器?下面的 MobileNet 基准可以帮助您做出决定。下面的测量描述了推理速度(单位为毫秒)——速度越低越好!

Image Credit: Alasdair Allan, Benchmarking TensorFlow and TensorFlow Lite on the Raspberry Pi
定制构建
如果您已经有了一个 Raspberry Pi 或一些组件,初学者工具包可能会包含您不需要的项目。
以下是我自己制作的零件(大约 250 美元/台)。
- 树莓 Pi 型号 3 b+(35 美元)
- 树莓 Pi 相机 v2(30 美元)
- Coral USB Edge TPU 加速器——加速模型推理(75 美元,链接
- Pi Foundation 显示屏— 7 英寸触摸屏显示屏(80 美元,链接
- SmartiPi 触摸支架(25 美元,链接
- 可调 Pi 摄像机支架(5 美元,连杆
- RPi 摄像机 24 英寸的柔性电缆($3,链接)
我很想听听你自己的构建列表!❤️给我发推特 @grepLeigh 或者在下面评论。
第 3 部分— Raspberry Pi 设置🍰
如果你购买了一个预装 NOOBS 的 SD 卡,我建议你先浏览一下这个概述:设置你的树莓派
在进行之前,您需要:
第 4 部分—主要计算机:下载和安装依赖项
rpi-vision是一套工具,可让您更轻松地:
- 在你的 Raspberry Pi 上安装很多依赖项(TensorFlow Lite,TFT 触摸屏驱动程序,将 PiCamera 帧缓冲区复制到 TFT 触摸屏的工具)。
- 将模型部署到 Raspberry Pi。
- 在你的电脑或谷歌云的人工智能平台上训练新模型。
- 为边缘 TPU 编译 8 位量化模型。
- 在您的主计算机上克隆
rpi-visionrepo(不是您的 Raspberry Pi)
$ git clone git@github.com:leigh-johnson/rpi-vision.git && cd rpi-vision
2.在您的主计算机上,创建一个新的虚拟环境,然后安装rpi-vision包。
$ pip install virtualenv; virtualenv -p $(which python3) .venv && source .venv/bin/activate && pip install -e .
3.在继续之前,验证你可以对你的树莓 Pi 进行 SSH。
如果您使用默认的 Raspbian 映像,您的 Pi 的主机名将是raspberrypi.local
$ ssh pi@raspberry.local
第 5 部分—主要计算机:创建配置文件
rpi-vision使用 Ansible 来管理你的 Raspberry Pi 上的部署和任务。Ansible 是一个自动化计算机配置的框架。
创建 Ansible 所需的 2 个配置文件:
。env/my-inventory.ini
如果您对 Pi 使用自定义主机名,请替换raspberrypi.local.
tee -a .env/my-inventory.ini <<EOF
[rpi_vision]
raspberrypi.local[rpi_vision:vars]
ansible_connection=ssh
ansible_user=pi
ansible_python=/usr/bin/python3
EOF
。env/my-vars.json
如果您对 Pi 使用自定义主机名,请替换raspberrypi.local.
tee -a .env/my-vars.ini <<EOF
{
*"RPI_HOSTNAME"*: "raspberrypi.local",
*"VERSION"*: "release-v1.0.0"
}
EOF
第 6 部分— Raspberry Pi:安装依赖项
$ make rpi-install
您将看到一个可行剧本的输出。 Ansible 是一个自动化配置计算机的框架。
您的 Pi 上安装了什么的快速摘要:
rpi-vision回购rpi-fbcp(从 PiCamera 复制 framebuffer 到 TFT 触摸屏显示器的工具)- TFT 触摸屏驱动器和 X11 配置
您可以通过打开playbooks/bootstrap-rpi.yml来检查在您的 Raspberry Pi 上运行的任务
在安装运行时,通读下一部分,了解 如何CNN工作以及 为什么 它们对计算机视觉任务有用。
第 7 部分 CNNs(卷积神经网络)简介
CNN 是驱动自动驾驶汽车和图像搜索引擎的关键技术。该技术对于计算机视觉来说是常见的,但也可以应用于数据中具有层次模式的任何问题,其中复杂模式可以由简单模式组装而成。****
视觉皮层建模
在 20 世纪 50 年代末和 60 年代,大卫·H·哈贝尔和托顿·威尔森在猫和猴子身上做了实验,以更好地了解视觉皮层。

Diagram of the implant installed in the skulls of cats with a trephine. Image Credit: SINGLE UNIT ACTIVITY IN STRIATE CORTEX OF UNRESTRAINED CATS
他们证明了纹状皮层中的神经元对有限视野中的刺激做出反应,他们称之为感受野。
他们注意到了同心重叠反应,其中复杂的模式是低水平模式的组合。
他们的发现还揭示了特殊化,其中一些神经元将只对特定形状或模式做出反应。
在 20 世纪 80 年代,受 Hubel 和 Wielson 的启发,Kunihiko Fukushima 在neocogniton、、上发表了一种能够学习具有几何相似性的模式的神经网络。

Diagram of a Neocogitron, the foundation for modern CNNS. Image Credit: Neocognitron: A Self-organizing Neural Network Model for a Mechanism of Pattern Recognition Unaffected by Shift in Position
新回旋加速器有两个关键特性:
- 学习到的模式是有层次的。 越来越复杂的图案是由越来越简单的图案组成的。
- 学习到的模式是位置不变和平移不变的*。网络学习到一个模式后,可以在不同的位置识别这个模式。在学习如何对狗进行分类之后,网络可以准确地对倒立的狗*进行分类,而无需学习全新的模式。****
neocogitron 模型是现代卷积神经网络的灵感来源。
可视化卷积运算:2D

(Left) 2D 4x4 Input matrix. (Middle) 2D 2x2 kernel. (Right) 2D 2x2 output feature map.
输入层被送入卷积层,卷积层使用滤波器转换输入的区域。
过滤器也被称为内核。

The filter “slides” to each possible position, and the result is added to the feature map.
对于输入矩阵中的每个位置,卷积运算对每个元素执行矩阵乘法。
产生的矩阵被求和并存储在特征图中。****
对输入矩阵中的每个位置重复该操作。
可视化卷积运算:3D

Image Credit: Applied Deep Learning — Part 4: Convolutional Neural Networks
CNN 的输入层通常是一个 3D 数据结构,具有 高度 、 宽度 、 通道 (RGB 或灰度值)。
我们在特征地图栈中越深入,每个地图层就变得越稀疏。这意味着过滤器检测到的特征更少。
特征地图堆栈的前几层检测简单的边缘和形状,看起来与输入图像相似。随着我们进入特征地图堆栈越来越深,对于人眼来说,特征变得越来越抽象。更深的特征层编码分类数据,像“猫脸”或“猫耳”。

Comparison of feature maps from the first convolution layer (block1_conv1) with later layers (block5_conv1). Image Credit: Applied Deep Learning — Part 4: Convolutional Neural Networks
你想了解更多关于 CNN 的信息吗?
您的依赖项安装现在可能已经完成了。要向前迈进,请跳到第 8 部分——部署预培训模型 MobileNetV2。****
如果您计划训练一个自定义分类器,或者想了解更多关于卷积神经网络的信息,请从这里开始:
- 应用深度学习—第 4 部分:卷积神经网络
- 使用 Scikit-learn 和 TensorFlow 进行机器学习,第 13 章,卷积神经网络,作者 Aurélien Géron
- 用 Python 进行深度学习,第五章计算机视觉的深度学习,Francois Chollet
第 8 部分—部署预训练模型(MobileNetV2)
现场演示(使用 TensorFlow 2.0)
I used this code to sanity-check the TensorFlow 2.0-beta0 wheel that I cross-compiled for my Raspberry Pi 3.
- 嘘到你的树莓皮
$ ssh raspberrypi.local
2.启动新的 tmux 会话
pi@raspberryi:~ $ tmux new-session -s mobilenetv2
3.通过按 control+b 垂直拆分 tmux 会话,然后"
4.启动一个fbcp进程,通过 SPI 接口将帧缓冲区从 PiCamera 复制到 TFT 显示器。让这个过程继续运行。
pi@raspberryi:~ $ fbcp
5.通过按下 control+b,然后按下 o 来切换 tmux 面板。
6.激活前面第 6 部分中安装的虚拟环境。
pi@raspberryi:~ $ cd ~/rpi-vision && . .venv/bin/activate
7.启动 mobilenetv2 代理进程。代理初始化大约需要 60 秒。
pi@raspberryi:~/rpi-vision $ python rpi_vision/agent/mobilenet_v2.py
您将看到模型基础的摘要,然后代理将打印推理,直到停止。点击查看您应该看到的要点。
这个演示使用了 ImageNet 分类器的权重,你可以在【image-net.org】的中查找。
感谢您的阅读!
恭喜您,您刚刚为您的 Raspberry Pi 部署了一个图像分类模型!✨
寻找更多针对 Raspberry Pi 和其他小型设备的机器学习实践示例?注册我的简讯!
我发布了现实世界中 ML 应用程序的例子(有完整的源代码)和漂亮的技巧,如自动消除边框注释的痛苦。
使用数学和 Python 优化您的投资
在 Python 的 PuLP 中使用线性优化

在 MBA 学习期间,我们学习了使用 Excel 和怀卡托大学的免费软件 WEKA 的所有预测建模技术。我们学习了基础概念,但从未涉足高级计算所需的硬技能。在学习 python 一段时间后,我认为重做我最初在 Excel 的求解器中做的一个线性优化项目会很有趣。本文的目标是在 python 的 PuLP 中重新创建项目,分享我一路上学到的东西,并将 python 的结果与 Excel 的结果进行比较。
线性优化的实际好处/应用是无穷无尽的。我强烈建议密切关注,至少在概念层面上,如果你还不熟悉的话,努力学习这个技巧。
目录
- 什么是线性优化?
- 任务/评论
- 数据上传和清理
- 使用纸浆的线性优化
什么是线性优化?
根据维基百科的说法,线性规划是“一种在其要求由线性关系表示的数学模型中实现最佳结果(如最大利润或最低成本)的方法。”这些来自卡耐基梅隆大学的课堂笔记对我理解这个主题非常有帮助。
用我自己的话来说,我会把它描述为一种解决特定变量(决策变量)的最小/最大解的方法,它与其他线性变量交织在一起,到了用笔和纸解决问题会非常困难的程度。
任务/评估:
这是一个关于投资组合的风险和收益的线性优化问题。我们的目标是最小化投资组合风险,同时满足 5 个约束条件:
- 投资总额将为 10 万美元
2.这个投资组合的年回报率至少为 7.5%
3.至少 50%的投资是 A 级的
4.至少 40%的投资是立即流动的
5.储蓄账户和存款单上的金额不超过 3 万美元
详细说明如下:

Source: pg 127
回顾解决线性优化问题的过程,有 3 个步骤:
- 决策变量:这里有 8 个决策变量。它们是我们的投资选择。
- 目标函数:我们希望将 8 项投资的风险最小化。下面是投资乘以各自的风险系数。

3.约束:最后,我们想要准确地定义我们的约束是什么。这些用代数表示,顺序与我们之前列出的约束相同:

如果你对约束#2 中的“7500”感到困惑,那就是我们期望的 7.5%的年回报率乘以我们的 100000 美元投资。
数据上传和清理:
现在我们已经设置了问题,让我们将数据上传到 pandas 并导入纸浆:
from pulp import *
import pandas as pd
df = pd.read_excel(r"C:\Users\Andrew\Desktop\Fin_optimization.xlsx")
df

Output 1
看起来不错。但是,为了继续进行,必须进行一些格式上的更改。
- 将“流动性”和“评级”列转换为二进制值。这与约束条件#3 和#4 有关。这些列中的相关字符串值是流动性的“即时”和评级的“A”。为了进一步计算,有必要将这些字符串值与其他字符串值区分开来。
- 为投资类型创建新的二进制列。约束#5 集中在储蓄和定期存款投资类型上,因此将它们与其他投资类型区分开来将有助于以后的工作。
- 为 Amt_invested 创建一个全为 1 的列。这对约束#1 很有用:100,000 美元的总投资组合约束。
#1a
df['Liquidity'] = (df['Liquidity']=='Immediate')
df['Liquidity'] = df['Liquidity'].astype(int)#1b
df['Rating'] = (df['Rating']=='A')
df['Rating']= df['Rating'].astype(int)#2
savecd = [1,1,0,0,0,0,0,0]
df['Saving&CD'] = savecd#3
amt_invested = [1]*8
df['Amt_Invested'] = amt_invested
df

完美。我们继续吧。
使用纸浆的线性优化:
使用纸浆的第一步是定义问题。下面的代码简单地将我们的问题定义为最小化(关于风险),并将其命名为“Portfolio_Opt”。稍后,我们将向这个“prob”变量添加更多内容。
prob = LpProblem("Portfolio_Opt",LpMinimize)
接下来,我们将创建一个决策变量(投资选项)列表。然后,我们将使用该列表为每个特性创建字典:
#Create a list of the investment items
inv_items = list(df['Potential Investment'])#Create a dictionary of risks for all inv items
risks = dict(zip(inv_items,df['Risk']))#Create a dictionary of returns for all inv items
returns = dict(zip(inv_items,df['Expected Return']))#Create dictionary for ratings of inv items
ratings = dict(zip(inv_items,df['Rating']))#Create a dictionary for liquidity for all inv items
liquidity = dict(zip(inv_items,df['Liquidity']))#Create a dictionary for savecd for inve items
savecd = dict(zip(inv_items,df['Saving&CD']))#Create a dictionary for amt as being all 1's
amt = dict(zip(inv_items,df['Amt_Invested']))
risks

Output of “risks” dictionary for reference
接下来,我们将决策变量定义为投资,并为其添加一些参数,
- Name :标注我们的决策变量
- 确保我们的解决方案中没有负资金
- 连续:因为我们处理的是美分对美元的交易。
inv_vars = LpVariable.dicts("Potential Investment",inv_items,lowBound=0,cat='Continuous')
最后,我们将修改后的决策变量添加到我们之前创建的问题变量中,并另外输入约束条件。我们对每个投资项目使用“for 循环”来遍历字典。
#Setting the Decision Variables
prob += lpSum([risks[i]*inv_vars[i] for i in inv_items])#Constraint #1:
prob += lpSum([amt[f] * inv_vars[f] for f in inv_items]) == 100000, "Investments"Constraint #2
prob += lpSum([returns[f] * inv_vars[f] for f in inv_items]) >= 7500, "Returns"Constraint #3
prob += lpSum([ratings[f] * inv_vars[f] for f in inv_items]) >= 50000, "Ratings"Constraint #4
prob += lpSum([liquidity[f] * inv_vars[f] for f in inv_items]) >= 40000, "Liquidity"Constraint #5
prob += lpSum([savecd[f] * inv_vars[f] for f in inv_items]) <= 30000, "Save and CD"
prob
下面是问题:

结果:
prob.writeLP("Portfolio_Opt.lp")
print("The optimal portfolio consists of\n"+"-"*110)
for v in prob.variables():
if v.varValue>0:
print(v.name, "=", v.varValue)

这与 Excel 的规划求解给出的结果完全相同。
参考我的 Github 查看完整的笔记本文件。
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
如果您觉得这很有帮助,请订阅。如果你喜欢我的内容,下面是我做过的一些项目:
Ubuntu 中的位置监视器检测器(在 python3 上用 opencv 构建)
因为我近视,我一直在寻找一种方法来控制我的眼睛到显示器的距离

并且保持适当的距离,因为坐得太近对眼睛不好。在学习了一点 opencv 库之后,我正在思考这个想法的简单实现。
根据人体工程学提示(从这里得到):
调整显示器高度,使屏幕顶部与眼睛齐平或略低于眼睛。当观看屏幕中间时,你的眼睛应该稍微向下看。将显示器放在距离眼睛至少 20 英寸(51 厘米)的地方,大约一臂的距离。
让我们在 Python3 中创建一个程序,它会在我们每次坐得太近时提醒我们(距离<51 cm or 20 inches) to monitor and display a message with warning. So, for face recognition part we will use default classifier from opencv, and tkinter as a way to send message on the screen.
Opencv is an awesome library, which allows you to do various operations on the image/video content such as convert to grayscale, make a mask, cropping, rescaling the image and many-many more.
After looking at 这个来自 opencv docs 的例子,我认为,这是我特别需要的成功目标——进行简单的人脸检测。只是稍微调整一下例子——去掉眼睛分类器,就这样。
接下来的问题是如何测量人脸到摄像头的距离。稍微搜索了一下,发现了来自 pyimagesearch 的超级文章。在这份材料中,你可以发现,测量距离现在可以从物体的高度计算,并从特定的距离知道物体的高度。
例如,在距离 1 米处,以像素为单位对象的高度是 100。如果我们运行检测并观察,对象的高度是 50 像素——那么对象大约远两倍。如果是 120 像素,那么这个物体距离我们不到 1 米。因此,我们简单地检查现在的面部高度是否大于从选定距离知道的面部高度——然后发送带有 tkinter 的消息。
Tkinter 是一个简单的 GUI 工具,它提供了我们所需要的:弹出窗口部件,当且仅当检测到人脸并且人脸离监视器太近时才会出现。仅仅创建一个带有警告文本的文本小部件就足够了。
所以,让我们实现所有这些吧!
安装 Tkinter:
sudo apt-get install python3-tk
安装其他 python 包:
pip3 install numpy opencv-python
创建 python 脚本:
touch distance_detector.py
用下面的代码填充脚本(给代码添加了很多注释,这样你就可以理解这里发生了什么):
distance_detector.py
快速解释:
- 使用 argparse 允许从 cmd(9–14)设置距离和已知高度;
- 函数,它创建 tkinter 消息并返回它(16–27);
- 从摄像机中捕捉视频,对其进行灰度处理,并进行人脸检测(32–43);
- 当未检测到人脸(44–46)或人脸远离时(56–59)—禁用 tkinter 消息。如果不这样做,警告信息不会在一次成功检测后消失;
- 当检测到人脸时—计算到检测到的人脸的距离(48–52);
- 如果距离小于选定距离—启用 tkinter 消息(53–55);
- 在 opencv 窗口中显示有用的信息,按 Q(60–81)后全部关闭。
在使用之前,我们需要根据你的脸校准它。为此:
- 运行
python3 distance_detector.py; - 坐在远处的摄像机前,这将限制眼睛;
- 注意
face_height参数并关闭脚本(在窗口中按 Q); - 用你的
face_height参数运行python3 distance_detector.py。
例如,当我从 51 厘米开始坐着时,我的面部高度是 300 像素。在我的情况下,我需要运行python3 distance_detector.py -f 300 -d 0.51。
它看起来像什么:
Yep, it works!
在本文中,你可以看到,在将想法分解成小块之后,实际实现变得并不困难。是的,您可以添加更多的技术来使检测工作更加准确并提高性能——但作为简单的例子,这应该足够了。
附:如果戴眼镜,要注意探测会不太准确。
感谢您的关注!祝你有美好的一天=)
使用 PyTorch 对人工神经网络进行事后分析
我与 机器学习 的第一次互动是在我需要完成一项任务的时候,我只是在互联网上搜索使用机器学习的方法。我最终使用 scikit-learn API 用于 ann。我完成了任务,但对后端发生的事情一点概念都没有。于是,后来我开始上 MOOCs,开始探索这个 “黑匣子”背后的数学。
在所有这些探索之后,当我回来使用 ann 时(这次是使用 Keras),我发现非常令人不安的是,像 model.fit 这样简单的一行代码正在后端自行完成所有事情。这是我决定转向一个框架的时候,这个框架给了程序员更大的权力,其代码更具可读性。
在这篇博客中,我将使用 PyTorch 用代码和数学来解释人工神经网络。所以没有任何进一步的拖延,我们开始吧。
PyTorch
这是一个深度学习框架,在研究人员中非常受欢迎,主要是因为它非常 pythonic 化且易于理解的语法。
如 PyTorch 教程[1]所述,典型的模型训练遵循以下六个步骤。
- 定义模型(带有所有可学习的参数)
- 迭代输入数据集
- 前进传球
- 计算损失
- 反向传播梯度
- 更新权重。
让我们回顾一下这些步骤背后的代码(PyTorch)和数学。
定义模型
在传统的人工神经网络中,应该有一个输入层和一个输出层以及可选的隐藏层。输入图层的大小与数据集中每个输入的大小相同。输出层的大小与类的数量相同,对于隐藏层,大小可能因问题而异。
我们将要使用的模型包含一个大小为 2 的输入层、一个大小为 3 的隐藏层和一个大小为 1 的输出层。下面是 PyTorch 中的代码。请注意,该代码还包含函数 forward。我们将在稍后的 向前传球 部分中讨论这一点。
import torch# defining model architecture
class ANN(torch.nn.Module):
d*ef __init__*(*self*):
super(NueralNetwork, *self*).__init__()
*self*.fc1 = torch.nn.Linear(2,3)
*self*.fc2 = torch.nn.Linear(3,1)
*def* forward(*self*, x):
x = torch.sigmoid(*self*.fc1(x))
x = torch.sigmoid(*self*.fc2(x))
*return* x
这里的模型有 13 个可学习的参数,包括 9 个权重参数(6 个用于隐藏层,3 个用于输出层)和 4 个偏差参数(3 个用于隐藏层,1 个用于输出层)。以下是该模型的图示。

Artificial Neural network Architecture [2]
数据集
第二步是准备数据集。在这篇博客中,我们的数据集是 XOR 门,它包含 4 行,每行有 2 个输入和 1 个输出。我们使用下面这段代码来定义它。
*# setting up data* X = torch.tensor(([0, 0],[0, 1],[1, 0], [1, 1]), dtype=torch.float)
y = torch.tensor(([0], [1], [1], [0]), dtype=torch.float)
向前传球
从这里开始,我们坚持数据集的第一行,即 X[0] = [0,0]和 y[0] = [0]。数学上,向前传球使用以下公式计算:*y^ = w 输入+ b 其中‘w’和‘b’分别代表权重和偏差。
我们使用下面这段代码来查看我们的初始权重和偏差。
nueralNet = ANN()
print(nueralNet)
params = list(nueralNet.parameters())
*# print(params)
# print(nueralNet.fc2.weight)* print("Weight vector for layer 1")
print(params[0])
print("Bias vector for layer 1")
print(params[1])
print("Weight vector for layer 2")
print(params[2])
print("Bias vector for layer 2")
print(params[3])
print('==========================')
out = nueralNet(X[0])
这会打印出以下内容
NueralNetwork(
(fc1): Linear(in_features=2, out_features=3, bias=True)
(fc2): Linear(in_features=3, out_features=1, bias=True)
)
Weight vector for layer 1
Parameter containing:
tensor([[-0.3122, 0.5551],
[-0.2819, -0.0666],
[ 0.6712, -0.2915]], requires_grad=True)
Bias vector for layer 1
Parameter containing:
tensor([ 0.2816, -0.4757, -0.4125], requires_grad=True)
Weight vector for layer 2
Parameter containing:
tensor([[-0.5761, -0.2721, 0.4413]], requires_grad=True)
Bias vector for layer 2
Parameter containing:
tensor([-0.1019], requires_grad=True)
我们将数据集的第一个实例传递给模型的代码行执行向前传递的计算。这将运行前面在 ANN 类中定义的 forward 函数。正向传递中的 Sigmoid 是由以下公式给出的激活函数:

以下是向前传球背后的数学计算。

这导致 y^ = 0.4112
计算损失
在向前传递之后,我们根据原始标签评估由我们的模型创建的输出。这是通过计算损耗来实现的,损耗实质上是测量标签和当前输出之间的距离。在我们的实现中,我们将使用流行的均方误差。
*import* torch.optim *as* optim
*# create your optimizer* optimizer = optim.SGD(nueralNet.parameters(), lr=0.01)
criterion = torch.nn.MSELoss()
*# in your training loop:* optimizer.zero_grad() *# zero the gradient buffers* loss = criterion(out, y[0])
print('Mean squared error or loss = ', loss)
数学上,它由下式给出

因此,在我们的例子中,因为我们只有一个输入实例,所以损失是= 0.1691。
反向传播渐变
在计算了损失之后,我们需要一个最小化它的方法来改进我们的方法。为此,我们首先计算梯度,然后将它们传播回来以更新可学习的参数。这是通过 PyTorch 中的以下代码完成的
loss.backward()
optimizer.step() *# Does the weight update*print(**"Weight vector for layer 1"**)
print(params[0])
print(**"Bias vector for layer 1"**)
print(params[1])
print(**"Weight vector for layer 2"**)
print(params[2])
print(**"Bias vector for layer 2"**)
print(params[3])
print(**'=========================='**)
我鼓励你通过这个链接(https://www.youtube.com/watch?v=Ilg3gGewQ5U)来深入理解反向传播和权重更新的工作原理。作为一个有趣的练习,在这个例子中尝试计算更新后的梯度和参数。
教育数据挖掘研究的潜在问题

Photo by Patrick Tomasso on Unsplash
用 MOODLE 查看混合学习环境的案例研究并讨论教育数据挖掘的潜在问题
进行教育数据挖掘(EDM)的目的是通过基于存储在 MOODLE 数据库中的日志数据使用机器学习技术进行研究,来识别学生如何在“混合”模块中在线学习。该结果可用于改进教学方法和测试关于学习的假设,并可实现软件以使其能够例行执行。然而,研究方法中的潜在风险和局限性似乎还不清楚。本文将通过使用 MOODLE 数据集查看混合在线学习的案例研究来讨论 blow 中的论点。
EDM 研究可能会受到图 1 所示的 1)学习环境、 2)在线学习平台和 3)科学范式的影响。

工作定义
在 Moodle 混合学习环境下的 EDM 案例研究主要使用机器学习技术。这两个概念的定义就像下面的句子。
机器学习,被认为是计算机科学的一个子领域,赋予“计算机无需显式编程就能学习的能力”(穆尼奥斯,2014,第 1 页)机器学习基于无分布统计,由无监督学习和监督学习技术组成。两者的区别在于研究者是否准备了标注的训练数据。无监督学习技术不需要带标签的训练数据,但不能获得决定性的证据。另一方面,监督学习技术必须花费时间来标记训练数据,而不是生成决定性的结果。(拉什卡等人,2016 年)
Moodle 是一个基于开源的免费学习平台,被称为 LMS 或电子学习的一种,由 Moodle HQ 协调,Moodle HQ 是一家由 30 名开发人员组成的澳大利亚公司,由全球 60 多家 Moodle 合作伙伴服务公司提供资金支持(Moodle 2016)。Moodle 作为最常见的教育技术程序之一用于高等教育(HE)(Bowler 2009),并用于混合学习、远程教育、翻转课堂以及学校、大学、工作场所和其他部门的其他电子学习项目(Costello 2013)。Moodle 为学生获取资源提供了更大的灵活性,并增加了任务的参与度,这可以带来更好的学习表现(柯克伍德·丹尼尔& Price 2014)。Moodle 可以让学生进行课堂学习活动,如调查、获取、练习、讨论、制作和在线协作,然后与他人分享成果。
三个研究问题示例
案例研究中行政领导和管理的利益相关者大多是教育工作者和研究人员。教育者的目标是识别学习者的行为以提高学习成绩。研究人员的目标是确定如何使用机器学习技术进行 EDM 方法。为了达到教育者的目标,测量学习结果的有用指标应该被预先识别,并且识别在线学习行为。通过这些过程,研究者可以满足教育者和他们自己的目标。案例研究考察了简单问题之后的三个问题,并通过讨论发现了 EDM 的应用层。
RQ1。预测考试结果的指标有哪些?
RQ2。一个普通学生在 Moodle 上的学习行为可以是什么?
RQ3。EDM 的结果能向教育者和研究者揭示什么?
混合学习的协作指标
混合学习环境中的学习行为可以在日志数据上表示,EDM 在针对上述研究问题的研究中被调整到该混合学习环境。Laurillard (2014)将“混合学习”定义为
“课堂面对面学习体验与在线学习体验的巧妙融合。”
混合学习很简单,因为在整合同步(面对面)和异步(基于文本的互联网)学习活动的优势方面有相当大的直观吸引力。由于几乎无限的设计可能性及其对如此多环境的适用性的挑战,混合学习在实施中相当复杂(Garrison & Kanuka,2004)。在混合学习环境中,大量的日志数据被记录并具有各种变量,因为混合学习导致协作、访问频率和灵活性的增加(Graham,2006)。因此,使用 EDM 的研究通常调整为混合学习,以确定在线学习行为的特征。(Berland 等人 2014;麦克拉伦等人,2007 年,2010 年)
混合学习的灵活性可以实现多种协作学习环境或活动。这可以增加对在线学习的承诺和在线学习的机会。相应地,可以积累大量的日志数据,并从日志数据中提取在线学习行为的显著特征。例如,混合学习
“使用软件开发工具的在线协作”(Berland 等人,2014 年,第 12 页;Kay 等人,2006)”和“交互式桌面协作”(Berland 等人,2014,第 12 页;马丁内斯等人,2011 年)。
Prata (2012)指出,当学生的错误被他们的伙伴反驳时,他们往往比那些伙伴选择不纠正他们的学生学到更多。Dyke 等人(2013 年)揭示了在协作学习过程中发生的偏离主题的讨论在学习过程的某些部分比在其他部分对学习更有害——具体来说,偏离主题的讨论在学习基本事实时比在讨论解决问题的替代方案时更有害。一些基于学生对在线讨论论坛的贡献的模型,甚至能够预测学生的最终课程成绩。(Ming & Ming 2012)因此,在协作混合学习环境中,学习数据可以反映出突出的特征,有价值的结果可以通过 EDM 提取出来。
然而,在线学习行为在表现世界时具有推理行为的特征(Davis et al .,1993),而混合学习方法提供的学习灵活性可以结合各种交付模式(Oliver & Trigwell,2005)并扩展学习体验。这是因为如果不能访问互联网,就不能记录日志数据,并且混合学习部分涉及
“在教师支持下的自定进度学习,以发展特定的知识和技能”。(奥利弗和特里格韦尔 2005 年,第 2 页;Valiathan,2002 年)
考虑到个体不同的学习速度和个体预先存在的知识,学习行为被不同地表示或不表示。此外,在线学习环境中使用的技术也在不断发展变化(Laurillard,2014)。因此,使用 EDM 从混合上下文中提取的结果是上下文相关的。需要更新在混合环境中使用 EDM 的实证研究。基于新技术的在线学习平台的特点以及学习环境也会影响研究结果。下一节将确定平台对研究的影响。
Moodle 的有限指标
本研究中考察的在线学习平台 Moodle 具有可定制的结构和学习内容协议等功能特性。它限制了研究者可以测量的内容,并迫使研究者以某种方式进行分析。由于 Moodle 的可定制结构,EDM 无法衡量对学生动机的影响。这是因为个人动机会受到各种因素的影响,如互联网速度、在线学习内容的设计、用户界面(贝卢斯&奥利维拉 2015),以及由学习者之间的关系构成的隐藏因素,这些因素无法在 Moodle 上生成。(塞尔温,2011 年)
虽然 Moodle 的特性可以为学习环境提供更多的灵活性,但它使开发者、设计者和教育工作者能够使用 Moodle 创建不同的学习平台,这些平台根据利益相关者的不同而具有不同的设计和功能。这意味着使用 Moodle 的通用学习平台并不存在。这可能导致难以衡量学习平台本身的影响,如使用 EDM 方法的动机。例如,当学生很少使用教育工作者上传的学习材料学习时,与使用 Moodle 的其他研究相比,研究人员很难理解学习材料或界面设计是否会降低学习者使用学习材料的动机。
Moodle 的可定制特性要求 Moodle 拥有设计良好的学习用户界面和结构。在实际的教育情况下,大多数教育机构没有很好地利用 Moodle 来提高学习成绩。(Bowler,2009)例如,教育工作者将许多学习材料上传到 Moodle,而没有考虑哪种学习设计最能激励个人参与在线行动,因此学生很少通过使用这些材料进行学习(Attwell & Hughes,2010;Finlayson 等人,2006 年)。Moodle 的学习设计的多样性来自其可定制的结构,当使用 EDM 分析数据时,它会掩盖影响因素。
此外,Moodle 兼容的协议限制了日志数据的变量。学习材料(如文档、幻灯片和电影)的容量受到全球决定的协议的限制,如 Moodle 中采用的可共享内容对象参考模型(SCORM)、Tincan API(它对学习内容的控制更强,比 SCORM 内容更安全)( Rustici 软件,2016 年)以及 Caliper(它可以记录比 SCORM 更详细的学习动作)( IMS 全球学习联盟,2016 年)。这样,数字内容的利益相关者,例如开发者、数字内容的设计者、教育者和学校,被迫遵循这些协议,并且数字内容是基于该框架开发的。诚然,个人对分块数字学习内容的学习表现和过程(可以作为学习中的定量变化来衡量)被记录在数据库中,并且数字内容的技术属性可以使 Moodle 上的学习内容个性化成为可能(Laurillard,2014)。而拥有 SCORM 协议讲座电影的 Moodle,并不记录学生看完电影的时间,学习资料的设计框架是预先确定的。
除了使用 Moodle 的限制之外,在线教师和课堂教师在角色和职责上的细微差别(McDonald & Reushle,2002;Oliver,1999)区分了对在线学习的影响。在使用 Moodle 的混合学习环境中,教师可以发挥整合作用,如促进讨论或充当教学设计者,这不同于他们在传统课堂教学环境中的权威角色(Selwyn,2011)。Moodle 也与协作学习兼容,因为它们允许讨论被记录和跟踪,并保持小组的界限(Easton,2003)。在学习环境中,教师的社会角色要求他们承担责任,如组织学习社区(Easton,2003),并鼓励学习者使社区更加活跃(Craig et al .,2009)。他们可以扮演“教学设计者”的角色,设计学习过程和内容,以及论坛的“促进者”。因此,EDM 的结果可以直接用于教师确定他们应该如何在线行为以及分析 Moodle 上的在线学习行为。虽然 Moodle 的功能方面限制了日志数据,但日志数据的属性会受到教育者角色的影响,例如学习设计者或促进者。
EDM 是基于社会科学范式的
用 EDM 进行研究的前提是,与学习活动有关的每一个要素都不能总是表现自己。这是因为 EDM 不是基于自然科学的特质,而是基于社会科学,虽然数据挖掘是计算机科学的一个分支。
自然科学的研究对象是
“自然发生的物体或现象,如光、物体、物质、地球、天体或人体”(Bhattacherjee 等人,2012 年,第 10 页)
鉴于,社会科学以研究为目的
“人或人的集合,如团体、企业、社会或经济,以及他们的个人或集体行为”(Bhattacherjee 等人,2012 年,第 10 页)。
社会科学的传统不同于自然科学的传统,例如“非常精确、准确、确定,并且独立于进行科学观察的人”(Bhattacherjee 等人,2012 年,第 1 页)。Bovo 等人(2013 年)关于对 Moodle 数据进行聚类以分析学生的研究提供了很少的行为差异,并生成很少的聚类(2 到 3)。因此,即使正确地采用了分析方法,EDM 方法也不总是能发现有益的信息。
EDM 的研究并没有假设教育环境中的每一种现象都可以转化为支持“科学主义”的静态数字变量(Hyslop-Margison & Nasee,2007)。
因此,需要对研究背景进行丰富而详细的描述,以追溯为什么研究不能获得显著的结果,或确定研究结果在多大程度上可以移植到其他教育环境中。(Bhattacherjee 等人,2012 年)这是因为 EDM 方法具有引导研究者对数据的态度和理解的自愿特征。这是由于假设形成、测试和提炼的迭代过程(Romero & Ventura,2013),这是数据挖掘过程本身的迭代方式,尤其是机器学习技术。关于产生假设的迭代过程的详细描述可以增加研究的可信度。
参考
Laurillard anon——2014 年——思考混合 learning.pdf。可从以下网址获得:http://kvab . be/denkersprograma/files/DP _ blended learning _ Thinking-about . pdf
Attwell,g .和 Hughes,j .(2010 年)。使用技术进行学习的教学方法:文献综述。地点:【http://dera.ioe.ac.uk/id/eprint/2021】T2。
Beluce,A.C .和 Oliveira,K.L. de,(2015 年)。虚拟学习环境中学生的学习动机。paideía;圣保罗,25(60),第 105–113 页。
Berland,Ryan|Blikstein,Paulo,(2014 年)。教育数据挖掘和学习分析:建构主义研究的应用。技术、知识和学习,19(1–2),第 205–220 页。
Bhattacherjee,a .,南佛罗里达大学,学者共享和开放教科书图书馆,(2012 年)。社会科学研究:原则、方法和实践,可在:https://open.umn.edu/opentextbooks/BookDetail.aspx?获得 bookId=79 。
鲍勒博士(2009 年)。在虚拟学习环境中学习"聊天":使用在线同步讨论进行一年级本科生辅导。在曼彻斯特大学举行的英国教育研究协会年会上。第 2-5 页。可在:http://www.leeds.ac.uk/educol/documents/184295.pdf买到。
克雷格,a .,古尔德,a .,科尔德维尔,j .,芥末,j .和杰里,p .(2009 年)。在线学习中角色和责任的认知:一个案例研究。国际博士研究杂志,第 4 期,第 205–223 页。
科斯特洛(2013 年)。开放源代码:看 Moodle 在高等教育中的应用。开放学习:开放、远程和电子学习杂志,28(3),第 187-200 页。
戴克,g .,豪利,I .,阿达姆松,库马尔,r .和罗斯,C.P .,2013 年。由会话代理支持的学术生产性谈话。在群体互动分析中的多产性。计算机支持的协作学习系列。斯普林格,波士顿,马萨诸塞州,第 459-476 页。可在:https://link . springer . com/chapter/10.1007/978-1-4614-8960-3 _ 25获取。
伊斯顿,s . s .(2003 年)。明确教师在在线远程学习中的角色。传播教育,52 卷 2 期,第 87–105 页。
Garrison,D.R .和 Kanuka,h .(2004 年)。混合学习:揭示其在高等教育中的变革潜力。互联网和高等教育,第 7 卷第 2 期,第 95-105 页。
格雷厄姆(2006 年)。混合学习系统。混合学习手册,第 3–21 页。
IMS 全球学习联盟。可从/activity/caliperram 获取。
国际教育数据挖掘协会(2011 年)。国际教育数据挖掘协会。国际教育数据挖掘学会可从/home 获得。
柯克伍德·丹尼尔、阿德里安和普莱斯、琳达(2014)。高等教育中技术增强的学习和教学:什么是“增强的”,我们如何知道?一篇重要的文献综述。学习、媒体和技术,39(1)第 6-36 页。
麦克唐纳,j .和勒什勒,s .(2002 年)。描绘高等教育中在线教师的角色:变革之风。澳大利亚 2002 年会议录:第 19 届澳大利亚高等教育计算机学会年会。澳大利亚高等教育计算机学会(ASCILITE),第 431-440 页。可在:http://eprints.usq.edu.au/4363买到。
穆尼奥斯(2014 年)。机器学习和优化。网址: https://www。T21CIMS。纽约大学。edu/∽munoz/files/ml _ optimization。pdf[访问时间 2016–03–02][WebCite 缓存 ID 6fiLfZvnG] 。可在:https://www.cims.nyu.edu/~munoz/files/ml_optimization.pdf买到。
Oliver 和 k . Trigwell(2005 年)。“混合学习”还能挽回吗?电子学习和数字媒体,2(1),第 17-26 页。
普拉塔博士(2012 年)。协作学习中的对话分析。国际电子教育、电子商务、电子管理和电子学习杂志。可在:http://www.ijeeee.org/show-33-387-1.html买到。
拉什卡,s .,朱利安,d .和哈特,j .(2016)。 Python:深入洞察机器学习,Packt 出版有限公司。
罗梅罗和文图拉(2010 年)。教育数据挖掘:对当前技术水平的回顾。《IEEE 系统、人和控制论汇刊》,C 部分(应用和评论),40(6),第 601–618 页。
Rustici Software,LLC,概述—锡罐 API。地点:【http://tincanapi.com/overview/】T4。
塞尔温,尼尔。(2011).教育和技术:关键问题和争论。伦敦;纽约州纽约市:布卢姆斯伯里学术出版社,布卢姆斯伯里出版公司的一个分支
作为商业智能工具的 Power BI
继我在 Strata 数据科学会议上的上一篇文章之后,我开始思考数据科学和商业智能的未来发展——也就是说,这两个简单的术语将如何改变我们工作、思考和生活的方式。
说实话,“数据科学”对我来说似乎有些遥远;然而,“商业智能”的概念可以大致概括我的工作,至少根据 Forrester Research 的定义是这样的:
一套方法、流程、体系结构和技术,可将原始数据转换为有意义和有用的信息,用于实现更有效的战略、战术和运营洞察和决策
我为微软的一个合作伙伴工作;我们使用 Microsoft Dynamics 365 帮助客户实现数字化转型之旅。最近,Microsoft Dynamics 365 进行了大幅扩展:销售、客户服务、营销等核心应用程序。可以与 Microsoft Power Platform(由 Power BI、Power Apps 和 Microsoft Flow 组成)无缝集成,以自动化业务流程、更高效地管理数据并改善客户体验。

几年前,当我写关于数据可视化的论文时,我第一次听说了阿碧电力公司。作为研究的一部分,我分析了一些 Power BI 报告。之后,我有机会在当地政府、高等教育和慈善机构的 Power BI 项目中工作。看到有多少有用的信息可以通过 Power BI 以一种简单、可读的方式提取和显示出来,真是令人着迷。
Power BI 是一个相对年轻的产品,于 2015 年作为数据可视化和分析工具加入了微软家族。尽管 Power BI 简要宣传了其配备自然语言处理功能的“提问,获得答案”功能,但其主要卖点是可视化、分析和数据连接功能,例如与 Dynamics CRM、Salesforce、Excel、Oracle 等的连接。

Screenshot from an archived Power Bi web page in June 2015, https://web.archive.org/web/20150610061450/https://powerbi.microsoft.com/en-us/
微软直到 2017 年 5 月才开始谈论商业智能这个口号,“前所未有的商业智能”,第一次占据了头版。

Screenshot from an archived page in May 2017, https://web.archive.org/web/20170504213718/https://powerbi.microsoft.com/en-us/
2015 年至 2017 年间,由于数据处理能力的提高和稳定的网络技术,所谓的数据丰富市场正在蓬勃发展。为了利用这些数据,一些公司开始寻找能够解决他们的问题、升级他们的 IT 系统或实施数据分析工具的专业人士。渐渐地,人们也明白了信息越丰富,处理起来就越困难。这就是为什么我们需要更多的“智能”来通过解释数据来帮助企业。
现在,让我们看看 Power BI 作为商业智能工具的优势:
首先,Power BI 的数据清理和准备以及交互式数据可视化功能使用户能够用一个工具做多件事情。这意味着一个公司不必在不同的软件和资源上投入太多。
其次,作为微软的产品,Power BI 可以利用微软平台上的其他尖端技术,如 Azure 认知服务或 AutoML 功能。鉴于许多组织已经订阅了 Microsoft enterprise,Power BI 允许用户使用他们已经在工作中使用的相同凭据登录。
第三,Power BI 的学习曲线并不陡峭,因为它的用户界面与其他微软产品如 Excel、Word 等非常相似。你还需要知道什么?也许可以拖放数据源和可视化,或者添加切片器、页面过滤器或报告过滤器,以便从不同的角度更好地理解您的数据。也可以通过各种微软在线学习资料自学。对于特定场景的帮助,您可以访问 Power BI 社区 ,在这里您可以提出问题并接受其他开发人员的建议。

Screenshot from one of my example Power BI dashboard
最后但同样重要的是,关于人工智能(AI)在 Power BI 上的应用,我不打算引用学术论文或谈论任何令人困惑的事情。一个关于问答功能的快速示例足以说明在 Power BI 中使用一些微软内置的人工智能工具是非常方便的:
Power BI 的问答功能是自然语言处理(NLP)的神奇之处。在 Power BI 控制面板上,您可以提出一些简单的数据问题,Power BI 将努力为您找到答案。例如,在下面的截图中,我输入“按年的总收入”,这个折线图就出现了。不是很方便吗?

Screenshot from one of my example Power BI dashboard
根据来自 Datanyze 的商业智能市场份额报告,Power BI 目前约占商业智能市场的 6%(基于使用给定技术的网站数量),略低于竞争对手 Tableau、SAP Crystal Reports 和 Microsoft SQL Server Reporting。

A screenshot from the market share webpage
但正如本文所讨论的,我感觉 Power BI 潜力很大。我们会看到的!
参考资料:
商业智能(BI)是一套方法、流程、架构和技术,可以改变原始的…
www.forrester.com](https://www.forrester.com/report/Topic+Overview+Business+Intelligence/-/E-RES39218) [## 数据科学与商业智能-数据大学
在当今由数据驱动的现代世界中,数据科学的发展势在必行。如果一个人真的很小心…
www.dataversity.net](https://www.dataversity.net/data-science-vs-business-intelligence/#)
2019 Gartner 分析和商业智能平台魔力象限:https://www.gartner.com/doc/reprints?id=1-3TXXSLV&CT = 170221&ST = sb
Power BI:我如何开始使用 Python 来自动化任务
和许多数据爱好者一样,我每天都使用 Power BI 来构建仪表板并可视化我的数据。然而,在构建和改进复杂的仪表板时,我经常使用不同的字段和相应的表来添加和删除小部件。经过无数次迭代之后,数据结构开始看起来像一团乱麻,产生了一个由许多表和许多列或字段组成的混乱列表,我曾一度用它们来构建我的仪表板。然而,最终最好能了解我在最终仪表板中实际使用的所有字段的概况。不幸的是,在 Power BI 中实现这一点的唯一方法是遍历每个小部件,手动查看并记下已经使用的字段。

Manually finding the data behind widgets in Power BI.
作为程序员,我们试图通过任何必要的手段来避免手工工作,所以我做了一些挖掘,看看是否有一些代码可以取代这种重复的工作。当然,如果我没有想出一个解决方案,我是不会写这篇文章的。事实证明,实现这一点相当简单。
解构 PBIX 文件
在不实际使用软件的情况下使用 Power BI 的第一步是深入研究。PBIX 文件。使用像 WinRAR 这样的免费压缩软件,你可以解压缩任何。PBIX。文件。这导致了不同文件和目录的结构,为分析和可能的操作提供了可能性。
当浏览解压缩文件时,你可以找到一个包含“布局”文件的“报告”文件夹。该文件包含有关您的仪表板的视觉结构的所有信息:
- 每个部件的 X/Y 坐标和尺寸
- 使用字段名称
- 所有设置和参数
- 头衔
- …还有更多
该文件由一个字符串组成,该字符串是 JSON、列表和字典的一种组合。使用 Python,我计划生成实际使用的数据字段和小部件的摘要。

The layout file opened in a text editor.
构建解析器
我希望一个简单的 JSON 解析器能够解决这个问题,但不幸的是,JSON 中嵌套了其他结构,这使得代码比我希望的要长一些,但总体来说还是很短。经过一段时间的困惑和构建异常场景后,我得到了一些相当不错的结果,返回了每个小部件的表和相应字段的列表。
使用 Pandas,我将这些信息总结成一个数据帧,删除所有重复的信息(例如,两个部件使用相同的数据)。结果是包含我在仪表板中使用的所有数据的干净的数据帧,如下所示:

Dataframe output in Jupyter Notebooks.
在线版本
好了,现在通过复制粘贴布局文件中的字符串,让我的函数来完成这项工作,Jupyter 笔记本中的所有功能都工作正常了。工作很好,但不是以一种非常用户友好的方式,所以下一步:将功能转换成一个可用的工具。
不想抛弃我的忠实伙伴 Python,我选择了 Flask 来构建一个 web 应用程序。作为一个我不是前端开发人员,我去了一个极简的方法,不要太关注视觉方面。

Online version of the tool.
基本上,它只是一个表单,将字符串作为输入,运行函数,返回数据帧,将数据帧转换为 HTML 表,最后显示给用户。该工具是免费的,不包含任何插件。它可能有时会有点慢,因为它是在 Heroku 服务器上托管的。

The end result, a table showing all the unique data fields used for the dashboard.
下一步是什么?
尽管我对这项任务的自动化很满意,但这仍然只是一个开始。展开脚本并在解压缩后的。PBIX 文件可以打开许多新的大门。例如,我简单地对文件做了一些小的操作,再次压缩并通过 Power BI 重新打开它,这似乎很有效。这意味着应该可以使用 Python 脚本来编辑仪表板。
现在还不确定我下一步会去哪里,但有些选择可能是:
- 跟踪更改
- 自动生成报告
- 创建更广泛的报告
- …?
如果你有任何其他想法或反馈,请在评论中留下。
想用工具的可以在这里找到。
关于作者 :我叫布鲁诺,是总部位于荷兰的人工智能技术纵向扩展公司dash mote的一名数据科学家。我们的目标是借助基于人工智能的解决方案,弥合图像和数据之间的鸿沟。
查看我在 上的其他作品 https://www.zhongtron.me 。
电源 BI:实施和/或选择
我真的越来越喜欢 Power BI 了。每天使用这个软件,我对这个产品的功能以及它通过添加新功能或优化现有功能逐月改进的方式感到惊讶。

Power BI desktop. (Source: microsoft.com)
然而,像任何软件解决方案一样,仍然有改进的空间,并且缺少一些可以使许多用户受益的功能。这些可能的改进之一是启用多选类型。在当前设置中,Power BI 将多选视为“或”,而不是“和”。让我快速向您介绍一下两者的区别。
AND vs. OR
和/或的概念非常简单,但对于那些不熟悉的人,我将用一个简单的例子来演示。假设您有一个简单的学生和他们的语言课程表,如下所示:

AND/OR data example
法语或英语:学生甲、乙、丙
法语和英语:学生甲、乙
默认的 Power BI 行为是将该选择视为 or。
在本文中,我将向您介绍如何在您自己的 Power BI 仪表板中设置 AND 和 OR 选项。
我为这个演示创建的样本数据如下所示:

Data for demo. 2 tables in total
第一个表包含一些学生的基本信息,而另一个表包含每个学生的多行,包含他们正在学习的语言课程的信息。这两个表以双向一对多的关系连接在一个学生 ID 上。
让我们写一些代码
在我们开始写代码之前,你应该熟悉 DAX 的基础知识。对于那些不熟悉的人来说,DAX 是一种编程语言,您可以在 Power BI 中使用它来创建过滤、计算、列等等。
首先,我们将创建一个非常小的表,其中包含我们稍后将用来创建“AND/OR”切片器的指标和值。这是一个 2x2 的表格,您可以从外部源添加,或者使用以下代码创建它:
ANDOR_Table =
DATATABLE(
“AND/OR”, STRING,
“Binary”, INTEGER,
{
{“AND”, 0},
{“OR”, 1}
}
)```
该表应该如下所示:

AND/OR table, used for the slicer
在我们开始编写度量之前,我最近采用了一个好的做法,将所有的度量放在同一个表中,以保持整洁。所以让我们首先创建一个空表,如下所示:
_Measures =
{BLANK()}
在该表中增加以下措施:
Selected_AndOr =
MAX(ANDOR_Table[Binary])
此度量将指示您是选择“与”还是“或”选择。您已经可以将切片器添加到包含和/或字符串作为项目的报表中。

AND/OR slicer
现在我们有了选择的一切,是时候开始创建后端了。我们将创建的下一个度量是计算您选择了多少行的度量。
Selected_counter =
IF(
ISFILTERED(‘Courses’[Course]),
COUNTROWS(
ALLSELECTED(‘Courses’[Course])
),
0)
我们在前面的度量中创建的计数器现在将用于向您想要查看选择结果的表中添加一个指示器,在本例中是 Student 表。
我们创建的最后一个度量将使用前一个度量,以查看该行是否满足“AND”标准。它将返回一个 0/1 标志。
Course_ANDOR_Check =
IF(
[Selected_counter] = 0,
1,
IF(
DISTINCTCOUNT(‘Courses’[Course]) = [Selected_counter],
1,
_Measures[Selected_AndOr]
)
)
现在一切都设置好了,是时候把它添加到视觉效果中了。对于每一个你想受此功能影响的部件,你应该在视觉层次上添加一个过滤器,如下所示: Course_ANDOR_Check 为 1

Filter you should apply to the visuals
搞定了。现在,您可以使用和/或切片器来更改功能,如下图所示。

Final result
这个解决方案结合了我自己的代码和我在网上找到的代码片段。由于我仍在发展我的 DAX 技能,我渴望改善这段代码。因此,如果您有任何问题或改进此解决方案的想法,请随时留下您的评论。
关于我:我叫布鲁诺,是总部位于荷兰的人工智能技术纵向扩展公司 Dashmote 的数据科学家。我们的目标是借助基于人工智能的解决方案,弥合图像和数据之间的鸿沟。
查看我在 上的其他作品 https://www.zhongtron.me 。
权力 BI:DAX 是什么?以及为什么你应该(或不应该)学习它
DAX 是什么?
DAX 代表数据分析表达式,它是由微软开发的语言,用于与各种平台(如 Power BI、PowerPivot 和 SSAS 表格模型)中的数据进行交互。它的设计简单易学,同时展示了表格模型的强大功能和灵活性。在某种程度上,你可以把它和 Excel 公式进行比较。使用 DAX 将真正释放 Power BI 的能力。
尽管我自己还远不是 DAX 专家,但我已经到了在日常工作中使用大量代码的地步,同事们也开始就此提出越来越多的问题。出于这个原因,我选择写这篇文章来解释为什么你应该(不应该)使用数据科学/数据分析工具箱中的这个工具。

Writing DAX in Power BI (image source: microsoft.doc.com)
为什么你应该学习 DAX
它开启了一个全新的世界
作为一名 Power BI 用户学习 DAX 很像是一名 Excel 用户,发现如何使用公式,你能够构建你的表格,添加一些图表,并点击 sum/average/…按钮(σ),但突然你会发现 VLOOKUP、IF 函数等等的世界。然而,这种比较并不完全有效,因为,或者至少在我看来,即使没有 DAX,Power BI 也已经是一个非常强大的工具,相比之下,Excel 中除了简单使用之外的任何东西都已经需要公式了。话虽如此,学习 DAX 将为你打开一个全新的 Power BI 世界。您将解锁的最重要的功能是能够以动态的方式选择、连接、过滤…数据。这意味着仪表板可以接受用户的输入,并使用它来动态生成计算列、度量和表。
头痛少了
不需要太多的经验就能达到你对着屏幕咒骂的地步,因为你的仪表板并没有给你你期望的结果。一旦你知道如何使用 DAX,你会惊讶地发现你可以避免或完全绕过(以某种黑客的方式)许多令人头痛的问题。一个非常简单的例子就是卡片小部件中的“空白”值。当在卡片中显示数字数据时,例如“收入”,如果您以没有收入显示的方式设置过滤器,它将返回“空白”。但是,显示“无收入”的更自然的方式应该是“0”而不是“空白”。通过一个非常简单的 DAX 表达式,您可以自己创建一个向公式添加“0”的度量,这意味着您将再也不会看到“空白”了。
这是一小段代码可以极大地改善用户体验的众多方式之一。

This must be relatable to some of you
加速你的仪表盘
你在 DAX 做得越好,你的仪表盘就越智能。通过使用 DAX,您可以创建更智能的计算列和/或度量,从而限制仪表板必须获取和可视化的数据。尽管一些 DAX 表达式可以测试数据引擎的限制,但是一个写得好的表达式可以加快速度,从而限制资源的使用。关于不使用 DAX 加速你的仪表盘的其他方法,你可以阅读我几个月前分享的这 5 个技巧。
DAX 比力量 BI 多
如果您愿意花时间学习 DAX,您新获得的技能不需要局限于 Power BI 环境。DAX 可用于表格形式的微软产品,如:
- 功率 BI
- Microsoft Analysis Services
- Microsoft Power Pivot for Excel
更不用说 DAX 语法也非常类似于 Excel 公式,这使得知识也可以转移到这个古老的、广泛使用的软件中。
它让你成为更好的数据专家
尽管 DAX 只能在支持它的环境中使用,但是知道如何使用 DAX 的技能远远超出了它的范围。由于 DAX 基于一个由不同的嵌套过滤器上下文组成的系统,性能是关键,它改变了您对表和过滤数据的思考方式。通过在早上编写一段智能的 DAX 代码,您可能能够提高下午早些时候编写的一些 Python 代码的性能。换句话说,通过学习 DAX,你将改进如何有效地合并、过滤、选择和操作数据的思维方式。
为什么你不应该学习达克斯
虽然我很乐意学习 DAX,并且我刚刚列出了一些令人信服的理由来说明为什么你应该开始使用它,但是我必须补充一点,DAX 并不适合所有人。以下是为什么学习 DAX 不应该是你的优先事项的一些原因:
陡峭的学习曲线
一夜之间完全理解 DAX 是不可能的。尽管您可以很快开始编写一些基本代码,但理解不同的过滤器上下文如何交互等肯定需要时间。(这是我仍然时常纠结的一件事)。你需要自己决定你会使用它多少,看看是否值得付出努力。
你可以用替代品做很多事情
在 DAX 中,甚至在您的数据最终出现在一个小部件中之前,就有可能进行大量的数据操作。对于任何不需要动态生成的东西,有很多选择。例如,使用 Python 可以很容易地向仪表板添加一些新的额外列。
仪表盘对外人来说可能变得晦涩难懂
将 DAX 添加到仪表板会增加复杂性。如果你是唯一一个构建或操作仪表板的人,这应该不是问题,但是对于那些在团队中工作的人来说,这可能会使事情变得复杂。我亲身经历了不熟悉这种语言的同事在使用 DAX 混淆导致的仪表板操作时陷入困境。
简化事情的愿望不应该是停止前进的好理由,但这是您在 Power BI 中采取下一步措施时应该考虑的事情。

80/20 法则
帕累托原则或 80/20 法则指出,用 20%的努力可以实现 80%的结果,反之亦然。死硬的 DAX'ers 会不高兴看到这一点,但我相信 80%可以做到没有 DAX。Power BI 是一个强大的工具,即使初学者也可以创建有用的仪表板和洞察力。当然,更高级的仪表板将绝对依赖于 DAX 的大分区,但许多仪表板相当简单,无需大量代码即可满足用户需求。这意味着,对于许多 Power BI 用户来说,这项投资根本不值得。在当今自由职业平台、24/7 连接、数字游牧者等等的世界中,将你的 DAX 部分外包给专业人士可能更容易。
从哪里开始学习 DAX
好吧,看来我已经说服你潜入达克斯的世界了。这是个好消息,因为更大的社区有益于任何软件环境。此时,您可能想知道从哪里开始;当然,学习 DAX 很有挑战性,但并不复杂本身。理解这些概念需要时间和努力,但你也不需要计算机科学博士学位就可以开始。
别担心,我不会给你推荐一些昂贵的在线课程。与大多数(如果不是全部)编程语言一样,有大量的免费资源、文档、视频和在线社区可以教你所有需要知道的东西。我经常访问的一些有用资源是:
- 电力毕社区:来源:)
- 立方体中的家伙:惊人的 YouTube 频道,有大量的教程
- r/PowerBI : Power BI 的子编辑
- DAX 官方文档
- https://dax.guide :一些更棒的文档
- DAX 格式化程序:使 DAX 代码更具可读性的免费工具
最后,除了这些免费资源,我强烈推荐阅读 Marco Russo 和 Alberto Ferrari 的 DAX 权威指南,它可以被认为是语言的圣经。
好了,现在你知道了,我希望这篇文章能帮助你决定是否要深入 DAX 的世界,如果是,你可以从哪里开始。
关于作者 :我叫布鲁诺,是总部位于荷兰的人工智能技术纵向扩展公司dash mote的一名数据科学家。我们的目标是借助基于人工智能的解决方案,弥合图像和数据之间的鸿沟。
查看我在 上的其他作品 https://www.zhongtron.me 。
基于神经网络的天然气价格预测&分类用例
由连续价格和价格运动方向解决的预测问题
回归时序分析&分类用例

Image by author
【https://sarit-maitra.medium.com/membership 号
F 时间序列预测是一项困难的任务,尤其是当我们处理股票数据的随机价格序列时。这里的随机指的是真正意义上的混沌,带有非平稳性。此外,由于股票数据的复杂性,开发有效的预测模型非常困难。然而,回报确实表现出某种可预测性和现代 ML 技术的应用,有效的特征工程有助于推动预测股票回报的极限。
在这里,我将使用机器学习算法在历史价格记录上训练我的机器,并预测预期的未来价格。让我们看看我们的算法能预测多准确。我将使用回归用例,并通过实现 LSTM 来解决问题;随后,将使用分类用例通过应用各种分类算法来解决问题。
我使用的是天然气每日现货价格,可在https://www.eia.gov/dnav/ng/hist/rngwhhdM.htm获取。因此,我们上传数据并做一些处理来清理数据,使数据为机器学习做好准备。

数据集从 1997 年 1 月 7 日到 2019 年 10 月 21 日,有 5732 个数据点。
数据准备

我们需要检查数据集中是否存在任何 NaN 值,如果存在,需要修复或删除以便机器理解。在这里,我用以前的值填充了缺少的值。

我们确保最终数据集中不存在缺失值。
df.plot(figsize=(10, 5))
plt.title('Daily Natural Gas Spot Prices', fontsize=12)
plt.ylabel('Dollars per Million Btu', fontsize=12)
plt.show()

数据质量检查

技术指标
技术指标是基于“气价”的数学计算。通过分析历史数据,我们可以使用指标来预测未来的价格走势。一些众所周知的技术指标如下—

移动平均线收敛-发散(MACD)比较价格的两个移动平均线。第一条移动平均线是 26 天指数移动平均线(EMA),第二条移动平均线是 12 天 EMA。从 12 日均线中减去 26 日均线。
布林线由基于标准差的上下波段组成,随着波动性收缩和扩大。波段是分析趋势强度和监控反转何时发生的有用工具。标准偏差通常设置为 2.0,决定了波段的宽度。标准差越高,价格越难达到上限或下限。标准差越低,价格越容易突破波段。
计算 MACD 的公式是:
MACD = EMA12(价格)EMA26(价格)
上限= 21 天 SMA + (21 天价格标准差 x 2)
较低波段= 21 天 SMA-(21 天价格标准差 x 2)

因此,这样我们就创建了 10 个可以用来预测未来价格的特征。然而,我们不会在我们的用例中使用所有这些特性。我们的数据量很小,我们需要使用一种简单的技术来获得最好的输出。
让拟合预处理和拟合原始单变量价格序列到 LSTM 网络。我们最初的单变量序列如下:

将数据分成训练集和测试集:
我们将 2018 年 12 月 31 日作为训练集,其余时间作为测试集。因此,我们将根据 21 年的数据(5530 个数据点)训练我们的模型,以测试(202 个数据点)并验证我们开发的模型可以预测的精确度。
显然,我们想知道如何确保模型在投入生产时表现良好,并看到前所未见的数据。这里,在没有用于训练预测模型的数据集上的测试性能是样本外性能。这些结果被认为代表了模型投入生产时的预期。出于模型的目的,我们将对数据集训练和测试进行分区。下面我们将我们的系列分成训练/测试样本。

可视化训练/测试分割:
ax = train_data.plot(figsize=(10, 5))
test_data.plot(ax=ax, color='r')
plt.legend(['train', 'test']);

数据标准化
我们缩小范围(0,1)中的值,并对数据进行归一化。在缩放时,我们将测试和训练数据相对于训练数据进行标准化,因为在此阶段我们无法访问测试数据。
scaler = MinMaxScaler(feature_range = (0,1))
train_data_scaled = scaler.fit_transform(train_data)
print(train_data_scaled); print(train_data_scaled.shape)

让我们创建一个具有 60 个时滞和 1 个输出的数据结构。目标是允许我们的网络回顾 60 个时间步,以预测下一步(61)。因此,每次网络预测输出时,它都检查前 60 个时间步,这是我们的回顾时间周期。
X_train = []
y_train = []for i in range(60, len(train_data_scaled)):
X_train.append(train_data_scaled[i-60:i,0])
y_train.append(train_data_scaled[i,0])X_train, y_train = np.array(X_train), np.array(y_train)
print(X_train); print(); print(y_train)

我们需要重塑数据;张量接受 3D 形状(batch_size,timesteps,input_dim)。
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
print(X_train.shape); print(); print(X_train)
现在,我们准备初始化 RNN 架构并构建模型。
递归神经网络
LSTM 细胞可以记住任意时间间隔内的数值,并产生 LSTM 记忆。存储器由三个不同的门处理:(1)输入门,(2)遗忘门,和(3)输出门。下面的等式表达了单个 LSTM 的计算过程

model = tf.keras.Sequential()# adding 1st LSTM layer and some dropout regularization
model.add(tf.keras.layers.LSTM(units=50, input_shape=(X_train.shape[1], 1), return_sequences=True, activation = 'relu'))
model.add(tf.keras.layers.Dropout(0.2))# adding 2nd LSTM layer and some dropout regularization
model.add(tf.keras.layers.LSTM(units=50, return_sequences=True))
model.add(tf.keras.layers.Dropout(0.2))# adding 3rd LSTM layer and some dropout regularization
model.add(tf.keras.layers.LSTM(units=50, return_sequences=True))
model.add(tf.keras.layers.Dropout(0.2))# adding 4th LSTM layer and some dropout regularization
model.add(tf.keras.layers.LSTM(units=50))
model.add(tf.keras.layers.Dropout(0.2))# adding output layer
model.add(tf.keras.layers.Dense(units=1))#compiling RNN
model.compile(loss='mean_squared_error', optimizer='adam')early_stopping = EarlyStopping(monitor='loss', patience=10)# fitting RNN on training set
model.fit(X_train, y_train, epochs= 100, batch_size=32,
verbose=2, callbacks=[early_stopping])
默认的 sigmoid 激活函数用于 LSTM 模块。该网络被训练 100 个时期,并且使用 32 的批量大小。一旦模型合适,我们就可以估计模型在训练和测试数据集上的性能。
为了更好地显示,下面的消息被截断。

让我们得到 2019 年的预测天然气价格;我们需要将训练集中的前 60 条记录追加到测试集中
dataset_total = pd.concat((train_data, test_data), axis=0)
print(dataset_total)
dataset_total = pd.concat((train_data, test_data), axis=0)inputs = dataset_total[len(dataset_total) - len(test_data)- 60:].values
inputs = inputs.reshape(-1,1)
inputs = scaler.transform(inputs) # transforming input dataX_test = []
y_test = []for i in range (60, 262):
X_test.append(inputs[i-60:i, 0])
y_test.append(train_data_scaled[i,0])
X_test, y_test = np.array(X_test), np.array(y_test)
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))
pred_price = model.predict(X_test)
pred_price = scaler.inverse_transform(pred_price)
print(pred_price)

a = pd.DataFrame(pred_price)
a.rename(columns = {0: 'Predicted'}, inplace=True);
a.index = test_data.index
compare = pd.concat([test_data, a],1)
compare

plt.figure(figsize= (15,5))
plt.plot(compare['gas price'], color = 'red', label ="Actual Natural Gas Price")
plt.plot(compare.Predicted, color='blue', label = 'Predicted Price')
plt.title("Natural Gas Price Prediction")
plt.xlabel('Time')
plt.ylabel('Natural gas price')
plt.legend(loc='best')
plt.show()

然而,如下所示,我们的得分指标并不令人印象深刻,我们还有改进的空间。我们需要重新访问数据、选定的特征、选择正确的超参数,还需要更新的数据。
test_score = math.sqrt(mean_squared_error(compare['gas price'], compare.Predicted))
print('Test Score: %.2f RMSE' % (test_score))
# Explained variance score: 1 is perfect prediction
print('Variance score (test): %.2f' % r2_score(test_data, pred_price))


分类用例
对于分类用例,让我们创建一些滞后序列,如下所示。
k = df.copy()lags = 5
# Create the shifted lag series of prior trading period close values
for i in range(0, lags):
k["Lag%s" % str(i+1)] = k["gas price"].shift(i+1).pct_change()k['price_diff'] = k['gas price'].diff()
k['ret'] = k['gas price'].pct_change()
k.head()

目标变量:
这里的目标是根据前 N 天的信息预测(t+1)值。因此,定义输出值为“target”,这是一个二进制变量,在明天的价格差>今天的时候存储 1。
# positive value = 1, otherwise, 0
k["target"] = np.where(k['price_diff']> 0, 1.0, 0.0)
k.head()


这里,为了简单起见,我们将只使用 2 个 lags 和返回值。
x = k[['Lag1', 'Lag2', 'ret']].dropna()
y = k.target.dropna()# # Create training and test sets
gkcv = GapKFold(n_splits=5, gap_before=2, gap_after=1)"""
Introduced gaps between the training and test set to mitigate the temporal dependence.
Here the split function splits the data into Kfolds.
The test sets are untouched, while the training sets get the gaps removed
"""for tr_index, te_index in gkcv.split(x, y):
xTrain, xTest = x.values[tr_index], x.values[te_index];
yTrain, yTest = y.values[tr_index], y.values[te_index];
print('Observations: %d' % (len(xTrain) + len(xTest)))
print('Training Observations: %d' % (len(xTrain)))
print('Testing Observations: %d' % (len(xTest)))

分类模型:
# Create the models
print("Accuracy score/Confusion Matrices:\n")
models = [("LR", LogisticRegression()),
("LDA", LinearDiscriminantAnalysis()),
("QDA", QuadraticDiscriminantAnalysis()),
("LSVC", LinearSVC()),
("RSVM", SVC(C=1000000.0, cache_size=200, class_weight=None,
coef0=0.0, degree=3, gamma=0.0001, kernel='rbf',
max_iter=-1, probability=False, random_state=None,
shrinking=True, tol=0.001, verbose=False)),
("RF", RandomForestClassifier(
n_estimators=1000, criterion='gini',
max_depth=None, min_samples_split=2,
min_samples_leaf=1, max_features='auto',
bootstrap=True, oob_score=False, n_jobs=1,
random_state=None, verbose=0))]
# iterate over the models
for m in models:
# Train each of the models on the training set
m[1].fit(xTrain, yTrain)
# array of predictions on the test set
pred = m[1].predict(xTest)
# Accuracy score and the confusion matrix for each model
print("%s:\n%0.3f" % (m[0], m[1].score(xTest, yTest)))
print("%s\n" % confusion_matrix(pred, yTest))


这里,虽然随机森林有 51.9%的准确率,但对真阳性和真阴性的分类相对较好。因此,我们将为现有用例考虑随机森林。
rfc = RandomForestClassifier(
n_estimators=1000, criterion='gini',
max_depth=None, min_samples_split=2,
min_samples_leaf=1, max_features='auto',
bootstrap=True, oob_score=False, n_jobs=1,
random_state=None, verbose=0).fit(xTrain, yTrain)pd.set_option('float_format', '{:f}'.format)train_pred = rfc.predict(xTrain)
rmse = np.sqrt(mean_squared_error(yTrain, train_pred))
print("RMSE_train: %f" % (rmse))
print('Train prediction values:')
train_pred = pd.DataFrame(train_pred);
train_pred.rename(columns = {0: 'TrainPrediction'}, inplace=True);
print(train_pred);print()pd.set_option('float_format', '{:f}'.format)
test_pred = rfc.predict(xTest)
print('Test prediction values:')
test_pred = pd.DataFrame(test_pred)
test_pred.rename(columns = {0: 'TestPrediction'}, inplace=True);
actual = pd.DataFrame(yTest)
actual.rename(columns = {0: 'Actual PriceDiff'}, inplace=True);
compare = pd.concat([actual, test_pred], 1)
print(compare)


摘要
在这里,我们看到了如何在同一数据集上使用回归和分类来解决预测问题。虽然我们只使用 LSTM 回归用例,但是,这也可以与其他算法进行比较,如线性回归或弹性网络等。
我可以到达 这里的 。
免责声明:所有此类使用风险自负。
使用 Visual Studio 代码启动您的 Python 项目
用 Visual Studio 代码设置几乎自动化的 Python 虚拟环境,创建 Jupyter 笔记本和更多内容。
为什么在数据科学中使用虚拟环境?
我们知道依赖管理对于包开发和软件开发人员的重要性。但是对于那些没有被部署到 PyPI 或 conda-forge 的从事数据科学的人来说呢?
虚拟环境可以帮助你修复坏掉的东西。
如果您使用 Python 已经有一段时间了,那么您会对安装了太多包的混乱开发环境感到沮丧。您不需要一次全部用到它们,并且试图找出哪些是您的项目所必需的,手工操作是令人沮丧的。
软件包并不总是在同一时间升级,许多软件包彼此不兼容,甚至与您正在运行的 Python 或 Anaconda 版本不兼容。来自 conda 内部不同渠道的包甚至不能保证不冲突。如果你把所有东西都下载到同一个大环境中,不可避免的是,你会以不一致的依赖关系结束,事情会崩溃。

this is not wise.
更不用说各种工具,如 pip、pipx、conda、poeties、hatch、pipenv、pyenv、virtualenv、pyvenv、pyenv-virtualenv、virtualenvwrapper、pyenv-virtualenvwrapper 和 venv……尽管听起来非常相似,但往往彼此不兼容。
如果您使用 Anaconda,这是您的项目何时中断的问题,而不是是否中断的问题。
不在容器外使用 anaconda 的另一个原因是——您不知道自己在运行什么。在一些奇怪的案例中,anaconda 的激活脚本严重破坏了 pyenv 之前的系统环境清理,唯一快速解决这个问题的方法是在. zshrc 前面加上HOST=$(hostname)

I feel confident running this code in my user root directory, don’t you?
虚拟环境有助于数据科学的再现性。
Python 有一个很棒的开源社区,但这也意味着各种工具和方法的激增。
如果你提供你在科学分析中使用的库的精确版本,你的结果将更可验证。事实上,这种依赖性管理在责任性和准确性方面可以发挥至关重要的作用。有时,Python 包中的错误被证明是统计模型中计算错误的根本原因。重要的是能够追踪您是否使用了错误的包,以便您可以在必要时验证或纠正您的结果。
能够为一个给定的项目冻结你的需求——并且只那些真正是需求的包——并且提供它作为一个环境文件以备将来参考,会让你成为一个更好的合作者和科学家。您将确切地知道您的模型使用了什么,何时使用,以及为什么使用。
将 pyenv 与 pyenv-virtualenv 结合使用可以让您安全地管理 python 安装
我已经写了一个(完全不全面,保证会崩溃)的指南,在这里用 pyenv 和 pyenv-virtualenv 设置你的系统。这是我最喜欢的管理多个安装的方法,因为它:
- 干净、灵活且可逆
- 抵抗用户错误
- 防止蟒蛇破坏我的环境的有效措施
如果你不想读完整本书,我把它浓缩成了一张备忘单:
安装并运行 Anaconda 卸载程序 conda install Anaconda-clean & & Anaconda-clean-yes 找到剩余的目录…
github.com](https://github.com/lorarjohns/awesomeVenvs/blob/master/pyenv_cheat_sheet.md)
我还使用 Arq 云备份,它的工作方式类似于 git,而且非常便宜,几乎是自动化的,在用这种方法建立新系统之前,我可以保护重要的文件。对于我想更直接访问的 Zotero、dotfiles 和较小的文件,我使用 Sync (本质上是一个端到端加密的、符合 GDPR 标准的 Dropbox)。
包含 pyenv 和 Visual Studio 代码的集成开发环境
设置好 pyenv 安装后,您可以为虚拟环境中的数据科学开发创建一些非常酷的工作流。让我们用 Anaconda 发行版创建一个项目,看看如何使用 Visual Studio 代码开发 Jupyter 笔记本并将其转换为。py 脚本
每个项目都应该有一个目录,每个目录都应该有一个虚拟环境。这个结构做两件重要的事情:
- 它可以让你的东西有条不紊,这样就更容易把项目分开,管理依赖关系,把不该放在那里的东西挡在外面。(谁喜欢撤销 git 提交?)
- 它允许您为每个目录(因此也为每个项目)创建一个单独的
.python-version file,,这意味着当您更改目录时,pyenv-virtualenv 可以自动切换到适合您的环境。
这里是TL;项目设置的 DR 版本:
- 制作一个项目文件夹,并将
cd放入其中 - 用
pyenv local $PYTHON_VERSION设置你的项目 Python - 运行这个命令(例如)来创建一个虚拟环境:
pyenv virtualenv anaconda3–2019.10 venv-cool_project如果您忽略 Python 版本,您的环境将使用当前在本地有效的版本。 - 通过使用 venv 的名称执行
pyenv local,激活您的新环境并将其设置为项目的本地 Python,并使用(例如)conda activate venv-cool_project激活环境
如果您在每次创建新项目时都运行下面的一行程序,您应该能够cd进出目录,并让您的虚拟环境自动激活和停用。(当然,您需要更改环境名称和 Python 解释器。)

$ mkdir $2 && cd $2 && pyenv local $1 && pyenv virtualenv venv-$2 && pyenv local venv-$2 && pyenv activate venv-$2
这里有一个 GitHub 要点和一个脚本可以帮你做到。下载它(你可能需要运行chmod +x newproj.sh来确保它是可执行的)。然后,通过向它传递您想要使用的 python 版本和项目名称,就可以使用它来创建新项目:

now you really have no excuse not to use venvs.
通过 Python 和 Jupyter 使用 Visual Studio 代码
Visual Studio 代码可以做很多很酷的花样。首先,如果您按照上面的说明进行设置,它可以自动为您的项目目录选择正确的虚拟解释器。
brew cask install visual-studio-code上手。
然后,进入 VS 代码的设置:
- 确保系统的终端与应用程序的终端同步:告诉 VS 代码使用带有“外部”变量的操作系统终端应用程序。
- 打开集成终端功能,该功能允许您在 VSCode 应用程序中使用终端模拟器。

VS Code user settings for integrated terminal and iTerm.app on macOS
- 用
⌘+⇧+P打开命令面板,选择Shell Command: Install 'code' command in PATH。这允许您从外部终端启动 VS 代码:code --启动应用程序,code .打开当前工作目录,code path/to/file/or/dir打开特定文件或目录。

one of my project directories, with an integrated terminal
- 为 VS 代码安装 Python 扩展。一旦启用,只要你用 python 扩展名保存了一个文件,编辑器就会知道它应该在 Python 上下文中被解释。
您会注意到,当您在集成终端中 cd 进入和退出项目目录时,python 解释器将自动检测您的 venvs,只要有一个.python-version文件存在(如果您正确使用 pyenv-virtualenv,应该有)。

The name of the detected virtual environment, in the lower left-hand corner
您还可以通过点击底部工具栏中的 Python 解释器名称,或者打开命令面板并键入Python: Select Interpreter,明确地告诉应用程序使用哪个 Python 解释器。

A list of available interpreters/environments and their paths
因为我们在一个终端,我们也可以创建和激活新的 venvs,就像我们通常在这里做的那样。
创建 Jupyter 笔记本
要使用笔记本,只需在 conda 环境中打开一个.ipynb文件或从命令面板中选择Python: Create New Blank Jupyter Notebook即可。然后,您可以像往常一样运行单元和创建笔记本,但额外的好处是您不依赖于您的 web 浏览器。

a jupyter notebook in a conda virtual environment in vscode
你也可以一键将笔记本快速转换成 Python 脚本,或者使用Python: Convert to python script:

notebook converted to a script
这对于将探索性分析转化为可运行的、可再现的程序来说非常方便。
满怀信心地完成伟大的项目
现在,当您开始一个项目时,您将能够在您的开发环境中控制包,轻松地将 conda envs 转换成 requirements.txt 文件,并更高效地将您的笔记本整理成适合生产的脚本。VS Code 提供了更多的工具,比如语法高亮、代码林挺、测试集成,以及与 GitHub、Docker、各种数据库等的接口,来增强你的项目。将这些工具添加到您的数据科学工作流程中,成为更高效的程序员和开发人员。
用机器学习能力驱动 SQL
使用单词嵌入在 SQL 中启用语义查询

Photo by Nathan Dumlao on Unsplash
当今世界,对机器学习的需求越来越大。这导致了巨大的努力来减少这些任务所需的技能/努力。最有希望的策略之一是将这些功能整合到标准 SQL 中,它始于 Oded Shmueli 论文,并在弹性搜索和 PostgreSQL 中得以实现。
为了在 SQL 中启用这些功能,我们需要向数据库实体(行/列/表)插入实际的语义。我们通过将单词嵌入向量合并到我们数据库中来做到这一点。因此,我们可以询问诸如“显示与此行相似的所有行这是标准 SQL 无法做到的。
我们需要在数据库中变得神奇的是以正确的方式加入单词嵌入。

All we need to get magic in our database is to incorporate word embedding in the right way
它是如何工作的
为了量化数据库内部的语义关系,除了所有数据库关系之外,还需要一种以某种方式表示它们的语义的方法。
嵌入是一种将序列映射到数字向量空间的技术,其中向量表示相关序列的各个方面(嵌入使用的是 word2vec )。提出的解决方案是构建数据库实体的嵌入,以表示它们的语义,然后访问这些向量(通过用户定义的函数)并比较它们的接近度(通过用户定义的函数)。
下图描述了查询执行的主要阶段:

We start by getting a SQL query with UDFs from the user and executing it, to execute the query the sentences are tokenized and the relational vectors are calculated and returned to the user.
因此,我们将通过允许使用以下 UDF 量化数据库内部的语义关系来增强查询:
- 余弦距离(a,b) 取向量 a,b 返回它们的余弦距离。
- vec(token) 接受一个令牌并返回其相关向量。
- 令牌实体 e 声明了一个可以绑定到令牌的变量。
- Contain(row,entity) 声明实体必须绑定到 tokenizing row 生成的 token。
它增加了什么功能
我之前展示的那些构建模块将允许进行高级查询,首先,我们可以运行“语义相似性查询”。
例如“按行业查找与潜在客户(语义)最相似的客户”。

We find all pairs of products a,b (only once per pair) and order those by similarity.
回想一下单词嵌入也支持归纳推理(经典国王之于男人就像女王之于?),换句话说,我们可以计算类比。
例如,“找到所有与自己相关的产品 a、b 对,就像花生酱与果冻的关系”。

First, we create a table with product names and the distance between them and the jelly/peanut butter vector. Afterward, we find all pairs of products a,b which relate close (from distance perspective) to themselves as peanut butter relates to jelly.
对数据库实体(如列、行甚至表)进行模糊连接的另一组新功能。在许多用例中,人们希望在不确切知道列/表需要使用什么的情况下运行查询。
例如,“在票证和用户之间给定未知模糊外键的情况下,查找用户‘Moshe’的所有票证”。

First, we declare two tokens e1, e2 that represent in our database and in our use case unknown column. Afterward, we find all the users called Moshe and find their tickets, since we don’t know the join columns on the ticket table we try to join on every column and keep those with small similarities. Note: we could enforce a 1:1 relation with rank with the distance ordered.
定论
这是向赋予数据库系统机器学习能力的一步。这些功能适用于各种各样的应用程序(包括数据集成、搜索等等)。
我希望你觉得有趣和有用,我愿意接受任何建设性的反馈。
TensorFlow 2.0 中的实用编码
tf.function、TensorArray 和高级控制流

An image by Gerd Altmann
摘要
与 PyTorch 的激烈竞争给我们带来了 TensorFlow (TF)的新版本。包经历了很多变化,但最关键的是session.run()的退役。默认情况下,TF 2 使用的是 eager 模式,而不是我们熟悉的构建和执行静态图的模式。这种代码可以用 pythonic 的方式编写,并转换成计算图。为了以静态图的形式执行代码,开发人员必须用@tf.function修饰想要的函数。
在这篇文章中,我将用例子来解释这些概念。我假设读者了解 Python 和机器学习(ML)的基础知识。如果你是这个领域的新手,欢迎!这个课程将会是一个很好的开始。在这里,你可以找到这篇文章的 colab 版本。
装置
要安装 TF 2.x,请访问这个页面。
要检查您的当前版本:
import tensorflow as tf
print(tf.__version__)
急切的执行
代码在 TF 2 中急切地执行。这意味着您可以在不调用session.run()或使用占位符的情况下向计算图提供数据。计算图是定义一系列操作的结构。这种结构允许我们通过沿着图形向后移动来自动计算导数。观看此视频了解更多细节。在 TF 1 中,开发人员必须创建一个图形,然后执行它。现在图形是动态构建的,执行类似于函数调用。
为了了解它的工作原理,让我们做一个简单的模型。例如,我们有一个包含 3 个训练示例的数据集,其中每个示例都是一个二维向量。
import numpy as npnp.random.seed(0)
data = np.random.randn(3, 2)
首先,我们必须初始化变量。TF 2 中没有变量范围。因此,保持所有变量在一个计数中的最佳方式是使用 Keras 层。
inputer = tf.keras.layers.InputLayer(input_shape=(2))denser1 = tf.keras.layers.Dense(4, activation='relu')denser2 = tf.keras.layers.Dense(1, activation='sigmoid')
然后我们可以定义一个简单的模型。我们可以仅仅通过调用函数来运行这个模型。在这里,数据进入具有 4 个隐藏单元的密集层,然后进入具有一个单元的最终层。
def model_1(data):x = inputer(data)
x = denser1(x)
print('After the first layer:', x)
out = denser2(x)
print('After the second layer:', out)return outprint(‘Model\’s output:’, model(data))...
After the first layer: tf.Tensor(
[[0.9548421 0\. 0\. 1.4861959 ]
[1.3276602 0.18780036 0.50857764 0\. ]
[0.45720425 0\. 0\. 2.5268495 ]], shape=(3, 4), dtype=float32)
After the second layer: tf.Tensor(
[[0.27915245]
[0.31461754]
[0.39550844]], shape=(3, 1), dtype=float32)
Model's output: tf.Tensor(
[[0.27915245]
[0.31461754]
[0.39550844]], shape=(3, 1), dtype=float32)
要从张量中获取 numpy 数组:
print('Model\'s output:', model_1(data).numpy())...
Model's output: [[0.27915245] [0.31461754] [0.39550844]]
然而,急切的执行可能会很慢。该图是动态计算的。也就是说,让我们看看它是如何在我们的模型中构建的。输入数据进入第一层,这是第一个节点。当添加第二个节点时,第一个节点的输出进入第二个节点,然后计算第二个节点的输出,以此类推。它允许我们打印模型的中间状态(就像我们在上面的例子中所做的那样),但是会使计算变慢。
静态图
幸运的是,我们仍然可以通过用@tf.function修饰模型来构建一个静态图。与动态图相反,静态图首先连接所有节点进行一个大的计算操作,然后执行它。因此,我们不能看到模型的中间状态,也不能动态地添加任何节点。
@tf.function
def model_2(data):
x = inputer(data)
x = denser1(x)
print('After the first layer:', x)
out = denser2(x)
print('After the second layer:', out)
return outprint('Model\'s output:', model_2(data))...
After the first layer: Tensor("dense_12/Relu:0", shape=(3, 4), dtype=float32)
After the second layer: Tensor("dense_13/Sigmoid:0", shape=(3, 1), dtype=float32)
Model's output: tf.Tensor(
[[0.27915245]
[0.31461754]
[0.39550844]], shape=(3, 1), dtype=float32)
第二个优点是静态图只构建一次,而动态图在每次模型调用后都要重新构建。当您重复使用同一个图形时,它会降低计算速度。例如,当您在培训期间重新计算批次损失时。
for i, d in enumerate(data):
print('batch:', i)
model_1(d[np.newaxis, :]) # eager modelfor i, d in enumerate(data):
print('batch:', i)
model_2(d[np.newaxis, :]) # static model...
batch: 0
After the first layer: tf.Tensor(
[[0.9548421 0\. 0\. 1.486196 ]], shape=(1, 4), dtype=float32)
After the second layer: tf.Tensor(
[[0.27915245]], shape=(1, 1), dtype=float32)batch: 1
After the first layer: tf.Tensor(
[[1.3276603 0.18780035 0.50857764 0\. ]], shape=(1, 4), dtype=float32)
After the second layer: tf.Tensor(
[[0.3146175]], shape=(1, 1), dtype=float32)batch: 2
After the first layer: tf.Tensor(
[[0.45720425 0\. 0\. 2.5268495 ]], shape=(1, 4), dtype=float32)
After the second layer: tf.Tensor(
[[0.39550844]], shape=(1, 1), dtype=float32)batch: 0
After the first layer: Tensor("dense_12/Relu:0", shape=(1, 4), dtype=float32)
After the second layer: Tensor("dense_13/Sigmoid:0", shape=(1, 1), dtype=float32)batch: 1batch: 2
内部打印只在图形构建期间调用,在第二种情况下,图形只构建一次,然后重用。对于大型数据集,时间上的差异可能是巨大的。
高级控制流程
AutoGraph 简化了 if/else 语句和 for/while 循环的使用。与 TF 1 相反,现在它们可以用 python 语法来编写。例如:
a = np.array([1, 2, 3], np.int32)@tf.function
def foo(a):
b = tf.TensorArray(tf.string, 4)
b = b.write(0, "test") for i in tf.range(3):
if a[i] == 2:
b = b.write(i, "fuzz")
elif a[i] == 3:
b = b.write(i, "buzz")return b.stack()...
tf.Tensor([b'test' b'fuzz' b'buzz' b''], shape=(4,), dtype=string)
现在数组的使用类似于 Java 中的数组。首先,用所需的数据类型和长度声明一个数组:
tf.TensorArray(data_type, length)
常见的数据类型有tf.int32, tf.float32, tf.string。要将数组b转换回张量,使用b.stack()。
TensorFlow 2.0 中的实用编码
关于数据集和 TFRecord 数据格式的所有信息

Photo by Fredy Jacob on Unsplash
摘要
在这篇博文中,我将展示如何在 TF 2 中使用tf.dataset。此外,我们从 numpy 数组创建一个数据集,并学习如何将图像和数组写入 TFRecord 文件或从 TF record 文件中读取图像和数组。
装置
要安装 TF 2.x,请访问这个页面。
要检查您的当前版本:
import tensorflow as tf
print(tf.__version__)
TF 2 中的数据集
向网络提供数据的最简单和最有效的方法是使用tf.dataset。这个职业在 TF 2 中经历了显著的变化。数据集本身现在是一个迭代器,可以用 for 循环进行迭代。
我们先做一个数据集。对于可以存储在内存中的少量数据,我们可以从 numpy 数组中创建一个数据集:
import numpy as np
np.random.seed(0)data = np.random.randn(256, 8, 8, 3)
dataset = tf.data.Dataset.from_tensor_slices(data)
print(dataset)...
<TensorSliceDataset shapes: (8, 8, 3), types: tf.float64>
按照惯例,第一个维度是训练样本的数量。迭代器可以产生任何批量。默认情况下,它会生成单个训练示例,即批量大小为 1。
for 循环在数据集的末尾停止。要多次迭代数据集,请使用.repeat()。
我们可以使用 Python 的枚举器或内置方法来枚举每个批处理。前者产生一个张量,推荐使用。
for i, batch in enumerate(dataset):
if i == 255 or i == 256:
print(i, batch.shape)...
255 (8, 8, 3)
...for i, batch in dataset.enumerate():
if i == 255 or i == 256:
print(i, batch.shape)
print(i.numpy(), batch.shape)...
tf.Tensor(255, shape=(), dtype=int64) (8, 8, 3) 255 (8, 8, 3)
...for i, batch in dataset.repeat(2).enumerate():
if i == 255 or i == 256:
print(i.numpy(), batch.shape)...
255 (8, 8, 3)
256 (8, 8, 3)
如果不需要整个数据集,可以从中提取所需数量的批次:
for batch in dataset.take(3):
print(batch.shape)...
(8, 8, 3)
(8, 8, 3)
(8, 8, 3)
要迭代大于 1 的批处理,必须指定批处理大小。
dataset = dataset.batch(16)for batch in dataset.take(3):
print(batch.shape)...
(16, 8, 8, 3)
(16, 8, 8, 3)
(16, 8, 8, 3)
我们也可以在将数据输入网络之前对其进行洗牌。这会用buffer_size元素填充一个缓冲区,然后从这个缓冲区中随机抽取元素,用新元素替换选中的元素。为了实现完美的混洗,缓冲区大小应该等于数据集的完整大小。buffer_size 5 有一个例子。在实践中,我们使用等于几个批量大小的缓冲区大小。
dataset = tf.data.Dataset.from_tensor_slices(np.arange(19))for batch in dataset.batch(5):
print(batch)...
tf.Tensor([0 1 2 3 4], shape=(5,), dtype=int64)
tf.Tensor([5 6 7 8 9], shape=(5,), dtype=int64)
tf.Tensor([10 11 12 13 14], shape=(5,), dtype=int64)
tf.Tensor([15 16 17 18], shape=(4,), dtype=int64)
...for batch in dataset.shuffle(5).batch(5):
print(batch)...
tf.Tensor([2 5 0 4 1], shape=(5,), dtype=int64)
tf.Tensor([ 6 9 3 12 10], shape=(5,), dtype=int64)
tf.Tensor([13 8 15 17 11], shape=(5,), dtype=int64)
tf.Tensor([18 16 14 7], shape=(4,), dtype=int64)
你可以看到,结果并不统一,但足够好。
但是,如果您以不同的顺序应用这些方法,将会得到意想不到的结果。它打乱了批次,而不是元素。
for batch in dataset.batch(5).shuffle(5):
print(batch)...
tf.Tensor([5 6 7 8 9], shape=(5,), dtype=int64)
tf.Tensor([15 16 17 18], shape=(4,), dtype=int64)
tf.Tensor([0 1 2 3 4], shape=(5,), dtype=int64)
tf.Tensor([10 11 12 13 14], shape=(5,), dtype=int64)
您也可以在使用.map()从数据集中读取数据的同时准备数据。函数顺序在这里是至关重要的。比如现在transform应用于每批。
def tranform(data):
mean = tf.reduce_mean(data)
return data - meanfor batch in dataset.shuffle(5).batch(5).map(tranform):
print(batch)...
tf.Tensor([ 2 3 -1 0 -2], shape=(5,), dtype=int64)
tf.Tensor([-2 -5 2 3 4], shape=(5,), dtype=int64)
tf.Tensor([-1 1 2 -5 3], shape=(5,), dtype=int64)
tf.Tensor([ 3 -3 7 -4], shape=(4,), dtype=int64)
读取和处理数据集通常非常耗时。为了让训练有一个稳定的数据流,从而让你的 GPU 保持忙碌,你可以预取几个批次。为此,您必须将.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)作为最后一个方法。通过使用autotune,您允许 TF 找到最佳的缓冲区大小。
dataset.shuffle(5).batch(5).prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
为图像制作 TFRecord 文件
只有当数组相当小并且可以存储在内存中时,才可以从 numpy 数组生成数据集。对于更大的数据集,比如图像,我建议使用 TF 自制的数据格式tfrecord。
注:我尝试过使用h5,但是支持微乎其微,数据读取较慢。因此,我们现在坚持使用tfrecord。
假设您希望将一个图像和一个包含动作的数组存储为一条记录:一个训练示例。
imgs = (np.random.randn(256, 8, 8, 3) * 255).astype(np.uint8)
acts = np.random.randn(256, 4).astype(np.float32)
首先,我们必须定义一个助手函数:
def _bytes_feature(value):
if isinstance(value, type(tf.constant(0))):
value = value.numpy()return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
从图像和动作中生成一个字节串。
def serialize_example(image, action):image = tf.image.encode_png(image)feature = {
'image': _bytes_feature(image),
'action': _bytes_feature(tf.io.serialize_tensor(action)),
}return tf.train.Example(features=tf.train.Features(feature=feature)).SerializeToString()
tf.train.Example是一个容易引起误解的记录名称。
现在,我们可以将所有示例写入一个文件:
with tf.io.TFRecordWriter('test.tfrecord') as writer:for xi, ai in zip(imgs, acts):
example = serialize_example(xi, ai)
writer.write(example)
读取 TFRecord 文件
要读取文件,我们必须以相反的顺序执行我们的操作。我们需要两个辅助函数:第一个用于将字节串解析为张量,第二个用于解码图像。
def parse_image_function(example_proto):
image_feature_description = {
'image': tf.io.FixedLenFeature([], tf.string),
'action': tf.io.FixedLenFeature([], tf.string),
}return tf.io.parse_single_example(example_proto, image_feature_description)def decode_image_function(record):
record['image'] = tf.cast(tf.image.decode_image(record['image']), tf.float32) / 255.record['action'] = tf.io.parse_tensor(record['action'], out_type=tf.float32)return record['image'], record['action']
现在我们可以从文件中读取。
dataset = tf.data.TFRecordDataset('test.tfrecord')dataset = dataset.map(parse_image_function, num_parallel_calls=tf.data.experimental.AUTOTUNE)dataset = dataset.map(decode_image_function, num_parallel_calls=tf.data.experimental.AUTOTUNE)for img, act in dataset.batch(16).take(3):
print(img.shape, act.shape)...
(16, 8, 8, 3) (16, 4)
(16, 8, 8, 3) (16, 4)
(16, 8, 8, 3) (16, 4)
此外,如果需要的话,您可以应用任何想要的混排或映射。
注:我试过用h5但是它的支持非常有限,读取速度也比较慢。因此,我们现在坚持使用tfrecord。
ML 中的实际数据问题
在构建第一个模型之前需要考虑的六个问题

Photo by Jacek Dylag on Unsplash
想象一下:我们刚刚为我们公司或客户的机器学习提出了一个巧妙的用例。当我们把这个想法带给我们的团队时,他们立刻被激起了兴趣;我们的想法不仅简单易懂,而且有清晰的商业案例和解决用户生活中反复出现的痛点的机会。我们的执行利益相关者也同样热情,并立即给予我们开发小规模试点所需的资金。在我们知道之前,我们收到了其他团队的经理和数据科学家的 ping 和电子邮件,他们听说了我们正在做的事情,并想知道他们如何才能参与进来。“很明显,我们已经有了一个赢家,”你听到有人说,“如果我们能找到数据就好了”。
这就是玫瑰色眼镜脱落的地方。访问干净、可靠的数据始终是机器学习工作流程中最困难的阶段。许多人惊讶地发现,获取、探索和转换数据会占用我们试点项目总开发时间的 70%以上,有些人很快就在这个过程中寻找捷径。正如我们很快会看到的,我们不能在这一关键步骤上有所懈怠:我们在这一阶段寻求识别的问题极其微妙,难以识别,如果不解决就会停止。
在我们做其他事情之前,我们必须找到数据存储在哪里,以及谁可以为我们提供访问权限。这一步通常比听起来要难。大型企业将把结构良好的数据放在十几个不同的筒仓中(每个筒仓都有自己的所有者),而初创公司和较小的公司缺乏时间和资金来投资便利的数据库基础设施。如果幸运的话,我们将能够与日常用户一起工作,在几天内绘制出我们需要的系统,并获得访问权限。如果没有,我们将在下一周跟踪数据所有者并填写访问请求,然后才能最终访问适当的后端。
在我们将所需的各种资源连接到我们的开发环境之后,真正的工作就开始了。下一步,通常被称为数据争论或数据清理,是探索、连接和转换我们的数据的迭代过程,以确保它既准备好建模又代表现实。这个过程远不如将预测模型整合到我们的 ML 管道中那么迷人,但它无疑是机器学习工作流程中最重要的阶段。正如亚马逊最近对自动招聘的尝试所示,任何基于有偏见的数据训练的模型都会产生有偏见的结果。在建模之前理清这些偏见对我们试点的成功至关重要。
我们在这个阶段寻找的线索很容易发现:错误的或缺失的值、重复的或标记错误的条目、意外的分布和极端的异常值在基本的汇总统计中都是可见的。然而,这些小问题中的一个或多个的存在可能预示着一个更深层次的问题,这个问题可能没有简单的解决方案:
- 抽样偏差:我们的训练数据排除了特定的子群,或者通常不代表总体人口,所以我们的模型不能很好地推广到现实世界。上面提到的亚马逊试点就是这个问题的一个很好的例子。如果你用不成比例的男性数据来训练一个招聘模型,就会错误地认为男性比女性更好。如果我们有足够的数据,我们可以使用重采样来纠正一些偏差,但如果偏差太严重,这个问题就不会发生。
- 一个接一个的错误:我们的一个上游循环、连接或映射没有按照我们预期的方式运行,因此,我们的一个变量不再意味着我们认为它意味着什么。这个问题很难诊断,并且经常不被发现,直到有人试图解释错误的模型。如果错误发生在数据收集或 ETL 阶段,那么我们可能必须让数据所有者参与进来,以找到解决方法。
- 非平稳性:特定特征的分布会随着时间而变化,所以我们必须考虑我们用来训练模型的时间范围。一个常见的例子是周销售额,当产品经历其生命周期的不同阶段时,它往往会随着时间而变化。如果我们能够从逻辑上解释这种不稳定性,我们也许能够使用一些简单的统计技巧来模拟我们的数据。然而,如果我们不理解变化背后的原因,我们可能会在数据集中遗漏重要的环境变量。
- 异方差:我们的一个特征的方差随着我们目标变量的变化而变化,表明我们训练数据中的测量误差或亚群体差异。与非平稳性类似,异方差的存在可能表明我们需要在建模之前找到或生成新的特征。
- 偏斜变量:我们的变量表现出强烈的左倾或右倾偏斜,如果我们不在训练前转换数据,这可能会导致我们的模型过度拟合异常值。解决这个问题的常见转换包括标准化、规范化或对数转换;我们必须试验几种不同的缩放方法,以找到最适合我们的数据的方法。
- 编码不当:我们对变量进行编码或索引的方式导致我们的模型做出不代表现实的假设。例如,单独使用线性标度来表示每个月,并不能准确地捕捉时间的循环性质(十二月和一月是相邻的,而不是相隔 12–1 = 11 个月)。如果我们坚持使用基于树的算法(特别是 LightGBM,它在处理类别方面非常出色),使用索引编码可能没什么大不了的,但是如果我们不生成新的特性来捕捉这些关系,其他技术就会受到影响。
这些问题没有正确的答案,没有灵丹妙药;每种方法都有六种,每种方法都需要不同的权衡。为了选择最好的方法,我们需要不断地产生关于数据来源的假设,使用可视化和统计来测试这些假设,并与数据的所有者一起验证我们的假设。这个过程既乏味又耗时,但它意味着在我们的构建过程中更多的是一个迭代过程:我们会在前面发现一些问题,但许多问题直到我们开始解释我们的模型之后才会被发现。最重要的是,我们尽可能长时间地处于探索模式,并留出足够的时间来测试和解决这些问题,而不会错过最后期限。
有一点是肯定的:当我们的用户和管理人员期望根据我们的工作做出决策时,我们不能忽略这一分析,并期望交付一个成功的试点。探索和理解我们的数据的重要性也是我看淡所有汽车炒作的原因;今天的产品远远不够智能,无法复制在野外捕捉这些问题所需的功能体验和批判性思维。亚马逊的人力资源问题相对较小,因为他们发现得早,但当信息不太灵通的公司将其有偏见的 AutoML 模型投入生产时会发生什么?在接下来的几年里,我们将有无数的例子可以探索。
数据科学实用调试
调试机器学习系统的初级读本。
付费墙是否困扰着你?点击 这里 可以绕过去。
序言
在写这个话题之前,我在谷歌上快速搜索了一下,看看有多少已经被覆盖,并很快观察到一个我在这个领域越来越多地看到的现象——数据科学=建模,充其量是建模+数据处理。开一个 MOOC,他们谈论不同的模型和架构,去训练营,他们会让你写代码来适应和训练机器学习模型。虽然我理解 MOOCs 和 bootcamps 为什么走这条路(因为这些机器学习模型是数据科学的核心),但他们确实让机器学习模型看起来是数据科学中唯一的东西。但是实践中的数据科学完全不同。没有经过整理的数据集或格式清晰的笔记本,只有大量杂乱无章、不干净的数据和复杂的流程。为了有效地实践数据科学,你需要成为一名优秀的程序员。句号。
事实:没有人能写出完美的代码

即使是约翰·斯基特——程序设计界的查克·诺里斯也不行。这是编写代码时不可避免且令人恼火的一部分。没有什么比一条冗长而晦涩的错误消息更能降低编写一个特别复杂的任务的难度了。因此,能够追踪和处理错误是数据科学家工具箱中的一项基本技能。
机器学习与传统编程略有不同。传统上,编程试图解决一个问题,并且会有一组步骤/逻辑必须被翻译成代码,瞧!它工作了。但是机器学习在这方面有点令人沮丧。你可以把每件事都做对,但还是达不到预期的结果。正因为如此,调试机器学习程序更加困难。

机器学习代码中的错误可能来自两个截然不同的来源-
- 纯编程/逻辑问题(包括数据处理逻辑)
- 问题和模型公式
正如 Andrej Karpathy 暗示的,大多数错误通常是因为数字 1。因此,在你深入研究数学之前,做一个基本的健全性检查,看看你是否正确地编写了代码。这不取决于你编程有多好,也不取决于你经验有多丰富。Andrej Karpathy 是斯坦福大学的计算机科学博士,曾在 OpenAI 工作过,现在是特斯拉的人工智能高级总监。这是一个令人印象深刻的侧写,不是吗?他写了许多关于很多话题的博客,这表明他对这些话题有着深刻的理解。他甚至在推特上指出了训练神经网络的常见陷阱。猜猜这些指针是什么?——“你忘了。zero_grad()"
总而言之,即使你是 Andrej Karpathy,你也会犯错误,需要知道如何调试才能有效。因此,在这篇博文的其余部分,我将涵盖如何调试机器学习模型的要点,既从编程方面,也从机器学习方面。在进入细节之前,我想先讨论几个【心态】,它们会对你大有帮助。
心态
1.开始称它们为错误,而不是错误
在 Dijkstra 的经典论文“关于真正教授计算科学的残酷性”中,他认为将 bug 称为错误是有道理的,因为它将错误直接归咎于程序员,而不是在我们睡觉时爬起来删除一行或一个缩进的小精灵。词汇的这种变化对您如何处理代码中的问题有着深远的影响。在程序“几乎正确”之前,英雄程序员会发现并修复一些无法解释的错误。但是一旦你开始称它们为错误,程序就是错误的,而犯错误的程序员应该在这个过程中找到一种方法来纠正它和他自己。它从一部“我对抗世界”的动作片变成了一部深思熟虑的自省剧,讲述了一个男人/女人在电影过程中给他们的性格带来了深刻的变化。
2.知道你是个白痴
在他的一次演讲中,杰瑞米·霍华德提到了一些深刻的东西,它直接来自于心态 1。
我们都有这个习惯…当我们发现一个 bug 时,我们会说,“呃!我是傻逼!”所以,不要等着去发现。我已经知道我是个白痴。当你调试的时候,让我们从这个假设开始。
杰瑞米·霍华德
如果我们代码中的某些东西不工作,这意味着我们认为以特定的方式工作,而不是以那种方式工作。所以你必须从认识到你在某些事情上是错误的开始,这对一些人来说可能很难。有经验的程序员会从头开始,检查每一步。但是新程序员倾向于高估他们对特定代码块的信心,并从检查中丢弃它们。因此,他们将飞过他们感到非常自信的代码块,并通过声明“我认为问题是 x ”来聚焦于一个代码块。但是调试从来不是“我认为问题是..”而是从“我不知道问题出在哪里,因为我犯了一个错误”开始。
编程调试
1.不要害怕阅读追溯

有多少次你看到一个像上面这样的追溯,并认为——通读所有这些?见鬼不要。。但是,您将放弃的是信息的金矿和调试的良好起点。
我知道回溯看起来很吓人(为了达到效果,我故意在黑屏上贴了一个非格式化的多线程回溯),但其实不必如此。如果你使用的是 jupyter 笔记本或任何有价值的 IDE,回溯将被格式化,并且更容易阅读。如果我告诉你,你不需要阅读整个追溯,仍然可以从中获得有用的信息,那会怎么样呢?

https://realpython.com/python-traceback/
让我们稍微分解一下回溯。 Chad Hansen @ RealPython 在解释追溯方面做得很好,我建议你看一下,跳过下一段(这是原文的摘录)。对于那些只想要摘要的人,请继续阅读。
阅读回溯的首要规则是从下到上。
蓝框: 回溯的最后一行是错误信息行。这是你的第一个线索,它是什么样的错误。Python 有许多类型内置错误,这些错误会导致不同类型的问题。
绿色框: 异常名称后是错误信息。请非常非常密切地关注这一部分,因为这实际上是为什么它不工作的答案。通常情况下,开发人员试图放入有意义的信息,这些信息可以直观地引导您找到问题所在。
黄框: 再往上追溯是从下往上移动的各种函数调用,从最近到最近。
红色下划线: 这些调用的第二行包含实际执行的代码。
黄色方框和红色下划线是定位错误的关键。但是这个特别的方面让很多人害怕,因为这可能会很长;尤其是如果你使用的是 Sci-kit Learn 或者 Tensorflow 这样的库。但是这一部分的伟大之处在于它给出了发生错误的文件名。现在,首先,您应该将重点放在由脚本引起的错误部分,并将来自库中的错误视为您所犯错误的结果(也有例外,在库中确实存在问题,但是您的分析起点应该始终是自省)。
总结一下:
- 查看错误类型,了解抛出的是哪种错误。在谷歌上搜索一下,看看其他人是否也面临同样的错误。
- 阅读错误消息,了解可能出现了什么问题。
- 在您的脚本中找到引发错误的那一行,并仔细考虑引发这个特定错误的可能性。

2.二进位检索
如果代码中的错误抛出一个异常和一个有用的回溯,您应该认为自己是幸运的。但很多时候,错误并不是那么表面的。它要么不抛出错误,要么以完全不同的形式出现,并引发一个无法解释的异常。这种错误是最难调试的。
这些错误通常在数据科学项目的数据处理管道中表现出来。而且,由于我们没有错误发生的确切位置或行号,我们需要找到那个位置。现在,你如何在一大段代码中找到错误呢?从头到尾阅读是一种可靠的方式,但这是一个非常低效的过程。
计算机工程师会立刻告诉你,当你从线性搜索到二分搜索法,时间复杂度从 O(N)到 O(log N)。具有讽刺意味的是,这些对这一点了如指掌的工程师在调试时会求助于线性搜索。

这个过程非常简单。让我们以一个简单的 bug 为例。pandas 数据帧中一列的 dtype 在代码中的某个地方发生了变化,这打乱了数据处理流程。
- 设计一个简单的测试用例来检查问题。对于我们的例子,它可以简单到检查特定列的 dtype。
- 将代码一分为二。您可以注释掉一半代码,或者在中途放一个记录器来检查 dtype。
- 找进攻劈。如果第一个块末尾的 dtype 是您所期望的,那么第二个块就是罪魁祸首,反之亦然。
- 选择攻击性分割,重复 2-4 次,直到你把注意力集中在出错的那条线上。
3.扔掉笔记本
现在我说了一句亵渎神明的话,引起了你的注意,让我澄清一下。Jupyter Notebooks 是一款出色的工具,我一直在使用它,但用于快速原型制作。一旦你在编码过程中取得了实质性的进展,并且你有了一个很长的代码块,它就会变得难以处理。
想象一下做我之前在笔记本上描述的二分搜索法。如果你的代码被分割成不同的单元,你将会一个单元接一个单元地执行,折磨你的键盘。如果你的代码被合并到一个单元格中,那么你将会拆分单元格,创建新的单元格来检查,等等。这是一个令人头疼的问题。
调试数据科学和调试代码之间有一条细微的界限,这两个过程需要稍微不同的工具。如果你正在解决你的模型中的一个难题,你必须重复很多并尝试不同的东西,没有比 Jupyter 笔记本更好的工具了。但是,如果你有一个隐藏的错误,比如数据管道,或者功能工程管道,抛弃 Jupyter 笔记本,使用你选择的 IDE。我的首选工具是 VSCode 和 Spyder,这取决于具体的用例。
当我告诉许多数据科学家“单步调试”代码并进行调试时,他们一脸茫然。当我告诉他们这个叫做调试器的神奇的东西时,我注意到他们睁大了眼睛,它可以让你放置断点,观察某些变量,让你一行一行地通过代码,甚至可以放置一个条件断点。几乎所有的 ide 中都有这个调试器,但是在高层次上功能是相同的。如果你是那些数据科学家中的一员,我强烈建议你立即检查调试器,无论是在 VSCode ,Spyder,PyCharm,还是任何其他支持它的 IDE 中。

4.隔离误差源
在机器学习中,错误可能是由于编程逻辑或数学造成的,能够快速诊断和隔离错误源非常重要,这样您就不会浪费一整天来跟踪错误。
我举个例子来说明我的观点。我正在训练一个用于时间序列预测的 LSTM,并对架构进行修补以适应一些外部变量。很快,我明白了网络没有正确训练,损失曲线看起来不对。这里可能有很多地方出错了 LSTMs 的数据处理(这总是数据泄露的陷阱),我为输入矩阵做的标准化工作,我对 LSTM 架构做的修补工作。因此,我只是创建了一个虚拟的时间序列作为外生变量的函数,关闭了标准化,并运行我的数据处理。当我检查格式化的数组时,一切正常。所以我排除了数据处理。然后,我通过 LSTM 运行数据,观察培训损失的下降。完美!修补也不是问题。现在,我已经将错误的来源隔离到标准化部分,我在标准化函数中放置了几个断点,逐步执行该函数,并找出了我犯的愚蠢错误。

2.数据科学调试
1.如果结果好得令人难以置信,那么它们很可能是真的。
你是否曾经运行一个模型,并在一个难题上达到 90%的准确率,并且你为自己没有付出太多努力就获得了如此出色的结果而感到欢欣鼓舞?但是,你脑中有个小小的声音在唠叨你,告诉你这是不可能的。我在这里给那个声音一个放大器。听听吧。通常情况下,那个声音是对的。
你一定是无意中发现了数据科学中被称为“数据泄露”的怪物。大家可能都知道,当你用来训练机器学习算法的数据碰巧含有你试图预测的信息时,数据泄漏就会导致夸大的性能。在你的模型中,有很多方式可能会发生数据泄漏,我在下面列出了其中的一些,以帮助你启动你的思路
- 当你对分类变量使用目标均值编码时,你使用整个数据集对它们进行编码,而不仅仅是训练。
- 当数据本质上是时态的,并且您最终使用未来的信息来拟合模型时。例如使用 K 倍交叉验证。
- 当数据中存在重复项,并且这些重复项在训练和验证中被拆分时。
- 当您对整个数据集运行 PCA 并在您的模型中使用提取的组件时。
2.不平衡数据集
如果你正在处理一个高度不平衡的分类问题,它有自己的 pitalls。
我看到人们最常犯的一个错误是度量标准,尤其是当存在高度的阶级不平衡时。我经历过太多的面试,面试者不停地谈论他如何在 xyz 欺诈检测类型的问题中获得 90%的准确率。即使经过相当多的刺激和线索,这些人中的大部分人似乎并没有将准确性悖论联系起来。当我提醒他们精确或回忆时,我会得到教科书上的答案或公式。在这一点上,我放弃任何进一步的讨论,例如 F1 的分数偏向多数派等等。
过采样或欠采样是处理不平衡类的训练的方法之一(这并不理想,因为您正在改变问题的固有分布)。但问题是,如果在进行验证分割之前进行过采样或欠采样,并在欠采样或过采样数据集上测量模型的性能,那么您将走上一条通向模型失败的黑暗而多风的道路。
3.误差分析
您的模型中的错误将准确地告诉您需要让您的模型表现得更好的故事。从你的结果中提取这个故事的过程就是错误分析。当我进行误差分析时,它包括两个部分——消融研究,以确定系统每个组件的误差/收益,如预处理、降维、建模等。,并对结果和其中的错误进行分析,就像吴恩达告诉我们做的。
通过打开或关闭机器学习管道的组件,或者用基本事实替换它们,您可以快速识别它们的影响。这也是识别有泄漏的特征的非常快速的方法。如果您发现模型过于依赖一个单一的特性,而放弃该特性会极大地影响性能,那么这应该是开始调查该变量的数据泄漏的一个线索。要回顾特性归属的最新方法,请查看我之前关于可解释性的博客系列。
错误分析的另一部分是吴恩达在 Coursera 的课程中教授的内容。这都是关于手动检查错误分类的案例,试图确定一个模式,并最终设计一个行动计划来减轻这种模式。在表格数据的情况下,通过尝试在不同的分类拆分上检查性能拆分,可以更有效地对该过程进行分类。你的错误集中在特定类型的样本上吗?也许它需要一个新的功能来帮助学习它们。
对于回归问题,您可以查看不同分类拆分处的误差,或者绘制预测-基础事实散点图或残差图,以进一步分析误差的来源。
让我们简短地浏览一下优秀的库 Yellowbrick 提供的不同可视化。
分类器可视化
1.混淆矩阵

https://www.scikit-yb.org/en/latest/
我们使用混淆矩阵来理解哪些类是最容易混淆的,而不是因为我们自己也被混淆了。因此,我们确定了最容易混淆的类别,并对它们进行分析以找出原因。例如,对于计算机视觉任务,我们查看这些类的样本,并检查人类是否能够容易地识别两者之间的差异。
2.ROC AUC 曲线

https://www.scikit-yb.org/en/latest/
曲线下的接收器操作特征/面积允许用户可视化分类器的灵敏度和特异性之间的权衡。
3.类别预测误差

https://www.scikit-yb.org/en/latest/
该图是对传统混淆矩阵的扭曲,可用于相同的目的。不知道为什么,我总觉得这个图比混乱矩阵直观多了。
2.回归可视化
1.残差图

https://www.scikit-yb.org/en/latest/
残差图显示了垂直轴上的残差和水平轴上的因变量之间的差异,允许您检测目标中可能受或多或少误差影响的区域。该图还显示了预测值与我们预测该值的能力之间的关系。它还显示了训练残差与测试残差的不同之处。分析这个图会给你很多关于为什么一个模型失败了,或者它失败在哪里的见解。
2.预测误差图

https://www.scikit-yb.org/en/latest/
预测误差图显示了数据集的实际目标与模型生成的预测值的对比。通过与 45 度线进行比较,我们可以使用此图诊断回归模型,其中预测与模型完全匹配。
这绝不是可能出错的事情的详尽清单,也不意味着会出错。这篇文章的目的是让你的思维过程开始,更有效地组织你的调试工作,并建立一个有利于调试的心态。如果我为一个阅读这篇文章的人做到了以上任何一点,我认为这种努力是值得的。
前去调试!

原载于 2019 年 12 月 14 日http://deep-and-shallow.com。
非参数功效分析、p 值、置信区间和偏倚检查指南
所有数据科学家都应该知道的实际实验基础
代码设置
R for Data Science 是一个免费的、令人惊叹的读物,学习如何使用本文中使用的 tidyverse 代码。
# purrr in parallel
library(furrr)
# Read R for Data Science if you haven't
# to learn about the tidyverse
library(tidyverse)
# process in parallel
plan(multiprocess)
为什么这篇文章教给你的东西很重要
随机实验是因果推断的黄金标准:如果你想得到一个对治疗效果的无偏(你的估计方法的平均值是真实值)估计,随机实验是最好的方法。
但是实验是昂贵的。因此,如果我们要投资做一个实验,我们需要知道我们将能够在实验中收集足够的数据,以便有很大的机会来测量一个效应(如果它存在的话)。一个能力分析会告诉我们需要多少数据。
我们还想知道我们测量的效果不仅仅是偶然的结果。只有在治疗没有效果的情况下(p 值),我们测量到的效果与我们在实验中测量到的效果一样极端的可能性足够低时,我们才会宣布我们的效果具有统计显著性。
此外,我们希望能够通过提供一个置信区间来提供测量效果的精确程度的一些感觉,置信区间是一个效果值的范围,如果以相同的方式对以相同方式重复的许多实验进行相同的计算,将包括大部分时间的真实效果(例如 95%)。
最后,我们想知道我们的实验设计是否有缺陷,以至于我们没有办法以无偏见的方式来衡量效果。
这篇文章将使你能够进行功耗分析,计算 p 值,获得置信区间,并检查设计中的偏差,而无需做出任何假设(非参数化)。
示例实验
假设我是一个狗足球营销人员。在以前的广告中,我用下面左边的图片展示了狗足球是多么有趣。我假设,如果我添加一个类似“泰迪批准”的有趣说明,看到广告的更大一部分人(p)会购买一个足球(p 称为“转化率”)。

为了测试我的假设,每次人们将要看到我的一个广告时,我会随机给他们看两张图片中的一张:一张对照图片(没有标签)或一张治疗图片,上面有“泰迪批准”。然后,我会比较各组内的转化率(p_{control}对 p_{teddy_approved}),看看标签是否提高了转化率(转化率= #客户订购足球/ #客户看到广告)。
假设
****零假设是没有效果的假设:在广告中添加“Teddy Approved”不会增加转化率;
h _ 0:p _ { teddy _ approved }—p _ { control } = 0
我们用我们的替代假设来检验这一点,即标签会增加转化率。
h _ a:p _ { teddy _ approved }-p _ { control } > 0
我们通过计算假设零假设为真的情况下,我们将看到我们的实验性测试统计数据(看到标签的客户和没有看到标签的客户之间的转换率差异) p 值。如果这种可能性足够低(例如,< 5%),我们拒绝 h0,并且可以说该标签显著地(在统计意义上)增加了转化率。在我们的功效分析中,我们将确定真正的差异 p_{teddy_approved} — p_{control}需要有多大,或者我们需要收集多少数据才能有很高的几率(例如> 80%)观察到具有显著 p 值的测试统计(即高功效)。
这里的关键是我们需要能够计算在零假设下看到我们的测试统计的不同值的概率:我们需要知道我们的样本转换率的差异分布是什么样的\ hat { p } _{ teddy _ approved }-\ hat { p } _假定转换率的真实差异是 0:p _ { teddy _ approved }-p _ { control } = 0。
我们可以使用一些参数假设。例如,我们可以假设正态性并进行 t 检验。然而,在许多情况下,数据是高度倾斜的,这个假设不成立。如果我们能够非参数化地模拟转换率的差异(没有任何假设),并自己描绘出这种分布,那就更好了。为此,我们需要一些数据。
史料
让我们假设我们有没有贴标签的历史信息:我们有一组看过对照组图片的顾客的数据,以及他们是否购买了足球。如果零假设为真,则该历史数据的转换率的数据生成过程应该类似于实验期间的过程,因为添加标签对转换率没有影响。*也就是说,我们可以认为这个历史数据集类似于在零假设下实验期间收集的假设数据集的实现。
*请注意,我们假设时间不会影响转化率的可变性。如果我们对使用模拟进行功效分析不感兴趣(例如,我们只想计算置信区间/p 值或确认我们的设计没有偏差),我们可以对真实的实验数据应用随机化推断来消除这种假设。
让我们创建一个玩具历史数据集,假设客户之间的转换是独立的,并且在看到控制广告后购买的真实概率 p_{control}是0.01。
# function to make historical data
make_historical_data <- function(
# one row per customer
number_of_customers = 1000,
# each customer's chance of ordering
null_conversion_rate = .01,
# to make results replicable
seed = 22)
{
set.seed(seed)
return(tibble(
# each customer that saw an ad 1,2,3,...
customer_id = 1:number_of_customers,
# whether the customer bought a football or not
# bernoulli draw with specified prob
customer_purchased = rbinom(number_of_customers, 1, null_conversion_rate) #
))
}
# make the data
historical_data <- make_historical_data()
# take a look
# 1 row per customer
# some purchases
historical_data %>% head()

零点下的模拟
根据我们的历史数据集,有两种主要方法来模拟在零假设下观察到的组间转化率的差异:
- ****随机化推断——探索随机分配到对照组和治疗组有多少可变性
- 引导 —探索数据生成过程中的可变性
我们将重点关注随机化推断,但自举也将在本文末尾描述。
随机化推断
随机化推断是我识别数据可变性的首选方法。我第一次遇到它是在格伯和格林的野外实验中。这本书是一个严谨,简洁,写得很好的因果推理和实验方法的符号介绍。我强烈推荐。
随机化推断背后的想法是,不确定性自然产生于对治疗组的随机分配。这有点像回答这样一个问题:如果我们只是不同地分配我们的治疗组,效果会有多大?
模拟你的效果的步骤如下
- 准备一个数据集,其实验单元的数量与你在实验中的数量相同。您可以使用历史数据进行功效分析,或者使用实验结果的实际数据来获得 p 值和置信区间。
- 在你的数据集中随机分配一个假设的治疗。处理分配方法应遵循您将在实验中使用的相同分配方法(例如,如果您将聚类分配给处理,在这里也要这样做)。
- 使用假设处理分配计算检验统计量
- 多次重复上述步骤
- 从各种假设的随机分配中计算出的检验统计量的分布可以认为是零分布下的检验统计量的分布。
我们可以把它变成一个函数:
# run one simulation with
# an assignment function
# and an effect size calculating function
randomly_assign_and_find_effect_size <- function(
# data to use for randomization inference
# historical for power analyses
# experiment outcomes for p values and confidence intervals
dataset,
# function that adds a random treatment assignment to the dataset
random_assignment_function,
# function that calculates the effect size using the assignment
effect_size_function,
# dummy var to allow us to use furrr::future_map_dbl
dummy
){
return(
# with your dataset
dataset %>%
# assign hypothetical treatment randomly
# how you would in the experiment
random_assignment_function() %>%
# estimate the effect given the hypothetical treatment
effect_size_function()
)
}# run the above function many times
randomization_inference <- function(
dataset,
# number of simulations to run
number_of_simulations,
# function that adds a random treatment assignment to the dataset
random_assignment_function,
# function that calculates the effect size using the assignment
effect_size_function,
# allows us to make results reproducible
seed = 42
){
set.seed(seed)
# purrr::map
# applies a function (here the assign and find effect function)
# to each element of the supplied vector 1:number_of_simulations
# purrr::map_dbl
# is like map but returns a vector of doubles instead of a list
# furrr::future_map_dbl
# is like map_dbl, but uses parallel computing to speed things up
future_map_dbl(
# for each simulation
1:number_of_simulations,
# randomly assign and find effect size
randomly_assign_and_find_effect_size,
# using the provided data set
dataset = dataset,
# and provided assignment function
random_assignment_function = random_assignment_function,
# and provided effect size generating function
effect_size_function = effect_size_function
)
}
例子
在我们的示例实验中,每一个看到广告的客户都会以 50%的概率随机看到没有标签的图片或者有标签的图片。我们可以按照这种方法建立一个分配函数:
add_hypothetical_random_assignment <- function(dataset){
return(
dataset %>% dplyr::mutate(
# randomly assign each customer to treatment or control
# in_treatment is a new column with 1 if treated and 0 otherwise
in_treatment=rbinom(n = nrow(dataset),
size = 1,
prob = 0.5 # treatment with 50% prob
)
)
)
}
我们测量的效果是转化率的差异。因此,让我们创建一个获得效果大小的函数:它计算治疗组和对照组的平均转化率,并取其差值。
calculate_difference_in_conversion <- function(dataset){
return(
dataset %>%
# get conversion for control and treatment
dplyr::group_by(in_treatment) %>%
dplyr::summarise(conversion=mean(customer_purchased)) %>%
# get difference in conversion rates
tidyr::spread(in_treatment,conversion) %>%
dplyr::mutate(difference_in_conversion = `1` - `0`) %>%
dplyr::pull(difference_in_conversion)
)
}
现在,让我们使用随机化推断来可视化我们的测试统计的分布(转换率的差异)。图表底部的线条显示了每次随机化的效果大小。曲线的高度表示在 x 轴的那个区域中发现了多少模拟效果尺寸。
simulated_effect_sizes_ri <- randomization_inference(
dataset = historical_data,
number_of_simulations = 10000,
# use the assignment function we just defined
random_assignment_function = add_hypothetical_random_assignment,
# use the effect size function we just defined
effect_size_function = calculate_difference_in_conversion
)

功率分析
既然我们知道了如何得到模拟的效果大小,我们就可以进行功效分析了。功效分析背后的想法是,我们想知道我们需要多少数据才能有很高的几率获得具有统计意义的结果。我们想在实验开始前做这件事,这样我们就不会浪费时间和金钱去做一个不能测量任何东西的实验。
假设在零假设下,如果有 5%或更少的机会看到转化率的变化或更大的变化,我们将宣布转化率的变化是显著的。我们在上一节中描绘了零值下的效应大小分布,因此很容易找到这个临界值点(它只是我们模拟的效应大小的第 95 个百分位数)。
critical_point_ri_1000 <- simulated_effect_sizes_ri %>%
# get 95th percentile
quantile(.95)

假设我们希望至少有 80%的机会得到一个重要的结果。这里的技巧是假设真实的效应大小是不变的,这样我们就可以通过真实的效应大小来改变零点下的效应大小分布。我们移动分布,直到其下 80%的区域超出临界区域。我们需要移动的量被称为最小可测量效应(“MME”),因为它是转化率中最小的真实差异,我们将能够以至少 80%的概率获得统计上显著的结果。这种最小的可测量效应可以通过取临界值和在零值下模拟的效应大小分布的第 20 个百分位数之间的差值来计算。
(minimum_measurable_effect_ri_1000 <- critical_point_ri_1000 -
simulated_effect_sizes_ri %>% # 95th percentile
quantile(.2) # 20th percentile
)

考虑到我们的转换率从 0.01 开始,0.019 的转换率的最小可测量差异似乎是不合理的。然而,所有这些都是假设我们将在实验过程中收集 1000 个客户(就像我们在历史数据中收集的一样)。如果我们增加样本量,我们可以得到更多的能量。让我们看看,如果我们创建一个 100 倍大的历史数据集,最小可测量的影响是如何变化的。
# 100x larger historical data
historical_data_100x <-
make_historical_data(number_of_customers = 100000)# randomization inference to simulate the null
simulated_effect_sizes_ri_100x <- randomization_inference(
dataset = historical_data_100x,
number_of_simulations = 10000,
random_assignment_function = add_hypothetical_random_assignment,
effect_size_function = calculate_difference_in_conversion
)
# critical point
critical_point_ri_100000 <- simulated_effect_sizes_ri_100x %>%
quantile(.95)
# mme
minimum_measurable_effect_ri_100000 <- critical_point_ri_100000 -
simulated_effect_sizes_ri_100x %>%
quantile(.2)

随着更多的数据,最小可测量的影响减少到 0.0016,这仍然很大,但至少是一个可能的影响大小来衡量。
如果我们一起绘制两个模拟分布,精度的提高会更加明显:

实验数据的 p 值
使用模拟效应大小很容易计算 p 值。我们想知道在零的情况下,我们有多大的机会看到至少和我们测量的效应大小一样极端的效应大小。我们可以通过找出测量的效应大小在我们的模拟效应大小的分布中的百分位数来找到这个。我们可以继续使用我们使用历史数据模拟的效应大小,但是我们可以将随机化推断应用到实际的实验数据中,以便在实际的实验过程中更好地了解零效应下的效应大小的分布。
让我们假设真实的效果大小是 0.0005:标签将增加 0.0005 的转化率。然后,我们将制作一个数据集来表示我们的实验结果。
true_effect_size <- 0.0005
experiment_data <- make_historical_data(100000, seed = 2) %>%
add_hypothetical_random_assignment() %>%
# average conversion is
# base conversion = 0.01
# + true_effect_size if in treatment
mutate(customer_purchased = rbinom(100000, 1, prob = .01 + in_treatment * true_effect_size))# difference in conversion rates in the experimental data
(
experiment_measured_effect <-
calculate_difference_in_conversion(experiment_data)
)
[1] 0.001496594
我们在实验中发现转化率有 0.0015 的差异。现在,为了获得效应大小的 p 值,我们将
- 应用随机化推断,利用实验数据追踪出在零效应下效应大小的分布
- 找出我们测量的效应大小的分布的百分位数
- 计算 1 减去百分位数= p 值
# trace out the null
simulated_effect_sizes_ri_experiment <- randomization_inference(
dataset = experiment_data,
number_of_simulations = 10000,
random_assignment_function = add_hypothetical_random_assignment,
effect_size_function = calculate_difference_in_conversion
)
# what percentile in the simulated effects distribution
# is the experiment measured effect
# ecdf is the "empirical cumulative distribution function"
# we are finding where in this function the experiment measured effect lies
percentile <-
ecdf(simulated_effect_sizes_ri_experiment)(experiment_measured_effect)
(p_value <- 1 - percentile)
[1] 0.0105
p 值为 0.01。我们发现该标签在 95%的水平上显著提高了转化率。p 值是测量效应右侧分布下的面积:

置信区间
只要你的实验设计是无偏的(见下一节),在零效应下模拟效应大小分布的置信区间是很容易的。只要改变分布,使其集中在实验测量的效应大小上。然后,95%置信区间的界限是该移位分布的 2.5 和 97.5 个百分点。
# shift the distribution of simulated effect sizes
# by the measured effect size
simulated_effect_sizes_ri_experiment_plus_measured_effect <-
simulated_effect_sizes_ri_experiment +
experiment_measured_effect(
confidence_interval_ri_experiment <-
quantile(
simulated_effect_sizes_ri_experiment_plus_measured_effect,
c(.025, .975) # 2.5th and 97.5th percentile
)
)
2.5% 97.5%
0.0002208507 0.0027817045
这个置信区间包含真实效应:0.0005。

检查偏差(重要!)
在我们的例子中,我们使用了一个非常简洁的实验设计:我们比较了同一时期的苹果和苹果组(因为客户被随机分配到他们),并计算了平均值的差异。在这种情况下,测得的转化率差异将是由添加的标签引起的转化率的真实差异的无偏估计:尽管我们测量的治疗效果是随机的,但平均而言,它将是真实的治疗效果:
e[\ hat { p }{ teddy _ approved }—\ hat { p }]= p _ { teddy _ approved }—p _ { control }
我们可以做一个直觉的检查,通过确认我们的模拟在无效效应大小下的平均值接近 0:在无效效应下 p _ { teddy _ approved }—p _ { control } = 0,所以如果我们有一个无偏的设计,hat { e }[\ hat { p }{ teddy _ approved }——\ hat { p }]=\frac{1}{n}\sum_{i=1}^{n}\hat{p}{ teddy _ approved } —\ hat { p } \大约为 0。
mean(simulated_effect_sizes_ri_100x)
[1] 7.434415e-07
鉴于平均值如此接近于 0,我们通过了这一检验。尽管这并不能证实设计是无偏的,但它给了我们更多的信心。
然而,许多实验并没有这样一个清晰的设计,测量的效应大小可能会有偏差。在这种情况下,你应该尽你所能来纠正设计,使其不偏不倚。但是,可能会出现这样的情况,即您运行了一个实验,但无法在实验后纠正偏差。在这种情况下,只要均匀地应用偏倚,您仍然可以使用相同的 p 值方法:随机化推断仍然表明实际分配测量的效应大小在其他假设分配下测量的效应大小的前 95%,即使所有测量的效应都有偏倚。然而,你需要调整你报告的效应大小和置信区间。这样做的一个快速和肮脏的方法是只报告测量的效应大小减去在零分布下模拟的效应大小的平均值。然后,用和上面一样的方法建立一个置信区间,但是以调整后的效果大小为中心。
例子
我们来做一个例子。假设我们只能向一个地理区域(例如,某个大都市区域)中的每个客户显示一种类型的广告。现在我们不能按客户随机分配治疗。我们可以随机将一半的地理区域分配给处理(添加标签)条件,另一半分配给控制(无标签)条件。然后,考虑两组之间的转换差异,这将使我们回到上面的干净设计。然而,考虑到高水平的聚类处理,这可能具有较低的精度。
为了更加精确,有人提出了一种设计,我们也查看前一时期的地理,以获得更多的“控制”数据。然后,我们将计算试验期内治疗的跨地区平均转化率与试验前期和试验期内随机分配到对照组的跨地区平均转化率之间的差异。为了简化起见,让我们假设所有地理周期都有相同数量的客户(这样我们可以很容易地取平均值)。
add_hypothetical_random_assignment_geo <- function(
dataset
){
# get the distinct geos
randomized_geos <- dataset %>%
dplyr::select(geo) %>%
dplyr::distinct() %>%
dplyr::pull(geo) %>%
# and randomize their order
sample()
# first half assigned to treatment
treated_geos <- randomized_geos[1:length(randomized_geos) %/% 2]
# add treatment assignment as a column to the data
return(
dataset %>%
dplyr::mutate(
in_treatment = dplyr::case_when(
# treatment col is 1 only if in treated geo
(geo %in% treated_geos) &
# and only during the second period
# (first period is all control)
(period==2) ~ 1,
T ~ 0
)
)
)
}get_effect_on_conversion_geos_biased <- function(
dataset
){
return(
# get mean of outcome for treatment and control period-groups
dataset %>%
dplyr::group_by(in_treatment) %>%
dplyr::summarise(conversion_rate=mean(observed_conversion_rate)) %>%
# get differnce between treatment and control period-groups
tidyr::spread(key=in_treatment,value=conversion_rate) %>%
dplyr::mutate(diff_in_conversion=`1` - `0`) %>%
dplyr::pull(diff_in_conversion)
)
}
这种设计是有缺陷的,因为实验期和前期之间的差异会意外地归因于治疗。例如,让我们假设在不进行治疗的情况下,转化率从前期到实验期自然会翻倍,看看会发生什么。
我们将有一个数据集,每个地理区域有两行(每个期间一行)。假设真实效果大小为 0.002。
set.seed(42)
true_effect_size <- .002geo_design_experiment_data <-
tibble(
# 2 periods
period = rep(1:2, each = 26),
# 26 geos a, b, c, ...
geo = rep(letters, 2)
) %>%
# add treatment indicator (1 for half of classes in period 2 else 0)
add_hypothetical_random_assignment_geo() %>%
# add conversion rates
mutate(
true_control_conversion_rate = case_when(
# random between .02 and .05 in first period
period == 1 ~ rep(seq(0.02, 0.05, length.out = 26), 2),
# doubled in second period naturally without treatment
T ~ rep(seq(0.02, 0.05, length.out = 26), 2) * 2
),
# add some noise to the true rate for the measured rate
observed_conversion_rate = true_control_conversion_rate +
rnorm(52, sd = .001),
# add the true effect size to the measured rate
# for treatment groups (treatment will == 1 for half of geos in period 2)
observed_conversion_rate = case_when(
in_treatment == 1 ~ observed_conversion_rate + true_effect_size,
T ~ observed_conversion_rate
)
)
# look at the first 6 rows for each period
# to see how preiod-geo data looks
geo_design_experiment_data %>%
group_by(period) %>%
slice(1:6)

然后,我们观察一个如下的实验结果。
(
measured_effect_size_geos_biased <- geo_design_experiment_data %>%
get_effect_on_conversion_geos_biased()
)
[1] 0.02418268
我们测得的转换差异是真实效果的 10 倍以上!
现在我们可以执行随机化推断来模拟空值下的效应大小。
simulated_effect_sizes_ri_geos_biased <- randomization_inference(
dataset = geo_design_experiment_data,
number_of_simulations = 10000,
random_assignment_function = add_hypothetical_random_assignment_geo,
effect_size_function = get_effect_on_conversion_geos_biased
)
如果我们有一个无偏的设计,在零值下的平均值应该是零。让我们对模拟值的平均值进行直觉检查。
(estimated_bias <- mean(simulated_effect_sizes_ri_geos_biased))
[1] 0.02392901
我们没有通过 gut 检查,因为平均值不接近 0。在这种情况下,我们会想看看是否有更好的、无偏见的方法来衡量效果(例如,添加周期固定效果或进行差异中的差异设计),但如果我们找不到更好的设计,我们至少会想调整我们报告的效果。
例如,不是报告 0.024183 作为效应大小,我们可以减去估计偏差(0.023929:零偏差下模拟效应的平均值)并报告 0.024183 - 0.023929 = 0.000254 作为效应大小。请注意,这并不一定会使您的调整估计无偏,但这是一个比您知道有偏差的结果更诚实的报告。
我们还希望调整我们的置信区间,将我们模拟的效应大小分布集中在这个调整后的效应大小(而不是测量的效应大小)的零值下,并得到 2.5%和 97.5%的百分位数。

摘要
在这篇文章中,我们已经讨论了非参数化的方法
- 模拟零分布下的效应大小:假设随机重新分配治疗,用假设分配多次计算效应大小。
- 做功效分析:最小可测量的效果是第 95 个百分位数减去第 20 个百分位数。
- 计算 p 值:找出在零值下的模拟效应大小比实验数据中测量的效应大小更极端的比例。
- 获得置信区间:将效应大小的分布集中在测量的效应大小的零值下,并获得置信界限的 2.5 和 97.5 个百分点。
- 检查并调整偏差:零值下模拟效应大小的平均值应为 0。如果不是,通过在零值下的模拟效应大小的平均值来调整测量的效应大小(并使用调整后的效应大小来定中心置信区间)。
在我的其他帖子中了解更多信息:
如果你想了解自举,请继续阅读。
号外:自举呢?
自举是模拟效果大小分布的另一种方法。
bootstrapping 不是使用数据中处理分配产生的自然变化,而是探索您抽取的特定人群样本的不确定性。这个想法是,假设您的数据遵循的最自然的分布就是在您的样本中实际观察到的分布。因此,只需从您的数据中取样并替换,即可创建一个模拟。
该过程工作如下:
- 您可以使用历史数据进行功效分析或计算实验测量效果的 p 值。您使用实验结果中的实际数据来建立置信区间。
- 从你的数据集中随机抽取替换数据(如果你的作业是聚类的,则在最高的聚类级别),直到你创建了一个与实验中相同大小的数据集(引导)。画得低一点会人为提高你的精度。
- 使用模拟数据集计算检验统计量
- 多次重复上述步骤
- 从具有替换的各种假设随机样本计算的检验统计量的分布可以被认为是当使用历史数据时在零分布下的检验统计量的分布,或者当使用实验数据时在真实效应大小下的检验统计量的分布。
- 使用历史数据时,功效分析的最小可测量效果仍然是第 95 个百分点减去第 20 个百分点。p 值仍然是 1 减去模拟效应大小分布中实际测量效应的百分位数。当用真实实验结果的替换来绘图时,可以使用自举效应大小的 2.5 和 97.5 个百分点来找到置信区间的界限。
make_bootstrap_and_find_effect_size <- function(
dataset,
cluster_variables_to_sample_with_replacement,
num_clusters_to_sample,
effect_size_function,
dummy # so we can use map
){
return(
# with your dataset
dataset %>%
# build a bootstrap
# first get the distinct sets of cluster variables
dplyr::select_at(cluster_variables_to_sample_with_replacement) %>%
dplyr::distinct() %>%
# sample these clusters with replacement until you reach
# the specified size
dplyr::sample_n(num_clusters_to_sample, replace=T) %>%
# attach back to the dataset to get the variables needed
# to estimate the effect size
dplyr::inner_join(dataset, by=cluster_variables_to_sample_with_replacement) %>%
# estimate the effect given the bootstrap
effect_size_function()
)
}bootstrapping <- function(
dataset,
number_of_simulations,
cluster_variables_to_sample_with_replacement,
num_clusters_to_sample,
effect_size_function,
seed=42
){
set.seed(seed)
future_map_dbl(
# for each simulation
1:number_of_simulations,
# randomly assign and find effect size
make_bootstrap_and_find_effect_size,
# using the provided data set
dataset = dataset,
# and the specified clusters
cluster_variables_to_sample_with_replacement = cluster_variables_to_sample_with_replacement,
# with the specified number of samples with replacement
num_clusters_to_sample=num_clusters_to_sample,
# and provided effect size generating function
effect_size_function = effect_size_function
)
}
例子
使用 bootstrapping,我们需要一个初始化的假设处理,这样当我们用替换进行采样时,处理也随之而来。
set.seed(42)
historical_data_w_assignment <- historical_data %>%
add_hypothetical_random_assignment()
然后,我们可以应用自举过程(注意,我们没有对数据进行聚类,所以我们只是使用每个单元进行采样)。
simulated_effect_sizes_bs <- bootstrapping(
dataset = historical_data_w_assignment,
number_of_simulations = 10000,
cluster_variables_to_sample_with_replacement = "customer_id",
# assume experiment data is the same size as input data
num_clusters_to_sample = nrow(historical_data_w_assignment),
effect_size_function = calculate_difference_in_conversion
)

随机化推理与自举
因果推理的巨人苏珊·艾希和圭多·安本斯“强调基于随机化的推理,而不是基于抽样的推理。”该链接是了解更多信息的绝佳途径。出于几个原因,我更喜欢随机推断而不是自举。
- 使用随机化推断更容易识别设计中的偏差。正如文章中所示,我们对实验数据进行了简单的 gut 检查,以确定我们的设计方法是否有偏差:检查使用实验数据的模拟效应大小在零下的均值是否为 0。使用 bootstrapping 进行这种检查要困难得多,因为如果对实验数据进行重新采样,它应该以测量的效果大小为中心,而不是 0。您可以使用历史数据,看看引导是否不等于 0,但通常实验期间的一些非线性时间差会导致偏差(这在历史数据中看不到)。
- 随机化推断会自动考虑样本中聚类的相关性。统计分析中一个非常常见的错误是没有考虑聚集的标准误差。如果我们在聚类级别(如上面的地理位置)分配治疗,然后在单元级别(如客户)执行分析,回归或 t 检验得出的默认标准误差不会考虑治疗分配中的相关性,您可能会高估实际的精确度。但是通过随机化推断,只要你在模拟实验中以同样的方式分配治疗,它就会自动解释这些聚集的标准误差。这也是我建议在聚类级别进行自举的一个重要原因:在单元级别对聚类分配处理进行重采样会产生不一致的方差估计。
- 随机化推断通常更快。Bootstrapping 需要计算量非常大的随机采样,每次模拟都要进行替换,而随机化推断只需要重新分配治疗。但是,如果重新分配治疗的成本很高,则随机化推断可能会较慢(例如,如果您将一半的产品类别暴露于治疗,然后计算每个客户被视为其应用治疗的产品的百分比,则您必须计算每个客户在每个新治疗分配中被视为治疗的产品的百分比,这将非常昂贵)。
用于分子机器学习的实用图形神经网络

Graph convolutions
直到最近,从业者将使用分子指纹(本质上是不同分子亚结构的一键编码)作为机器学习模型的输入。然而,该领域正开始朝着使用深度学习自动学习指纹本身(自动特征工程)的方向发展。这是一个实现简单神经指纹的演示。
1.化学指纹
化学指纹[1]长期以来一直是用于将化学结构表示为数字的表示方法,这些数字是机器学习模型的合适输入。简而言之,化学指纹表明是否存在化学特征或亚结构,如下所示:

Chemical fingerprints
在我的另一篇博文中提供了化学指纹的简要总结。
使用 RDkit [2]可以在 Python 中轻松计算指纹,如下所示:
How to make a fingerprint in Python
上面,我们计算了阿伐他汀的指纹,这种药物在 2003-2013 年间创造了超过 1000 亿美元的收入。

Atorvastatin/Lipitor
2.图形卷积
几年前的某个时候,人们开始意识到[3]我们可以计算一个可微指纹,而不是计算一个不可微指纹。然后,通过反向传播,我们不仅可以训练深度学习模型,还可以训练指纹生成函数本身。承诺将是学习更丰富的分子表示。

Circular vs Neural fingerprints [3]
其思想是将相邻节点的特征聚集在一起。这就是“图形卷积”这个名字的由来,因为我们对每个原子的相邻原子进行卷积(执行某种聚合)。
我们可以使用py torch-Geometric【4】实现上面提出的神经图指纹算法。以下实现允许批量训练(PyTorch-Geometric 将一批分子/图形建模为一个大的不连接图形)。
2a。原子特征和键连接(边指数)
我们将使用这些 atom 特性:
A)一个原子序数(它也决定原子类型)
b)连接在原子上的氢的数量。
这些是基本特性,但对我们的目的来说已经足够了。
最后,我们可以定义模型。
我们可以测试我们的模型,以确保它的工作:
3.通过反向传播学习指纹
[3]中一个有趣的发现是,随机初始化的神经指纹在模拟化学特征方面与传统指纹一样好,甚至更好。

Duvenaud et al. [3]
如果随机初始化的指纹和传统指纹一样好,那么如果我们通过反向传播来训练指纹,它们肯定会做得更好。
这是希望和承诺,但我们必须确保我们不要在小数据集中过度拟合噪声。让我们在真实数据集上尝试我们的神经指纹。我们将使用 DeepChem [7]的 BACE [6]回归数据集。
我们现在在我们的神经指纹之上建立一个小的 MLP(多层感知器)。我们只给它一个隐藏层(维度 100):
定义我们用于培训和验证的效用函数:
最后,我们的优化器和训练循环:
Training loop
注意,当我们训练模型时,我们训练神经指纹以及它上面的线性层。
4.展望未来,以及当前的最新技术
上述数据集包含约 1000 个分子和 1 个靶标。目前的数据集有 10 万多个分子和 100 多个目标。因此,大规模多任务监督预训练可用于获得非常丰富的表示[8]。
此外,人们开始使用无监督图预训练技术[9],跟随 NLP 中无监督预训练的成功之路[10]。

Pretaining graph neural networks on millions of molecules
5.结论
我们看到神经指纹可以用来代替传统指纹。随机初始化的神经指纹表现得和传统指纹一样好,甚至更好。如果有足够的数据并采取措施避免过度拟合,经过训练的神经指纹有可能形成更丰富的表示。
6.后续步骤
在 LinkedIn 上与我联系,让我知道你是否最终使用了神经指纹。
参考
[1] D 罗杰斯,m 哈恩。扩展连接指纹。化学信息与建模杂志,50(5):742–754,2010。
[2] G .兰德鲁姆。RDKit:开源化学信息学。【www.rdkit.org】T2。【于 2013 年 4 月 11 日获取】。
[3]迪韦瑙德、马克劳林、阿吉莱拉-伊帕拉吉雷、戈麦斯-邦巴雷里、希尔泽尔、阿斯普鲁-古齐克、亚当斯。用于学习分子指纹的图上卷积网络。 arXiv 预印本 arXiv:1509.09292,2015。
[4] M .菲,J. E .伦森。PyTorch 几何图形的快速图形表示学习。 arXiv 预印本 arXiv:1903.02428,2019。
[5]鲁梅尔哈特、辛顿、威廉斯。通过反向传播误差学习表征。性质 323,533–536(1986)doi:10.1038/323533 A0
[6] R. Vassar,D. M. Kovacs,Y. Riquang,P. C. Wong。健康和阿尔茨海默病中的β-分泌酶 BACE:调节、细胞生物学、功能和治疗潜力。 J 神经科学 。 2009 年 10 月 14 日;29(41): 12787–12794.
[7] DeepChem:药物发现和量子化学的深度学习模型。https://github . com/deep chem/deep chem,访问时间:2017–09–27。
[8] B. Ramsundar 等人,《药物发现的大规模多任务网络》。arXiv 预印本 : 1502.02072,2015
[9]胡、刘、戈麦斯、兹特尼克、梁、、莱斯科维奇。图形神经网络的预训练策略。 arXiv 预印本 : 1905.12265,2019
[10] J .德夫林、m .张、k .李、k .图塔诺瓦。BERT:用于语言理解的深度双向转换器的预训练。arXiv 预印本 : 1810.04805,2018
NLU 任务注意机制实用指南
测试动手策略以解决注意力问题,从而改进序列到序列模型
聊天机器人、虚拟助手、增强分析系统通常会接收用户的查询,例如“给我找一部史蒂文·斯皮尔伯格的动作片”。系统应该正确地检测意图“寻找电影”,同时用值“动作”填充槽“类型”,用值“史蒂文·斯皮尔伯格”填充槽“导演”。这是一个自然语言理解(NLU)任务,称为意图分类&槽填充。通常使用基于递归神经网络(RNN)的方法,以及利用具有序列到序列模型的编码器-解码器架构,可以获得最先进的性能。在本文中,我们展示了通过添加注意机制来进一步提高性能的实践策略。

Why kids just need your time and attention
2014 年,在 Sutskever 通过发现序列到序列模型而彻底改变了深度学习之后,正是 2015 年注意力机制的发明最终完成了这个想法,并打开了我们每天都享受的惊人的机器翻译的大门。注意力很重要,因为当与神经单词嵌入结合使用时,它已经被证明可以在机器翻译和其他自然语言处理任务中产生最先进的结果,并且是 BERT、GPT-2 等突破性算法的一个组成部分,这些算法正在创造 NLP 准确性的新纪录。因此,注意力是我们迄今为止最大努力的一部分,以创造真正的机器自然语言理解。如果成功的话,它将对社会和几乎每一种商业形式产生巨大的影响。
在本系列的第一篇文章中,我们介绍了机器翻译任务来激励序列到序列模型。我们还提供了一个实用的实现来解决槽填充任务。在本文中,我们将通过添加注意机制来改进我们的解决方案,在航班请求查询的自然语言理解方面实现最先进的准确性。我们将把注意力机制引入并编码到我们的序列对序列模型中。我们强烈建议在阅读本文之前,先阅读下面的第一篇文章。
如何预测客户询问背后的意图?Seq2Seq 型号说明。在 ATIS 数据集上演示的槽填充…
towardsdatascience.com](/natural-language-understanding-with-sequence-to-sequence-models-e87d41ad258b)
我们之前的序列到序列模型对于长句子有严重的问题。例如,该模型既不能理解“下午 4 点以后”的航班时间,也不能理解下面用户查询中的航空公司名称“continental”。
>>> input_query = "list all flights from oakland to dallas after 4 pm with continental"
>>> print(predict_slots(input_query, encoder_model, inf_model))O O O O B-fromloc.city_name O B-toloc.city_name O B-depart_date.month_name B-depart_date.day_number I-depart_date.day_number O
为什么该模型在短查询上做得很好,但在长查询上却产生无意义的预测?上下文向量被证明是序列到序列模型的瓶颈,它使得处理长句非常具有挑战性。直到 Bahdanau 等人,2014 和 Luong 等人,2015 提出解决方案。作者介绍了“注意力”,并展示了它如何通过根据需要关注输入序列的相关部分来提高机器翻译系统的质量。
但在深入研究注意力技术之前,我们先用一个童话来激发注意力机制。
需要关注的湖泊
巴福是个年轻人,他相信自己是宇宙中最美丽的人。每天他都会穿过附近的森林,到达附近的湖边。然后,他会俯身在湖面上,花很多时间欣赏自己的美丽,因为他的脸会倒映在水面上。一天,在做这件事的时候,他掉进了水里,淹死了。当湖水开始抱怨并泪流满面时,周围森林里的树木问湖水:“你为什么哭?”。湖水回答说:“我哭是因为巴福死了”。树回答说:“你不应该哭!我们,这些树,才是应该感到极大痛苦的人,因为每天,当巴福经过时,我们都希望,他能停下来片刻,敢看我们一眼,这样我们也能有机会欣赏他的美丽。你一直有他在你的海岸!”树继续说:“巴福从未考虑过我们。现在,他死了,我们永远不会赞美他的美丽。”湖水回答:“啊哈!巴福长得好看吗?我不知道。我哭是因为,每次他看着我的时候,我只有一次机会从他眼中看到自己的美丽。”
你在问,为什么我要给你讲这样一个故事,虽然这篇文章讲的是机器学习。嗯,你是对的。但是,让我们考虑一下动机。在我们的故事中,所有三个演员,巴福、湖和树都需要关注。注意力是你所需要的吗?社交网络真的全是关于注意力吗?那么自然语言理解吗?让我们深入主题。
注意机制的背景
和社交网络一样的是关注度;理解序列数据也可以是关于注意力的。我们在上一篇文章中构建的序列到序列模型过于强调单词之间的接近,但它也过于关注上游上下文而不是下游上下文。传统单词嵌入的另一个限制,如我们之前的实现中所使用的,是假设单词的含义在句子中相对稳定。通常情况不是这样。另一方面,注意两个用户查询,将它们转换成一个矩阵,其中一个查询的单词构成列,另一个查询的单词构成行。因此,注意力学习语境关系。这在自然语言理解中非常有用,因为它还允许调查单个查询的某些部分如何与其他部分相关,这被称为自我关注。如果我们能够建立语义依赖图的有向弧,说明一个拥挤的句子之间的关系,那么我们就能够理解句子的意思。

(source)
注意力模型对经典的序列对序列模型的改进如下。编码器不是传递编码阶段的最后一个隐藏状态,而是将所有隐藏状态传递给解码器。如果你需要回忆隐藏状态是如何工作的,请参考我们下面的文章,其中详细解释了循环网络。
递归神经网络解释。使用 FCNNs、CNN、RNNs 和嵌入对客户评论进行分类。
towardsdatascience.com](/sentiment-analysis-a-benchmark-903279cab44a) [## 基于 LSTM 的非洲语言分类
厌倦了德法数据集?看看 Yemba,脱颖而出。力学的 LSTM,GRU 解释和应用,与…
towardsdatascience.com](/lstm-based-african-language-classification-e4f644c0f29e)
每个编码器的隐藏状态与输入句子中的某个单词最相关。解码器给每个隐藏状态一个分数,然后将每个隐藏状态乘以其 softmaxed 分数。通过这种方式,解码器放大分数高的隐藏状态,淹没分数低的隐藏状态。它对每个单词都重复这一过程,因此在每个解码步骤中都很注意。

当序列到序列模型注意时,编码器将所有隐藏状态传递给解码器,而不是只传递编码阶段的最后一个隐藏状态。此外,该模型执行评分练习以产生注意向量,从而允许解码器在每个解码步骤中注意输入句子的特定部分。

source: Luong et al., 2015
注意填槽
提出了基于注意力的学习方法,并在意图分类和槽填充方面实现了最先进的性能(来源)。我们利用官方的 Tensorflow 2.0 神经机器翻译教程,在添加必要的开始和结束标记后,修改代码以处理来自 ATIS 数据集的用户查询作为输入序列,意图槽填充作为目标序列。下面我们定义超参数,以及一个 TensorFlow 数据集迭代器,它将用于批量处理数据以降低内存消耗。
注意,我们需要将编码器创建为 Python 类。
实现 Bahdanau 注意力,描述为下面的加法等式(4),其中 h_t 是编码器输出, h_s 是隐藏状态。

Bahdanau 附加注意在下面的类中计算。
现在我们可以如下实现解码器。编码器和解码器都具有嵌入层和具有 1024 个单元的 GRU 层。
稀疏分类交叉熵被用作损失函数。下面我们定义优化器和损失函数以及检查点。
使用教师强制技术、Tensorflow 内置函数的梯度计算和反向传播的定制训练循环允许我们调整嵌入权重、GRU 细胞权重和注意力权重。下面的函数对一批数据执行训练步骤。计算梯度并执行反向传播。使用 64 批次大小和 256 维嵌入。
正如我们在下图中看到的,损耗从初始值 0.51 下降到 18 个周期后的 0.0005。

推理的执行类似于训练循环,只是我们在这里没有使用教师强制。解码器在每个时间步长的输入是其先前的预测以及隐藏状态和编码器输出。当模型预测结束令牌(EOS)时,我们停止预测。我们存储每个时间步的注意力权重。
下面我们给出了准备查询的代码。我们还实现了一个函数,它将查询语句和词汇变量作为输入,创建一个向量张量,执行编码器和解码器进行推理。该函数还计算注意力权重。还有一个额外的功能可以在热图中绘制注意力权重。
对于一个玩具示例“列出所有飞往波士顿的航班”,槽填充质量是合理的。产生的注意力剧情挺有意思的。它显示了在预测用户查询时,模型关注输入句子的哪些部分。通过查看单词“to”和“Boston ”,去波士顿旅行的意图被正确地标记为目的地城市(B-toloc.city_name)。
translate("list all flights to boston")

当我们向系统提交一个稍微复杂一点的查询“我想乘坐 continental 下周上午 8 点的航班”时,我们很高兴地看到模型如何识别我们希望乘坐的航空公司“continental”,以及相对时间“下周”。
translate('I want to fly next week at 8 am with continental')

如果我们问“请告诉我机场 mco 到丹佛的最便宜的票价”,该模型有趣地使用“最便宜”和“票价”这两个词来识别目的地城市“丹佛”的相对成本(B-cost_relative label)意图。而这一切,都发生在“MCO”这个特定的机场。令人惊讶的是,用户查询中的单词“airport”并没有影响将“MCO”标记为机场的决定。从训练集来看,该模型显然也学习了机场代码。为了更好地概括,我们预计单词“airport”附近的 3 个字符的缩写会影响模型的决策。
translate('show me the cheapest fare in the airport mco to denver')

下一个查询很复杂:“我想乘坐西北航空公司的航班从底特律飞往华盛顿,大约上午 9 点出发”。这个模型完全正确!这里的注意机制很神奇。
translate('i want to fly from detroit to washington on northwest airlines and leave around 9 am')

最后,又一个成功预测的例子:“从克利夫兰到达拉斯的航班票价代码 h 是多少”。
translate('what what is fare code h on a flight from cleveland to dallas')

结论
在本文中,我们通过加入 Bahdanau 注意机制,改进了我们之前的自然语言理解的序列对序列模型。我们通过正确识别客户查询中我们应该注意的语义上有意义的信息,证明了注意力如何显著提高对长句的预测。
由于在 ATIS 数据集上实际实现了几个关于航班请求的模型,我们证明了注意力是多么重要,这种类型的机制如何在机器翻译和其他自然语言处理任务中产生最先进的结果。
在接下来的文章中,我们将把我们的结果与从 Transformer 嵌入的强大单词结合起来。
变压器 DIY 实用指南。经过实践验证的 PyTorch 代码,用于对 BERT 进行微调的意图分类。
towardsdatascience.com](/bert-for-dummies-step-by-step-tutorial-fb90890ffe03) [## 伯特解释道。迷失在翻译中。被变形金刚发现。
打造下一个聊天机器人?伯特,GPT-2:解决变压器模型的奥秘。
towardsdatascience.com](/lost-in-translation-found-by-transformer-46a16bf6418f)




浙公网安备 33010602011771号