使用 Python 的整体设备效率
面向工业工程师的 Python
测量制造业生产率
图片由 Marek Szturc 拍摄,可在 Unsplash 获得
整体设备效率
总体设备效率(OEE)是监控和提高工艺效率的一种行之有效的方法;它被认为是一种诊断工具,因为它不提供给定问题的解决方案。OEE 是一个关键的过程指标,用于衡量有效性以及与有效机器性能的偏差。它在全面生产维护(TPM)和精益计划中被广泛使用,用于定位低效率的来源和量化低效率的程度。
OEE 的目标是减少六大设备损失:设备故障、设置和调整、轻微停工、减速、启动报废和产品报废。这些损失分为三类:
- 可用性:考虑可用性/时间损失,包括所有与计划外停机(如设备故障、材料短缺)和计划内停机(如转换时间)相关的事件。可用性衡量机器或单元运行时间占总理论可用时间的比例。
- 性能:考虑性能/速度损失,包括所有阻止机器或单元以最大/最佳速度运行的因素(例如,慢循环、小停止)。性能衡量在给定的运行中生产的单位占可能生产的单位总数的比例。
- 质量:考虑质量 损失,包括导致不符合客户质量标准和规格的缺陷单元的所有因素(如返工、报废、缺陷)。质量衡量无缺陷单位占生产的总单位的比例。
OEE 计算
OEE 可以通过其三个组成部分相乘得到:
OEE 公式
它被分解为:
OEE 配方成分
其中:
- =总可用时间
- B=运行时间
- C =生产能力
- D =实际产量
- E =产品产量(与实际产量相同)
- F =实际良品(即产品产量减去废品)
通过测量 OEE 和潜在损失,公司和组织可以深入了解如何系统地改进制造流程,从而消除浪费。
OEE 作为基准和基线
作为基准,OEE 可用于将给定制造流程的性能与行业标准、相似(或相同)制造流程或同一制造流程不同班次的结果进行比较。
作为基线,OEE 可用于监控和跟踪给定制造流程的改进和废物减少。
什么被认为是“好的”OEE?
- 100% —完美生产:没有停机时间,没有废料,产品以最高速度生产
- 85% —离散制造商的世界级水平:这是一个合适的长期目标
- 60% —典型的离散制造商:表示有很大的改进空间
- 40% —低:常见于开始跟踪和改善制造绩效的公司
在下面的示例中,让我们获取一台机器的 OEE,该机器的总可用时间为 100 小时,但由于某些物料短缺和意外维护而运行了 85 小时,该机器的最大生产率为每小时 10 件,但在运行中生产了 700 件,其中 50 件有缺陷。让我们来看看 Python 代码!
综合效率图
在前一示例中分析的机器的 OEE 为 65%,根据之前解释的 OEE 基准,可以认为该机器的 OEE 高于标准值(即 60%),但仍低于世界级 OEE(即 85%)。
任何旨在提高 OEE 和减少特定流程浪费的公司或组织的真正挑战不是提高 OEE 本身,而是随着时间的推移保持 OEE。
把 OEE 想象成赛车的速度。尽管 F1 的一些赛车速度可以达到 220 英里/小时(约 354 公里/小时)或更高,但发动机在某些时候会出现故障(即车手无法让赛车长时间保持最大速度)。生产机器和设备也是如此:由于大多数情况下 100%的 OEE 是不可能维持的,因此 85%或以上的 OEE 仍然被认为是世界级的 OEE。
总结想法
OEE 是生产设施中的运营经理跟踪给定制造过程性能的一个很好的工具。这是一个必须不断监控的指标,以确定其组成部分中的机会领域,从而在必要时制定和执行行动计划。
有效的生产计划和调度,以及准确的预防性维护计划,可以导致有效运行时间( B )的增加,这将提高 OEE 可用性部分。对操作人员进行充分的培训可以通过减少小停车来提高实际产量( D ),从而提高 OEE 性能。最后,在流程改进项目中使用精益和六西格玛工具可以减少最终结果的浪费和可变性( F ),从而提高 OEE 质量。
Python 代表了一种简单而有效的工具,只需几行代码就能计算出流程的 OEE。同样,它允许获得 OEE 图,以图形化方式显示其组成部分和损失,从而显示过程中的实际附加值百分比。
—
如果你觉得这篇文章有用,欢迎在GitHub上下载我的个人代码。你也可以直接在 rsalaza4@binghamton.edu 给我发邮件,在LinkedIn上找到我。有兴趣了解工程领域的数据分析、数据科学和机器学习应用的更多信息吗?通过访问我的媒体 简介 来探索我以前的文章。感谢阅读。
——罗伯特
克服 Apache Spark 最大的痛点
理解大数据
关于 Spark 最具挑战性的方面以及数据科学家和工程师如何克服它们的高级指南
卡姆拉奈季诺夫创作的电脑照片—www.freepik.com
大约 6 年前,我第一次使用 Apache Spark,当时,它是“大数据”分析领域的开端。毫无疑问,掌握 Spark 是任何想成为数据科学家或数据工程师的人的职责;毕竟,除此之外,如何利用海量数据和分布式 CPU 计算来创建尽可能最好的机器学习模型?
如果那时我可以看到 2020 年的未来,我可能会有点惊讶,很大一部分 ML/AI 从业者仍然不使用 Spark 或只将其用于数据工程,而不是机器学习。一部分自然是因为兴趣部分转移到面向 GPU,而不是面向 CPU 的机器学习技术,尤其是深度学习。但是对于图像和自然语言处理之外的大多数应用程序,面向 CPU 的技术的有用性是毋庸置疑的,令人惊讶的是许多数据科学家仍然严重依赖单机 ML 工具,如 Scikit-learn 以及 XGBoost 和 LightGBM 的非分布式版本。
就我个人而言,我觉得这很遗憾,因为如果使用得当,Spark 对于任何处理数据的人来说都是一个非常强大的工具,可以帮助我们避免浪费时间来找出如何将大型数据集放入内存和处理器,并允许我们完全控制数据分析工作流,包括提取数据、生成模型以及将模型部署到生产和测试中。
在 Apache Spark 上举办了研讨会并指导了几十名数据科学家和工程师,我能够理解用户在使用该工具时通常面临的最大困难,为什么会发生这些问题以及如何克服它们。这份由两部分组成的指南是为那些不仅想使用 Spark,而且想真正理解 Spark 的内部原理以解决复杂问题并生成高性能、稳定代码的人准备的。
注意,我假设读者已经对 Spark 有了基本的了解,例如,什么是 Spark 驱动程序和执行器,数据集被划分为分区,什么是懒求值以及 Spark 的基本数据结构。
第 1 部分:Spark 的分区和资源管理
挑战
与单处理器的 vanilla Python(例如 Pandas)不同,在 Pandas 中,内部处理的细节是一个“黑盒”,使用 Spark 执行分布式处理需要用户做出大量决策:
- 每个数据集使用多少个分区?
- 何时对数据集进行重新分区?
- 要用多少个 Spark 执行器,给它们分配多少内存和多少内核?
- 给火花驱动器分配多少内存?
让事情变得更复杂的是:
- 一个典型的处理流水线将涉及多个操作,每个操作可能会产生大小显著不同的数据集,这使得不太可能找到适合整个数据流水线的单一参数集。
- 某些 Spark 操作会自动改变分区的数量,这使得用户更难跟踪每个数据集使用了多少分区。例如,连接操作会将输出数据集的分区数量更改为在
spark.sql.shuffle.partitions
配置参数中指定的数量。
知道什么是重要的
关于分区和资源管理的每个决定的含义可以总结如下:
- 如果我们为一个特定的表使用太多的分区,由于优化和并行化开销等多种原因,可能会导致资源的缓慢/浪费,因为Spark 作为单个分区处理一定量的数据所需的 CPU 总量比作为多个分区处理要快得多。即使在同一个物理服务器中处理分区时也是如此;
- 如果我们为一个特定的表使用太少的分区,这可能会导致资源的缓慢/浪费,因为许多执行器在处理完他们的分区 s 后将会“空闲”,等待其他分区完成处理;
- 如果我们过于频繁地执行重新分区,可能会由于改组而导致速度变慢/浪费资源,改组包括在执行器之间重新安排分区。洗牌是一种高成本的操作,无论是从处理还是内存方面来说,它都严重限制了我们稍后将讨论的 Spark 的处理优化;
- 如果我们没有使用足够的重新分区,可能会由于不平衡分区导致的执行器过载和空闲而导致速度变慢/浪费资源;即非常大的分区可能会导致执行器在内核和内存方面资源不足,并使其他执行器在等待分区处理时处于空闲状态;
- 如果我们在每个执行器上使用太多的内核,由于并行化水平低于预期,可能会导致速度变慢/资源浪费。当执行器控制多个内核时,它会尝试创建多个线程来同时处理多个分区,从而允许线程共享内存中的变量。然而,这种并行化并不完美,可能会受到 I/O 操作的限制(例如写入 HDFS 或其他分布式数据存储)。通常,即使用户为每个执行器分配了多个内核,也会使用单个内核。此外,由于一个执行器只能在一个物理服务器上运行,因此不可能在执行器中分配用户请求的所有内核。用户可以通过 Spark UI 监控执行器有效使用的内核数量;
- 如果我们在每个执行器上使用的内核太少,由于创建每个执行器所需的开销,可能会导致速度变慢/资源浪费。由于 Spark 知道同一个执行器中的内核肯定在同一个物理服务器中,因此它可以优化处理,使它们之间的并行化开销比跨执行器的并行化开销小得多。
为了处理流水线的多个步骤中分区和资源需求的变化,以及跟踪每个中间数据集的分区数量的困难,有两种策略可以单独使用或组合使用:
- 当计算资源在用户池中共享或用户同时运行多个作业时,通过
spark.dynamicAllocation.maxExecutors
和spark.dynamicAllocation.enabled
参数激活执行器的动态分配可以大大减少 Spark 计算资源的闲置; - 将一个大型处理任务分割成多个较小的任务,每个任务的数据集大小变化较小,这使我们能够更好地调整配置参数和每个步骤的分区数量。对于每一个更小的任务,我们也可以适当地设置参数
spark.default.parallelism
和spark.sql.shuffle.partitions
,以避免不断的重新分区。像 Airflow 这样的管道工具对于设置和部署由较小作业组成的复杂数据任务非常有用。
第 2 部分:Spark 的不变性、惰性评估和执行计划优化
挑战
Spark 用户首先学到的两件事是不变性和惰性求值的概念。不变性意味着 Spark 数据集不能被修改;对数据集的每个操作都会创建一个新的数据集。惰性评估基于数据集上有两种操作的事实:
- 转换产生一个新的 Spark 数据集作为输出(Spark 具有不变性,因此它永远不能修改现有的数据集,只能创建新的数据集);
- 动作将 Spark 数据集作为输入,但会产生 Spark 数据集以外的东西,比如写入存储、创建局部(非 Spark)变量或在用户 UI 中显示某些东西。
转换与行动说明(图片由作者提供)
当流程调用转换操作时,这会导致创建一个不可变的、内存中的“执行计划”,该计划描述了如何基于其他数据集生成数据集。但是,此时不会生成实际数据。只有当一个动作操作被调用时,Spark 才会评估输入的执行计划,以生成计算输出所需的数据。因为输入本身可能有依赖于其他数据集的执行计划,所以这些计划被递归地评估,并且该过程继续。
如果在某个时刻触发了另一个动作,要求再次重新创建相同的中间数据集,则该过程重复进行;重新创建执行计划,并且需要再次执行执行计划的所有步骤。为了防止这种情况发生,用户可以**“保存”中间数据集**。当通过触发执行计划创建“持久化”数据集时,数据集将被保存到分布式内存中(或一些配置的分布式存储,如果内存中没有足够的空间),它将保留在那里,直到手动“取消持久化”或直到 Spark 的垃圾收集器识别出数据集“超出范围”(不能由运行的代码访问)。
许多 Spark 用户没有深入思考不变性和惰性评估的概念及其实际后果,认为知道我们应该**“持久化”一个将被多次使用的中间数据集**就足够了,防止重复计算不止一个操作需要相同的中间数据集。
然而,事实是,很好地利用不变性和懒惰评估远不止于此。事实上,它们与一个鲜为人知的方面密切相关,即通过 Spark Catalyst 完成的执行计划优化。不深入理解这些概念很容易导致缓慢、不稳定的 Spark 处理,以及用户在调试和解密神秘的错误消息上浪费大量时间。
知道什么是重要的
- Spark 在分区级别执行转换,而不是数据集级别
我们可能会有一个错误的印象,即调用一个操作会导致该操作之前的一系列转换被逐个执行,每个步骤都会生成一个中间数据集。如果是这样的话,如果数据帧 2 是通过转换数据帧 1 生成的,Spark 将首先创建数据帧 1,然后创建数据帧 2,就像我们使用 Pandas 时的情况一样。
但事情没那么简单。当一个动作被调用时,所有必要的执行计划实际上被合并成一个执行计划,除了洗牌的情况,每个分区的处理都是独立完成的。这意味着转换是在分区级别完成的,而不是数据集级别。因此,对于一个分区,Spark 可能仍在生成数据帧 1,而对于另一个分区,Spark 可能已经在从数据帧 1 生成数据帧 2。例如,我们可能有如下情况:
(图片由作者提供)
从性能角度来看,这是一种强大的机制,因为它防止处理器和内存浪费,即在移动到下一个中间数据集之前等待整个中间数据集生成。分区级转换的另一个优点是,我们将在下一节看到,它允许 Spark 更好地优化处理。
- Spark 优化执行计划,执行计划越大,优化效果越好
Spark 催化剂无疑是 Spark 最有价值的特性之一。,因为实现高效的分布式处理比实现高效的单核或单内存处理要复杂得多。本质上,**Catalyst 将优化执行计划以最大化分布式性能。**例如,在 Spark 中,一次在同一行中执行多个操作,然后移动到下一行,以此类推,比在同一列中执行多个操作,然后移动到下一列要快得多,因此执行计划会相应地得到优化。
需要考虑的一个重要因素是单个大型执行计划比多个小型执行计划有更大的优化潜力。类似地,通过利用转换是在分区级别而不是数据集级别完成的这一事实,Spark 可以为相同的分区组合多个转换,并优化它们以获得更好的性能。以我们之前的例子为例,优化后的结果如下所示:
(图片由作者提供)
- Spark 的惰性求值和分区级转换使得调试更加复杂
然而,从开发/调试的角度来看,如果发生了错误或者出现了性能瓶颈,惰性评估与分区级转换相结合,会使用户很难准确地找出是哪个处理步骤导致了错误或瓶颈。毕竟,Spark UI 只会告诉用户哪个触发操作导致了错误或瓶颈,而不是实际的转换。
解决方案是**“强制”Spark 每次**生成一个完整的中间数据集,方法是插入多个persist()
语句,后跟动作,如count()
或take()
。然而,由于前面提到的处理器/内存浪费和缺乏优化,这些代码的效率非常低,所以最好只在开发目的(而不是生产)和临时的基础上使用这种解决方案。
- 太大/太多的执行计划也会成为问题
我观察到的一件事是,执行计划并不总是与操作的数量成线性关系,有时是多项式或指数关系。由于执行计划存储在 Spark 驱动程序的内存中(与存储在 Spark 执行器内存中的持久化对象不同),这可能会导致 Spark耗尽驱动程序内存,或者由于 Spark Catalyst 的优化而变得极其缓慢。这有点讽刺,因为 Catalyst 应该在部署到 Spark 执行器时使代码运行得更快;但是在火花驱动器中运行的催化剂会成为处理本身的瓶颈。
对于具有数千列的数据帧来说尤其如此,在这种情况下,我们需要跨所有列执行多个操作。Spark 的不变性使情况变得更糟,因为它不断地有新的执行者计划实例被 Catalyst 创建和评估。
坚持可能是处理这个问题的有效策略。它将消除在生成持久化的中间数据集之前创建执行器计划的新实例的需要,以及防止 Catalyst 在创建数据集之前试图优化执行计划。
然而,对于复杂、长的管道,执行计划变得非常庞大,甚至不频繁的持久化也不能防止由优化或驱动程序耗尽内存导致的缓慢,因为持久化可以防止大型执行计划的多个实例化,但不能首先防止它们的创建。此外,过多的持久化本身也会成为一个问题,因为它会占用分布式内存和存储空间。虽然经常尝试去持久化和/或尝试构建代码以最大化垃圾收集肯定有所帮助,但这是一项艰巨的工程任务,并不能总是防止资源耗尽。
一种解决方案是名为检查点的技术,它包括从用户定义的适当存储系统中保存和加载中间结果。当有效使用时,检查点既可以防止执行计划规模的巨大增长,又可以防止 Spark 的分布式资源与持久化对象发生冲突。
另一个解决方案,已经提到过,是将一个大的处理任务分割成更小的任务。这样,执行计划就不会变得太大,也不需要担心驱动程序或执行程序内存中的剩余对象。
结论
当然,关于 Spark,本文还会涉及更多内容,例如,如何构建代码以提高 Catalyst 优化的质量。但是我希望这篇文章可以帮助一些人克服 Spark 的困难,并利用它在数据工程和机器学习方面的非凡能力和多功能性。虽然 Spark 仍然是一个具有挑战性的工具,但我可以诚实地证明,在我使用它的 6 年时间里,Databricks 和开源社区如何在用户友好性和灵活性方面大大改进了它。
如果你想学习如何使用 Spark 和 Apache Hadoop YARN 并行运行多个分布式作业,比如模型训练的多个实例,查看我的另一篇文章。
克服确认偏差
为什么确认偏差是数据科学的克星,以及如何与之对抗
下面是文章的音频版本,由作者为你朗读。
让我们来谈谈一种令人讨厌的心理效应,这种效应可能会破坏你有效应对每天涌入的信息的能力: 确认偏差 。
让你陷入麻烦的并不是你不知道的事情。你确定的事情并不是这样的。马克·吐温。
这是什么?
*“*确认偏差 是以确认或强化个人先前信念的方式搜索、解释、偏好和回忆信息的倾向。”—维基百科
是啊,但这是什么?
这个:
保罗 J 插图,经许可使用。
如果你热衷于研究心理学,让我们将确认偏差与其他一些现象区分开来:
- 确认偏差**:你的已有观点改变了你对信息的感知方式。
- 一厢情愿的盲目**:你的利己主义改变了你对信息的感知方式,尤其是在道德决策的背景下。(不要和 故意视而不见 混淆),这是一个来自法律的术语。)
- 滤泡指智能隔离在线回音室。如果一种算法被设计成对你喜欢的内容进行优先排序,并且你倾向于喜欢来自已经同意你的人的内容,那么你就不太可能接触到可能改变你想法的内容。
**我不会用如何从回音室里爬出来的建议来烦你——你已经全都听过了。"某事某事……寻求……某事……与你意见相左的人的智力训练。"对吧?没错。
图片由作者提供。
一厢情愿的盲目是一个棘手的问题,因为这是一个相对较新的理论,没有太多关于如何克服它的研究。
我最喜欢引用的关于一厢情愿的盲目的话,在我们称之为一厢情愿的盲目之前。
不过,确认偏差自 20 世纪 60 年代以来就一直存在,所以让我们来关注一下这个问题。
过滤泡沫是关于扭曲的信息,而确认偏差是关于扭曲的感知。
****确认偏差不是指你的社交媒体反馈与自身过于一致的情况。这要微妙得多:即使你接触到与你的观点不一致的信息,你也可能不会接受。你可能会记错。你可能会找到忽略它的理由。你将继续挖掘,直到你看到的数字是你想看到的。头脑就是那样滑稽。
确认偏差意味着我们都可以看到同一个数字,并对它有不同的理解。事实不再仅仅是事实。
换句话说,确认偏差是数据科学的死对头,因为它意味着一个事实不再仅仅是一个事实,无论你投入多少数学和科学去得到它。你和我可以看着同一个数字,然后感知到不同的。努力让自己接触更好的信息来源并不足以解决一个始于你眼前的问题。
确认偏差是数据科学的宿敌。
在信任自己的大脑时,你应该小心。(那个叛徒!)如果你从一个强烈的无知的观点开始,去搜寻信息,你很可能在完成后以同样的观点结束。何必呢?除非你预先对抗确认偏见并且按照正确的顺序做事,否则你对数据的尝试注定是浪费时间。
有兴趣了解更多关于扭曲的数据感知吗?见我关于 apophenia 的文章。图片:维基百科
确认偏差和新冠肺炎
一言以蔽之:如果你对新冠肺炎有强烈的看法,然后你去寻找支持它们的证据,你会认为你看到了……不管那些看法有多古怪。你也将更难吸收指向相反方向的证据。
如果你感到害怕,你去寻找让你感觉更好的信息,确认偏见反而会让你感觉更糟。
更糟糕的是,每当你感到恐慌,并试图用一个愉快放松的互联网会话来抚慰自己时,你很可能会找到更多恐慌的理由。
快速恐慌不是一种美德——通常会适得其反。
虽然恐慌会让你肾上腺素激增— “确认偏差:一个奇怪的技巧来提高你的家庭健身计划”—但它不会帮助你做出更好的决定。这通常会适得其反,削弱你清晰思考的能力,对你的情绪或应对压力的能力没有任何好处。无论你的处境有多糟糕,冷静的现实主义会比恐慌更好地为你服务。
如何对抗确认偏差
结果是,一个强有力的开始意见会搞乱你的决策和你的心情。幸运的是,有四种简单的方法可以对抗确认偏见:
- 不要固执己见。
- 强调决定,而不是意见。
- 专注于你能控制的事情。
- 改变获取信息的顺序。
图片由作者提供。
保持你的观点宽松
这一条可能说起来容易做起来难,尤其是如果你已经在头条新闻中煎熬了一段时间,但是保持开放的心态,不要太认真地对待你的观点,这是一个很好的智力练习,即使它们是基于大量的数据(嗨, Bayesians )。
**这里还有一个值得了解的偏见:过度自信偏见。(一种根深蒂固的偏见,在这种偏见中,一个人对自己判断的主观信心确实大于这些判断的客观准确性,尤其是在信心相对较高的时候。”—维基百科)是啊,但是是什么呢?你可以通过养成不严格坚持自己观点的习惯来减轻压力。
松散地持有你的观点会减少他们扭曲你的感知的能力。
我所知道的最好的决策者是适应性强的。他们有接受新信息并承认错误的技能。在你跑去钦佩那些坚定不移地做出判断的领导者之前(坚定而忠诚,对吗?),花点时间从心理学家的角度来看他们:他们受到确认偏见的阻碍,无法恰当地处理新信息(或者他们是出色的演员)。
如果你渴望抑制自己的自大倾向,那就从科学家的书里取一页吧。有没有注意到应用科学的研究论文充满了警告,谦逊地提醒读者那些可能使他们的结论无效的未知情况?这是一个不错的目标标准。
强调决策,而不是意见
在我看来,呃,放松你的观点对你的情绪和决定的控制的最好方法是养成先做决定的习惯。如果一个观点在森林里消失了,并且没有任何行动受到它的影响,这个观点重要吗?
通过我们的决定——我们的行动——我们影响着周围的世界。
想象一下,我相信尼斯湖水怪是真的。如果我的信念不以任何方式影响我的决定——与世界的互动,这有什么害处呢?另一方面,如果我的信念确实影响了我的行为(有意识地或下意识地),那么也许我应该在它会影响的行为的背景下评估我的观点。这涉及到在发表意见之前思考行动(按重要性顺序),然后作为第二步形成测试假设。我的文章是这里的。
图片由作者提供。
“这些信息会让我采取不同的行动吗?”
*每当你发现自己收到新信息时,记得问问自己:*“这是可操作的吗?”如果是,它会影响您的哪些决定?
关注你能控制的事情
谈到新冠肺炎,我们许多人已经发现我们的决策受到新规则的约束。如果我们不花一点时间去认识到我们一直担心的一些决定已经不再摆在桌面上,我们会在没有作用的意见上浪费不必要的精力。
难道你不想专注于对那些在你控制之下的事情做出决定和计划吗?(或者把那段时间用在给你带来快乐的活动上?)
例如,在 2020 年初的纽约(是的,我知道)我这个月不再有去剧院的选择。无论我对在拥挤的空间里会如何影响我的健康有什么看法——这是我从阅读世卫组织的出版物中粗略得出的看法——我都无法将这些信息作为输入做出决定。
这个月,即使我想,我也不再有在人群中的选择。这个决定不在讨论范围内。
也许我最好将我的认知努力转移到其他地方,转向对我来说已经摆在桌面上的决定,例如是否远程帮助(是的),是否在线订购卫生纸(不,这是我所有教科书的目的),以及是否投入大量精力拍摄关于数据科学的家庭视频系列(也许不是,除非我已经放在 YouTube 上的视频获得更多流量)。
埃德温·胡珀在 Unsplash 上拍摄的照片
远离那些你无法影响的事情并不是对无知的呼唤。比较下列使用你精神能量的方法:
- 担心你的市长正在努力应对的一个决定,同时努力思考如果你处在他们的位置,你会怎么做。
- 从你所做的决定的角度来考察你的市长的决策技巧(例如,投票支持/反对市长连任)。
- 考虑你应该采取哪些行动——如果有的话——来回应(或准备)你的市长正在选择的每一个可能的选项。
- 决定你是否应该尝试运用你自己的影响力/努力去影响市长的决定或其潜在后果。
其中,(2)-(4)是更有用的观点(除非你的兴趣是学术),而(1)是对决策学生的良好实践()“如果我是负责人,我会如何做出这个决定,我能在这里学到什么技能?”但对于情绪失控的人来说,这并不是一个有效的利用空间的方法。
区别不在于你寻找什么信息。这是你寻求它的方式。作为奖励,你可能会发现你能控制的比你意识到的要多。
请注意,这种差异不是消息灵通与否的问题。区别在于你是否明确地将你的精力集中在该由你做出(或影响)的决定上。纠结于别人的决定很可能会让你感到无能为力,被模糊的信息淹没(尤其是当负责决策的人比你掌握更多信息的时候)。如果你发现自己在压力大的时候采取了(1)这样的观点,转向(2)、(3)或(4)可能会给你带来一些缓解。作为奖励,你可能会发现你能控制的比你意识到的要多。
改变获取信息的顺序
既然你已经把注意力集中在行动和决定上了,是时候揭示大笑话了:消除确认偏见的最有效方法是在你寻找信息之前*计划好你的决策。*
在你看到球落在哪里后,用一种防止你移动球门柱的方式来框定你的决策。
换句话说,重要的是,在你看到球落地的地方后,要以一种防止你移动球门柱的方式来框定你的决策。好奇想了解更多?我有一篇一般的文章加一篇循序渐进的新冠肺炎决策指南帮你出。
现在是完全不同的东西…
感谢阅读!如果你在这里玩得开心,并且对人工智能感兴趣,这里有一个初学者友好的介绍供你娱乐:
在这里欣赏整个课程播放列表:bit.ly/machinefriend
喜欢作者?与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用此表格取得联系。
过量饮食评论[1312.6229]
过量进食的理论综述
演职员表:https://en.wikipedia.org/wiki/Object_detection
我已经计划阅读主要的物体探测论文(虽然我已经粗略地阅读了它们中的大部分,但我会详细地阅读它们,好到足以写一篇关于它们的博客)。这些论文与基于深度学习的对象检测相关。随时给建议或询问疑惑会尽我所能帮助大家。我将在下面写下每篇论文的 arxiv 代码,并在下面给出博客(我写的时候会不断更新)和他们论文的链接。任何从这个领域开始的人都可以跳过许多这样的论文。当我读完所有的论文后,我还会写下它们的优先级/重要性(根据理解主题的必要性)。
我写这篇博客是考虑到和我相似并且仍在学习的读者。万一我犯了任何错误(我将通过从各种来源(包括博客、代码和视频)深入理解论文来尽量减少错误),任何人都可以随意地在博客上强调它或添加评论。我已经提到了我将在博客末尾涉及的论文列表。
我们开始吧:)
Overfeat 论文探讨了三个计算机视觉任务分类,定位,检测分别按照难度递增的顺序。每个任务都是下一个任务的子任务。所有任务都使用一个框架和一个共享的功能学习库来解决。
分类
用于分类的架构类似于 alexnet,但有一些改进。作者准备了两种不同的架构:快速和准确。与 alexnet 架构的不同之处包括没有对比度归一化、汇集区域不重叠、由于步幅较小而具有较大的第一层和第二层特征地图。我将添加显示快速和精确架构的表格。训练和推理步骤的执行是不同的。我在这里解释训练步骤,后面解释推理步骤**。**分类器的训练是在 221*221 大小的单一尺度上完成的。通过首先将图像大小调整为 256,然后裁剪为 221,提取了 5 个随机裁剪及其水平翻转。我不会详细讨论训练的细节,比如学习速度,体重下降等等。该架构可以在图 1 中看到。
图一。过度饮食建筑
ConvNets 和滑动窗口效率(在 convnets 内部应用滑动窗口)
我们知道滑动窗口方法可以改善分类和定位的结果,但是增加了许多倍的计算。ConvNets 在应用滑动窗口技术方面具有内在的效率,因为它们共享重叠区域的共同计算(大部分将重叠)。我将在 CNN 的内部解释这个滑动窗口是如何工作的。拿这个图做参考。
图二。
我希望你意识到感受野(即使你在这篇博客之后没有阅读它,这是一个重要的概念)。上图分为两个子部分,先考虑第一部分。应用第一次卷积后,输出变为 1010(如果计算卷积运算的输出尺寸,请阅读此)。)之后是最大池层,类似地在后面的层中给出 11 的最终输出。这里我们可以直观地说,最终的 11 编码了 1414 输入的信息(基本上这是一个感受域)。没有跳转到第二部分,过滤器大小没有变化,唯一的变化是输入大小(1616)。当第一个卷积被应用,随后是最大池时,输出大小变成 66,在先前的情况下是 55,现在当具有 55 滤波器大小的卷积将被应用时,输出大小将是 22 而不是 11。如果您看到 22 输出中的蓝点,它会对输入中蓝色部分的所有信息进行编码,而不会看到黄色区域。类似地,22 输出的第二个点((输出矩阵上的(0,1)位置)将永远看不到输入的前两列和底部两行(我们的模型的感受野是 14*14,因此输入大小的增加线性地增加了输出大小)。
多尺度分类
在推理时,为了提高分类精度,他们使用了多视图投票。他们使用培训中讨论的类似策略生成 10 幅不同的图像,并对预测进行平均。但这忽略了图像中的许多区域(因为与滑动窗口方法不同,我们只对每幅图像使用 5 次裁剪)。因此,他们采用了一种更好的方法,解释如下:
他们使用了 6 种不同的输入比例,从而生成了不同大小的第 5 层要素地图(这些大小请参考下表)。
图三。多尺度方法的空间维度
在这里我们可以看到不同尺度下模型输入大小的变化是(36,72)。这里的 36 值(二次采样率)类似于我们在上一节中观察到的 2 像素移动。第 5 层(上表中的预池)的输出将因规模而异。应用 33 最大池。考虑输出形状为 1717 的第一个比例。这里,如果在没有填充的情况下应用 max-pooling(33 ),则生成的输出大小将为 55。在这里,我们可以观察到最大池的输出没有来自预池化特征地图的最后两行和列的任何输入。由于 CNN 具有本地连接性,在原始图像的情况下,这两个列和行变成大约 30。为了解决这个问题,考虑到起始像素位置为(x{0,1,2},y{0,1,2},总共应用了 9 次最大池。因此,现在输出将是 3*3 的形状(前面提到的 9 倍是 2d 的行和列)。作者在图 4 中解释了这一点。
图 4。
从上一段继续。因此,我们得到的总输出是(55)(33)的形状。应用 5×5 卷积来获得最终的分类器图。它的形状是(11)(33)C,这里 C 是因为这是我们得到预测的最后一步。对于其他比例,最大池化后生成的输出地图会有所不同。对于其他规模,它将是类似的,我想你现在可以计算我们如何得到输出尺寸。现在,通过首先在每个尺度上取空间最大值(33C 或 69*C 中的最大值)来计算最终输出。然后对所有尺度的输出进行平均,最终得到每个图像的一个类输出。
我们完成了分类,我们的主要任务是对象检测,我们甚至还没有开始。
别担心,现在没那么久了。我们几乎涵盖了所有的理论。
本地化
分类训练网络由回归网络代替分类图层,并对其进行训练以预测每个空间位置和比例的边界框。回归网络从第 5 层获取合并的要素地图,随后是大小为 4096 和 1024 的两个 FC 层。最终输出有 4 个单元(见图 5。).特征提取层(前 5 层)的权重是固定的,并且使用 l2 损失来训练模型。训练是在多个尺度上进行的(不同于在单个尺度上训练的分类网络,在多个尺度上仅生成预测)。这将使预测跨尺度正确匹配,并增加合并预测的可信度。
图 5
为了生成对象边界框预测,我们跨所有位置和规模同时运行分类器和回归器网络。由于这些图层共享相同的要素提取图层,因此在计算分类网络后,只需重新计算最终回归图层。
由于模型将预测多个框(在本地化的情况下将有一个框),我们需要一些策略来消除所有的坏预测。应用了一个贪婪的合并策略,如图 6 所示(你可以跳过它,因为我没有看到任何其他论文使用这种策略,NMS(将在未来的博客中讨论)使用得更频繁)。
图 6
这里,match_score 被计算为 b1 和 b2 的中心之间的距离之和。box_merge 计算边界框坐标的平均值。
侦查
在检测的情况下,与定位任务的主要区别在于当不存在物体时,需要预测背景类别。因此,我们现在从定位网络获得的先前属于某个类别的盒子现在属于背景,因此我们可以移除这些预测,并且仅获得在哪个类别中被有把握地预测的预测。
过量论文总结到此结束。如果我做错了请重点指出,提出疑问作为评论。
参考文献:
- https://arxiv.org/pdf/1312.6229
- https://medium . com/coin monks/review-of-over feat-winner-of-ils vrc-2013-localization-task-object-detection-a6f8b 9044754
- https://www.youtube.com/watch?v=JKTzkcaWfuk
- https://www.youtube.com/watch?v=3U-bZgKFS7g&t = 70s
- Alexnet
- 感受野:https://medium . com/ml review/a-guide-to-receptive-field-algorithm-for-卷积神经网络-e0f514068807
论文列表:
- OverFeat:使用卷积网络的综合识别、定位和检测。←你完成了这篇博客。
- 丰富的特征层次,用于精确的对象检测和语义分割(RCNN)。 [ 链接到博客 ]
- 用于视觉识别的深度卷积网络中的空间金字塔池。 [ 链接到博客
- 快速 R-CNN 【链接到博客】
- 更快的 R-CNN:用区域提议网络实现实时目标检测。【博客链接】
- 你只看一次:统一的,实时的物体检测。【博客链接】
- SSD:单次多盒探测器。[博客链接]
- R-FCN:通过基于区域的完全卷积网络的目标检测。【博客链接】
- 用于目标检测的特征金字塔网络。【博客链接】
- DSSD:解卷积单粒子探测器。[博客链接]
- 密集物体检测的焦点丢失(视网膜网)。【博客链接】
- YOLOv3:一种渐进的改进。[博客链接]
- 狙击手:高效多尺度训练。[博客链接]
- 标注像素和区域的高分辨率表示。【博客链接】
- FCOS:全卷积一级目标检测。[博客链接]
- 物为点。[博客链接]
- CornerNet-Lite:高效的基于关键点的对象检测。【博客链接】
- CenterNet:用于对象检测的关键点三元组。[博客链接]
- 用于实时目标检测的训练时间友好网络。【博客链接】
- CBNet:一种用于目标检测的新型复合主干网络体系结构。【博客链接】
- EfficientDet:可扩展且高效的对象检测。[博客链接]
和平…
机器学习中的过拟合和欠拟合
在这篇文章中,你将了解什么是过度拟合和欠拟合。您还将学习如何防止模型过拟合或欠拟合。
在数据集上训练模型时,人们面临的最常见问题是过拟合和欠拟合。过度拟合是机器学习模型性能不佳背后的主要原因。如果你曾经面临过过度拟合的问题,也不用担心。浏览一下这篇文章。在本文中,我们将通过一个运行示例来展示如何防止模型过度拟合。在此之前,让我们先了解什么是过拟合和欠拟合。
我们在机器学习中的主要目标是正确地估计训练数据集中的分布和概率,以便我们可以有一个可以预测测试数据集的分布和概率的通用模型。
过度拟合:
当模型学习到数据中的模式和噪声,以致损害模型在新数据集上的性能时,称为过拟合。该模型与数据拟合得如此之好,以至于它将噪声解释为数据中的模式。
过度拟合的问题主要发生在其决策边界是非线性的非线性模型中。在逻辑回归的情况下,线性决策边界的例子可以是直线或超平面。如上面的过度拟合图所示,您可以看到决策边界是非线性的。这种类型的决策边界是由非线性模型(如决策树)生成的。
我们也有非线性模型中的参数,通过这些参数我们可以防止过度拟合。我们将在本文后面看到这一点。
欠拟合:
当模型既没有从训练数据集学习也没有在测试数据集上很好地概括时,它被称为欠拟合。这种类型的问题并不令人头痛,因为这可以很容易地通过性能指标检测出来。如果性能不好,尝试其他模式,你一定会得到很好的结果。因此,对拟合不足的讨论不像对拟合过度的讨论那样频繁。
非常适合:
既然我们已经看到什么是过度拟合和欠拟合,让我们看看什么是良好拟合。
在欠拟合和过拟合中间的点是好的拟合。
在现实世界中,得到一个完美的模型是非常困难的。你不可能一下子找到完美的模特。首先,您将有一个将在生产中使用的初步解决方案,然后您将根据您随时间收集的数据重新训练该模型。
如何解决过度拟合
在这一节中,我将通过一个真实的案例研究来演示如何防止模型过度拟合。我们将使用亚马逊美食评论数据集,并在此数据集上训练决策树。我还会提供一个 GitHub 链接供进一步参考。我假设你们都知道什么是决策树。我们将使用 sklearn 库。
您可以通过使用像 K-fold 交叉验证和超参数调整这样的技术来防止模型过度拟合。一般人们用 K 重交叉验证来做超参数调优。我将通过一个决策树的例子来展示如何做到这一点。首先,我来解释一下什么是 K 倍交叉验证。
K-fold 交叉验证:在这种技术中,我们通常将数据集分为三个部分。训练部分包含 80%的数据,测试部分包含 20%的数据。此外,在训练期间,训练数据集被分成 80:20 的比例,80%的数据用于训练,20%用于交叉验证。在决策树中,超参数是树的深度和每个节点上的数据点的数量,在此之后节点将被分割。我们将看到 k-fold 交叉验证是如何工作的。这里我举一个 5 重交叉验证的例子。因此,训练数据集将被分为 5 个部分,随机选择 4 个部分进行训练,1 个部分进行验证。这将重复 5 次,并且该度量的平均值将是该时期的最终度量。在一个时期中,树的深度和分裂值是固定的。例如:在一个纪元中,我们可以让树的深度为 100,分割值为 200。让训练数据集 D 被分成 D1、D2、D3、D4 和 D5。对于超参数的固定值,验证计算如下所示。
交叉验证计算。
这有助于监控培训,因为在培训期间,我们会根据看不见的数据验证模型。现在让我们以准确性作为衡量标准。训练后 A1 是训练精度。如果训练精度和测试精度都接近,则模型没有过拟合。如果训练结果非常好,而测试结果很差,则模型过度拟合。如果训练精度和测试精度较低,则模型有欠拟合。如果模型欠拟合或过拟合,那么我们改变超参数的值,并再次重新训练模型,直到我们得到一个很好的拟合。
超参数调整:在这个过程中,我们取一系列的超参数,然后我们监控所有可能的超参数组合的交叉验证准确性。我们采用给出最佳精度的超参数(这里我们将精度作为度量)。然后我们用那个超参数训练模型,然后测试。以下是如何计算每个超参数组合的交叉验证准确度。
交叉验证计算示例。
我们可以使用 sklearn 库提供的 GridSearchCv 或 RandomSearchCv 来做超参数。GridSearchCv 将检查所有可能组合的交叉验证,但 RandomSearchCv 通过随机选择组合进行检查。下面是进行超参数调整的代码。要查看完整代码,点击 此处 。
进行超参数调整的代码。
我希望这能弄清楚什么是过度拟合和欠拟合,以及如何解决它们。
难以理解为什么我们会交叉熵。通过这篇 博客 来有一个清晰的直觉。
在这篇博客中,你会对交叉熵和对数损失在机器学习中的应用有一个直观的了解。
medium.com](https://medium.com/@riteshk981/cross-entropy-log-loss-and-intuition-behind-it-364558dca514)
如果您在理解校准方面有困难,请浏览此 博客 。
在这篇博客中,我们将学习什么是校准,为什么以及何时应该使用它。
medium.com](https://medium.com/analytics-vidhya/calibration-in-machine-learning-e7972ac93555)
参考资料:
过度合身和不够合身:视觉上解释得像你五岁
关于机器学习和深度学习中过度拟合和欠拟合的检测和预防,我们需要知道的一切
每周,我的花园里都会长出一束花。你可以把它看成一个数据集。这是一个向我的孩子有趣地介绍复杂概念的好方法。在这篇文章中,我们将通过这个绚丽的花园来强调:
- 理解数据集的重要性。
- 过拟合和欠拟合
- 特异性和一般化的平衡
- 检测过拟合和欠拟合
- 防止过度配合
- K 重交叉验证
- 分割训练、验证、测试
了解数据集
我花园里所有的花都一模一样。但我不得不撒了点小谎。我说有些花帮助我们的兔子长得更快更聪明。其他的花什么都不做。
在两个星期里,我们采摘了花园里所有的花,把蓝色的 T42 树枝放在魔法花上,紫色的树枝放在普通花上。对于每一朵花,我都发明了它是不是魔法,但这并不重要。我们有一个花的数据集,我们知道它们在棍子上的 X,Y 位置,以及棍子的颜色,它们是神奇的还是普通的。
出于实际原因,在我的插图中,花将被替换为点。
一般化
我和我的孩子用我们种在花园里的棍子制作了我们的小数据集。我们称之为训练集。我现在希望我的孩子能够根据现有的数据预测花园里下一朵生长的花会是神奇的还是普通的,取决于它们的位置。我又等了一周,等待数据集的另一部分。我们没有摘这些花,甚至没有把树枝放在它们的位置上。我们将这组花称为验证集。
你将如何做出预测
这些是我孩子的游戏规则。当他试图为我们的兔子划定花园中种植魔法花的区域时,他需要做出选择。他必须做出选择:
- 一根刚性绳索
- 一根柔性绳索
- 多根柔性绳索
接下来的规则很简单:尽可能以最好的方式布置电缆,将的神奇花卉区与普通花卉区区分开来。每当我的孩子试图解开的绳子时,我就会根据验证集向他报告他所犯的错误。
你猜对了。所以是 3 种不同的型号,参数或多或少。它可以是任何预测模型,但是例如,我将使用神经网络插图来说明这些绳索。
欠拟合
在理解什么是欠配合之前,更容易理解过配合。当模型过于简单时,出现欠拟合。或者当模型对于给定数据不够好时。
这是很少遇到的情况。另外,我的孩子没有选择刚性绳。使用听起来太死板而没用的东西似乎很荒谬。
如果我的孩子使用硬绳:
- 他会试着把他已经知道种类的花放在一起。
- 他会用我的反馈一步步评估刚性绳索的正确位置以提高他对它的预测。
但是这种刚性绳索的选择不够灵活,不适合我们的数据。在机器学习或深度学习中,无论使用什么算法(SVM、安、随机森林),我们都必须确保我们的模型对于我们的数据有足够的特征。因此,了解数据集非常重要。
过度拟合
如果你已经明白什么是欠拟合:过拟合是的反义词。经常使用以下比喻:
- 欠配合:试着用苍蝇拍杀死哥斯拉。
- 过度配合:尝试用火箭筒杀死一只苍蝇。
我的孩子选择用我向他提出的最后一个选择来划定神奇的花区:多根柔性绳索。人们总是倾向于使用过多的功能,而不是最基本的功能。本能的,我们都是孩子!
这是我们太容易犯的错误。为什么不用什么都有而不是什么都没有?我们大多数人对此都有偏见。作为我的孩子,我们也会选择带上所有的绳索。
多亏了这束绳子,我的孩子终于设法把魔法花生长的所有地方都围了起来。
"100%!恭喜你!”
然后我们用我们的验证集和哎呀 …本周的神奇花朵进行评估,准确性非常差。甚至比刚性绳更糟糕,导致不匹配。
我承认我真的没有孩子,我从来没有在这篇文章之外编过这个花的故事。但是我的孩子是我编程的模型。我邀请你去考虑你的模特。因为如果我们让他们选择使用许多功能,那么我们就可以确定我们的模型会使用所有的功能**。那它就会超负荷。**
找到一个好的平衡点
我们刚才已经看到的是我们必须在特异性和普遍性之间找到正确的平衡。
- 一般化:将应用于一组可见数据中的不可见数据的能力。
- 特异性:在最细微的细节中发现数据的能力。
在训练一个模型时,你应该小心找到正确的平衡点:
- 过拟合 : 低泛化,高特异性
- 欠拟合:高泛化,低特异性
因此与直觉相反的是,预测神奇花朵生长区域的模型应该是简单的柔性绳子。不是那几根柔韧的绳子。
检测过度拟合
嗯,我们必须小心过度合身。知道这一点很好,但是我们如何检测它呢?有两种思想流派:
- 验证设置上的错误可以被监控。训练集上的误差将继续减少,训练的特异性越来越强。当验证集上的错误开始增加时,您会注意到开始过拟合。
- 验证集上的度量可以被监控。这在视觉上和上一点是一样的。
我更喜欢度量监控。我对知道描绘普通花和神奇花的区域的线条的误差距离不感兴趣。我感兴趣的是那些线条的精确度。对我来说,误差用来调整绳索的位置。该度量用于评估他们的位置。
一旦确定,防止过度拟合的最简单和最直观的方法是应用提前停止。如果在验证集上准确度开始下降,是时候停止我们的训练以避免失去泛化和过度拟合。
防止过度拟合的另一个显而易见的方法是检查我们的模型对于给定的数据集是否包含太多的特征。换句话说,如果我们的模型有过度拟合的趋势,那么我们可以逐渐移除特性和/或它们的灵活性。这允许我们获得一个更适合数据集的模型。
验证和测试设备
当我们修改模型的架构、学习率或参数初始化方法时:这些是我们正在修改的超参数。
- 参数:由模型自身在训练过程中选择并更新以最小化误差。
- 超参数 : 提供给型号。这些元素无法在培训期间学习。它们定义了模型以及如何优化其性能。
让我们回到基础。我们需要一个验证测试来确定我们没有执行任何可能导致过度拟合的特异性。但是我们用验证集来评估这个性能。调整超参数后,验证数据本身变得有偏差。我们用它们在某种程度上“训练”我们的超参数。
这就是为什么有必要将数据集分成 3 个不同的集合。一个给训练的模特。另一个是验证我们的培训和调整我们的模型。最后,一个测试设定来验证我们的模型。该套件只能在最末端使用。你不应该使用这个设置来重新调整你的模型。
k 重交叉验证
另一种防止过度拟合的方法是所谓的 K 倍交叉验证。让我解释一下。当我们分割数据集时,我们把测试留到最后。然后我们将我们的训练集划分为个子集。
在我们的花卉数据集中,我们可以方便地进行。我们用每个周的花来划分我们的数据集。在第一次迭代中,我们用前两周训练我们的模型,用最后一周评估它。在第二次迭代中,我们为每次迭代切换并重复过程**。**
这将限制特异性到一个星期的魔法花,这些魔法花本来会生长在更稀少的区域。这个导致了泛化。在最后,我们将能够在接下来的一周评估我们的模型,我们的测试集。
防止过度拟合的其他方法
还有其他方法可以防止过度拟合:
- 集合:几个不同模型的组合****投票预测。
- 规则化:人为强制模型变得简单的方法。
然而他们很快就会有自己的文章😜
结论
- 了解你的数据集。
- 不要对你的模型的特性过于贪婪。
- 总是评估你的模型。
- 注意在特异性和概括性之间的正确平衡。
- 监控错误或度量的行为以提前停止培训。
- 保留一个测试集。
- 使用分区数据集上的交叉验证防止过度拟合。
知识就是分享。
支持我,一键获取 中我所有文章的访问 。
来源图像
其他图片有自制的和免费使用的
过度拟合,不仅仅是一个问题
识别过度拟合的实例
图片来自 Pixabay ,由 alehigalgo 提供。
我多年来一直从事机器学习和深度学习模型的研究。在这段时间里,我发现有些情况下过度拟合并不明显。同样,我也见过其他人,比如朋友、同事或学生,他们不能在自己的问题中识别过度匹配。
因此,我决定制作这个小指南,讲述我遇到的难以识别过度拟合的案例,以及在试图解决它时遇到的主要错误。
什么是过度拟合?
当处理经典的机器学习或深度学习问题及其模型时,如人工神经网络或用于分类的 SVM,有一个初始训练阶段。在训练期间,模型被输入选定的数据,以便教会它如何对给定的数据进行分类。
一旦用数据训练了模型,就该用新数据测试它了。这被称为测试阶段,它证明模型是否能够从以前从未学习过的数据中提取信息。
如果模型对训练数据有好的结果,但对测试数据有坏的结果,则称该模型过度拟合。这些结果来自评估指标,如准确度、召回率、精确度……通常,当第一次学习机器学习时,据说训练期间的准确度或召回率接近 100%,但测试期间没有,这意味着过度拟合。
有时候这并不完全正确。可能有 70%的召回涉及过度拟合的情况。因此,这里列出了一些不容易识别过度拟合的情况。
案例 1:兴趣类的问题
这个案例是我在生物信息学问题中看到比较多的一个。先说个例子。在这个例子中,我们是生物信息学家,试图找到一个深度学习模型,该模型必须能够检测神经退行性疾病的存在。神经退行性疾病通常有阶段,越早发现,治疗越有效,因为疾病还没有发展到那种程度。
我们关注的疾病有 3 个阶段,阶段 1 是疾病的初始表现,阶段 3 是最晚期的表现。因此,检测阶段 1(感兴趣的类别)比阶段 2 和 3 更重要,因为治疗会更有效。
在训练过程中,我们达到了预期的情况,我们几乎完美地检测到了第一阶段的病例。在测试过程中,阶段 3 的病例被最好地检测到,而阶段 1 的病例被正确分类的最少。这显然是一个过拟合问题,因为模型无法对新数据进行分类。
有时这并不像看起来那样清楚。当我在学习深度学习时,我面临过这样的问题。我的模型在训练时对阶段 1 有 70%的回忆,是最好的分类,阶段 3 有 50%的回忆,是第二好的分类。测试时,第一阶段的召回率为 60%,而第三阶段的召回率为 80%。这些结果表明这个模型不好,但是我没有足够的经验去理解它。首先,这个模型对于医学专家来说是没有用的,因为他们想要一个可以在疾病不明显的时候诊断疾病的模型。第二,即使第一阶段的回忆没有显著变化,第三阶段的回忆还是有显著变化。这意味着训练数据不能代表测试期间可能出现的情况,并且模型不能概括其知识。
正如我之前所说,这在生物信息学问题中非常常见,在一些科学论文中,作者无法发现他们过度拟合了他们的模型,或者没有以适当的方式解决它。
大多数人使用数据扩充技术是为了产生更多不同案例的新例子。这在一些情况下很有用,比如图像增强问题。在图像恢复问题中,模型识别噪声之类的伪像,并试图消除它们。它在训练中学习的不同类型的工件越多,它对新图像的处理就越好。一些作者用人工噪声创建新的图像,以便在训练期间增加多样性。
在其他问题中,比如医学问题,人工数据可以代表自然界中不可能存在的病例。用错误的数据训练模型可能会使它产生偏差。这在学生或刚开始机器学习的人中间很常见。他们不检查增加的数据,然后有像这样的不现实的情况:
案例 2:不平衡数据
第二种情况是我在新手中看到的。当我还是个大学生时,我也遇到了同样的问题。有些问题是使用的数据非常不平衡。例如,在我使用的数据集中,90%的数据来自类 0,10%来自类 1。
当将数据分为测试和训练数据时,我使用了保留策略,80%的数据用于训练,20%用于测试。很明显这是个坏主意。在训练过程中,模型显示了很高的准确性和回忆价值。测试中的这些值也很高,因为数据来自相同的数据集。我并没有在那一刻意识到,因为我在训练和测试阶段都取得了不错的成绩。
当使用非常不平衡的数据时,模型倾向于学习最常见的类。我的情况是 0 级。大多数人没有意识到的问题是,模型将大多数的例子归为一个特定的类。发生这种情况是因为该模型了解到给定的示例更有可能来自该类,所以它不会从其他类中提取信息。
在这种情况下,大多数人试图解决过度拟合问题,从代表过多的类中删除示例,或者从代表不足的类中重复数据,以便每个类包含相同数量的元素。
如果选择了删除数据的解决方案,您可能会从定义该类的可能案例范围中删除重要的和有代表性的数据。然后,该类的评估指标可能会变得更差,因为新的示例可能包含模型无法学习的用于抽象知识的特征。
另一方面,重复数据可能会使代表性较低的类的分类产生偏差。当重复数据时,大多数例子都要重复多次。该模型可以学习那些重复的例子作为该类的最具代表性的例子,并忽略该类中其他代表性较低的例子。例如,如果我们正在训练一个动物分类器,并且我们只重复灰色猫的图像,那么我们的模型可能只将灰色猫识别为猫。
一个类别的数据种类很少也会在分类中产生偏差。也就是说,该模型是欠拟合的,并且在训练期间显示出较差的结果。一个众所周知的袋子是图里奥·里贝罗等人在 2016 年[1]测试的一个区分狼和哈士奇的模型。最终的结果是一个模型,如果背景中有雪,它会将图像中的动物检测为狼。
案例 3:数据种类少
有时数据很难获得。有时没有可用的资源或数据,而获取这些资源或数据又很昂贵。当这种情况发生时,人们倾向于只使用他们拥有的少量数据。例如,医疗数据很难获得。有时研究人员只有一个病人或几个病人的数据(几个来源)。这意味着大多数数据示例是相关和依赖的。
当数据来自少数来源时,模型在部署用于生产时可能会出现问题。在我的案例中,我只有一个病人的视网膜图像,并且我正在尝试制作一个系统来检测视网膜图像中的血管。由于示例数量较少,我不得不将数据分成训练和测试数据进行交叉验证。在培训和测试中,我都取得了不错的成绩。
在部署该系统时,对第二名患者进行了测试。不出所料,结果很糟糕。人体的器官通常互不相同。
与前一种情况一样,所有类别中的少量数据可能会产生偏差,但不会过度拟合。只检测到与训练模型的少数案例非常相似的案例。
当这种情况发生时,缺乏经验的人很难找到问题的根源。因此,为了能够做出特别的解决方案,最好尝试不同的技术,而不仅仅是机器学习。当你没有更多的数据时,这将是一个快速的解决方案。
经验是伟大的老师
这里解释的案例是一些人们通常不会识别过度拟合问题的例子。都是我亲身遇到的案例。我发现它们都很有趣,尤其是对那些刚刚起步的人来说。这个世界上没有人生来就什么都懂,实践和实验是我们最好的老师。永远不要停止学习和实验。
如果你对这篇文章有任何疑问或修正,请不要犹豫,通过 LinkedIn 向我提出问题或建议。
谢谢您们。
参考
[1] M. T .里贝罗,s .辛格,c .盖斯特林,“我为什么要相信你?《解释任何分类器的预测》(2016),第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集。
事件表中的重叠时间段问题
在数据科学领域,人们往往认为清理数据很无聊,并希望更多的机器学习和建模挑战,但有时可能会出现一些问题,如有趣的脑筋急转弯或数据清理期间的算法难题。这是我最近在清理活动桌时遇到的一个有趣的例子。它不像疯狂的 ML 算法或新的很酷的 python 工具,而是一个值得花几分钟去解决的有趣的小问题。
当我们构建任何类型的数据管道或 ML 管道时,尤其是在处理预测问题时,事件表是非常常见的。事件表类似于事务表,通常具有开始时间、结束时间、人员 ID 和其他事件属性。每一行都是一个事件,一些事件可能会重叠,这可能是一个问题。现实世界中的事件表可能如下所示:
person_id start end
0 1 2017-01-01 2017-06-01
1 1 2017-02-01 2017-03-01
2 1 2017-05-01 2017-08-01
3 1 2017-09-01 2017-10-01
4 1 2017-11-01 2018-02-01
5 1 2018-01-01 2018-03-01
有时我们可以忍受,但有时我们希望将那些重叠的行合并成一行,最早开始时间和最晚结束时间如下:
person_id start end
0 1 2017-01-01 2017-08-01
1 1 2017-09-01 2017-10-01
2 1 2017-11-01 2018-03-01
也就是说,由于第二个事件和第三个事件都与第一个事件重叠,即使第三个事件不与第二个事件重叠,我们希望合并的事件具有第一个事件的开始时间和第三个事件的结束时间。这看起来很简单,但要找出一个好的解决方案并不容易。
在这里,我用 python 来解决这个问题。
简单案例:
在第一种解决方案中,我们可以将此视为图连通性问题。每个事件都是一个节点。如果两个事件重叠,那么它们用一条边连接。因此,前三个事件将形成一个连通子图,第三个事件是一个孤立节点类子图,第四个和第五个事件将形成另一个连通子图。我们在原始数据帧中给每个子图分配一个 group_id。现在,我们可以按 group_id 分组,并按 min(开始时间)和 max(结束时间)聚合,然后我们就有了答案。
为了实现这一点,我们将创建一个邻接矩阵来表示该图。首先,我们要定义“重叠”。这里的重叠是指:对于任意两个节点 A 和 B,如果节点 A 的开始日期在节点 B 的结束日期之前,节点 A 的结束日期在节点 B 的开始日期之后,那么 A 和 B 重叠。我们可以用numpy
数组实现逻辑来创建一个稀疏矩阵。存在几种算法来解决连通图问题。我们可以使用一个现成的功能scipy.sparse.csgraph.connected_components
来实现这一点。scipy
用类似于 Tarjan 算法的东西实现函数。然后,我们只要做简单的熊猫groupby
方法就能得到我们想要的结果。
就其数学方法而言,这是一个很好的解决方案。那只是一个人的。如果我们有数千或数百万人,这通常是真实世界的情况,图形将会变得非常大,空间复杂性将以 n 为单位增长。我们仍然可以通过在创建邻接矩阵的行中添加另一个“&”逻辑来实现这一点。
理论上,Tarjan 算法的时间复杂度是线性的。n^2
空间的复杂性使其无法扩展。这让我思考如何和pandas
一起用可扩展的方式解决。
老实说,我不是pandas
的粉丝,因为它的查询方式不直观,但是如果我们有一个矢量化的解决方案,它确实会提供很好的性能。
矢量化的解是这样的。
让我们忘掉所有的图、节点和边,用一些纯粹简单的时间序列数据来重新思考这个问题。首先,我们打破开始时间和结束时间,通过排序创建一维时间序列。我们有一列start_end
来表示是开始时间还是结束时间,其中1
是开始时间,-1
是结束时间。然后,我们对列cumsum
中的序列执行累积求和。通过查看,我们注意到新的开始时间是其cumsum
为1\. A
的开始时间,新的结束时间是其cumsum
为0
的结束时间。所以现在我们有了一个new_start
列。接下来,我们对new_start
列执行另一个累积求和来得到group_id
。最后,我们做同样的汇总得到答案。
看一下mergedf
可能更容易理解这里发生了什么。
person_id time start_end cumsum new_start group
0 1 2017-01-01 1 1 True 1.0
1 1 2017-02-01 1 2 False 1.0
1 1 2017-03-01 -1 1 False 1.0
2 1 2017-05-01 1 2 False 1.0
0 1 2017-06-01 -1 1 False 1.0
2 1 2017-08-01 -1 0 False 1.0
3 1 2017-09-01 1 1 True 2.0
3 1 2017-10-01 -1 0 False 2.0
4 1 2017-11-01 1 1 True 3.0
5 1 2018-01-01 1 2 False 3.0
4 1 2018-02-01 -1 1 False 3.0
5 1 2018-03-01 -1 0 False 3.0
性能
由于我不想太深入的研究 scipy 的connected_component
的实现,分析它的复杂性,所以我只想用一种实证的方式比较一下可扩展性。我所做的只是将示例n
重复几次,以创建一个大的事件表,并随着n
数量级的增长来测量运行时间和峰值内存。
在 n 达到 10 之前,这两种方法在运行时和内存上都有点相似。在那之后,很明显,图表方法很快在运行时和内存中爆炸。我实际上是在一台 200GB RAM 的机器上用n=100000
运行图形方法时遇到了内存泄漏。肯定有一些方法可以优化图形方法,这是我很想看到的,但我在这里试图指出的是,scipy 的connected_component
背后的操作比 pandas 中的矢量化更加占用内存。我猜有时候丑陋的算法可能会打败美丽的算法。
size mem runtime approach
0 10 82 0.083125 graph
1 50 83 0.111390 graph
2 100 85 0.094990 graph
3 500 150 0.458318 graph
4 1000 305 1.544724 graph
5 5000 6964 27.119864 graph
6 10000 27576 113.710234 graph
7 100000 inf inf graph
8 10 82 0.092768 vectorization
9 50 83 0.125092 vectorization
10 100 83 0.121607 vectorization
11 500 86 0.091670 vectorization
12 1000 89 0.168066 vectorization
13 5000 101 0.154213 vectorization
14 10000 115 0.224327 vectorization
15 50000 216 1.523057 vectorization
16 100000 351 2.482687 vectorization
17 500000 1407 6.840897 vectorization
18 1000000 2707 12.607009 vectorization
在疫情和抗议声中俯瞰纽约市的犯罪
到目前为止,犯罪数据讲述了 2020 年纽约市怎样的故事?
简介:纽约是怎么回事?
这些天来,纽约市的犯罪率有了非常矛盾的想法。一些政治家说,暴力犯罪在纽约猖獗,但其他一些人说,由于封锁,这里比以往任何时候都安全。随着选举的临近,我想这只是一直以来被用于政治利益的公共话题之一。但我个人也得到了很多版本的故事。
我有朋友和家人说它变得不适合居住了。与此同时,我还没有从仍在这座城市的外国朋友或同事那里听到任何不同的消息。这让我有兴趣回到我差不多一年前的关于纽约犯罪的老项目,这次做些不同的事情。
相互矛盾的信息无处不在。来源:谷歌
主题
基于上述非常矛盾的观点,我可以想到这两个相关的问题:
- 我们能找到 COVID19 或抗议对犯罪率的总体影响吗?
- 纽约变得比以前更加暴力或危险了吗?2020 年什么类型的犯罪最突出?
数据
鉴于 2020 年与前几年有着前所未有的不同,这不是一件容易的事情。为了不涉及政治观点或预设,我选择开源数据来做研究。我们将在 NYCOpenData 上重新查看 NYPD 的投诉数据,但这一次,我们将选取过去五年(从 2015 年开始)的数据。然而,要回答这些话题,有一个巨大的警告和问题。
- 我们必须假设 NYPD 的犯罪记录系统在任何特定事件中没有出现任何故障。记录并没有因为疫情或抗议而停止或更改可能会影响记录在数据集中显示方式的过程。
- 由于这篇文章的时间是 2020 年 11 月初,我们错过了这一年另一半的数据。因此,我们只提取了每年前六个月的数据来做测试。
众所周知,犯罪率会随着季节的变化而变化。如果我们将前几年的数据与时间一起绘制,我们可以清楚地看到一些相同的季节性。
p.s.2 大部分过程我都是用 Python 搭配 Pandas & Matplotlib 包。为了简单起见,我将在整篇文章中保留最少的代码,但是如果您对数据处理、可视化或某些细节感兴趣,请随时联系我以获取更多信息。
我们能找到 COVID19 或抗议对犯罪率的总体影响吗?
在应用任何统计数据之前,我们可以从简单的图表中发现明显的差异。上半年中期(3 月左右),2020 年起的犯罪数据有一个大的低谷。之后,犯罪率略微恢复到温和水平,如下图所示。
如果我们把一个更详细的时间框架放在轴上,并专注于 2020 年,我们可以发现一些重大事件和下降之间的巧合。
从上图来看,行政命令与下跌多少有些关联。令我们惊讶的是。抗议并没有明显增加犯罪事件的总数。(我们会深入挖掘,在后面的部分考察犯罪类型。)
统计分析
到目前为止,我们可能已经看到了这两组之间的差异,但让我们只是说,我们不考虑时间因素,而只考虑每天犯罪数量的总分布的差异。通过统计测试(安德森-达林测试、T5【一种统计测试,测试给定的数据样本是否来自给定的概率分布),我们可以看到过去 5 年和 2020 年代的分布是否相同。在设定下(零假设是“2020 年的数据取自同一个分布”,置信度设定为 95%),我们理直气壮地否定了假设,说 2020 年的数据不是从一个 5 年数据的同一个分布产生的。
这是一个图表(直方图和累积分布函数),以直观的方式显示了这两者之间的不同。
p.s .虽然日犯罪案件数是离散值,但对于通常用于连续数据的 Anderson-Darling 检验来说,采样数已经足够大了(5 年直方图类似于正态分布)。
直到这一点,我们已经证明了 2020 年的犯罪率不同于之前的 5 年。此外,我们可以推测,COVID 似乎会显著影响犯罪率,但抗议不会以这种方式出现。为了对后一部分进行一点扩展,我们应该进入下一个主题,研究犯罪类别。
纽约变得比以前更加暴力或危险了吗?2020 年什么类型的犯罪最突出?
在这里,我将使用相同的任意规则对犯罪事件进行分类(更多解释请参见我的上一篇文章)。我们将调查与之前的 5 年相比,2020 年是否有任何类型的犯罪激增。
另外,为了避免与犯罪类型相关的可能的季节性,我们将只使用上半年的数据。还有,5 年数据是过去 5 年数据的平均值。
从 5 年和 2020 年数据的差异可以看出,2020 年大多数类型的犯罪事件都有所下降。然而,在某些类型中有一些明显的增加。入室盗窃、机动车盗窃、纵火和谋杀在 2020 年会有一定程度的增加。
让我们绘制这些类型随时间的变化图,以便更仔细地观察。
我们可以从上面的情节中总结出一些要点:
- 大多数犯罪似乎都受到了 COVID19 关闭的影响,因为我们在 3 月份看到了几乎相同的下降。然而,由于某种原因,机动车辆盗窃、入室盗窃、纵火和赌博在 3 月份似乎没有受到影响。可能这些类型的犯罪并不一定受到关闭的限制。
- 总的来说,在经历了 3 月份的大规模下跌后,大多数犯罪事件在一定程度上有所反弹,但仍低于年初的数字。我们可以推测,犯罪事件在某种程度上与经济活动水平相关,特别是非暴力犯罪(例如,社会商业相关犯罪、盗窃、欺诈)
- 在这些分布的后半部分(大约 5 月),我们可以看到一些事件的高峰。具体的暴力犯罪(如破坏财产罪、纵火罪、严重伤害罪、入室盗窃罪)已经超过了过去 5 年的正常水平。我们可能将此归因于 5 月 29 日开始的一系列抗议活动。入室盗窃和纵火案达到创纪录的高水平,因为这在正常情况下并不经常发生。
- 抗议确实引发了特定类型的犯罪事件,这对这座城市来说是灾难性的。也就是说,大多数犯罪事件仍然低于 5 年的平均水平,媒体广泛报道的一些具体事件(入室盗窃和纵火)仅在一周内上升。当我们看到一些趋势时,恶性袭击和机动车辆盗窃可能是新的问题,但是这里的可用数据 并不完全支持纽约市这些天变得更加暴力的说法,在我看来。
值得指出的一点是,谋杀案件的实际数量远小于其他案件(例如,规模为 100 起)。因此,该百分比会随着轻微的波动而显得更加吓人。然而,这并不是淡化令人痛心的事件!对于像谋杀和枪击这样的犯罪,感知和原因远比数字更重要。这需要更详细的数据来确定根本原因(如街头帮派、个别事件)。这种探索的局限性阻止了我们在这个问题上获得更具体的见解。
结论:现在还很难说
鉴于多个前所未有的事情同时且紧密发生(纽约市上一次宵禁是在 1945 年!).然而,看真实的数据并试图描绘出这个主题的真实概貌是有趣的,或者说是负责任的。
为了结束这项研究,我只能轻率地得出结论:由于疫情,纽约市的犯罪率较低,尽管它在一到两周内经历了“激进抗议”的高峰。很容易得出这样的结论:纽约市现在是一个犯罪的粪坑,但与过去相比,这个城市至少在上半年还不算太糟。如果我们想得出任何可靠的结论,关键是要继续跟踪数据,看看总体犯罪数字是否有系统性的变化。
下一步是什么?
过了 2020 年(希望,我们能做到😀),我会再次重温这个主题,看看是否有更有趣的东西可以调查。还有一个更有影响力的事件,11 月份的美国大选,这将是另一个观察政治活动如何影响城市犯罪率的绝佳机会。
感谢阅读长文!有任何想法和意见,欢迎随时回复,或者通过 Linkedin 联系我!
附:如果你对新冠肺炎的一些州级信息感兴趣,一定要访问这个整洁的个人仪表盘来跟踪曲线。
杰夫
覆盖 Power BI 中的日期过滤器
在 Power BI 中覆盖标准的日期过滤可能比您想象的要复杂
伊加特·库尚列夫在 Unsplash 上拍摄的照片
最近,我的一个朋友从他的客户那里得到了一个有趣的请求。他们需要根据日期选择来查看过去指定时间段的值。
例如,如果他们选择 2020 年 3 月 31 日,他们需要查看前 12 个月的值,因此从 2019 年 4 月 1 日开始。此外,他们需要一个选项来选择特定的日期,并查看所选日期的年初值。同样,如果他们选择 2020 年 3 月 31 日,他们需要查看从 2020 年 1 月 1 日开始的值。视觉效果将被调整为基于用户选择动态变化。
也就是说,编辑交互作为一种从特定视觉中移除过滤器的方式,并不是一种选择,因为它会完全拒绝用户选择任何特定的值进行过滤。
棘手的部分
这里最棘手的事情是建立日期维度和事实表之间的正确关系。因为,如果您依赖于日期维度和事实数据表之间的“正常”关系,数据将根据日期选择进行筛选,日期选择充当这两个表之间的关系。
让我们前往 Power BI Desktop,看看是否可以做些什么。
正如您在上面的模型视图中所看到的,DimDate 和 FactOnlineSales 与 DateKey 列相连接。因此,只要我在切片器中选择日期,我的条形图就只显示那些由切片器选择过滤的值,如下面的屏幕截图所示。
这里的主要问题是:我们如何“覆盖”从日期切片器传递的值。
解决方案的关键
这里的关键是断开日期维度和事实表之间的“常规”关系。我们需要一个独立的,不连续的日期表,这将有助于定义我们需要在条形图视觉显示值的时间框架。
因此,我将使用以下定义创建一个新表:
Dates = DISTINCT(FactOnlineSales[DateKey])
这将包括事实表中的所有日期。该表与我们的事实表保持断开,正如您在模型视图中看到的:
现在,让我们创建一个新的度量,它将计算我们指定的日期内的销售额:
Sales Amt =
VAR MaxDate = MAX(Dates[DatesDateKey])
VAR MinDate = CALCULATE(MIN(Dates[DatesDateKey]),
ALLEXCEPT(Dates,Dates[DatesDateKey])
)
VAR SalesAmt = CALCULATE(SUM(FactOnlineSales[SalesAmount]),
FILTER(FactOnlineSales,FactOnlineSales[DateKey] >= MinDate && FactOnlineSales[DateKey] <= MaxDate))
RETURN
SalesAmt
我们在这里所做的基本上如下:我们正在定义一个变量,该变量将选择最后选择的日期(MaxDate)。然后,我们定义我们的起点:在这种情况下,它将在日期表中找到第一个日期,用 ALLEXCEPT 函数覆盖现有的过滤器上下文。最后,我们使用过滤器函数计算 SalesAmt 值,以便根据变量中设置的日期限制时间范围。在我们将新的度量拖到条形图视觉效果后,我们将得到以下结果:
因此,如果我选择 2008 年的第 4 季度,我将看到从开始(事实表中的第一个日期值)到所选期间的所有值。如果我选择 2009 年第 3 季度,我将得到以下值:
很酷吧,哈?
微调我们的解决方案
但是,如果我们只需要查看所选日期年初以来的值,或者所选日期之前 12 个月的值,该怎么办呢?
断开连接的表保持不变,因为,别忘了,它是解决这个问题的关键要素。我们将稍微改变一下计算销售额的方法。
如果要查看年初以来的所有值,可以像这样调整 measure:
Sales Amt StartYear =
VAR MaxDate = MAX('Dates'[DatesDateKey])
VAR MinDate = STARTOFYEAR('Dates'[DatesDateKey])
VAR Result = CALCULATE(
SUM(FactOnlineSales[SalesAmount]),
FILTER(FactOnlineSales,
FactOnlineSales[DateKey] >=MinDate && FactOnlineSales[DateKey]<=MaxDate)
)
RETURN
Result
所以,唯一的区别是 MinDate 变量的定义。这里,我们使用 DAX 函数 STARTOFYEAR 来获取所选日期的第一个日期。
另一个选项使您能够查看您根据需要定义的尾随期间:以下示例显示了过去 12 个月,但您可以轻松地修改它:
Sales Amt -12 Months =
VAR MaxDate = MAX('Dates'[DatesDateKey])
VAR MinDate = DATE(YEAR(MaxDate),MONTH(MaxDate)-12,DAY(MaxDate))
VAR Result = CALCULATE(
SUM(FactOnlineSales[SalesAmount]),
FILTER(FactOnlineSales,
FactOnlineSales[DateKey] >=MinDate && FactOnlineSales[DateKey]<=MaxDate)
)
RETURN
Result
同样,唯一的区别在于起点的定义。通过组合日期和月份函数,我们告诉我们的度量我们想要计算过去多长时间。在这种情况下,它是-12 个月,但您也可以使用年、季度、天作为标准。
年初的例子
-12 个月的示例
结论
正如您所看到的,我们可以使用一些非标准的技术来覆盖过滤器的常规行为,比如创建定制的非连接表。
感谢阅读!
过采样和欠采样
一种不平衡分类技术
我简介
不平衡分类问题是当我们的训练数据的类别分布存在严重偏斜时我们所面临的问题。好吧,偏斜可能不是非常严重(它可以变化),但我们将不平衡分类识别为一个问题的原因是因为它可以影响我们的机器学习算法的性能。
不平衡可能影响我们的机器学习算法的一种方式是当我们的算法完全忽略少数类时。这之所以是一个问题,是因为少数民族阶层通常是我们最感兴趣的阶层。例如,当构建一个分类器来根据各种观察结果对欺诈性和非欺诈性交易进行分类时,数据中的非欺诈性交易可能比欺诈性交易多,我的意思是想一想,如果我们有等量的欺诈性交易和非欺诈性交易,这将非常令人担忧。
图 1:欺诈检测问题的类别分布示例
应对这一挑战的方法是*随机抽样。*执行随机重采样有两种主要方式,各有利弊:
过采样 —从少数类中复制样本
欠采样 —从多数类中删除样本。
换句话说,过采样和欠采样都涉及引入一种偏向,从一个类别中选择比另一个类别更多的样本,以补偿数据中已经存在的不平衡,或者如果采取纯粹随机的样本,可能会出现的不平衡(来源:维基百科)。
我们将随机抽样定义为一种幼稚的技术,因为在执行时,它不假设任何数据。它涉及创建我们数据的新转换版本,其中有一个新的类分布,以减少数据对我们机器学习算法的影响。
注意:我们称随机重采样为幼稚,因为在执行时,它不对数据做任何假设。
在本文中,我们将利用 2014 年启动的imbalanced-learn
框架,主要关注 SMOTE(另一种不平衡数据技术)的实施。多年来,已经实现了额外的过采样和欠采样方法,并使该框架与流行的机器学习框架scikit-learn
兼容。访问不平衡学习获取安装指南和完整文档。
from sklearn.datasets import make_classification
from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler
from collections import Counter# defining the dataset
X, y = make_classification(n_samples= 10000, weights=[.99])# class distribution
**print**(Counter(y))Counter({0: 9844, 1: 156})
完整的代码你可以访问我的 Github 。
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/kurtispykes/demo/blob/master/notebook/kpy_random_sampling_example.ipynb)
随机过采样
随机过采样包括用替换从少数类中选择随机样本,并用该样本的多个副本补充训练数据,因此单个样本可能被选择多次。
随机过采样可能增加过拟合发生的可能性,因为它精确复制了少数类的例子。以这种方式,举例来说,一个符号分类器可能构建表面上准确的规则,但实际上覆盖了一个复制的例子。”—第 83 页,从不平衡数据集学习,2018。
对于受偏斜分布影响的机器学习算法,如人工神经网络和支持向量机,这是一种非常有效的技术。然而,在许多情况下建议调整目标类分布,因为为严重不平衡的数据集寻求平衡分布会导致算法过度适应少数类,从而导致我们的泛化错误增加。
我们应该意识到的另一件事是计算成本的增加。当我们训练模型时,增加少数类中的示例数量(特别是对于严重偏斜的数据集)可能会导致计算量增加,并且考虑到模型多次看到相同的示例,这不是一件好事。
尽管如此,过采样是一个相当不错的解决方案,应该进行测试。下面是我们如何用 Python 实现它…
# instantiating the random over sampler
ros = RandomOverSampler()
# resampling X, y
X_ros, y_ros = ros.fit_resample(X, y)# new class distribution
**print**(Counter(y_ros))Counter({0: 9844, 1: 9844})
随机欠采样
随机欠采样与随机过采样相反。该方法试图从多数类中随机选择和移除样本,从而减少变换数据中多数类中的样本数量。
“在(潜在的)随机欠采样中,大量数据被丢弃。[……]这可能是一个很大的问题,因为这些数据的丢失会使少数和多数实例之间的决策界限更难了解,从而导致分类性能的损失。”——第 45 页,不平衡学习:基础、算法和应用,2013 年
欠采样的结果是在多数类中具有较少样本的变换数据集——可以重复该过程,直到每个类中的样本数量相等。
尽管存在严重的不平衡,但在少数类有足够数量的例子的情况下,使用这种方法是有效的。另一方面,考虑有价值的信息被删除的可能性总是很重要的,因为我们随机地将它们从我们的数据集中删除,因为我们没有办法检测或保存在多数类中信息丰富的例子。
为了更好地理解这个方法,这里有一个 python 实现…
# instantiating the random undersampler
rus = RandomUnderSampler()
# resampling X, y
X_rus, y_rus = rus.fit_resample(X, y)# new class distribution
print(Counter(y_rus))Counter({0: 156, 1: 156})
结合两种随机抽样技术
与单独执行的方法相比,结合使用两种随机采样方法有时可以提高整体性能。
其概念是,我们可以对少数类应用适度的过采样,这改善了对少数类示例的偏差,同时我们还对多数类执行适度的欠采样,以减少对多数类示例的偏差。
为了在 Python 中实现这一点,利用imbalanced-learn
框架,我们可以在过采样和欠采样技术中使用sampling_strategy
属性。
# instantiating over and under sampler
over = RandomOverSampler(sampling_strategy=0.5)
under = RandomUnderSampler(sampling_strategy=0.8)# first performing oversampling to minority class
X_over, y_over = over.fit_resample(X, y)
**print**(f"Oversampled: {Counter(y_over)}")Oversampled: Counter({0: 9844, 1: 4922})# now to comine under sampling
X_combined_sampling, y_combined_sampling = under.fit_resample(X_over, y_over)
**print**(f"Combined Random Sampling: {Counter(y_combined_sampling)}")Combined Random Sampling: Counter({0: 6152, 1: 4922})
包裹
在本指南中,我们讨论了不平衡分类的过采样和欠采样。在许多情况下,我们可能会遇到不平衡的数据集,应用随机抽样可以为我们提供一个非常好的模型来克服训练中的这个问题,并仍然保持一个可以很好地推广到新示例的模型。
让我们继续 LinkedIn 上的对话…
[## Kurtis Pykes -人工智能作家-走向数据科学| LinkedIn
在世界上最大的职业社区 LinkedIn 上查看 Kurtis Pykes 的个人资料。Kurtis 有两个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/kurtispykes/)
非少数类的过采样
有意或无意地使用 SMOTENC 方法
平衡法~律师和数据集;图片来自 Robert J. Debry Associates
我的一个朋友最近决定申请法学院,这是一项需要多年准备的艰巨任务。参加 LSAT 本身就是一项令人生畏的任务,而且这仅仅是收集你的建议并使每一份申请适合学校的漫长过程中的第一步。
就在那时,我决定创造一个预测器,它可以告诉你被某所法学院拒绝、列入等候名单或录取的概率。这帮助了她,也有望帮助其他人决定去哪里申请并投入他们的精力和金钱(法学院的申请并不便宜!).
我从一个非盈利网站搜集数据,该网站允许以前的法学院申请者输入他们在法学院的统计数据和成绩。我在这里使用的统计数据是本科 GPA、LSAT、URM 和工作经验作为我的预测指标。值得注意的是,所有这些数据都是自我报告的。
法学院通常是根据排名来评判的,而排名每年都会变化。排名基于许多变量,包括毕业率、即将入学班级的 LSAT 中值分数、就业安置成功率以及即将入学班级的本科生 GPA 中值。法学院每年都会调整他们的入学标准。我在创建预测器时考虑到了这一点,将最近一年的数据“加权”得比往年更重。
实际上,这听起来是个好主意,但是我在 Python 实现上遇到了一些麻烦。衡量最近一年的最佳方法是什么?我将 2019 年至 2020 年的入学周期作为我的测试数据,并希望 2018 年至 2019 年比之前的几年更重要。
首先,我复制了一些行,发现 F-measure 得分比我的基线模型有所提高。
school_df = school_df.append([df_cycle18],ignore_index=True)
然而,我认为这并没有给模型提供任何额外的信息。我想为我的模型创建新的信息和新的可行数据,所以我考虑使用 MCMC,但最终使用了 SMOTE。为了使用 SMOTE 来增加最近一年的观测数据,我不得不重组这个问题。
入学年是现在的目标,我的新预测指标是 GPA,LSAT,URM,工作经验和决定(旧目标:录取,拒绝,等待)。我通过创建一个字典并将其传递给 SMOTE 的sampling_strategy
参数来指定要过采样的周期:
cycles = np.unique(smote_cycle_y)for cycle in cycles:
smt_dict[cycle] = smote_cycle_y[smote_cycle_y==cycle].shape[0]smt_dict[np.max(smote_cycle_y)] = smt_dict[np.max(smote_cycle_y)]*2oversample = SMOTE(sampling_strategy = smt_dict)
然后我看了看我的数据,发现 SMOTE 默认只处理连续变量。它将我的接受、拒绝或等待变量转换成浮点数。我之前定义良好的分类问题也有一些浮动,因此创建了 3 个以上的类。作为一个快速的解决方案,我将这些浮点数四舍五入为整数 0、1 或 2,这一结果令人惊讶地好。然而,我需要一个更好的解决方案。
接下来,我开始搜索多标签分类 SMOTE,并争论创建一个自定义类,然后我找到了答案,SMOTENC。SMOTENC 允许您识别哪些类是名义类,哪些是范畴类,因此称为“NC”。最近一年的过采样现在是无缝的:
smt_dict2 = {}for cycle in np.unique(smt_cycle_y):
smt_dict2[cycle] = smt_cycle_y[smt_cycle_y==cycle].shape[0]smt_dict2[np.max(smt_cycle_y)] = smt_dict2[np.max(smt_cycle_y)]*2oversample = SMOTENC(sampling_strategy = smt_dict2, categorical_features = [0,3,4], random_state = 7)
结果很好,再次提高了 F 值。现在我在我的少数民族类上使用了来自我的原始数据的 SMOTENC,这非常有帮助。
smote_2_y = df_resample_train[['decision_numeric']] #original targetsamp_dict_2 = {}max_class = np.max(smote_2_y['decision_numeric'].value_counts())for cycle_id_smote in np.unique(smote_2_y):
samp_dict_2[cycle_id_smote] = max_classoversample_2 = SMOTENC(sampling_strategy = samp_dict_2, categorical_features = [2,3])smote_2_y = df_resample_train.decision_numericX_fin, y_fin = oversample_2.fit_resample(smote_2_X,smote_2_y)
如您所见,F-measure 增加了,对数损失减少了,我们的整体模型得到了改善:
希望本文对您有所帮助,并向您展示了过采样的强大功能。要阅读更多关于 SMOTE 方法论的内容,请查看这里的文档和这个伟大的解释。这里是代码报告和工作法学院预测应用(使用 streamlit 制作)的链接!)
如果这篇文章对你有帮助,我很乐意听听!在LinkedIn上联系我。
VAEs 过采样
使用 VAEs 生成欺诈交易
我作为一个研究团队的成员与 VAEs 一起工作了一段时间。我总是想知道它们在实践中何时何地被使用。当然有很多很好的例子。在这篇博客中,我想向你展示如何使用 VAE 在高度不平衡的信用卡欺诈检测数据上训练一个分类器。
- 如何训练一个简单的 VAE
- 如何使用训练有素的 VAE 平衡数据?(我将讨论两种策略)
- SMOTE 的讨论与比较
使用简单的流程生成复杂的数据。图片摘自[http://PhD thesis-bio informatics-maxplanckinstitute-molecularplanphys . Matthias-scholz . de/]
1.如何训练简单的 VAE?
VAE 是一种生成模型,试图通过将特征与一组潜在变量相关联来捕捉特征的联合概率分布。
假设有一个简单高斯分布的变量z
。如果我们通过一个密集的神经网络传递这个变量,z
的不同维度开始混合在一起,并在输出的维度(在这种情况下是数据的特征)之间创建高度复杂的相关性。
p(x) = p(x|z)p(z)
在提供的代码中,您将看到如何定义一个模型并训练它。
为了简化,我修改了目标,使用 MSE 作为 VAE 目标的重建部分的目标函数。所以我们训练的不是爱尔博,而是一个伪爱尔博。
我选择只在少数民族班训练 VAE。这完全忽略了数据中的结构(在这种情况下,正常的事务是什么样子)。然而,我认为这没关系,因为少数类不同于正常数据。
2.如何使用训练有素的 VAE 平衡数据?
一旦我们有了训练好的模型,我们就可以使用生成尽可能多的样本。
2.1 通过正向传递生成样本
如所解释的,使用 VAE 的分级设计,我们可以通过简单地使正态高斯分布的样本通过解码器网络来从模型中采样。根据模型的能力,我们可能会也可能不会准确地捕捉到这种分布。这就是所谓的方差高估。
使用这种方法,开箱即用的 XGBoost 分类器给出的平均精确召回分数为 0.50
2.2 通过潜在空间中的插值生成样本
另一种生成样本的方法是使用 VAE 的 autoencoder 属性。
首先,我们通过编码器网络传递现有数据。一旦我们找到了最佳点(每个潜在维度的平均值),我们就可以用它在不同的点之间进行插值。例如,我们可以取另一个第二个数据点,找到它的最佳潜在变量值,然后在潜在空间中的值之间随机插值。
Z1 ~ p(Z1 | x1),z2 ~ p(z2 | x2)
z = a Z1+(1-a)z2
x~p(x | z)
如果我们对所有可用的数据进行编码,并对它们进行许多随机配对插值,我们就可以生成许多新的样本,用少数类来扩充数据。
该方法显著改善了分类结果。我发现 XGBoost 和 Random Forest 给出了或多或少相同的召回率、f1 值和 0.65 的平均精确召回率。
结论
我们可以看到使用 VAE 对数据集进行过采样是多么容易。这种方法工作得非常好,它提高了 F1 分数和回忆分数,同时保持大约相同的准确度(并且非常高,达到 99.98%)。
然而,似乎像 SMOTE 这样更好的方法更适合过采样的工作。平心而论,如果我们有大量的维度,SMOTE 的计算量要少得多。这还能进一步改进吗?
最后的想法
我想我留给你几个问题。我可能会创建其他博客来回答这些问题。
——你认为这是 VAE 的一个好用途吗?
-您认为使用 VAE 可以在多大程度上改善过采样过程?
-你还能想到其他能受益于 VAE 的应用吗?
-有没有办法通过将信息传播到 VAE 来提高分类器的性能?
请查看这个存储库,获取工作示例、代码和更详细的讨论。
https://github.com/hosseinsadeghi/oversampling_vae
聚类算法概述
实践聚类算法:Python 中的演练!
凯利·西克玛在 Unsplash 上的照片
使聚集
聚类是一种无监督的技术,其中相似数据点的集合被分组在一起以形成聚类。如果簇内(同一簇内的数据点)相似性高,而簇间(簇外的数据点)相似性低,则称该簇是好的。聚类也可以被视为一种数据压缩技术,其中一个聚类的数据点可以被视为一个组。聚类也称为数据分割,因为它对数据进行分区,使得一组相似的数据点形成一个聚类。
聚类与分类有何不同?
分类算法是区分组和分类的好技术。分类需要手动标注数据,当数据集很大时,这是一个很累的过程。反过来的过程怎么样?即将相似的数据点分割在一起,并给它们分配聚类标签。聚类算法不需要标签来进一步处理,因为它是一种无监督的技术。
集群的要求
- 可量测性
- 应对不同属性的潜力
- 对噪声和异常值的鲁棒性
- 面对高维数据的能力
聚类方法
最常见的聚类方法是,
- 分割方法
- 分层方法
- 基于密度的方法
- 基于模型的方法
分区方法: 分区方法包括对数据进行分区,对相似项的组进行聚类。该方法中常用的算法有,
- k 均值
- k-水母类
- k 模式
分层方法: 分层方法是将数据进行分层分解。存在两种类型的分层方法,
- 凝聚(自下而上的方法)
- 分裂(自上而下的方法)
基于密度的方法: 基于密度的方法用于异常检测。高密度的数据点被分组在一起,留下低密度的数据点。
- 带噪声应用的基于密度的空间聚类
- 光学(排序点以识别簇结构)
基于模型的方法: 基于模型的方法涉及应用模型来寻找最佳的聚类结构。
- EM(期望最大化)算法
在这篇文章中,我们集中讨论一些分割方法和基于密度的方法的算法。
用 Python 加载所需的库
分割方法
1.k 均值聚类
k 均值聚类是一种经典的聚类方法。K-Means 通过计算一个聚类的均值来迭代地重新定位聚类中心。
- 最初,K-Means 随机选择 k 个聚类中心。
- 计算每个数据点和聚类中心之间的距离(通常使用欧几里德距离)。数据点被分配给与其非常接近的聚类。
- 在所有数据点被分配到一个聚类之后,该算法计算聚类数据点的平均值,并将聚类中心重新定位到其对应的聚类平均值。
- 这个过程一直持续到聚类中心不变。
PC:作者
使用 K-Means 的优点是可伸缩性。K-Means 在大数据上表现很好。使用 K-Means 的主要缺点是对异常值敏感。在计算聚类的均值时,离群点会产生严重的影响。聚类结果根据 k 值和聚类中心的初始选择而不同。K-Means 算法仅适用于球形数据,而不适用于任意形状的数据。
2.k-模式聚类
K-Means 适用于连续数据。分类数据呢?K-Modes 聚类可以解决这个问题。该算法非常类似于 K-Means,但是 K-Modes 不是计算聚类的平均值,而是计算聚类的模式(一个经常出现的值)。
- 最初,K-Mode 随机选择 k 个聚类中心。
- 计算每个数据点和聚类中心之间的相似性。数据点被分配给与其具有高相似性的聚类。
- 在所有数据点被分配到一个聚类之后,该算法计算聚类数据点的模式,并将聚类中心重新定位到其对应的聚类模式。
- 这个过程一直持续到聚类中心不变。
如何为 k 找到一个最优值?
肘方法和轮廓指数是最常用的方法来寻找 k 的最佳值。
基于密度的方法
1.基于密度的噪声应用空间聚类
DBSCAN(带噪声的应用程序的基于密度的空间聚类)是一种基于密度的聚类算法,能够对任意形状的数据执行良好的操作。DBSCAN 查找密集的数据点,并将其分配给一个聚类,将不太密集的数据点从该聚类中分离出来
数据库扫描术语
- 如果一个数据点 q 在另一个数据点 p 的半径ϵ 内,那么该数据点 q 被认为是该数据点 p 的ϵ-neighborhood(ε邻域)。
- 如果点 p 的ϵ-neighborhood 由 MinPts(最小点)中提到的值组成,则称数据点 p 为核心对象*。*** 例如,考虑 MinPts = 5,如果 p 的ϵ-neighborhood 由 5 个数据点组成,则 p 被称为核心对象。
- 如果点 p 在 q 的ϵ-neighborhood 内,则称一个数据点 p 是从点 q 直接密度可达的。
它是如何工作的?DBSCAN 检查每个数据点的ϵ-neighborhood。如果 p 是核心对象,并且其在ϵ-neighborhood 的数据点高于 MinPts 的值,则它在核心对象 p 周围形成新的聚类。DBSCAN 迭代地直接寻找密度可达的数据点,并且可以合并其他少数聚类。该过程继续进行,直到不再有要分析的点。
PC:作者
使用 DBSCAN 的缺点是存在超参数ϵ和 *MinPts。*产生的结果因超参数的选择值而异。
2.光学
**OPTICS(排序点以识别聚类结构)**是一种聚类算法,克服了在 DBSCAN 中使用超参数所面临的缺点。光学与 DBSCAN 非常相似,但它使用灵活的半径ϵ.值
光学术语
- 使点 p 成为核心对象的𝜖′的最小值(小于𝜖′的值)称为 p 的核心距离。
- p 的核距离和 p 与 q 之间的欧氏距离的最大值称为点 q 相对于另一个对象 p 的可达距离
PC:作者
摘要
聚类在销售和营销行业中用于识别正确的客户群。网飞使用聚类算法将兴趣相似的观众分组。聚类是研究工作的一个领域,可用算法的许多变体都处于开发阶段。
在我的 Kaggle 笔记本中找到这篇文章:https://www . ka ggle . com/srivignesh/overview-of-clustering-algorithms
参考文献:
[1]韩家玮和 Micheline Kamber,数据挖掘:概念和技术第二版(2006)
快乐的机器学习!
谢谢!
人体姿态估计神经网络概述—HRNet+higher rnet,架构和常见问题— 2d3d.ai
高分辨率网络(HRNet) 是一种用于人体姿势估计的最先进的神经网络,这是一种图像处理任务,可以在图像中找到对象的关节和身体部位的配置。该网络的新颖之处在于保持输入数据的高分辨率表示,并将其与高到低分辨率子网络并行组合,同时保持有效的计算复杂性和参数计数。
演示视频 HRNet 姿势估计超过世界纪录侏儒发射!
在本帖中,我们将介绍:
- 为什么选择 HRNet?
- 人力资源网与建筑
- HigherHRNet:用于自下而上人体姿态估计的尺度感知表征学习
- 演示视频
- 代码常见问题
为什么选择 HRNet?
可可排名第一
可可排名第一
PoseTrack2017 排名第二
MPII 排名第六
HRNet 解释道
当处理人体姿态估计时,我们需要能够检测图像中的人,并估计他的关节(或关键点)的配置。因此,存在两种可能的姿势估计方法:
自顶向下和自底向上的姿态估计
自下而上的方法首先找到关键点,然后将它们映射到图像中的不同人物,而自上而下的方法首先使用一种机制来检测图像中的人物,在每个人物实例周围放置一个边界框区域,然后估计边界框内的关键点配置。
自上而下的方法依赖于单独的人检测网络,并且需要为每个人单独估计关键点,因此它们通常是计算密集型的,因为它们不是真正的端到端系统。相比之下,自下而上的方法首先通过预测不同解剖关键点的热图来定位输入图像中所有人的无身份关键点,然后将他们分组到人实例中,这有效地使他们更快。
自上而下的方法更为普遍,并且目前实现了更好的预测准确性,因为它将两项任务分开,以使用为每项任务训练的特定神经网络,并且因为自下而上的方法由于图像中不同人的比例变化而在预测关键点方面存在问题(也就是说,直到 HigherHRNet 出现——如下)。自顶向下方法中不存在这种比例变化,因为所有人员实例都被规范化为相同的比例。而自底向上的方法被认为更快,因为
HRNet 使用自上而下的方法,该网络是基于由另一个网络(FasterRCNN)在推理\测试期间检测的人包围盒来估计关键点而构建的。在训练期间,HRNet 使用给定数据集的带注释的边界框。
两个数据集用于训练和评估网络
- COCO —超过 20 万张图片和 25 万个人实例,标注了 17 个关键点。COCO 数据集评估还需要评估人的边界框,这是使用 FasterRCNN 网络完成的。评估度量是对象关键点相似性(OKS)——标准的关键点检测准确度度量。
- MPII 人体姿势——大约 25000 张照片,40000 个主题。MPII 评估是使用数据集中带注释的边界框完成的。
体系结构
以下是基于 git 项目中代码的神经网络图,之后是研究论文中描述的网络图。
基于公开开源的 HRNet 网络架构
HRNet 网络体系结构如本文所述
需要注意的重要结构是,网络计算高分辨率子网络(分支 1)与低分辨率子网络(分支 2–4)并行。子网络通过融合层融合,使得每个高到低分辨率表示反复接收来自其他并行表示的信息,导致丰富的高分辨率表示。
输入图像为 256 x 192 或 384 x 288,对应的热图输出尺寸为 64 x 48 或 96 x 72。前两个卷积根据预期的热图大小减小输入大小。网络输出热图大小和 17 个通道-热图中每个关键点(17 个关键点)的每个像素的值。
所描述的开源架构是针对 32 通道配置的。对于 48 个通道,从第一个过渡层开始改变每一层,直到 48 个通道,不同的乘数为 2。
本文中的交换块是开源中的一个模块,交换单元是开源中的保险丝层。在纸图中,过渡层看起来像子网络的独立融合,而在代码中,当创建较低分辨率(较高通道)的子网络时,过渡是基于与另一个卷积层的融合,该融合导致先前最低分辨率的子网络。此外,在开放源代码中,最后一层的融合仅针对高分辨率分支(分支 1)进行计算,而不是针对纸图中看到的所有分支。
下采样是在融合部分(或交换单元)从高分辨率分支转移到较低分辨率分支的跨距=2 的卷积,对于双倍和三倍下采样,仅在最后一次下采样中扩大通道的数量。这要么是代码中的错误,要么是论文中没有明确解释。很可能是代码中的错误,因为信息没有从较大分辨率映射到较深通道中的第一个下采样,即 git 中的开放问题。
如果有疑问,使用基于开源的图表——这是运行训练有素的网络时使用的图表。
网络培训
- 对于权重初始化,作者使用 ImageNet 分类数据集上的不同输出层训练相同的网络,并使用权重值作为姿势估计训练的初始化值。
- 在 COCO 数据集上训练 HRNet-W32 的 210 个历元需要大约 50-60 小时,使用 4 个 P100 GPU—参考。
higher rnet:自下而上人体姿态估计的尺度感知表示学习
这是同一个研究小组使用 HRNet 作为主干的自下而上姿势跟踪的新网络。作者解决了自下而上姿态估计中的尺度变化问题(如上所述),并表示他们能够通过输出多分辨率热图和使用 HRNet 提供的高分辨率表示来解决该问题。
HigherHRNet 在 COCO 数据集上的表现优于所有其他自下而上的方法,对于中等身材的人来说收益尤其大。HigherHRNet 还在 CrowdPose 数据集上取得了最先进的结果。作者表示,这表明自下而上的方法比自上而下的方法对拥挤的场景更鲁棒,但在同一数据集上没有与常规的自上而下的 HRNet 结果进行比较。
该网络的主干是常规的 HRNet,但在末端增加了一个部分,用于输出更高分辨率的热图:
体系结构的右侧部分输出两个热图,一个用于低分辨率,一个用于高分辨率,分辨率分别为 128 x 128 和 256 x 256。在推断期间,两个热图被平均聚合到更高的分辨率,并且最高值的点被选择用于关键点检测。梯形是一个反卷积层,它输出 2 倍高的分辨率,后面是 4 个剩余块。此外,对于每个关键点,计算输出标量标签,近标签值形成属于特定人的一组关键点,远标签值指示属于不同人的关键点组。标签是根据本文中描述的“关联嵌入”方法计算的。仅针对最低分辨率的热图训练和预测标签值,因为作者发现,根据经验,较高分辨率的热图标签值无法很好地预测,甚至无法收敛。
在训练期间,损失函数是热图预测损失和标签值损失的加权平均值(根据关联嵌入方法,相同组的标签之间的较小距离导致较低的损失,不同组的标签之间的较大距离也是如此)。每个热图分辨率损失是根据地面实况独立计算的,它们是总和。
检查 HigherHRNet 的开源代码还没有推理代码可用于创建基于训练好的网络的演示姿势估计视频。
演示视频
演示视频基于 HRNet 中的推理脚本(这是一个修改过的脚本,在 joins 之间画棍子,运行时不打开 pop 图像— 脚本链接)。感谢罗斯·史密斯的 Youtube 频道。
视频特征
- 1920X1080 像素,每秒 25 帧,56 秒(1400 帧)。
- 多人、挑战性场景的好例子——同质和异质背景、变化的背景、不同的摄像机角度,包括放大和缩小,以及姿势令人敬畏的侏儒。
运行时信息
- 带有 Resnet50 的 FasterRCNN 用于人员检测
- 使用具有 48 个通道和 384 x 288 分辨率输入图像的 HRNet。
- 戴尔笔记本电脑酷睿 i5–7200,32GB 内存,GeForce 940MX,使用 Ubuntu 18.04。推断期间 GPU 达到 100%利用率。
- 跟踪一帧中所有边界框的平均时间:1.14 秒
- 一帧中所有姿态估计的平均时间:0.43 秒
- 一帧解析的平均总时间:1.62 秒
- 代码在整个视频上运行推理的总时间:2586.09 秒
演示中的问题
当评估图像处理算法的结果时,重要的是要注意该算法哪里执行得不好,这给出了其固有问题的线索:
- 具有木制背景的赤膊者在 faster CNN 中未被很好地检测到-这可能是 faster CNN 网络的训练数据问题,没有足够的赤膊样本或没有足够的背景颜色类似于人的颜色的样本
- 大黄色蹦床被检测为人(分钟 00:11)-这可能显示了同质场景的快速 CNN 的固有问题。
- 在边界框中检测到 17 个关键点,即使框中没有人或没有显示所有关节 HRNet 的构建方式是必须预测所有 17 个关节,即使它们不是可视的。
- 值得注意的是,在视频的开始,即使有遮挡,也有很好的姿势估计。处理图像中由于模糊而丢失的信息是一件棘手的事情,HRNet 能够很好地解决这个问题。
- 另外值得一提的是,矮人手持的棍子估计不是四肢之一,这也是一个积极的迹象。
代码常见问题
- 姿势跟踪在 RGB(https://github . com/leoxiaobin/deep-high-resolution-net . py torch/issues/41)中完成,而人检测基线训练网络在 BGR(https://github . com/leoxiaobin/deep-high-resolution-net . py torch/issues/15)中完成
- 使用 coco 数据集 API pycocotools 与 python 3【https://github.com/cocodataset/cocoapi/issues/49 不兼容。HRNet 基本上可以工作,但是一旦你开始使用 pycocotools,可能会有例外。
- 必须使用 numpy 1.17:https://github . com/leoxiaobin/deep-high-resolution-net . py torch/issues/177
- 如何用自己的数据集训练网络:https://github . com/leoxiaobin/deep-high-resolution-net . py torch/issues/68
- 在推理中,考虑使用 model.no_grad 来提高性能和降低内存使用量(我还没有测试过)
- 第三个关节参数似乎总是为零,对于 joints_3d_vis 对象,前两个参数总是具有相同的生存能力标志,而第三个参数也是零,来自 coco . py–> _ load _ coco _ key point _ annotation _ kernal()。joins dataset->getitem()->affine _ transform 中,关节的大小为 3,作为仿射变换的准备,但第三个参数从未使用过(可能是遗留的,或者,它被放置在适当的位置,供以后用于 HigherHRNet)。同样的事情似乎发生在 MPII 数据集上。
- 在验证\测试期间,不使用带注释的关节(即使它们保存在 dataloader 管道中),因此测试运行期间打印的准确性结果不正确。试运行期间的整个精度计算流程是多余的。在运行结束时,他们使用 coco api 来计算正确的精度度量
- 推理配置为 384X288(但自述文件说使用 256X192)
图像和关节变换
- 演示/推断— box_to_center_scale()根据框缩放图像,但不清楚 pixel_std=200 做什么。关于它有几个公开的问题:
https://github . com/Microsoft/human-pose-estimation . py torch/issues/26
https://github . com/leoxiaobin/deep-high-resolution-net . py torch/issues/23
https://github . com/leoxiaobin/deep-high-resolution-net . py torch/issues/9
https://github . com 别管它了。 - 中心和比例是根据原始图像中检测到的注释 bbox 的位置。中心是原始图像上 bbox 的中心,比例应该是 bbox 相对于原始图像的大小—来自 coco . py–> _ load _ coco _ person _ detection _ results()。bbox 由 x,y,w,h = box[:4] (x,y,width,height)构成。计算比例时,会根据预先配置的 pixel_std 和 1.25 比例进行纵横比和归一化。
- 推理-> get _ pose _ estimation _ prediction 返回原始图像上的坐标(没有旋转,只有每个边界框的中心和比例)
- JointsDataset-> getitem-> get _ affine _ transform 获取一个变换,该变换根据原始图像比 bbox 大多少来放大原始图像的比例,然后将图像置于 bbox 的中心。
- 然后,warpAffine 将原始图像转移到中心,并提供缩放比例,这意味着我们应该在输出图像中看到 bbox 的区域。输出图像被裁剪,其’ 0,0 点对应于原始图像上的点,该点在转换后落在 0,0 坐标上,裁剪是从该点向右下移动完成的。
- 在训练期间,仿射变换还具有随机旋转缩放和翻转类关节 Dataset → getitem()
- JointsDataset 中 self.db 中的对象通过引用进行更改。self.db 填充在 coco dataset-> _ load _ coco _ person _ detection _ results()类的第 246 行。
- 变换计算是:x_new(of x_old),y_new(of y_old),z = T*(x_old,y_old,1)
好地方看例子:https://docs . opencv . org/master/DD/d52/tutorial _ js _ geometric _ transformations . html - 变换后关节位置可以是负的,它们使用与图像相同的变换矩阵进行传递,并且由于存在向中心的变换和根据边界框的放大比例,一些关节可以在框外。
- MPII 的中心和比例尺标注不完全清楚—https://github . com/leoxiaobin/deep-high-resolution-net . py torch/issues/51
原载于 2020 年 6 月 13 日https://2d3d . ai。
自然语言处理中的标记化算法综述
符号化方法介绍,包括子词、BPE、词块和句子块
汉娜·赖特在 Unsplash 上的照片
⚠️读了我在⚠️博客中的原帖
本文是对标记化算法的概述,从词级、字符级和子词级标记化,重点是 BPE、单字 LM、词块和句子块。这意味着专家和初学者都可以阅读。如果有任何概念或解释不清楚,请与我联系,我很乐意澄清任何需要。
什么是标记化?
标记化是自然语言处理的第一步,它的任务是将一个文本序列分割成具有语义意义的单元。这些单元被称为记号,记号化的难点在于如何得到理想的拆分,使得文本中的所有记号都具有正确的含义,并且没有遗漏记号。
在大多数语言中,文本由空格分隔的单词组成,其中每个单词都有一个语义。我们稍后会看到使用符号的语言会发生什么,其中符号的含义比单词复杂得多。现在我们可以用英语工作。举个例子:
- 原文:我吃了一个汉堡,味道不错。
- 标记化文本:[‘I ‘,’ ate ‘,’ a ‘,’ burger ‘,’,’ and ‘,’ it ‘,’ was ‘,’ good ‘,’.']
“Burger”是一种食物,“and”是连词,“good”是积极的形容词,等等。通过这种方式标记,每个元素都有一个意义,通过连接每个标记的所有意义,我们可以理解整个句子的意义。标点符号也有自己的记号,逗号分隔从句,句号表示句子的结束。下面是另一种标记化方法:
- 标记化的文本:[‘I ate ‘,’ a ‘,’ bur ‘,’ ger ‘,’ an ‘,’ d it ‘,’ wa ‘,’ s good ‘,’.’]
对于多词单位“我吃了”,我们可以只添加“我”和“吃了”的含义,对于子词单位“bur”和“ger”,它们没有单独的含义,但通过将它们连接起来,我们可以得到熟悉的单词,我们可以理解它的意思。
但是我们怎么处理它呢?这是什么意思?作为人类和说英语的人,我们可以推断出‘it’是代词,字母‘d’属于前一个单词。但在这种标记化之后,前面的单词“an”在英语中已经有了意义,冠词“an”与“and”非常不同。这个怎么处理?你可能会想:**坚持使用单词,给标点符号赋予它们自己的符号。**这是最常见的记号化方式,叫做词级记号化。
单词级标记化
它只包括用空格和标点符号分割句子。Python 中有很多这样的库,包括 NLTK、SpaCy、Keras、Gensim,或者你可以自定义正则表达式。
对空白进行拆分也可以拆分应该被视为单个标记的元素,例如 New York。这是有问题的,尤其是名字、借用的外国短语以及有时由多个单词组成的复合词。
像“不要”这样的词,或者像“约翰的”这样的缩写词呢?获得标记“不要”或“做”和“不要”更好吗?如果文中出现错别字,burger 变成了‘birger’怎么办?我们人类可以看到这是一个打字错误,用’汉堡’代替这个词并继续,但机器不能。错别字会影响整个 NLP 管道吗?
单词级标记化的另一个缺点是它产生了巨大的词汇量。每个标记都保存在一个标记词汇表中,如果这个词汇表是用所有输入文本中的所有唯一单词构建的,那么它将创建一个巨大的词汇表,这会在以后产生内存和性能问题。一个当前最先进的深度学习架构, Transformer XL ,拥有 267735 的词汇量。为了解决词汇量大的问题,我们可以考虑用字符代替单词来创建标记,这就是所谓的字符级标记化。
字符级标记化
由 Karpathy 在 2015 年首次推出,它不是将文本拆分成单词,而是拆分成字符,例如,smarter 变成了 s-m-a-r-t-e-r。词汇量大大减少到语言中的字符数,英语加上特殊字符为 26 个。拼写错误或生僻字处理得更好,因为它们被分解成字符,而这些字符在词汇表中是已知的。
在字符级标记序列已经显示出一些令人印象深刻的结果。来自 OpenAI 的拉德福德等人(2017) 表明字符级模型可以捕捉文本的语义属性。来自 Deepmind 的 Kalchbrenner 等人(2016) 和 Leet 等人(2017) 都演示了角色层面的翻译。这些都是特别令人印象深刻的结果,因为翻译的任务是捕捉潜在文本的语义理解。
减小词汇表的大小需要权衡序列长度。现在,每个单词被拆分成所有的字符,标记化的序列比初始文本长得多。单词“smarter”被转化为 7 种不同的符号。此外,标记化的主要目标没有实现,因为字符,至少在英语中,没有语义。只有当字符连在一起时,它们才有意义。作为单词和字符标记化之间的中介,子单词标记化产生了子单词单元,比单词小,但比字符大。
子词级标记化
子词标记化示例
子词级标记化不会转换大多数常用词,而是将罕见词分解成有意义的子词单元。如果“不友好”被标记为一个罕见的词,它将被分解为“un-friend-ly”,这些都是有意义的单位,“un”表示相反的意思,“friend”是一个名词,“ly”将其转化为副词。这里的挑战是如何进行细分,我们如何做到“不友好”而不是“不友好”。
截至 2020 年,最先进的深度学习架构,基于 Transformers、使用子词级标记化。 BERT 对这个例子做了如下的记号化:
- 原文:我有一个新的 GPU。
- 标记化文本:[‘i ‘,’ have ‘,’ a ‘,’ new ‘,’ gp ‘,’ ##u ‘,’.’]
词汇表中的单词被标记为单词本身,但是在词汇表中找不到“GPU ”,它被视为一个罕见的单词。根据一种算法,决定将其分割成‘gp-u’。“u”前的##表示这个子词与前一个子词属于同一个词。BPE、单字标记法、单词块和句子块是最常见的子词标记化算法。因为这是一个介绍性的帖子,所以会对它们进行简单的解释,如果你对更深入的描述感兴趣,请告诉我,我会为它们每个做更详细的帖子。
BPE
由 Sennrich 等人在 2015 年推出,它迭代合并最频繁出现的字符或字符序列。该算法大致是这样工作的:
- 获得足够大的语料库。
- 定义所需的子词词汇大小。
- 将单词拆分为字符序列,并附加一个特殊的标记,分别显示单词的开头或单词的结尾的词缀/后缀。
- 计算文本中的序列对及其频率。例如,(’ t ‘,’ h ‘)有频率 X,(’ h ‘,’ e ')有频率 y。
- 根据出现频率最高的序列对生成一个新子词。例如,如果(‘t’,‘h’)在一组对中具有最高的频率,则新的子词单元将变成‘th’。
- 从步骤 3 开始重复,直到达到子词词汇大小(在步骤 2 中定义)或者下一个最高频率对是 1。按照这个例子,语料库中的(’ t ‘,’ h ‘)将被替换为’ th ',再次计算对,再次获得最频繁的对,并再次合并。
BPE 是一种贪婪和确定性的算法,不能提供多重分割。也就是说,对于给定的文本,标记化的文本总是相同的。关于 BPE 如何工作的更详细的解释将在后面的文章中详述,或者你也可以在的许多其他文章中找到。
Unigram LM
Unigram 语言建模( Kudo,2018 )基于所有子词出现都是独立的假设,因此子词序列是由子词出现概率的乘积产生的。算法的步骤如下:
- 获得足够大的语料库。
- 定义所需的子词词汇大小。
- 通过给出一个单词序列来优化单词出现的概率。
- 计算每个子字的损失。
- 按损失对符号进行排序,保留单词的前 X %(例如 X=80%)。为了避免超出词汇表实例,建议将字符级作为子词的子集包括在内。
- 重复步骤 3-5,直到达到子词词汇大小(在步骤 2 中定义)或者没有变化(步骤 5)
Kudo 认为,unigram LM 模型比 BPE 模型更灵活,因为它基于概率 LM,可以输出多个分段及其概率。它不是从一组基本符号开始学习,也不是像 BPE 或单词块那样用某种规则进行合并,而是从大量词汇(例如,所有预标记的单词和最常见的子字符串)开始,然后逐渐减少。
文字片
word piece(Schuster and Nakajima,2012 )最初用于解决日语和韩语语音问题,目前已知用于 BERT,但精确的标记化算法和/或代码尚未公开。它在许多方面与 BPE 相似,只是它基于似然性而不是下一个最高频率对来形成新的子词。算法的步骤如下:
- 获得足够大的语料库。
- 定义所需的子词词汇大小。
- 将单词拆分成字符序列。
- 用文本中的所有字符初始化词汇表。
- 根据词汇建立一个语言模型。
- 通过组合当前词汇中的两个单元来生成新的子词单元,以将词汇递增 1。当添加到模型中时,从所有可能性中选择最能增加训练数据的可能性的新子词单元。
- 重复步骤 5,直到达到子词词汇大小(在步骤 2 中定义)或者可能性增加低于某个阈值。
句子片断
到目前为止,所有的标记化方法都需要某种形式的预标记化,这构成了一个问题,因为不是所有的语言都使用空格来分隔单词,或者有些语言是由符号组成的。SentencePiece 可以接受特定语言的预发音。你可以在 Github 找到开源软件。例如, XLM 使用了句子片断,并为中文、日文和泰文添加了特定的前缀词。
SentencePiece 在概念上类似于 BPE,但它不使用贪婪的编码策略,实现了更高质量的标记化。SentencePiece 认为字符分组中的模糊性是训练期间模型正则化的一个来源,这使得训练变得更慢,因为有更多的参数需要优化,并且不鼓励 Google 在 BERT 中使用它,而是选择 WordPiece。
结论
历史上,标记化方法已经从单词发展到字符,最近发展到子单词级别。这是对标记化方法的一个快速概述,我希望文本是可读和可理解的。关注我的推特了解更多 NLP 信息,或者在那里问我任何问题:)
人工智能关键技术综述
探索大数据和人工智能
构建人工智能平台的关键组件
人工智能是一门研究和发展模拟人类智能延伸和扩展的理论、方法、技术和应用系统的新技术学科。人工智能研究的目标是让机器执行一些需要智能人类才能完成的复杂任务。也就是说,我们希望机器可以代替我们解决一些复杂的任务,不仅仅是重复的机械活动,而是一些需要人类智慧参与的活动。在本文中,我将向您概述人工智能技术以及构建人工智能平台的关键组件。
人工智能的关键技术
人工智能的关键技术自下而上可分为基础设施层和算法层。在基础设施层,有基础硬件,包括 CPU、GPU、专用人工智能芯片、高速网络。在这个基础硬件之上,我们可以搭建算法框架,比如 Tensorflow、Caffe、Mxnet、Torch、Keras、PyTorch、Theano 等。基础设施层之上是算法层。算法层最具代表性的是机器学习算法,包括深度学习、迁移学习、一般对抗网络、强化学习等一系列机器学习算法。
基础设施层
基本硬件
随着我们介绍人工智能基础设施的基础硬件,分为 CPU、GPU、专用芯片、高速网络四个方向,我们来说两个类似的:CPU 和 GPU。CPU 主要针对一组串行执行的任务进行优化,而 GPU 主要针对复杂的图形和图像计算算法进行优化。两者的区别在于,CPU 是串行执行的,而 GPU 是更小、更高效的计算单元,它们一起并行处理计算。此外,还有一种专门为人工智能算法开发的特殊芯片,即谷歌的 TPU 芯片。
为了充分利用人工智能的能力,我们需要高速网络。在一些复杂数据模型的训练和计算过程中,我们需要巨大的网络带宽保证。如今,网络已经成为整体机器学习性能的重要组成部分。现在我们通常有 10G、20G、40G 网络。随着 Infiniband 网络技术的出现,相信在未来,网络将为人工智能的学习和训练提供更广阔、更快捷的通道。
Webaroo.com.au 在 Unsplash 上的照片
算法框架
基础设施中的第二层是算法框架。算法框架可以简单理解为运行算法的框架。就像一个建筑框架,我们可以在上面运行我们的业务。Google 开发了一个非常著名的框架,叫做 TensorFlow,友好、快捷、方便。TensorFlow 让我们在里面实现相关的 AI 算法,运行这些方法。
照片由🇨🇭·克劳迪奥·施瓦茨| @purzlbaum 在 Unsplash 上拍摄
算法层
我刚才讲了算法框架,那我就讲算法。首先,机器学习是人工智能中的一个核心概念。我们所有人都要学习,我们人类的知识传递也是通过这样的学习方法进行的。我们学习祖先的知识,然后通过推理创造新的知识。我们也希望机器有这样的能力:通过学习之前的信息,让机器更像有智能,能够对未来新的输入做出相应的反应。这叫做机器学习。
机器学习过程
你知道机器学习最典型的过程是什么吗?首先,我们收集历史数据,然后我们训练历史数据。我们从训练结果中得到一个模型。然后,随着新数据输入到模型中,我们可以预测我们的结果。这是最典型的机器学习过程。我们可以简单的说,通过训练产生一个模型,然后用模型来指导新数据的预测。
我们以房价预测为例。首先,我们假设房价只和房子的面积有关。我们可以输入一维数据,如历史房价和房屋面积的一维数据。在房价模型中,我们可以通过训练预测进入新房区域后的房价。这是一个最简单的机器学习的例子。
机器学习的分类
在机器学习中,我们可以将其分为监督学习算法、非监督学习算法和半监督学习算法。那么它们之间有什么区别呢?
布鲁诺·马丁斯在 Unsplash 上的照片
首先,我们来看看监督学习。有监督的学习意味着这种学习在训练时是有标签的。如果标签是离散的,我们称之为分类。如果标签是连续的,我们称之为回归。这是监督学习。
无监督学习是无标签的,也就是我们常说的聚类。
最后一种半监督学习,也就是说它里面的样本有的是有标签的,有的是没有标签的,放在一起进行更复杂的混合运算,那么它就变成了半监督学习算法。
深度学习算法
当我们谈论人工智能时,我们经常会听到机器学习和深度学习等概念。其实它们是一种包容关系,人工智能包括机器学习和深度学习,而机器学习中的一种特定的学习形式叫做深度学习。它主要基于神经网络算法。目前,深度学习已经在图像识别、语音识别、自然语言处理、音频识别、社交网络过滤、机器翻译、医学图像分析、棋盘游戏程序等领域取得了很大进展。
神经网络
说到深度学习算法,就不得不说卷积神经网络和递归神经网络。神经网络类似于人脑的神经传递,从一个输入单元到下一个输入单元得到一个结果。这是一个简单的神经网络的原理,就是模拟人脑中神经传递信息。它将信息从一个神经元传递到另一个神经元,然后向下传递。随着神经网络算法的兴起,深度学习算法可以解决很多实际问题。
BP 神经网络
神经网络算法发明后,很多问题在一定程度上得到了解决。同时,人们也在不断优化这个算法。首先,一个应用非常广泛并且非常经典的是 BP 神经网络。BP 神经网络比原神经网络多了一个隐含层。输入层和输出层中还有其他隐藏层。通过梯度下降的方式可以大大降低计算量和计算难度。
卷积神经网络
但是有了 BP 神经网络之后,我们发现 BP 神经网络的计算量还是很大。它有时无法在我们可接受的时间范围内给出最优解,或者给出最优解的时间太长,不符合我们某些应用的需求。然后出现了卷积神经网络(CNN),它本质上也是一种神经网络算法,但它优化了 BP 神经网络中的内容,它使计算更快,并且它可以在许多问题上获得最大化。出色的解决方案。它通过高度并行地处理相关信息来提高其计算效率。同时,大大降低了 BP 神经网络之间的计算复杂度。因此,卷积神经网络目前可以在很多问题上快速达到最优解。
递归神经网络
目前通过这种学习算法训练出来的递归神经网络模型,已经达到了可以写诗的结果。它不是单项输出,它是用以前的输出作为重新输入,重新进入模型行计算。这个模型很神奇,因为它有一定的记忆能力。
迁移学习
在传统的机器学习中,我们针对特定的问题训练了几个不同的模型,每个模型都可以解决这类问题。在迁移学习中,我们试图将某个领域(我们称之为原域)的训练结果存储为知识。我们在原始域中训练算法。经过训练,我们希望它能解决新的问题,这就是所谓的有针对性的任务。在我们输入目标任务后,它可以根据在解决原有领域的问题时积累的知识来推断新的问题,并且可以在不需要训练的情况下得到结果。我们称之为迁移学习,将之前学习的结果转移到新的问题中。
生成对抗网络
生成对抗网络(GAN)中的“对抗”一词是指生成的两个网络之间存在竞争关系。在这两个网络中,一个负责生成样本,另一个负责确定样本的正确性。产生样本的小组希望愚弄鉴别样本的小组。鉴别样本的群体希望不要被产生样本的群体的结果所愚弄,它们之间有一定的竞争和对抗关系。在这样的关系中产生一个学习结果更准确。
强化学习
强化学习也是机器学习的一个重要分支。它的本质是解决决策问题,即自动决策,连续决策。主要包括两部分,首先是代理人,然后是环保行动奖励。强化学习的目标是获得最多的累积奖励。我们举个例子,一个婴儿学走路就是一个强化学习的过程。宝宝是一个代理人,他希望通过一个动作比如走路来改变环境的状态。他走的每一步都是状态的变化。如果他迈出了正确的一步,我们会给他奖励。否则,如果他没有迈出正确的一步,那么我们就不给他奖励。通过这个过程,一个小宝宝慢慢学会了走路。
人工智能平台
构建人工智能平台的目标是为用户提供人工智能研发所需的基础设施、算法、计算能力和相关数据。许多大公司规划并建立了统一的人工智能 R&D 云平台,部署在混合云资源池中,并直接连接到 IT 大数据平台。统一的人工智能研发云平台主要由数据平台、深度学习平台和人工智能能力平台组成。其中,数据平台可以提供开放的公共数据集,提供数据挖掘所需的海量数据,可以很好地挖掘大部分大事务所存储的大数据优势,提供数据标注和数据共享服务。深度学习平台是基于 AI 硬件的 AI 集群,集成了主流的深度学习算法框架,为 AI 开发提供基础设施支持,如算法和计算能力。人工智能能力平台提供核心人工智能能力,如语音语义、图形图像和智能数据分析,以及数据模型管理和共享机制。
摘要
让我们总结一下这部分所学的内容。在这一部分,我们首先对人工智能的关键技术进行了概述。它包括基础设施层和算法层。然后我们解释了基础设施层的一些主要基础设施,包括硬件 CPU,GPU,网络,和专用芯片以及我们在基础设施层构建的底层技术算法的框架,非常有代表性的是 Google 的 Tensorflow。我们还介绍了一些经典的人工智能算法,如对抗学习、强化学习等。
补充一下,人工智能的发展也必须依靠大数据技术。它需要大量的数据来支持。技术创新才刚刚开始,还有更多的新技术需要我们不断学习。技术创新才刚刚开始,还有更多的新技术需要我们不断学习。
Google BigQuery 主要特性概述——练习撰写营销分析请求
来源: Unsplash
在本文中,我们将研究 BigQuery 的主要功能,并通过具体的例子展示它们的可能性。您将学习如何编写基本的查询并在演示数据上测试它们。
企业积累的信息越多,在哪里存储信息的问题就越尖锐。如果你没有能力或者没有意愿维护自己的服务器, Google BigQuery (GBQ)可以帮忙。BigQuery 为处理大数据提供了快速、经济高效且可扩展的存储,它允许您使用类似 SQL 的语法以及标准和用户定义函数编写查询。
什么是 SQL,BigQuery 支持哪些方言
结构化查询语言(SQL)允许您在大型数组中检索数据、添加数据和修改数据。Google BigQuery 支持两种 SQL 方言:标准 SQL 和过时的遗留 SQL。
选择哪种方言取决于您的偏好,但是 Google 建议使用标准 SQL,因为它有以下好处:
您可以在 BigQuery 文档中了解更多关于方言差异的信息。
默认情况下,Google BigQuery 查询在遗留 SQL 上运行。
您可以通过几种方式切换到标准 SQL:
- 在 BigQuery 界面的查询编辑窗口中,选择显示选项并取消勾选使用遗留 SQL 旁边的复选标记:
图片由作者提供
- 在查询之前,添加#standardSQL 行,并从新行开始查询:
图片由作者提供
从哪里开始
首先,下载您的演示数据表,并将其上传到您的 Google BigQuery 项目。最简单的方法是使用 OWOX BI BigQuery Reports 插件。
- 打开 Google Sheets 并安装 OWOX BI BigQuery Reports 插件。
- 打开下载的包含演示数据的表格,选择OWOX BI big query Reports–>上传数据到 BigQuery :
图片由作者提供
- 在打开的窗口中,选择您的 Google BigQuery 项目,一个数据集,并为将要存储加载数据的表想一个名称。
- 指定加载数据的格式(如屏幕截图所示):
图片由作者提供
如果您在 Google BigQuery 中没有项目,创建一个。要做到这一点,你需要在谷歌云平台中有一个活跃的付费账户。不要让你需要链接银行卡吓到你:在你不知情的情况下,你不会被收取任何费用。此外,当你注册时,你会收到 12 个月的 300 美元,你可以用它来存储和处理数据。
在讨论 Google BigQuery 特性之前,让我们记住传统 SQL 和标准 SQL 方言中的基本查询是什么样的:
Google BigQuery 特性
在构建查询时,您将最常使用聚合、日期、字符串和窗口函数。让我们仔细看看每一组函数。
聚合函数
聚合函数为整个表提供汇总值。例如,您可以使用它们来计算平均支票金额或每月总收入,或者您可以使用它们来选择购买次数最多的用户群。
这些是最常用的聚合函数:
让我们看看演示数据,看看这些函数是如何工作的。我们可以计算交易的平均收入、最高和最低金额的购买、总收入、总交易和唯一交易的数量(以检查购买是否重复)。为此,我们将编写一个查询,在其中指定 Google BigQuery 项目的名称、数据集和表。
#传统 SQL
**SELECT**
**AVG**(revenue) **as** average_revenue,
**MAX**(revenue) **as** max_revenue,
**MIN**(revenue) **as** min_revenue,
**SUM**(revenue) **as** whole_revenue,
**COUNT**(transactionId) **as** transactions, EXACT_COUNT_DISTINCT(transactionId) **as** unique_transactions
**FROM**
[owox-analytics:t_kravchenko.Demo_data]
#标准 SQL
**SELECT**
**AVG**(revenue) **as** average_revenue,
**MAX**(revenue) **as** max_revenue,
**MIN**(revenue) **as** min_revenue,
**SUM**(revenue) **as** whole_revenue,
**COUNT**(transactionId) **as** transactions, **COUNT**(**DISTINCT**(transactionId)) **as** unique_transactions
**FROM**
`owox-analytics.t_kravchenko.Demo_data`
结果,我们将得到以下结果:
图片由作者提供
您可以使用标准的 Google Sheets 函数(SUM、AVG 和其他函数)或使用数据透视表,在带有演示数据的原始表格中检查这些计算的结果。
从上面的截图可以看出,交易数和唯一交易数是不一样的。这表明我们的表中有两个事务具有相同的 transactionId:
图片由作者提供
如果您对唯一的事务感兴趣,请使用计算唯一字符串的函数。或者,您可以使用 GROUP BY 函数对数据进行分组,以便在应用聚合函数之前消除重复项。
日期函数
这些函数允许您处理日期:更改它们的格式,选择必要的字段(日、月或年),或者将日期移动一定的间隔。
在下列情况下,它们可能有用:
- 将不同来源的日期和时间转换为单一格式,以设置高级分析
- 创建自动更新的报告或触发邮件(例如,当您需要过去两个小时、一周或一个月的数据时)
- 创建群组报告,其中需要获取几天、几周或几个月的数据
这些是最常用的日期函数:
有关所有日期函数的列表,请参见遗留 SQL 和标准 SQL 文档。
让我们来看看我们的演示数据,看看这些函数是如何工作的。例如,我们将获取当前日期,将原始表中的日期转换为格式% YYYY -% MM-% DD,将其删除,并添加一天。然后,我们将计算当前日期和源表中的日期之间的差值,并将当前日期分成单独的年、月和日字段。为此,您可以复制下面的示例查询,并用您自己的查询替换项目名称、数据集和数据表。
#传统 SQL
**SELECT** **CURRENT_DATE**() **AS** today,
DATE( date_UTC ) **AS** date_UTC_in_YYYYMMDD,
**DATE_ADD**( date_UTC,1, 'DAY') **AS** date_UTC_plus_one_day, **DATE_ADD**( date_UTC,-1, 'DAY') **AS** date_UTC_minus_one_day, **DATEDIFF**(**CURRENT_DATE**(), date_UTC ) **AS** difference_between_date, **DAY**( **CURRENT_DATE**() ) **AS** the_day,
**MONTH**( **CURRENT_DATE**()) **AS** the_month,
**YEAR**( **CURRENT_DATE**()) **AS** the_year
**FROM**
[owox-analytics:t_kravchenko.Demo_data]
#标准 SQL
**SELECT**
today,
date_UTC_in_YYYYMMDD,
**DATE_ADD**( date_UTC_in_YYYYMMDD, INTERVAL 1 **DAY**) **AS** date_UTC_plus_one_day,
**DATE_SUB**( date_UTC_in_YYYYMMDD,INTERVAL 1 **DAY**) **AS** date_UTC_minus_one_day,
DATE_DIFF(today, date_UTC_in_YYYYMMDD, **DAY**) **AS** difference_between_date,
**EXTRACT**(**DAY** **FROM** today ) **AS** the_day,
**EXTRACT**(**MONTH** **FROM** today ) **AS** the_month,
**EXTRACT**(**YEAR** **FROM** today ) **AS** the_year
**FROM** (
**SELECT**
**CURRENT_DATE**() **AS** today,
DATE( date_UTC ) **AS** date_UTC_in_YYYYMMDD
**FROM**
`owox-analytics.t_kravchenko.Demo_data`)
运行查询后,您将收到以下报告:
图片由作者提供
字符串函数
字符串函数允许您生成一个字符串,选择和替换子字符串,计算字符串的长度以及子字符串在原始字符串中的索引序列。例如,使用字符串函数,您可以:
- 使用传递到页面 URL 的 UTM 标签过滤报表
- 如果源和活动名称写在不同的寄存器中,将数据转换为单一格式
- 替换报告中不正确的数据(例如,如果活动名称打印错误)
以下是处理字符串最常用的函数:
您可以在遗留 SQL 和标准 SQL 文档中了解更多关于所有字符串函数的信息。
让我们看看演示数据,看看如何使用所描述的功能。假设我们有三个分别包含日、月和年值的列:
图片由作者提供
使用这种格式的日期不太方便,所以我们可以将这些值合并到一列中。为此,请使用下面的 SQL 查询,并记住在 Google BigQuery 中替换您的项目、数据集和表的名称。
#遗留 SQL
**SELECT**
**CONCAT**(the_day,'-',the_month,'-',the_year) **AS** mix_string1
**FROM** (
**SELECT**
31 **AS** the_day,
12 **AS** the_month,
2018 **AS** the_year
**FROM**
[owox-analytics:t_kravchenko.Demo_data])
**GROUP** **BY**
mix_string1
#标准 SQL
**SELECT**
**CONCAT**(the_day,'-',the_month,'-',the_year) **AS** mix_string1
**FROM** (
**SELECT**
31 **AS** the_day,
12 **AS** the_month,
2018 **AS** the_year
**FROM**
owox-analytics.t_kravchenko.Demo_data)
**GROUP** **BY**
mix_string1
运行查询后,我们在一列中收到日期:
图片由作者提供
通常,当您在网站上下载页面时,URL 会记录用户选择的变量的值。这可以是支付或交付方法、交易号、购买者想要提取物品的实体店的索引等。使用 SQL 查询,您可以从页面地址中选择这些参数。考虑两个例子来说明如何以及为什么要这样做。
例 1 。假设我们想知道用户从实体店提货的购买次数。为此,我们需要计算从 URL 中包含子字符串 shop_id(实体店的索引)的页面发送的交易数量。我们可以通过以下查询来实现这一点:
#遗留 SQL
**SELECT**
**COUNT**(transactionId) **AS** transactions,
**check**
**FROM** (
**SELECT**
transactionId,
page CONTAINS 'shop_id' **AS** **check**
**FROM**
[owox-analytics:t_kravchenko.Demo_data])
**GROUP** **BY**
**check**
#标准 SQL
**SELECT**
**COUNT**(transactionId) **AS** transactions,
check1,
check2
**FROM** (
**SELECT**
transactionId,
REGEXP_CONTAINS( page, 'shop_id') **AS** check1,
page **LIKE** '%shop_id%' **AS** check2
**FROM**
`owox-analytics.t_kravchenko.Demo_data`)
**GROUP** **BY**
check1,
check2
从结果表中,我们看到 5502 个交易(check = true)是从包含 shop_id 的页面发送的:
图片由作者提供
例二。您已经为每种交付方法分配了一个 delivery_id,并在页面 URL 中指定了该参数的值。要找出用户选择了哪种交付方法,您需要在单独的列中选择 delivery_id。
为此,我们可以使用以下查询:
#传统 SQL
**SELECT**
page_lower_case,
page_length,
index_of_delivery_id,
selected_delivery_id,
**REPLACE**(selected_delivery_id, 'selected_delivery_id=', '') **as** delivery_id
**FROM** (
**SELECT**
page_lower_case,
page_length,
index_of_delivery_id,
**SUBSTR**(page_lower_case, index_of_delivery_id) **AS** selected_delivery_id
**FROM** (
**SELECT**
page_lower_case,
**LENGTH**(page_lower_case) **AS** page_length, **INSTR**(page_lower_case, 'selected_delivery_id') **AS** index_of_delivery_id
**FROM** (
**SELECT**
**LOWER**( page) **AS** page_lower_case,
**UPPER**( page) **AS** page_upper_case
**FROM**
[owox-analytics:t_kravchenko.Demo_data])))
**ORDER** **BY**
page_lower_case **ASC**
#标准 SQL
**SELECT**
page_lower_case,
page_length,
index_of_delivery_id,
selected_delivery_id,
**REPLACE**(selected_delivery_id, 'selected_delivery_id=', '') **AS** delivery_id
**FROM** (
**SELECT**
page_lower_case,
page_length,
index_of_delivery_id,
**SUBSTR**(page_lower_case, index_of_delivery_id) **AS** selected_delivery_id
**FROM** (
**SELECT**
page_lower_case,
**CHAR_LENGTH**(page_lower_case) **AS** page_length, STRPOS(page_lower_case, 'selected_delivery_id') **AS** index_of_delivery_id
**FROM** (
**SELECT**
**LOWER**( page) **AS** page_lower_case,
**UPPER**( page) **AS** page_upper_case
**FROM**
`owox-analytics.t_kravchenko.Demo_data`)))
**ORDER** **BY**
page_lower_case **ASC**
结果,我们在 Google BigQuery 中得到这样一个表:
图片由作者提供
窗口功能
这些函数类似于我们上面讨论的聚合函数。主要区别在于,窗口函数不在使用查询选择的整个数据集上执行计算,而只在部分数据上执行计算——子集或窗口。
使用窗口函数,您可以在组节中聚合数据,而无需使用 JOIN 函数来组合多个查询。例如,您可以计算每个广告活动的平均收入或每台设备的交易数量。通过向报表中添加另一个字段,您可以很容易地找到,例如,黑色星期五广告活动的收入份额或移动应用程序的交易份额。
除了查询中的每个函数,您还必须详细说明定义窗口边界的 OVER 表达式。OVER 包含您可以使用的三个组件:
- 分区依据—定义将原始数据划分为子集的特征,如 clientId 或 DayTime
- 排序依据-定义子集中行的顺序,例如小时 DESC
- 窗口框架-允许您处理特定要素子集内的行(例如,仅当前行之前的五行)
在此表中,我们收集了最常用的窗口函数:
您可以在遗留 SQL 和标准 SQL 的文档中看到所有聚合分析函数和 导航函数的列表。
例 1 。假设我们想要分析客户在工作时间和非工作时间的活动。为此,我们需要将事务分为两组,并计算感兴趣的指标:
- 第 1 组—在 9:00 至 18:00 的工作时间内购买
- 第 2 组—00:00 至 9:00 和 18:00 至 23:59 的下班后购买
除了工作和非工作时间,形成窗口的另一个变量是 clientId。也就是说,对于每个用户,我们将有两个窗口:
让我们使用演示数据来计算平均、最大、最小和总收入、交易总数,以及每个用户在工作时间和非工作时间的唯一交易数。下面的请求将帮助我们做到这一点。
#传统 SQL
**SELECT**
date,
clientId,
DayTime,
avg_revenue,
max_revenue,
min_revenue,
sum_revenue,
transactions,
unique_transactions
**FROM** (
**SELECT**
date,
clientId,
DayTime,
**AVG**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** avg_revenue,
**MAX**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** max_revenue,
**MIN**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** min_revenue,
**SUM**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** sum_revenue,
**COUNT**(transactionId) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** transactions,
**COUNT**(**DISTINCT**(transactionId)) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** unique_transactions
**FROM** (
**SELECT**
date,
date_UTC,
clientId,
transactionId,
revenue,
page,
**hour**,
**CASE**
**WHEN** **hour**>=9 **AND** **hour**<=18 **THEN** 'working hours'
**ELSE** 'non-working hours'
**END** **AS** DayTime
**FROM**
[owox-analytics:t_kravchenko.Demo_data]))
**GROUP** **BY**
date,
clientId,
DayTime,
avg_revenue,
max_revenue,
min_revenue,
sum_revenue,
transactions,
unique_transactions
**ORDER** **BY**
transactions **DESC**
#标准 SQL
#standardSQL
**SELECT**
date,
clientId,
DayTime,
avg_revenue,
max_revenue,
min_revenue,
sum_revenue,
transactions,
unique_transactions
**FROM** (
**SELECT**
date,
clientId,
DayTime,
**AVG**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** avg_revenue,
**MAX**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** max_revenue,
**MIN**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** min_revenue,
**SUM**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** sum_revenue,
**COUNT**(transactionId) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** transactions,
**COUNT**(**DISTINCT**(transactionId)) **OVER** (**PARTITION** **BY** date, clientId, DayTime) **AS** unique_transactions
**FROM** (
**SELECT**
date,
date_UTC,
clientId,
transactionId,
revenue,
page,
**hour**,
**CASE**
**WHEN** **hour**>=9 **AND** **hour**<=18 **THEN** 'working hours'
**ELSE** 'non-working hours'
**END** **AS** DayTime
**FROM**
`owox-analytics.t_kravchenko.Demo_data`))
**GROUP** **BY**
date,
clientId,
DayTime,
avg_revenue,
max_revenue,
min_revenue,
sum_revenue,
transactions,
unique_transactions
**ORDER** **BY**
transactions **DESC**
让我们以 clientId 为 1020 的用户为例,看看结果会怎样。56660.68668686661 在该用户的原始表中,我们有以下数据:
图片由作者提供
通过运行该查询,我们将收到一份报告,其中包含来自该用户的平均、最小、最大和总收入,以及该用户的交易总数。正如您在下面的截图中看到的,这两笔交易都是用户在工作时间进行的:
图片由作者提供
例二。现在是一个更复杂的任务:
- 根据事务的执行时间,将所有事务的序列号放在窗口中。回想一下,我们通过用户和工作/非工作时隙来定义窗口。
- 报告窗口内下一个/上一个交易(相对于当前交易)的收入。
- 在窗口中显示第一笔和最后一笔交易的收入。
为此,我们将使用以下查询:
#遗留 SQL
**SELECT**
date,
clientId,
DayTime,
**hour**,
**rank**,
revenue,
lead_revenue,
lag_revenue,
first_revenue_by_hour,
last_revenue_by_hour
**FROM** (
**SELECT**
date,
clientId,
DayTime,
**hour**,
**DENSE_RANK**() **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** **rank**, revenue,
**LEAD**( revenue, 1) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** lead_revenue,
LAG( revenue, 1) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** lag_revenue,
**FIRST_VALUE**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** first_revenue_by_hour,
**LAST_VALUE**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** last_revenue_by_hour
**FROM** (
**SELECT**
date,
date_UTC,
clientId,
transactionId,
revenue,
page,
**hour**,
**CASE**
**WHEN** **hour**>=9 **AND** **hour**<=18 **THEN** 'working hours'
**ELSE** 'non-working hours'
**END** **AS** DayTime
**FROM**
[owox-analytics:t_kravchenko.Demo_data]))
**GROUP** **BY**
date,
clientId,
DayTime,
**hour**,
**rank**,
revenue,
lead_revenue,
lag_revenue,
first_revenue_by_hour,
last_revenue_by_hour
**ORDER** **BY**
date,
clientId,
DayTime,
**hour**,
**rank**,
revenue,
lead_revenue,
lag_revenue,
first_revenue_by_hour,
last_revenue_by_hour
#标准 SQL
**SELECT**
date,
clientId,
DayTime,
**hour**,
**rank**,
revenue,
lead_revenue,
lag_revenue,
first_revenue_by_hour,
last_revenue_by_hour
**FROM** (
**SELECT**
date,
clientId,
DayTime,
**hour**,
**DENSE_RANK**() **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** **rank**, revenue,
**LEAD**( revenue, 1) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** lead_revenue,
LAG( revenue, 1) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** lag_revenue,
**FIRST_VALUE**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** first_revenue_by_hour,
**LAST_VALUE**(revenue) **OVER** (**PARTITION** **BY** date, clientId, DayTime **ORDER** **BY** **hour**) **AS** last_revenue_by_hour
**FROM** (
**SELECT**
date,
date_UTC,
clientId,
transactionId,
revenue,
page,
**hour**,
**CASE**
**WHEN** **hour**>=9 **AND** **hour**<=18 **THEN** 'working hours'
**ELSE** 'non-working hours'
**END** **AS** DayTime
**FROM**
`owox-analytics.t_kravchenko.Demo_data`))
**GROUP** **BY**
date,
clientId,
DayTime,
**hour**,
**rank**,
revenue,
lead_revenue,
lag_revenue,
first_revenue_by_hour,
last_revenue_by_hour
**ORDER** **BY**
date,
clientId,
DayTime,
**hour**,
**rank**,
revenue,
lead_revenue,
lag_revenue,
first_revenue_by_hour,
last_revenue_by_hour
我们可以使用一个已知用户的例子来检查计算结果:clientId 10 204 2036767636767
图片由作者提供
从上面的截图中,我们可以看到:
- 第一笔交易在 15:00,第二笔交易在 16:00
- 15:00 交易后,16:00 有一笔交易,收入 25066(列 lead_revenue)
- 在 16:00 交易之前,15:00 有一笔交易,收入 3699(列 lag_revenue)
- 窗口内的第一笔交易发生在 15:00,此交易的收入为 3699(first _ revenue _ by _ hour 列)
- 该查询逐行处理数据,因此对于所讨论的事务,窗口中的最后一个事务将是它本身,并且 last_revenue_by_hour 和 revenue 列中的值将是相同的
结论
在本文中,我们研究了最流行的函数组:聚合、日期、字符串和窗口。然而,Google BigQuery 有许多更有用的功能,包括:
- 允许您将数据转换为特定格式的转换函数
- 允许您访问数据集中多个表的表通配符函数
- 正则表达式函数,允许您描述搜索查询的模型,而不是它的确切值