熊猫小贴士:需要速度
像 PYTHON 专家一样进行数据分析
个人最喜欢的一句俏皮话
Source: Unsplash.
概观
我的上一篇文章展示了一个评估一组人脸对以确定这两个人是否有血缘关系的简单过程。几个片段像黑盒一样被轻轻带过。让我们来看看其中的一个片段,一个简单的单命令行程序:我最近学习的 Python 熊猫特性,现在经常使用。
DataFrame 包含面对 p1 和 p2 ( 图 1 )和特征(type dict )的列表,文件名作为关键字,面编码作为值。
**Fig. 1.df_pairlist.head(5): top 5 of >100,000 rows. Notice samples are repeated, but pairs are unique (i.e., member 1 of family 7 is the bother of member 9 of family 7, each with various face samples paired. Specifically, Nchoose*(2*) face pairs, where N is the total number of faces of the two relatives).
原来你在这里!在短短一行代码中,我们对数百万对进行了评分,而没有考虑内存或速度问题,同时为配对创建了一个分数列,作为同一个数据帧的一部分。
那又怎样?谁在乎呢。
在我们认识到这一点之前,让我们确保我们看到了问题。
首先,读取上面描述的数据结构。
Load lists of pairs and features for each face present in the list
请注意,有> 100,000 对,但有~10,000 个面:面以肯定、唯一和错误对的形式重复。
如何衡量所有配对的相似性(即每行计算一个分数)?
胸围#1。一个人可以循环,这是使用熊猫或 NumPy 的最不理想的方式之一。举个例子,
60.61587691307068 seconds
这太慢了。太慢了!!!
我们没有充分利用 pandas 的优势,甚至没有充分利用 NumPy 的矢量化能力。点击了解更多。
那么我们还能做什么呢?
胸围#2。我们可以将整个矩阵读入内存,但这不仅是多余的,因为通常有数百万对,每个元素是一个大小为 512(即 512 x 8 字节)的向量。因此, I/O 开销将会很慢,内存使用将不是最佳的,并且在大多数情况下,内存量不足。
**解决方案。**我们可以将字典对象的键映射到一个面孔列表。然后,对每一行应用一个度量(即面 A 和面 B ,或者列 A 和 B)。因此,每一行都有各自配对的分数。
因此,Python Pandas 提供了一个奇妙的解决方案。
A 1 liner: map features from a list of strings as keys to a dictionary pointing to the feature vector and following a functional programming paradigm for optimal performance.
这里我们指定 *axis=1,*意味着跨列应用(即,逐行)。
3.53662109375 seconds
**几乎不费吹灰之力就实现了近 18 倍的速度提升!**请注意,加速比只会随着样本量的增加而增加。这里,只有 FIW 的一个子集用于演示目的。
这个有效的技巧可以推广到许多基于 ML 的问题中遇到的情况。
动机(又名 Spiel)
为了使一篇博客的长度与前一篇文章提供的信息量相匹配,一些值得强调的内容被跳过或略过。拉胡尔·阿加瓦尔对 ROC 曲线做了一个聪明的评论,这让我恍然大悟——没有提供分析或定义,只有实现。因此,我相应地进行了更新(参见 ROC 上的部分)。然后,其他部分似乎值得额外的解释。这激发了我在这篇文章中最喜欢的一行代码上写这篇博客:一个狭窄的主题,一个快速的博客。
作为一个新的博客作者,我计划继续学习、适应和提高。此外,我写信给的是你。因此,你的意见、回答和问题对我的这个新爱好来说是很有价值的。请分享任何积极和消极的想法。提问。提出改进建议,甚至提出自己的想法!
总而言之,我希望你喜欢它。敬请关注更多博客!
相关博客
- Sofia Heisler ,优化熊猫代码速度的初学者指南
- Robbert van der Gugten , 高级熊猫:优化速度和内存
- 塞尔瓦拉塔姆·拉维南,了解使用熊猫时的优化需求
学习 Python 中两个最常用的熊猫数据结构
Pandas 是由 Wes McKinney 开发的高级数据操作工具。它建立在 Numpy 包之上,意味着 Pandas 需要 Numpy 来运行,它的关键数据结构被称为 DataFrame。数据帧允许您存储和操作观察行和变量列中的表格数据。
image by Edureka
如果你已经知道熊猫,系列和数据框,那么你可以跳过这个博客,可以从这里跟随它的第二部分。
熊猫装置
要安装熊猫,只需在您的终端上键入以下命令。您可以使用以下方法之一安装 pandas。
# conda
conda install pandas# or PyPI
pip install pandas
为什么用熊猫?
数据科学家使用熊猫有以下优势:
- 轻松处理缺失数据
- 一维数据结构使用系列,多维数据结构使用数据帧
- 它提供了一种有效的数据切片方法
- 它提供了一种灵活的方式来合并、连接或整形数据
系列
序列是一维(1-D)数据结构,可以有任何数据结构,如整数、浮点和字符串等。当你想执行任何种类的计算或返回一维数组时,这是很有用的。一个系列不能有多列。
在上面显示的代码片段中,数据可以是:-
- 标量值可以是整数、浮点和字符串等类型。
- 字典可以有键和值对形式的数据
- Ndarray 可以有 n 维数组
我们将逐一探讨上述每一个问题。我们将使用 Jupyter Notebook 运行我们的 python 代码,如果您还没有安装,请点击此处进行安装
**使用 python 列表的系列:**以下代码片段使用 python 列表创建整数类型的系列。
当您运行上面的代码时,它将产生以下输出
注:索引默认从 0,1,2,…(n-1)开始,其中 n 为数据长度。
系列使用 python 字典:
上面的代码片段产生以下输出。
系列使用 Ndarray:
上面的程序产生如下输出。
数据帧
数据帧是以行和列的形式存储数据的多维数组。数据帧是存储数据的标准方式。下图显示了如何使用 python 字典创建数据框。
上面代码的输出如下所示。
总结
- Pandas 是一个处理和分析数据的开源库。
- Pandas 非常容易处理缺失值,并且它为我们提供了一种有效的数据切片方法。
- Series 是一维数组,能够保存任何类型的值。
- DataFrame 是以表格形式存储值的多维数组。
我希望你喜欢读这篇文章,你也可以访问我的 网站 ,在那里我会定期发布文章。
如果你想给我任何建议,那么我很乐意听听你们的意见。永远欢迎你。
编码快乐!!!
订阅 我的邮件列表,直接在您的收件箱中提前获得我的文章,或者关注我自己在 Medium 上发表的文章【The Code Monster】,以完善您的技术知识。
了解你的作者
希曼舒·维尔马毕业于印度勒克瑙的 APJ 阿卜杜勒·卡拉姆大学博士。他是 Android & IOS 开发人员、机器学习和数据科学学习者、金融顾问和博客作者。
用 UPSNet 进行全景分割
结合语义和实例分割的完整场景理解
计算机视觉社区已经提出了一个称为全景分割的新任务。全景分割要求一个场景不仅在语义上被分解,而且要求一个类的每个实例,比如一辆车或一个人,被唯一地标记。它需要一种算法来理解像天空和地面这样的无形的东西和像汽车和人这样的可计数的 T2 事物之间的区别。介绍该任务的论文中的以下图片清楚地显示了不同任务之间的差异:
虽然以前的方法侧重于使用启发式方法来组合输出以产生全景分割,但优步研究公司提出了一种统一的端到端可训练架构,称为 UPSNet,它利用语义和实例分割任务之间的互补性来提高准确性和性能。
架构使用与 Mask R-CNN (与 FPN 的 ResNet)相同的主干。在这个主干之上,有两个平行且独立的头部:语义头部和实例头部。最后,这两个头的输出进入另一个头,称为全景头。
实例头与 Mask-RCNN 中使用的实例头相同。虽然语义头是显着的使用可变形卷积,全景头是本文的主要贡献。全景头负责通过处理语义和实例结果来产生最终输出。它完全没有参数,因此不需要培训。这里发生的事情是,如果有像素没有实例输出,那么语义头的输出直接被这些像素接受。对于既有实例标签又有语义标签的像素,softmax 用于确定它是否应该被标记为该实例或语义类。
此外,作者还为“未知”类构建逻辑,以避免做出错误的预测。这背后的基本原理是,对于任何像素,如果来自语义头的“thing”类的 logit 的最大值大于来自实例头的 logit 的最大值(下图中的 max(X thing)- max(X stuff)),那么我们很可能遗漏了一些实例。因此,这些像素必须标记为未知。
这是本文的主旨。这是一个相当简单的想法,但结果是惊人的,我已经在生产中使用它们。我强烈推荐您尝试一下资源库https://github.com/uber-research/UPSNet,我将留给您这篇论文的以下结果:
参考:
UPSNet:一个统一的全景分割网络——余文雄,廖,赵恒双,,,尔辛尤默,拉克尔乌尔塔逊
[论文分析] —规范等变卷积网络和二十面体 CNN
Photo by Kevin Crosby on Unsplash
概观
在这篇文章中,我将尝试总结这篇与几何深度学习相关的非常有趣的论文
原始文件
[## 规范等变卷积网络和二十面体 CNN
对称变换的等方差原理使神经网络的理论基础方法成为可能。
arxiv.org](https://arxiv.org/abs/1902.04615)
注意:在这个总结中,我已经最小化了对数学符号的使用(也因为 Medium 不是数学友好的),所以如果你想更深入地了解形式主义,请参考 GitHub 版本(顺便说一下,GitHub 本身并不渲染数学,但至少你可以使用 Chrome 插件,如Tex All Things
相关论文的分析、总结、备忘单- NicolaBernini/PapersAnalysis
github.com](https://github.com/NicolaBernini/PapersAnalysis/tree/master/Geometric_Deep_Learning/CNN/Gauge_Equivariant_Convolutional_Networks_and_the_Icosahedral_CNN)
介绍
卷积滤波器是从域到共域的滤波器映射
在 CNN 的上下文中,
- 该滤波器基于卷积运算
- 结构域和共结构域都被称为特征图谱
等方差是关于滤波器及其应用领域的属性
- 当一个变换被应用于滤波器域并且它完全反映在滤波器输出上时,那么该滤波器被称为相对于该变换是等变的
Equivariance means the transformation propagates through the filter
- 如果该变换被滤波器完全吸收,使得在滤波器输出中没有其应用的痕迹,则该滤波器被称为相对于该变换是不变的
Invariance means the transformation gets abosrbed by the filter
对称性确定一组与特定空间相关的全局变换(它们不依赖于任何过滤器),这些变换在不改变空间结构的情况下映射空间本身:它们依赖于特定空间的几何形状。
与作用于图像或更一般的平面特征地图的经典 CNN 层相关的基本思想是
- 为了能够使过滤器在平面上滑动,特征属于
或者等同地
- 在下方滑动飞机
因此,在用卷积滤波器一次又一次地处理它之前,更正式地使用平面对称(取决于它的几何形状)之一来转换平面本身
将这一过程推广到一般空间,我们希望
- 一些以同样方式工作的东西,因此改变了空间本身以进行连续的处理
- 卷积滤波器应该可以独立地学习它在平面上的特定位置(这通常被称为权重共享,它使得 CNN 处理位置独立)
就空间推广而言,让我们考虑流形 M,一个与欧几里得空间局部同胚的空间
使用流形的主要挑战是它们不保证具有全局对称性,但是它们具有局部规范变换:本文的目标是设计卷积滤波器,其目的不是与任何全局对称性等变(因为在这种情况下不可能假设它们存在),而是与局部规范变换等变。
这是关键,因为
- 我们最终想要获得的是一个能够共享权重的卷积滤波器,这意味着独立于输入特征图上的特定位置学习权重
- 如何使其等同于过滤器应用于输入空间中任意点的方式
- 在存在全局对称的情况下,到达空间中任意点以应用滤波器的工具是通过输入空间的全局对称变换,例如在平面的情况下,这是移位操作
理解这种“滑动”操作不能对学习产生影响是关键(否则学习的结果将取决于具体的操作细节),这种操作仅允许权重共享。
因此,如果算子(例如卷积)在设计上与这种变换是等变的,那么它可以通过共享权重来学习,因为在学习过程中,滤波器通过对称变换应用于空间中的任何点,并且它不影响学习到的权重(事实上,在平面上的经典 CNN 中,卷积算子在平面上滑动的具体方式是不相关的)。
该论文中提出的挑战不是假设全局对称,而只是局部规范并研究相关的变换,以便设计能够通过权重共享来学习的卷积算子。
在缺乏整体对称性的情况下,我们遇到的第一个问题是如何到达空间中的任何一点(这是一个必需的特征)。一个可能的解决方案是并行传输 rt,但是并行传输实际上是路径相关的。
From the paper
不用说,路径是完全任意的,事实上它定义了被称为标尺的局部切线框架,这意味着一个简单的过滤器会将这个任意的标尺选择传播到它的结果,影响学习。好的
因此,我们想要的是一个滤波器,它对于这个不可避免的任意局部规范选择是不变的,源于并行传输,使得输出特征图不受在输入特征图中选择的局部规范的影响。
细节
然后让我们介绍一下规范,它是流形切线空间和欧几里德空间之间的一个位置相关的可逆局部线性映射
因此,一个量规也在(映射欧几里得参考系)中定义了一个局部参考系
量规变换是局部框架的位置相关可逆变化
因此,潜在的想法是建立一个具有这种特性的过滤器,这取决于它必须处理的特定流形
“用于无重影高动态范围成像的注意力引导网络”(CVPR,2019)——论文综述
HDR 重建的最新进展
Source: Attention-guided Network for Ghost-free High Dynamic Range Imaging (AHDRNet)
“你只是想要关注,你不想要我的心”——查理·普斯
关于报纸
用于无重影高动态范围成像的注意力引导网络 (AHDRNet)是目前 HDR 使用包围曝光图像生成图像的最先进技术。它出现在 2019 年 CVPR,可以在这里阅读。第一作者,董公是阿德莱德大学的博士后研究员。他的兴趣包括机器学习和计算机视觉中的优化。
注 :本帖使用的图片结果、网络表示、公式、表格均来源于 论文 。
在我们开始之前,我建议你看看我播客中的一个片段,我们在这里讨论了计算摄影和 HDR 成像的现状。
埃尔问题 a
为了从多重曝光包围的 LDR 图像生成 HDR 图像,LDR 图像的对准对于具有帧内运动的动态场景非常重要。合并前未考虑未对准,导致重影(以及其他)伪像。已经有几次成功的(几乎)尝试通过使用光流估计来补偿帧之间的这种运动。事实证明,基于流量的方法的缺点并没有很好地服务于 HDR 事业。
这可以从卡兰塔里等人的一次尝试中看出。al ,其中不考虑饱和图像区域中的精确空间重建,对于具有严重运动的输入帧,可以观察到对准伪像。这可以从 AHDRNet 的作者在下图中提供的结果中看出。专门针对高度动态的的重建的另一个尝试包括输入( Wu et .al )声称利用 CNN 架构在学习失调和补偿鬼影伪像方面的明显实力。然而,以下结果表明仍有改进的余地。
注意营救
作者建议使用注意机制来解决这一双面问题,即通过使用注意机制来减少对准伪影+精确的 HDR 重建。如果你想一想,注意力网络只是堆叠在一起的几个 Conv 层,然后(通常)是一个 sigmoid 激活,允许网络专注于重要的和相关的应用程序。
这里,注意力网络被用于抑制对准伪影,并且通过关注括号中的图像相对于参考图像的空间动态,集中于将曝光更好的图像区域注入到生成的图像中。对应于参考图像的区域被突出显示,而具有严重运动和饱和度的区域被抑制。我们将会看到注意力信息矩阵是如何被处理和实现的,就其背后的数学而言。
网络的注意力部分集中于决定哪些图像区域对网络输出的准确性有更好的贡献。接下来是一个基于注意力输出的合并网络,试图从 LDR 输入中创建 HDR 内容。注意力机制越好,对合并网络的输入就越好,从而允许它利用输入的更相关部分中的信息。使用扩展的密集残差块开发了合并网络,这改进了梯度流、分层学习和收敛。整个网络以端到端的方式被训练,因此两个子网络相互影响,并且一起学习。
履行
Overview
预处理
非线性 LDR 输入( I1,I2,I3 )通过应用逆 CRF(这里是伽马校正)并除以它们相应的曝光时间而被转移到线性域。
γ = 2.2
线性和非线性输入( Ii,Hi )沿着信道维度被连接以形成 Xi 。 X1 、 X2 和 X3 馈入网络产生相应的 HDR 输出。
H is the generated image, f (.) represents the AHDRNet network and θ the network parameters
当网络拥有可供其支配的线性化输入信息时,它表现得更好。这已在 Kalantari 等人的研究中得到观察和利用。艾尔以及吴等人。艾尔。
体系结构
整个网络由两个子网络组成——注意网络和融合网络。
Attention network
如上所述,注意力网络通过突出显示和使用来自与参考图像相对应的相邻图像(非参考图像)中的区域的信息,有助于避免对准伪影。它通过以下方式实现。
注意力不是从连接的图像对中提取的,而是直接应用于连接的图像对。首先将 Xi 通过 Conv 层提取 64 通道特征图子。
然后,参考特征图( Z2 或 Zr )连同相邻图像特征图( Z1 和 Z3 )被馈送到关注模块,该关注模块参照 Zr 生成关注图来标记非参考特征图中的重要区域。
这是对两对( Z1 、 Z2 )和( Z3 、 Z2 )进行的。这在上面的网络表示中可以看得很清楚。
在我们进入注意力模块做什么之前,让我们看看如何处理生成的注意力地图。生成的注意力图本质上是一个包含[0,1]之间的值的 64 通道矩阵。该矩阵用作一种权重矩阵,其中每个元素表示相邻图像的特征矩阵中相应元素的重要性,参考 Z2 。这是通过使用从( Z1 , Z2 )生成的注意力图,通过进行注意力图和 Z1 的逐元素乘法来获得Z’1来实现的。
该操作导致 Z1 中的重要特征(关注度更接近 1)获得较高的数值,而不太重要的特征获得相应的较低值。这仅表现在来自 Z1 的重要图像区域,其在网络中前进以对最终的 HDR 输出做出贡献。同样的事情发生在( Z3 , Z2 )之间得到Z’3。
现在我们有了所有与构建 HDR 图像最相关的输入片段,我们沿着通道维度将它们连接起来,如下所示
注意模块
我们来看看这些注意力地图是如何产生的。本文中使用的注意模块由 2 个 Conv2d 层组成,输出 64 通道矩阵,之后分别是 ReLU 和 sigmoid 激活。它将相邻和参考图像(2×3 = 6 个通道)的连接特征向量作为输入。最终,sigmoid 激活用于将输出包含在[0,1]范围内。
关注结果
Attention map examples from the paper. ( a ) to ( c )— Alignment attention; ( d ) to ( f ) — Exposure attention
在(a)至©中,从上述结果可以观察到,非参考图像中具有运动差异的区域是如何被抑制的(深蓝色区域),而与参考图像具有对应关系的区域被突出显示(较亮的蓝绿色)。在(d)到(f)中,在相邻帧中曝光较好的区域被突出显示,而饱和区域被抑制。
合并网络
级联的特征图( Zs )作为输入被提供给合并网络。作者所用的归并网络是张等人提出的剩余稠密网络。艾尔。代替传统的 Conv 操作,作者使用了扩张卷积来传播更大的感受野,因此称之为扩张残余致密块(DRDB)。在合并网络中有 3 个这样的块,它们由基于密集级联的跳跃连接和剩余连接组成,已经证明它们对于 CNN 在解决梯度消失方面非常有效,允许更好的反向传播、分级学习,并因此帮助和改善收敛性能。在建议的 AHDRNet 网络中,每个 DRDB 包括 6 个 Conv 层,增长率为 32。
DRDB
作者还采用了局部和全局剩余跳跃连接,将低级特征旁路到高级特征。局部残差学习在 DRDBs 内实现,而全局残差学习用于将包含纯信息的浅层特征图从参考图像转移到后面的阶段。这一点以及其他网络规格可以在合并网络图中观察到。
损失函数
就像卡兰塔里 et 一样。al ,在 μ 定律色调映射生成的图像和色调映射的地面真实图像之间计算损失。所有实验的 μ 都被设置为 5000。 μ 定律可以定义为
μ-law
L1 损失也是如此。定量比较 PSNR 和 HDR-VDP-2 分数在本文中提出传达 L1 损失更好地重建更好的细节相比,L2 损失。
实施规范
该架构是使用 PyTorch 实现的。规格和超参数是-
- 重量初始化:Xavier
- 优化器:ADAM
- 学习率:1 X10–5
- 批量:8 个
- GPU: NVIDIA GeForce 1080 Ti
- 一幅图像(1500x1000)的推断时间:0.32 秒
结果
这些网络在 Kalantari et 提供的数据集上进行了训练和测试。艾尔。作者提供了定量和定性的比较之间的几个变量的网络,在 PSNR 和 HDR-VDP-2 评分。
- ahdr net—ahdr net 的完整模型。
- DRDB 网络(即无注意事项的 AHDRNet)
- RDB 网(即没有膨胀的阿德尔网)
- RDB 网络(即没有注意力和扩张的 AHDRNet)
- RB-Net(即 AHDRNet,不包含注意、扩张和密集连接)。RBs 取代 DRDBs。
- 深 RB 网。使用更多的 RBs。
结果显示了 AHDRNet 的每个组件对于整个网络的功效是如何重要的,即注意力是重要的,扩展卷积是重要的,密集连接是重要的,剩余学习是重要的。
Visual results, from the paper
与最先进技术的比较
与当前最先进的方法(基于学习和不基于学习)的比较揭示了 AHDRNet 如何击败现有的方法。最接近的竞争对手显然是 Kalantari et。al 的实现仅次于 AHDRNet。作者还提供了使用光流对齐图像(AHDRNet + OF)的 AHDRNet 变体的结果。
视觉结果显示了网络在生成的 HDR 输出中注入细致细节的功效,即使在剧烈运动的情况下也不会产生任何对准伪影。这是从论文中摘录的一些结果-
结论
AHDRNet 是第一个解决 HDR 图像生成问题的基于注意力的方法。注意力机制的技巧已经被用于对齐输入的 LDR 图像。先前的图像对准尝试使用了基于光流的方法,这些方法具有一些不准确性,并且对于帧之间的剧烈运动表现不佳。然而,基于注意力的方法在 HDR 重建以及消除对准伪影方面表现得非常好。大量的实验揭示了 AHDRNet 如何在定性和定量上取代现有的方法,并且它已经成为 HDR 图像生成中新的最先进的技术。
参考
- 闫庆森,董工,施勤峰,安东·范·登·亨格尔,,伊恩·里德,,,注意力引导网络无鬼高动态范围成像 (2019),2019
- 名词(noun 的缩写)K. Kalantari 和 R. Ramamoorthi,动态场景的深高动态范围成像 (2017),ACM Trans。图表
- 吴尚哲、许家瑞、戴宇荣、唐志强,具有大前景运动的深高动态范围成像 (2018),欧洲计算机视觉会议(ECCV),2018 年 9 月
【反欺骗】零拍人脸反欺骗的深度树学习
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 © 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,因此参数总数为 784784+783。
多维标度(MDS):
MDS 的目标是将高维数据投射到低维表面。对于每对观察值,相似性距离δ作为算法的输入给出。结果将是一个 x 维空间中每个数据点的坐标向量。目标函数是最小化在 x 维空间中的投影距离 delta_x 与数据中每对点之间的实际距离 delta 的误差。.即 argmin {(delta_x — delta) }。的编号。要估计的参数是的数量。数据点 x (您想要投影的尺寸)。举个例子:如果你想把 5 种不同的菜系投射到一个二维空间中。参数= 52 = 10。旋钮就是 x. 的大小
- 无监督学习方法
K 均值聚类:
问题是为给定的输入数据集找到 K 个 T21 代表。这些代表被称为聚类中心(或)质心,并且被选择为使得在同一聚类中从每个点到其质心的距离最小。目标函数是 argmin I(k){(x-m(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 知识库。