TowardsDataScience-博客中文翻译-2020-三十-
TowardsDataScience 博客中文翻译 2020(三十)
在 30 天内完成 Udacity 的商业分析纳米学位项目
在 Udacity 的 30 天免费试用期内完成一个纳米学位有多现实?

最近,我的一个朋友分享说,Udacity 有一个纳米学位项目的 30 天免费试用。在撰写本文时,2020 年 4 月/5 月,按月订阅 Udacity 的费用为 329 英镑/月(415 美元/月)。我看了一下教学大纲,发现商业分析项目中有一些有趣的项目,也提供了来自数据科学领域专业人士的反馈。我认为这是学习描述统计学、高级 Excel、SQL 和 Tableau 技能的好机会。看完细则后,我发现如果你在 30 天内完成了 Nanodegree 项目,你不用支付一分钱,但如果你不能在这段时间内完成,你的付费订阅将开始。接受挑战!
编辑:自从写了这篇文章,Udacity 将免费试用期减少到了 7 天。如果你在 7 天内完成了这个项目,请告诉我们!
Udacity 建议我每天花大约 1 到 2 个小时在纳米学位上,但我发现我在 3 到 4 个小时的时间段内工作得最好,周末大约每天 2 次,偶尔在工作日之后工作 2 个小时。我能够以 2 倍的速度浏览大多数关于 SQL 的视频,但我必须放慢速度,并复习关于高级 SQL 函数、高级 Excel 函数和 Tableau 的一些内容。
完成这一纳米学位的预计时间为 3 个月,每周 5-10 小时,总共完成 60-120 小时。对于一个完全不熟悉所教授的概念的人来说,我认为 100 多个小时的完成时间是现实的。然而,我发现自己已经接近 50 到 60 小时了。
总之,我很高兴我挑战自己在 30 天内完成这个项目,但是如果你是新手,我不建议你这么做。也就是说,即使你开始这个项目,然后支付第二个月的费用,这也是值得的。
我喜欢的:
- 业务分析领域知识,即不同行业的业务指标
- 为项目做准备的练习
- 快速和建设性的项目反馈,有时在项目提交后的几个小时内
- 嵌入式编码练习(即通过 Jupyter 笔记本)
- 有职业教练现场指导的职业辅导服务
哪些方面可以改进:
- 物有所值——就本课程所教授的内容而言,每月 329 英镑对于教授 Excel、SQL 和 Tableau 来说是相当高的价格。也就是说,我可以看到项目评审和反馈部分的价值,因为我的许多评审人员就如何改进我的工作提供了宝贵的行业见解。
纳米学位项目
以下是我在纳米学位最喜欢的三个项目的简要概述,以及我从它们中学到的东西。这 3 个项目占据了纳米学位项目的大部分时间。我没有包括所有项目的视觉效果,因为我相信课程的大部分价值在于学习如何展示你的成果。
项目#1 —用 Excel 探索纽交所数据、损益表、利润表
项目任务:
- 我制作了一张简明的 PowerPoint 幻灯片,比较了 2007 年至 2016 年非必需消费品和日用消费品领域标准普尔 500 公司的总运营支出。
- 我用 Excel 中的标准普尔 500 公司选择器小部件创建了一个损益表
- 我在 Excel 中创建了一个带有财务预测的动态损益表,其中包含三个不同的场景
数据集:
主要工具: Excel、PowerPoint
使用的具体函数:索引、匹配、命名范围、数据验证、下拉菜单、VLOOKUP
业务主题:损益表、损益表、财务建模、沟通
项目#2 — Chinook 音乐商店和 SQL 查询
项目任务:
- 我执行了各种 SQL 查询来创建视觉效果,显示了:
- 根据所服务的客户数量,音乐商店的前 3 名员工
- 邮政编码与帐单邮政编码匹配的美国客户地图
- 按售出曲目数量排列的最受欢迎的音乐流派
- 显示在奇努克花费最多的前 10 个国家的地图
数据集: Chinook 音乐商店销售和员工数据库
主要工具: SQL、DB 浏览器 for SQL Lite
使用的具体函数:分组、连接、排序、子查询
业务主题:业务分析、库存跟踪、数据库设计、客户旅程
项目# 3——YouTube 数据和 Tableau 可视化
项目任务:
- 我创建了一个由美国大陆组成的仪表盘,它可以在地图和条形图上快速传达美国哪里有最多的观众,并带有沿海和非沿海州的过滤器。
- 我用标准化的“喜欢/不喜欢”比率创建了多个条形图,发现最受欢迎的视频类别“音乐”拥有最多的“喜欢”,但“不喜欢”仍然比第二受欢迎的视频类别“娱乐”少。
- 我制作了一个饼状图,让观众快速评估上传视频最多的视频类别。
数据集: YouTube 收视率数据
主要工具: Tableau
使用的具体功能:过滤、计算字段、仪表板、映射、色盲可视化
业务主题:收视率、客户细分、时间序列分析、数据可视化
非常感谢你的阅读!如果你对纳米学位有任何疑问,请在下面的评论中告诉我。祝你好运,继续学习!
人工智能的复杂网络
作为之前关于 Swift for TensorFlow (S4TF) 和the hardware powering modern AI的起源的文章的后续,本文是专注于机器学习和网络的两篇文章的第一部分。第二部分将关注作为数据平台的 Web,本文将关注人工智能的新兴工具,以及 Web 将如何成为以一种前所未有的方式连接智能的一流平台

介绍
虽然人工智能工具在网络上的发展并不是最近的事情,但我们将在 2019 年 9 月开始,在日本福冈的一家酒店举办一场研讨会,作为万维网联盟技术全体会议/咨询委员会(W3C TPAC) 会议的一部分。在这次会议上,一群行业专家对新兴技术有着独特的观点,这些技术有可能将 Web 和 AI 结合在一起,为智能解决方案创建一个无与伦比的开发平台。
在这次会议中,坐着微软 Edge 团队的一名成员,在他右边坐着苹果 Safari 开发团队的一名成员,然后是来自英特尔的一名工程师(也是 Web / WebNN W3C group 的机器学习的创始人),以及来自谷歌的两个人,包括 TensorFlow.js 的一名主要专家。
里面的对话涵盖了广泛的主题,包括 WebAssembly 、硬件设计和编译工具链,还包括几个关于相关技术的演示,这些技术将不可避免地有助于面向 Web 的机器学习规范。
许多材料之前已经共享过,是主要浏览器供应商和更广泛的领域专家社区之间日益丰富的数据共享的一部分,它显示了网络作为一流人工智能平台的发展正在进行的承诺。
找到与开发者的和谐
支撑这场运动的技术并不完全集中在网络上,也不应该完全集中在网络上。至关重要的是,在创建一套跨平台的通用且无处不在的机器学习工具的过程中,经验和发展也是非常重要的,这使得 Web 易于开发。
同样,TensorFlow 针对特定介质有多种排列( TensorFlow 、 TensorFlow Lite 、 TensorFlow.js 等)。)高级机器学习工具通常需要在缩小到特定问题领域之前呈现更广泛的需求视图,在方法上具有兼容性和通用性,以简化开发人员的入口点。考虑典型的编程范例,如函数式编程(FP)或面向对象编程(OOP):这两种编程范例都在各种语言中使用,以确保易于被开发人员采用,并有一套熟悉的解决问题的方法。

简化低级到高级 ML 工具分离
这种机器学习方法的简化对于向所有开发者开放可访问性是必不可少的。由于苹果( Core ML 和谷歌( ML Kit) 的专有工具,移动工具变得更加丰富,这两个工具都支持使用 Python 编写的解决方案,以及像 TensorFlow 这样的机器学习库,可以毫不费力地移植到分别用 Swift 和 Kotlin 编写的产品上。
2017 年 4 月,谷歌的 DeepMind 团队一直在开发 TensorFlow 的高级抽象,宣布了这个名为 Sonnet 的项目的开源,旨在让其他开发者更容易从通用开发模型中受益,同时仍然使用 TensorFlow 生态系统中固有的高度调整和强大的功能。
当然,这些开发人员工具中的大多数仍然是基于 Python 的,但 Python 几乎不是编写机器学习模型的最佳工具。它缺乏静态类型、对定制操作的支持、一流的并发性,并且不能很好地扩展:它本质上仍然是一种解释型的单 CPU 语言,这对于仍然大量使用 C++的科学研究案例来说不是很好。事实上,机器学习中的 Python 一般只是对 C++中执行的计算操作的抽象,需要移植到每个平台,同时还要考虑沿途的硬件架构。随着我们在边缘设备(如智能手机)上构建越来越多的解决方案,以及向 ML 库中添加新的操作(由于语言的性质,这对于 Python 来说并不直接),这变得越来越复杂。目前 TensorFlow 的运营每年增长约 15-20 %,并且已经支持了数千个项目:使这个生态系统可用于网络的任务不可避免地非常复杂。
推动模型的发展
这就是 Swift for TensorFlow 最初出现的地方。谷歌的工程师们对 Swift 作为未来一流 ML 语言的能力深信不疑,他们为编译工具链做出了贡献,在基于 LLVM 的堆栈中添加了自动微分等功能,目标是让 Swift 执行安全、并发和编译的模型,为开发人员提供熟悉和直观的体验,以及模型开发期间的编译时检查和调试。

简化实现目标性能和工具的 Swift 方法
然而,用 Python 编写的看似无限的库不会很快迁移到 Swift,更重要的是,专注于 C++和 Python 的学术论文和教学材料不会迁移到 Swift。重要的是 Swift 支持导入 Python 模块和类型互操作性(见下面的 Swift 片段),但是,尽管如此,在未来一段时间内,大多数研究将使用 Python。
import Pythonlet np = Python.import("numpy")
print(np)
let zeros = np.ones([2, 3])
print(zeros)
Python 中缺乏性能就是为什么 2017 年 3 月谷歌宣布了 XLA(加速线性代数),这是一个针对 TensorFlow 的编译器,允许每个操作针对目标架构进行优化,使用 JIT 编译,用于 CPU、GPU,当然还有谷歌的 TPU。XLA 是 TensorFlow 核心的一部分,因此它可用于用 Python 开发的每个模型。为了支持额外的架构(例如,边缘设备上不断增长的 npu ), XLA 通过使用 LLVM IR 支持新后端的添加。

TensorFlow 编译器生态系统
事实上,XLA 是 TensorFlow 的一部分,并由 LLVM 提供支持,这意味着它也可用于其他基于 TensorFlow 的工具,特别是可以与用于 TensorFlow 的Swift一起使用。然而,对于像 Python 这样的解释语言来说,基于 JIT 的模型编译更多的是一种权宜之计,而不是一种现代机器学习语言的方法。如果我们真的想提高高级现代语言中机器学习的性能,那么编译工具链需要将模型视为代码。
作为代码的模型
用于 TensorFlow 的 Swift 令人兴奋的发展之一是来自 LLVM 的支持,以支持将模型作为代码进行 Swift 编译(以及通过 Jupyter 等工具调试模型的 LLDB)。
编译期间优化代码是通过中间表示(IR)实现的。由于 Swift SIL,Swift 拥有了这种能力,这使它成为一种速度惊人的语言,当与类型安全和并发性相结合时,它使开发生产就绪代码变得快速而简单,占用空间最小。 Swift for TensorFlow 致力于将编写程序时可用的相同类型的现代和高性能开发引入机器学习,并使每个开发人员都可以使用机器学习,无论解决方案需要多深或多浅。
Swift 通过对自动区分(AD) 的一流语言支持实现了这一目标。下面是一个常见的例子,展示了如何导入一个 C 函数并使其在 Swift 中可区分(是的,Swift 可以导入 C 库以及 Python)。
import Glibc
func sillyExp(_ x: Float) -> Float {
    let 𝑒 = Float(M_E)
    print("Taking 𝑒(\(𝑒)) to the power of \(x)!")
    return pow(𝑒, x)
}
@differentiating(sillyExp)
func sillyDerivative(_ x: Float) -> (value: Float, pullback: (Float) -> Float) {
    let y = sillyExp(x)
    return (value: y, pullback: { v in v * y })
}
print("exp(3) =", sillyExp(3))
print("𝛁exp(3) =", gradient(of: sillyExp)(3))
这将我们带到一个世界,在这个世界中,编译工具链不仅仅是在转换为可执行程序的过程中优化人类可读的指令,而是完全支持将机器学习操作优化为目标平台的模型,安全且具有高级别的抽象。
然而,这篇文章不是关于 Swift 的状态,而是关于网络人工智能的状态,所以让我们回到我们目前所处的位置,以及 Python 社区是如何解决性能缺陷的。
说到广告,Python 社区已经开发了各种工具,最著名的是现在已经不存在的哈佛大学开发的亲笔签名。同样,这更多的是对 Python 缺陷的修补,而不是重大的飞跃,但它是令人印象深刻的 Python 优化工具生态系统的一部分,当您试图最大限度地利用您的模型时,它变得必不可少。
当核心开发人员在 2017 年开始为 JAX 做出贡献时,亲笔签名就变得过时了,这是一个亲笔签名的基于 XLA 的工具,用于进一步提高基于 Python 的机器学习模型的性能。虽然经常有局外人的观点认为 JAX 正在与 Swift 争夺 TensorFlow ,但现实是两者都可能仍然是高性能机器学习的强有力选择,并将继续愉快地共存,这要归功于它们的共同点。
让我们来点诗意的吧
在本文的前面,我提到了对高级抽象的需求,以简化新开发人员的入门。Sonnet 非常适合 TensorFlow 抽象,但从那时起,DeepMind 已经用前面提到的 JAX 增加了 TensorFlow 的使用,以获得它带来的性能优势,因此 Sonnet 不再与它们的使用相关。
俳句是 DeepMind 的项目,用于在 JAX 之上提供类似 OOP 的概念,本质上取代了 Sonnet 作为一个高级 API。它简化了 JAX 的使用,提供了一种熟悉的编程方法,并且可以访问 JAX 库的纯性能功能。
现在,如果你试图开发具有轻松和性能的模型,那么结合俳句和 JAX 是一个强有力的方法,并受益于现有 Python 生态系统的成熟。
俳句仍然被认为是 alpha 版本,但是与 Swift 的当前状态相比,它可以被认为是足够成熟的,可以在今天使用,只是需要一些温和的谨慎。
到目前为止,我已经从性能工具和开发人员体验(DX)的角度介绍了人工智能开发的现状,但是这些与 Web 有什么关系呢?
陷入网中
2018 年谷歌公布 tensor flow . js;流行的 JavaScript 和 Web 机器学习库。当时,这是一种学习机器学习的好方法,特别是如果你是一名网络开发人员,但不是一种推动机器学习或人工智能研究边界的伟大工具。
例如,TensorFlow.js 缺乏对许多操作的支持、隐式浏览器支持以及固有的硬件支持。这是一个很好的业余爱好工具,但严肃的数据科学家不会用它来替代云提供的机器学习工具。
从那时起,TensorFlow.js 发展迅速,实验也在不断增加,但与用于移动机型的 Core ML 和 ML Kit ,或用于 edge 设备的 TensorFlow Lite 相比,TensorFlow.js 甚至还不够。在发布过程中,我从来没有想到会是这样,直到我加入了 Web 组的机器学习。
让我们首先讨论 TensorFlow.js 的位置,因为有很多夸张的说法,主要是 web 开发人员,认为这将使 JavaScript(和 Web)成为开发和交付模型的最佳场所。这与事实相差甚远:TensorFlow.js 更接近 TensorFlow Lite 和 Core ML,旨在解决相同的问题。
如果你想让你的移动或边缘解决方案更智能、更主动或更个性化,你不必求助于将每一点数据发送到你的云 ML 服务,然后返回指令。您在目标设备上开发模型,并利用提供的硬件(如 GPU 或神经处理单元(npu))和操作系统嵌入式工具向最终用户提供计算机视觉、语音识别或个性化功能。网络没有对等物。这是一个没有生产就绪的 ML 生态系统的平台。
TensorFlow.js 旨在将移动和边缘开发者正在开发的相同工具带到 Web 上,以便最终用户体验可以以几乎相同的方式得到增强。这本质上为最终用户带来了额外的好处,如隐私优先的解决方案(不再过分热情地将用户数据发送到云服务),并为开发人员开发具有完全离线功能的渐进式 web 应用程序(pwa)提供了更多激励。
然而,我们还没有实现 TensorFlow.js,这并不是因为存在不可避免的障碍,这些障碍将不可避免地影响任何让机器学习在网络上与移动和边缘设备平等的尝试。
编织复杂的网络
我们先来讨论一下 TensorFlow.js 的实际工作原理。首先,它必须实现与其他 TensorFlow 库相同的操作。这意味着支持数以千计的操作,每年以难以跟上的速度呈指数级增长。而且 TensorFlow.js 不仅仅是将 Python 和 C++中的模型翻译成 JavaScript(因为那样会很简单,对吧?),但是它需要来自硬件的操作支持,通过每个浏览器。
目前,它通过使用 WebGL 来访问设备的 GPU 加速来运行指令,这种工作方式相当粗糙。没错,通常用于执行 3D 转换的 Web GPU 语言正被用于运行机器学习操作。这是有道理的:GPU 长期以来一直在较低层次上这样做,但通过浏览器看到这种情况发生令人兴奋。

TensorFlow.js 架构(由 TensorFlow.js 社区提供)
那为什么这看起来如此不合理呢?世界已经变了。通过编译工具(例如通过 LLVM 的 XLA)和神经处理单元形式的定制硬件(包括运行在所有最新 Android 和 iOS 设备上的谷歌的 TPU 和各种定制的ARM npu)推动 GPU 进行 ML 算术为优化让路。应该没有必要再利用 WebGL hacks 了。
再来看工装。早些时候,我们谈到了 XLA 和 LLVM 如何通过操作和交付针对目标平台(硬件和软件)优化的编译后的机器代码,为 Python 和 Swift 模型提供优化。对于 Swift,这是通过 Swift SIL 实现的,它是 Swift 程序的中间表示(IR ),使程序能够在构建过程中重新解释。Python 没有相应的功能,因此它依赖于实时(JIT)优化——它们是 Python 模型的优秀助推器,但工作量很大,仍然达不到静态类型编译语言的要求,因为无法保证操作树的哪个分支将执行。

Swift 编译器基础设施
XLA 可能会在 Python 模型上提供 15%的改进,而 TensorFlow 的 Swift 可以快 400%。
就其松散的类型和解释而言,JavaScript 看起来更接近 Python 而不是 Swift,因此 XLA 路线看起来像是基于浏览器的 ML 的逻辑方式。除了 XLA 创建机器码,TensorFlow.js 的很多好处是它可以在浏览器中运行。
如何将 JavaScript 编译成浏览器可以理解的优化的机器指令? WebAssembly 对吧?其实不是,简单来说,WebAssembly 在自己的内存空间中运行,无法与你的模型交互。毫无疑问,它将为使用 ML 的各种复杂终端解决方案提供性能改进——事实上 TensorFlow.js 有一个 WASM 后端,其他项目如 ONNX 有自己的 WASM 项目——但这些都是通过 CPU 或 WebGL 间接操作的。一般来说,它们比纯 JavaScript 更快,所以每次开发模型时都应该使用它们,但是要将 IR 优化的潜力与本机硬件支持相匹配,它们还有很长的路要走。
这就是网络神经网络 API (WebNN) 讨论的由来。要让 TensorFlow.js 等于 TensorFlow Lite 、 Core ML 和 ML Kit ,就意味着浏览器厂商需要入局。在模型执行上,考虑它们与 Android 或 iOS 处于同一操作层面。这需要标准化(从来不会很快)和合作(即使有最好的意图也可能很复杂)。
让我们记住,大多数机器学习开发是用一种低效的语言(Python)完成的,然后通过像 Core ML 和 TensorFlow Lite 这样的工具转换到每个目标平台,这意味着如果他们想充分利用机器学习,每个开发人员都必须知道 Python,不管他们开发什么。尽管现代设备内置了对机器学习操作的隐式支持,但像 Swift 和 Kotlin 这样的语言无法直接访问,但情况仍然如此。
把它扯下来
XLA 优化工具目前能够优化由 JAX、茱莉亚和皮托奇构建的模型。它通过另一个称为 HLO(或高级优化器)的 IR 组件来实现这一点。这通过一个可扩展的框架来支持各种架构的各种后端,LLVM IR 能够针对各种 CPU 和 GPU 进行优化。本质上,XLA·HLO 是基于 IR 的优化的另一个抽象。
退一步讲,很明显,Swift SIL、XLA 和 LLVM IR 都面临着同样的问题。Rust 还有 MIR 和 HIR ,利用 LLVM IR,连同 Go。如果我们能够在现代语言中结合 XLA 和 LLVM IR 的方法,我们将能够轻松、安全地开发具有 C++性能的模型。tensor flow 的 Swift承诺了这一点,但是其他语言呢?

LLVM IR 支持的各种编译器基础设施
如果我们试图开发一个支持编程语言和机器学习模型的统一编译器工具链,我们很快就会陷入困境。对硬件发展的依赖以及数字抽象和 ML 开发之间的差异意味着这两个问题的规模完全不一致。抽象成 ML 建模的中间表示对于这种工作是至关重要的。
这就是 MLIR(多层次中间代表制)的用武之地。MLIR 是 Google 基于 LLVM IR 创建一个可扩展工具链的尝试,以允许模型的优化编译。目前 MLIR 支持各种方言,包括 TensorFlow IR、TensorFlow Lite、XLA HLO 和 LLVM IR,但它为将来添加更多方言提供了基础。Google 向 LLVM 捐赠了 MLIR,因此它可以从 LLVM 社区在开发强大的指令优化基础设施方面的丰富经验中受益。

将 XLA·HLO 加入 LLVM IR
到那时,一种能够为浏览器优化一组通用指令的方言可能并不遥远。WebNN 一直在寻找用例来进一步实现这一点,以及实现这一点所需的基本操作,在此阶段不要过多关注 MLIR。
这需要超越 CPU 和 GPU。在未来,各种设备拥有不同的 npu 来完成特定任务并不是不可行的,比如语音识别或计算机视觉。操作系统已经做出了将特定指令输送到哪里的智能决策,但网络仍然对 CPU 和 GPU 只有天真的理解。
浏览器供应商已经通过允许为一组指令指定优选的处理器架构来解决这一问题,如果不可用,将选择后备。这将使 npu 能够在浏览器中使用,并与 MLIR 的优化操作相结合,这可能意味着直接来自 web 应用程序的机器学习模型的接近本机的性能,最终使 web 应用程序获得与移动设备一直受益的 ML 工具相同的访问级别。
我们离这还有很长的路要走,因为网络事实上不是一个平台。这是一个生态系统。更容易将浏览器描述为平台(即 Chrome / Chromium、Firefox / Servo、Safari / WebKit 等。)对于 web 应用程序,但是这些标准的互操作性依赖于合作,对于机器学习,它需要仔细考虑编译器技术和硬件路线图的非常不同的现实。
这是一个正在进行的讨论,可能是一段时间以来网络上最大的变化。它将为 ML 驱动的 web 解决方案带来隐私承诺,并开创一个 web 应用程序和便携式产品的新时代,这些应用程序和产品刚刚进入现代移动设备。
由于图路线的可能排列,使用解释的 IR 优化模型的当前方式是低效的。机器学习已经是耗电大户,因此降低执行模型所需的能耗是任何未来 ML 工具的一个基本基准。这可以通过转移到 C++来实现,或者通过将大多数 ML 开发转移到可以提供类似性能的语言来实现,这些语言具有更容易的入口点,允许每个人访问能够实现高效模型训练和执行的工具。后者将是更可取的。
MLIR 和 WebNN 将改善性能、工具和网络内外的机器学习选项,但已经有了高级机器学习工具,尚未被大多数开发人员利用。通常采用的方法是将性能问题归咎于算法,而大多数流行的库已经支持针对特定架构的优化。使用像 XLA(和 JAX)这样的功能已经可以在培训和模型执行的可靠生产性能方面产生影响。尽可能使用 WebAssembly 也将在 Web 上提供优势。
MLIR 和 WebNN 正在发展,但是开发人员不仅要学习他们正在使用的库的层和算法 API,还要学习如何从提供的各种工具中获得性能增益。这意味着了解目标硬件以及如何打开它。
虽然本文的大部分内容都集中在 TensorFlow 上,但是对所提到的工具所做的部分努力是关于跨高级工具的统一性和通用性,以简化研究和开发收益。这意味着像 PyTorch 和 ONNX 这样的项目正朝着同一个方向前进,并且有一定程度的互操作性和兼容性,这将使开发人员更容易选择工具。
基于组成的宏基因组序列聚类
基于寡核苷酸组成的宏基因组序列聚类
聚类是将数据点分组的任务,将相似的点聚在一起,不同的点则相距较远。聚类应用于序列分析,尤其是在宏基因组学领域(你可以从我之前的文章这里阅读更多关于宏基因组学的内容)。宏基因组样本可能包含来自数千种物种的序列,我们必须将这些代表不同分类学水平的序列分组,以支持下游分析。这个分组过程被称为宁滨宏基因组学。在这篇文章中,我将解释我们如何根据寡核苷酸组成来结合宏基因组序列。
寡核苷酸组成
寡核苷酸被认为是少量核苷酸的连续串。在计算术语中,我们将寡核苷酸定义为 k-mers (大小为 k 的单词)。寡核苷酸组成被认为在微生物物种中是保守的,并且在物种之间是变化的[1][2]。这通常适用于大小在 2 ( 二核苷酸/二聚体)到 8 ( 八核苷酸/八聚体)之间的寡核苷酸[2]。特定大小的寡核苷酸在序列中的出现频率为我们提供了该特定序列的基因组特征。这些基因组特征可能因物种而异,这是由于以下因素的影响,
- DNA 结构
- 复制和修复过程
- 进化的压力
在这个比较中,我考虑了三聚体(也称 三聚体 或 三核苷酸 )及其组成(三核苷酸组成)。当我们合并反向称赞时,有 32 (4 /2)个不同的三聚体。我们通过计算三核苷酸出现的次数并除以三核苷酸的总数来获得每种不同三核苷酸的标准化频率。我们通常在分析中使用寡核苷酸 的 归一化频率,以避免由于序列长度不同而导致的任何不均匀性。
Normalised freqeuncy of kᵢ
= Number of occurrences of kᵢ / total number of k-mers
(where kᵢ is the iᵗʰ k-mer)
你可以从题为DNA 序列矢量化的文章中了解更多关于如何获得这些 k-mer 频率向量的信息。
ACGT 序列到 k-mer 频率向量的有效转换
medium.com](https://medium.com/computational-biology/vectorization-of-dna-sequences-2acac972b2ee)
两个基因组在一起
我在下面的例子中使用的参考基因组是从 发酵生物学微生物群落标准 的 纳米孔 GridION 和 PromethION 模拟微生物群落数据群落发布 中获得的。
两个基因组的基因组签名
让我们考虑一个简单的例子,我们有两个基因组铜绿假单胞菌和金黄色葡萄球菌。我们可以获得每个基因组的标准化三核苷酸频率向量。我使用了文章DNA 序列的矢量化中介绍的方法。你可以看看,甚至尝试不同的 k 值。我在本文中使用了 k=3 。
如果我们绘制这两个基因组的标准化三核苷酸频率,它将如图 1 所示。

图一。铜绿假单胞菌和金黄色葡萄球菌的标准化三核苷酸频率
我们可以看到在两个基因组的三核苷酸组成谱之间有明显的分离。我们可以使用这个特性来分离序列。
对来自两个基因组的混合序列进行聚类
让我们考虑一下我使用 100 个长度为 10,000bp 的读数模拟的样本数据集,这些读数来自每一种物种铜绿假单胞菌和金黄色葡萄球菌。我使用名为 SimLoRD 的工具来模拟读取。下面是我使用的示例命令。
simlord --read-reference <REFERENCE_GENOME_FILE> --fixed-readlength 10000 --num-reads 100 <OUTPUT_PREFIX>
一旦我们获得了所有读数的标准化三核苷酸频率向量,我们可以获得如下的 PCA 图(图 2)和 TSNE 图(图 3)。

图二。来自每一物种铜绿假单胞菌和金黄色葡萄球菌的长度为 10,000bp 的 100 个读数的标准化三核苷酸频率向量的 PCA 图

图三。每个物种铜绿假单胞菌和金黄色葡萄球菌的 100 个长度为 10,000bp 的读数的标准化三核苷酸频率向量的 TSNE 图
我们可以看到这两个物种的序列有明显的区别。
对来自三个基因组的混合序列进行聚类
另一个我们可以考虑的简单例子是有三个基因组绿脓杆菌、金黄色葡萄球菌和大肠杆菌。这三个基因组也有不同的基因组特征。如果我们绘制从这三个基因组模拟的读数的标准化三核苷酸频率向量,该图将如下所示。

图 4。每个物种铜绿假单胞菌、金黄色葡萄球菌和大肠杆菌的 100 个长度为 10,000bp 的读数的标准化三核苷酸频率向量的 PCA 图

图五。每个物种铜绿假单胞菌、金黄色葡萄球菌和大肠杆菌的 100 个长度为 10,000bp 的读数的标准化三核苷酸频率向量的 TSNE 图
与前面的图一样,这里我们也可以看到三个物种的序列之间有明显的区别。因此,我们可以应用许多聚类和机器学习技术来分离这些序列。
示例工具
- MaxBin 使用四核苷酸频率以及期望最大化算法和概率方法来结合重叠群。
- MrGBP 使用寡核苷酸成分(可能具有稍微不同的表示)与 DBSCAN 结合重叠群。
- LikelyBin 通过马尔可夫链蒙特卡罗方法使用五核苷酸频率。
如果基因组有相似的组成会发生什么?
可能存在这样的情况,即使我们有两个不同的物种,它们可能具有相同的寡核苷酸组成模式。比如考虑两个基因组粪肠球菌和单核细胞增生李斯特菌。如果我们绘制这两个基因组的标准化三核苷酸频率,它将如图 6 所示。

图六。粪肠球菌和单核细胞增生李斯特菌的标准化三核苷酸频率
如果我们绘制从这两个基因组模拟的读数的标准化三核苷酸频率向量,该图将如下所示。

图 7。来自各物种粪肠球菌和单核细胞增生李斯特菌的长度为 10,000bp 的 100 个读数的标准化三核苷酸频率向量的 PCA 图

图 8。从每个物种粪肠球菌和单核细胞增生利斯特氏菌中读取 100 个长度为 10,000bp 的标准化三核苷酸频率向量的 TSNE 图
从这些图中,我们可以看出这两个物种之间很难找到明显的区别。因此,在这种情况下,我们必须使用额外的信息,如物种丰富度来聚类序列。
最后的想法
我希望你对我们如何使用基于组成的宁滨方法聚类宏基因组序列有了一个基本的概念。我希望这将有助于你的研究,并在你的研究项目中自由使用这些信息和技术。我附上了包含代码的笔记本,这样你就可以研究更多的基因组。
你也可以从下面列出的我以前的文章中读到更多关于宏基因组学和相关分析的内容。
深入了解微生物群落的数据
towardsdatascience.com](/metagenomics-who-is-there-and-what-are-they-doing-9f204342eed9) [## 新冠肺炎与之前发现的冠状病毒有多相似
不同冠状病毒基因组组成谱的简单比较
towardsdatascience.com](/how-similar-is-covid-19-to-previously-discovered-coronaviruses-c3d9f25840f7)
感谢您的阅读!
干杯!
参考
[1]卡林,S. 等。细菌基因组的组成偏差和进化含义。细菌学杂志,179(12),3899–3913(1997)。
[2] Dick,G. J. 等微生物基因组序列签名的社区范围分析。基因组生物学,10(8),R85 (2009)。
组合学习是机器学习的未来

下一波智能将如何实现
自 20 世纪 50 年代以来,机器学习已经走过了漫长的道路,当时设计了简单机器学习算法的统计方法,并引入了贝叶斯方法进行概率建模。接近二十世纪时,对支持向量机和基本神经网络等模型的研究随着反向传播的发现而爆发,这是基于艾伦·图灵在计算机方面的进展。几年后,大规模计算的可用性已经让位于大规模神经网络,这些网络可以在围棋上击败世界冠军,生成现实艺术和阅读。历史上,机器学习的进步是由计算能力的可用性推动的。
随着让经典计算中的计算机芯片变得越来越强大的动力开始枯竭(接近最小分子大小的比特),机器学习的发展不再能够依赖计算能力的稳定增长来开发更加强大和有效的模型。作为回应,机器学习正在转向组合学习。
组合学习是基于一个模型不能做所有事情的想法。当深度神经网络仅用于一项任务时——比如,识别恶性或良性癌细胞,或将图像分类为狗或猫——它们可以表现得相当好。不幸的是,在神经网络中观察到的一点是,它们只能很好地做一件事。随着人工智能的应用越来越复杂,单一的神经网络只会越来越大,从而导致更多神经元的新并发症。
如前所述,这种持续增长的能力已经走到了尽头。通过组合这些神经网络中的几个来执行完整任务的各个部分,模型作为一个整体在这些复杂的任务中表现得更好,同时保持合理的计算空间。当一项任务被分解成几个神经网络时,每一个独立的网络都可以专注于它们的领域,而不是需要被一个网络覆盖。这类似于要求总统(或总理)在有或没有劳工部长、国防部长、卫生部长和其他部门支持的情况下做出决定。
例如,考虑下面的任务:为一家高档餐馆创建一个聊天机器人,它与用户互动,可以执行普通任务,如查询菜单或预订,以及进行闲聊。

作者创作的图形。
对话显然可以分为三个部分:寒暄和闲聊、信息检索和行动。我们可以选择一个更加分布式的系统,而不是一个机器学习模型过程来接收之前的交互并输出响应:

作者创作的图形。
一个神经网络推断出手头的任务是什么——如果用户正在引导对话,期待一句客套话、信息或一个动作——并将任务分配给一个专门的网络。通过使用分布式模型而不是更直接的东西,如编码器-解码器网络或 text GAN *,实现了两个好处:
- 精确度更高。因为任务被委托给三个独立的模型,每个模型都专注于自己的领域,所以模型的性能得到了提高。
- 运行时间更快。虽然训练分布式模型通常是一个更困难的过程,但分布式模型在进行预测时要快得多,这对于需要快速响应的项目来说是必不可少的。这是因为分布式模型可以被认为是“分裂”单一模型,因此信息只通过与当前任务相关的有用神经元,而不需要流经整个网络。
*编码器-解码器网络和 gan 由多个网络组成,或许可以将其视为组合模型本身。在这种情况下,它们被认为是单一的,只是因为组合模型扩展了它,使它更有效。所描述的组合模型更像是一个“组合-组合模型”。
或者,考虑使用 GAN s(生成模型)来取代传统的数据扩充方法,这些方法在许多情况下不适合上下文,并且会产生过多的有害噪声。通过不断将新的 GAN 生成的数据汇集到模型中,解决了两个问题:
- 参差不齐的分类标签。数据收集的一个大问题是,模型往往会做出与标签中相同比例的预测。如果猫狗数据集中有 75%的标签是“狗”,那么该模型在大多数情况下也会推荐“狗”。通过使用 GANs,可以创建额外的图像来平衡等级不平衡。
- 过度拟合。GANs 是一个通常通过数据扩充来解决的问题,它提供了一种解决方案,可以更好地适应各种环境。比如说,对名人面孔的扭曲,可能会在物理上导致图像与其类别不相关。另一方面,遗传算法提供了过度拟合所需的额外变化,可以更有效地提高模型学习的有效性。

作者创作的图形。
或者,例如,考虑一个双模型系统,该系统更有建设性地允许模型首先学习容易学习的(原始难度评估模型可以以高置信度/概率解决的)样本,并且仅在深度神经网络掌握了之前的样本之后引入更困难的训练样本。

作者创作的图形。
通过首先建立基础概念,然后针对更困难的数据样本微调权重,这种渐进难度学习可能比传统的学习方法更有效。这种想法依赖于组合模型框架,它由两个或更多由逻辑流链接的子模型组成。
应该注意的是,合成学习不同于集成方法,因为合成学习中的 a 模型执行不同的任务,并且 b 基于上下文的关系是合成学习的关键部分,这在集成方法中是不存在的。
也许作文学习方法如此有效的原因是因为我们的大脑本质上也是作文的。我们大脑的每个部分都专门从事一项特定的任务,它们的信号经过协调和聚合,形成一个有效的、动态的决策者。
组合学习比标准建模困难得多,标准建模需要选择正确的算法和准备数据。在组合系统中,有无数种方法可以构建每个模型之间的关系,所有这些都依赖于上下文。在某种程度上,构建组合学习模型是一门艺术。确定利用什么类型的模型以及什么关系需要额外的编码、创造性思维和对模型本质的基本理解,但在处理人工智能在 2020 年和未来需要解决的复杂问题时,这是非常有益的。
人工智能的未来在于作曲学习。
编辑(3/12/22):Yann le Cun 最近推广的的“世界模型”概念很好地展示了写作学习在工作中的作用。
理解主成分分析
通过识别关键组件及其影响,简化预测模型的开发

马德琳·拉格斯代尔在 Unsplash 上的照片
复杂数据的分析通常涉及多维数据集的处理。大多数时候,数据中有很多噪声,人们必须在这些噪声中寻找有用的信息或信号。此外,可视化倾向于超出三维空间的数据是一项艰巨的任务。
主成分分析(PCA)是一种降低数据集维度的技术,同时保留数据中有意义的变化。这种方法确保在不损害从数据中提取的信息的情况下降低数据的复杂性。这就是所谓的“T6”降维。
方法背后的动机
通常,差异很大的要素往往包含有意义的信息。预测算法试图学习这些变化,从而能够识别数据中的模式。因此,具有高方差的特征是建立精确预测模型的基本要求。PCA 可以捕捉数据中的这些变化,并可以促进预测算法提供良好的结果。
PCA 还能够生成彼此独立的特征。这对于识别不相关的数据尤其重要。构建预测模型时,建议选择相互独立的要素以避免多重共线性。
当数据集中的要素相互关联时,就会出现多重共线性。选择特征时的想法是拥有一组独立特征,通过这些独立特征可以预测从属特征或目标变量的值。如果我们有相关的特征,一个特征的微小变化可能会影响其他特征,从而导致模型精度的降低。
使用 PCA 的另一个原因是减少我们为预测模型选择的特征数量。我们选择的特征数量越多,我们的模型就越复杂,模型就越会过度拟合数据。过度拟合会导致在我们的训练集上有极好的模型性能,但在我们的测试集上有相当差的性能。使用 PCA 背后的想法是保留数据集中的信息,同时降低模型的复杂性。
逐步理解 PCA 的方法
为了理解主成分分析,让我们以小鼠和基因数据集为例。

小鼠基因数据集
让我们考虑仅从数据集中选取一个要素来绘制图表。我们把这个特征称为“基因 1”。考虑到我们只有一个特征,我们的图中只有一个维度。

一维数据表示
现在,让我们用两个特征来绘制我们的图,即‘基因 _ 1’和‘基因 _ 2’。在这种情况下,我们将处理二维空间。

二维数据表示
继续相同的趋势,现在让我们考虑三个特征,“基因 _1”、“基因 _2”和“基因 _3”。现在,我们将有一个三维图。

三维数据表示
注意:值较大的“基因 _3”数据样本的大小较小,因为它们将位于远离原点的位置,而值较小的样本将更靠近原点。
现在,如果我们想画一个有第四个特征的图,需要一个四维图,这是不可能画出来的。
PCA 来拯救
主成分分析(PCA)可以获取这些四维或多维数据,并将其压缩到二维图中,这使我们更容易进行分析。

压缩成 2D 空间的多维空间
让我们试着理解这是如何可能的…
让我们回到之前的二维图,以获得背后的直觉。

“X”代表数据中心的数据分布
中间标有“ X 的点代表数据的中心。一旦我们知道了中心,我们就试着移动图形,使中心与轴的原点重合。

将数据以原点为中心
然后,我们通过原点传递一条线,并将数据点投影到这条线上。

将数据投影到直线上
为了使穿过原点的线成为数据上的最佳拟合线,我们需要将数据投影到线 (黑色细线)上的长度最小化。这也可以通过最大化数据投影点与原点 (红线)的距离来实现。
为了理解这个最小化和最大化的概念,让我们建立一个直觉。

穿过数据的最佳拟合线
当我们试图得到一条更适合我们数据的线时,投影线(细黑线)的长度逐渐减小,而投影点离原点的距离长度(红线)逐渐增加。
为了从数学上理解这一点
假设我们在 x-y 平面上有一个数据点。投影线的长度用' b 表示,投影点到原点的距离用' c 表示,最后数据点到原点的距离用' a 表示。

应用毕达哥拉斯定理
我们很清楚,当把一个点投影到一条线上时,投影线垂直于这条线。
因为这个性质是真的,所以我们有一个直角三角形,并且这里可以应用'毕达哥拉斯定理'。
因此,我们可以观察到 a 的值将保持不变,即使我们试图改变蓝线的方向。这里我们仅有的变量是 b 和 c 的值。
我们还可以观察到,当我们试图用蓝线来拟合我们的数据时,' b '的值会减小。因此,' b 的值也减小。
根据'毕达哥拉斯定理'如果' b '减少,而' a '保持不变,我们保持等式的唯一方法是增加' c '的值。
直觉上,需要最小化的是' b ,但实际上更容易计算的是' c '。
因此,PCA 试图最大化' c '的值。
现在我们已经清楚了这一点,我们将尝试把这一方法应用于我们手中的数据。

计算距离平方和(SS)
PCA 执行以下步骤
- 它将点投影到要拟合的线上。
- 它测量投影点到原点的距离。在这种情况下, dₓ ,其中“ x ”代表数据点。
- 然后计算距离的平方,去掉负号。
- 最后,它总结了所有的平方距离。
于是我们看到了这个: d₁ +d₂ +d₃ +d₄
这个量被称为距离平方和。我们将用' SS '来表示它。
我们现在将蓝线旋转一个小角度,同时计算平方距离的总和( SS ),试图找到数据的最佳拟合线。重复该过程,直到我们达到 SS 的最大值。

主成分 1(简称 PC1)
我们最终获得了上图中数据的最佳拟合线。对于这条线,我们有最大值的 SS 。这条最佳拟合线也被称为“主成分 1 或“ PC1 ”。
假设对于这种数据分布,PC1 斜率为“0.25”。
这意味着,我们在“X 轴”方向上每移动 4 个点,就在“Y 轴”方向上移动 1 个点。因此,我们可以理解,PC1 在“基因 _1”轴上分布更广,在“基因 _2”轴上分布相对较少。
这类似于食物食谱。为了生成 PC1,我们将首先添加 4 部分“基因 _1”和 1 部分“基因 _2”。这告诉我们,对于 PC1 来说,相对而言,‘基因 _ 1’比‘基因 _ 2’更重要。
这种使 PC1 的不同特征的组合叫做‘线性组合’。
因此,对于 PC1,我们有一个线性组合:-
- 基因 1 的 4 个部分
- 基因 2 的 1 部分

通过遵循变量的“线性组合,我们得到 PC1 的向量解释。
利用毕达哥拉斯定理,我们可以测量矢量 PC1 的大小。
奇异值分解用于将 PC1 向量缩放为单位向量。当我们将 PCA 与 SVD 一起应用时,我们得到了以下结果。

对数据应用奇异值分解(SVD)
我们可以观察到,尽管 PC1 单位向量的线性组合的值已经改变,但是对于各个分量,比率保持不变。
PC1 的这个单位向量被称为'特征向量或'奇异向量。Gene_1 和 Gene_2 各自的比例称为“加载分数”。加载分数有助于告诉我们哪个特征更重要,例如就投射到 PC1 上的数据而言,加载分数可以告诉我们 Gene_1 比 Gene_1 重要 4 倍。
PC1 的加载分数为:-
- 基因 1 0.97 份
- 基因 2 0.242 份
PCA 将最佳拟合线的距离平方的总和或距离平方的总和称为 PC1 的“特征值”,将特征值的平方根称为 PC1 的“奇异值”。
主成分 2
主分量 2 或 PC2 就是一条穿过原点并垂直于 PC1 的直线。
这是根据正交性的原理完成的。如果两个向量互相垂直,则认为它们是正交的。因此,数据在一个轴上的任何变化或移位或一个向量的移动不会在正交轴或正交向量上产生任何相应的变化或移位。这两个向量本质上是相互独立的。

数据以类似于 PC1 的方式投射到 PC2 上。
由于 PC2 与 PC1 正交,因此 PC2 的配方为:-
- -1 部分基因 _1
- 4 部分基因 _2
通过 SVD 缩放后,PC2 的加载分数为:-
- -0.242 份基因 _1
- 基因 2 0.97 份

然后旋转 PC1 和 PC2,使 PC1 保持水平。然后,我们使用主成分上的投影点来绘制新维度平面中的数据点。

我们现在有了一个全新的框架来分析我们的数据。原来的轴心对我们毫无用处,可以被淘汰。
我们观察到大多数数据点倾向于位于 PC1 上。这给我们带来了一个重要的观察结果。与 PC2 相比,PC1 上存在大量的数据变化,事实上,与任何其他主成分相比,PC1 表现出最大量的数据变化,因此是我们数据分析中的一个重要特征。
这是因为机器学习算法试图学习的是数据的变化,以便在未来做出预测。
记住特征值
- SS(PC1 的距离)= PC1 的特征值
- SS(PC2 的距离)= PC2 的特征值
特征值用于计算每个主成分变化的贡献。
- SS(PC1 的距离)/n-1 = PC1 的变化
- SS(PC2 的距离)/n-1 = PC2 的变化
其中' n '代表样本大小,或者简单地说,数据点的数量。
假设:
- PC1 的变化= 15
- PC2 = 3 的变化
因此,总变差= 18
- PC1 占 PCs 总变化的(15/18) = 0.83 = 83%
- PC2 占 PCs 总变化的(3/18) = 0.17 = 17%
三轴数据

PCA 的概念基本保持不变。
- 找到数据的中心。
- 穿过原点绘制一条最佳拟合线。
- 最佳拟合线称为 PC1。
- 画一条与 PC1 正交的线,称为 PC2。
- 同样,画一条与 PC1 和 PC2 都正交的第三条线,称为 PC3。
因此,所有的 PC 都是相互垂直的。
注:理论上,每个变量或特征有一个主成分分析轴,但实际上,主成分分析轴的数量要么是变量的数量,要么是样本的数量,以较小者为准。
一旦我们画出所有的 PC,我们可以使用特征值即 SS 距离来确定每个 PC 所占的变化比例。
所以,让我们假设在这种情况下,
- PC1 占 79%
- PC2 占 15%
- PC3 占 6%
我们可以画出这个 PCA 的碎石图。

我们可以观察到 PC1 和 PC2 占了 94%的数据。因此,我们可以在分析中忽略 PC3,只使用 PC1 和 PC2。
这就是'降维'发挥作用的方式。我们有效地将数据依赖的维度数量从三个减少到两个。
然而,有时我们确实会遇到这样的情况。

在这种情况下,每个主成分的贡献几乎相等,只考虑几个主成分不足以解释所有的变化。然而,即使有噪声的主成分分析也有助于数据的聚类。

结论
在我们的数据分析中采用主成分分析有其合理的好处。然而,在使用这种方法时,我们确实面临一些缺点。
一旦我们通过 PCA 运行我们的特征,它就有效地创建了一组新的特征,这些新的特征保存了原始特征中存在的信息。如此创建的这组新特征是主要组件。如果没有领域专业知识,我们真的无法判断这些主要组件代表什么。因此,我们普遍丧失了可解释性。
然而,这并不是我们预测模型的重大挫折。如果我们能够从我们的数据中提取重要的信息,那么这种方法仍然是一种有用的技术,我们可以使用它而不用担心模型过度拟合数据。
感谢您阅读这篇博客。我希望听到你对此的想法。
参考
- Josh Starmer, StatQuest:主成分分析(PCA),循序渐进。https://www.youtube.com/watch?v=FgakZw6K1QQ
- 饶彤彤,了解 PCA(主成分分析)。https://towards data science . com/understanding-PCA-fae3e 243731d
理解 AUC-ROC 曲线
深入了解 AUC-ROC…
预测建模是指在我们没有答案的情况下,利用历史数据开发一个模型,对新数据进行预测。— 杰森·布朗利

图 1:51 区限制标志——来源:【https://www.atlasobscura.com/places/area-51
当我们进行监督学习时,我们经常试图为一些特定的度量优化我们的算法。在分类任务中,我们模型的输出是一个离散的标签,受试者操作特征曲线下的面积——或 AUC-ROC 曲线——可能是一个我们试图根据我们试图解决的问题进行优化的指标。
不幸的是,51 区与 AUC 无关(或者说是吗?嗯)我对此没有什么见解,所以我们可以忽略图 1——但这是一张很酷的照片,然而在这篇文章中,我的目的是分解 AUC-ROC 指标。在这篇文章结束时,你会知道:
- 什么是 ROC 曲线。
- 如何创建 ROC 曲线。
- 推导绘制 ROC 曲线时使用的指标的公式。
- 如何解读 ROC 曲线?
- 曲线下的面积是多少。
如果你以前没有遇到过什么是困惑矩阵,我建议你打开另一个窗口,熟悉一下我关于这个主题的最后一篇文章。当你对混淆矩阵感到舒服时,理解 ROC 曲线将是轻而易举的事。
打破混乱矩阵
towardsdatascience.com](/confusion-matrix-un-confused-1ba98dee0d7f)
什么是 ROC?
ROC 曲线是一个图表。其目的是说明我们的分类模型在不同阈值下区分类别的能力。让我们深入探讨一下这个问题…
我们将从简要概述混淆矩阵开始。混淆矩阵将我们的预测分为两类错误和正确预测,如图图 2 所示。第一类错误被称为假警报,因为当它实际上是负的时候我们说是正的,而当真正的标签是正的时候我们的分类模型预测是负的时候,第二类错误是 a,因此留给我们的只有真正的正标签和真正的负标签。

图 2:模型区分 0-1 范围内不同阈值的能力图。来源:https://stack overflow . com/questions/57280577/sensitivity-specificity-plot-python
图 2 上的垂直虚线表示当前模型的阈值。我们可以通过沿该轴向右或向左移动该线来改变模型的阈值,但是,这将导致灵敏度或特异性的折衷,具体取决于我们移动它的方式。如果您还不理解这两个术语,请不要担心,看看如果我们将它向左或向右移动,模型会做出不同的权衡。
如何创建 ROC 曲线
现在,我们可以通过在 0-1 之间的各种阈值设置下绘制真阳性率(TPR)与假阳性率(FPR ),来推断模型在各种阈值下区分类别的能力,这正是 ROC 曲线。我知道你在想什么…我们如何得出这些指标?问得好。
真实阳性率(也称为敏感度或召回率)是从我们的模型预测中正确识别的实际阳性数量的度量。这一点很重要,因为当分类器进行预测时,我们作为数据科学家应该想知道我们的数据中所有实际正确标签(以及有错误的标签和原因)的正确预测比例,因为在理想世界中,我们希望我们所有的预测都是正确的。
然而,由于我们并不是生活在一个理想的世界中(或者你可以在 twitter 上分享为什么你认为我们是这样做的),甚至我们人类,可能是地球上最聪明的生命,也可能难以区分阶级,例如,细胞是否是恶性的。我们分类的假警报数量被称为假阳性率。
导出指标的公式
要获得真正的阳性率,我们必须从我们的模型中识别所有真正的阳性预测(正确预测的阳性预测),并将其除以真正的阳性和我们的模型分类为阴性的阳性标签(假阴性-第二类错误)。

图 3:敏感度公式(真阳性率)。
另一方面,为了从我们的模型预测中得出假阳性率,我们必须识别来自我们的模型的假阳性预测——假警报——并将其除以假阳性和我们的模型归类为阴性的阴性标签(真阴性——正确剔除)。

图 4:假阳性率的公式(1-特异性)。
回顾一下,到目前为止,我们已经了解到 ROC 曲线是一个图形图,它说明了我们的分类模型在不同阈值下区分类别的能力。此外,我们现在知道该图是通过将真阳性率作为假阳性率的函数来绘制的,并且我们知道如何导出这些指标。太棒了。我们知道这些信息就足够了,但是它意味着什么,我们能从中推断出什么来推动商业价值呢?
如何解读 ROC 曲线
我们现在知道这个图是通过将真阳性率作为假阳性率的函数来绘制的。
如果我们沿着 x 轴绘制假阳性率,值的范围从 0 到 1,代表不同的概率阈值,沿着 y 轴绘制真阳性率,从 0 到 1,代表概率阈值,当 x = y(当假阳性率概率阈值等于真阳性率概率阈值时)时,该图明确表示,随着我们增加假警报数量的阈值,真阳性率也会增加。
当我们的模型做出更多积极的预测时,它会增加我们的真阳性率和假阳性率,从而降低我们的真阴性率。简而言之,我们正在用更积极的预测来取代消极的预测。
因此,当 x=y 时,我们的模型就像随机猜测一样好,因为模型的真实阳性率随着我们做出的更积极的预测而增加——用雷曼的话说,它正在努力区分不同的类别。在一些教科书中,您可能会看到假阳性率被称为 1 特异性,因为通过增加假阳性率,我们抵消了真阴性率(正确剔除),因为我们沿着 x 轴移动时会做出更多的阳性预测。

图 5:显示随机猜测模型的 ROC 曲线
注意:特异性(也称为选择性和真阴性率)是通过我们的模型预测(正确拒绝率)正确识别的实际阴性标记的量的量度。

图 6:特异性公式(真阴性率)。
如果不参考图 5 并尝试理解图表中发生的事情,你现在可能已经开始理解 ROC 曲线的要点了。然而,如果您对 ROC 曲线有更深的直觉,您可能会意识到,能够很好地区分类别的模型具有较低的假阳性率和较高的真阳性率——从视觉上看,这意味着这些图将紧挨着图表的左上角(参见图 7 )。这就把我们带到了这篇文章的最后一部分。
作为数据科学家,我们的任务是能够理解我们试图解决的问题类型。这有助于我们了解在不同的设置中优化什么。例如,在我们试图预测恶性肿瘤的情况下,我们可能希望设置我们的阈值来优化灵敏度,以便我们谨慎地不要说患者实际上患有恶性肿瘤,而在垃圾邮件检测设置中,我们不想将重要邮件归类为垃圾邮件,因此我们优化特异性。
什么是 AUC?
如果我们想量化我们的模型区分类别的能力(ROC ),我们使用 AUC,它代表曲线下面积。“下方面积”是 ROC 曲线下方图的百分比。

图 7: ROC 曲线显示了一个模型,该模型可以很好地区分具有高 AUC 的类别。来源:https://stats . stack exchange . com/questions/260845/什么是测试中更重要的-敏感性-特异性-或-roc-auc
你现在知道什么是 AUC-ROC,如何创建图,如何推导 TPR 和 FPR,如何解释 ROC 曲线,以及什么是 AUC 曲线。这篇文章到此结束,尽管您可能希望扩展您已经获得的知识基础,以了解 AUC-ROC 如何扩展到多类分类任务,其中我们的分类器旨在将我们的实例分类到三个或更多类中的一个,或者您可能还想知道 AUC-ROC 如何处理不平衡数据。下面,我给你参考了一些好的资料来源,让你扩展知识面!
参考文献:
数据学校。(2014 年 11 月 20 日)。 ROC 曲线和曲线下面积(AUC)解释。https://www.youtube.com/watch?v=OAl6eAyP-yo
USMLE 生物统计学。(2016 年 4 月 29 日)。敏感性与特异性:权衡。https://www.youtube.com/watch?v=yax-n3ROboE
Scikit 学习文档。接收机工作特性(ROC)。https://scikit-learn . org/stable/auto _ examples/model _ selection/plot _ roc . html
维基百科。多类分类。https://en.wikipedia.org/wiki/Multiclass_classification
Python 中的理解和生成器表达式
Python 理解能力的综合指南。

作者图片
要理解 Python 的理解能力,首先理解理解的概念很重要。编程中的理解只不过是以简洁明了的方式编写(现有的)代码,通常只有一行。它正在通过缩短现有序列来构建新序列。擅长理解代码是一项相当重要的技能。你需要理解实现来应用编程的理解能力。Python-3 支持理解,

Python 3: Image by author 支持的理解类型
在本文中,我们将深入探讨这一点。让我们首先修改一下用 Python 创建列表的方法。
在 Python 中定义列表的不同方法
- 直接列出元素,
 一个列表可以通过枚举/列出方括号中的所有元素来直接定义,用逗号分隔。
Output:
-------
num_cube[] = [8, 64, 216, 512, 1000]
f " num _ cube[]= { num _ cube } "print()中使用的是 Python 3.6 中引入的字符串插值的 f-string 格式样式。
2.使用 for 循环,
定义一个空列表,并使用内置方法 append() 在 for 循环中添加元素。在这里,我们创建了一个从 1 到 10 的偶数立方体列表。我们已经为循环定义了一个空列表 num_cube_floop 和一个来迭代范围(1,11)* 。条件 n%2==0 将确保输出表达式 n**3 只计算偶数。*
Output:
-------
num_cube_floop[] = [8, 64, 216, 512, 1000]
3.使用' += '运算符,
类似于上面的例子我们已经定义了一个空列表 num_cube_op 和,因为循环正在迭代输入文件 list_input.txt 。在使用操作符 '+=' 将输入数据添加到列表之前,我们使用 split( ) 分离输入数据。
Output:
-------
num_cube_op[] = ['8', '64', '216', '512', '1000']
4.使用 map() ,
在这里,我们使用 map() 从现有的列表中创建一个列表。对于刚接触 map() 和 lambda 表达式的人来说,
map(function_to_apply,iterable)——它将‘function’应用于一个‘iterable’的所有元素。
lambda 参数:输出表达式 —匿名函数,它是在没有名称的情况下定义的,并且不遵循正常的 python 函数约定。
在下面的代码中, map( ) 将 lambda 表达式(计算数字的立方)应用于现有列表数字,并在将结果分配给新列表 num_cube_maps 之前使用 list( ) 转换输出。
Output:
-------
num_cube_map[] = [8, 64, 216, 512, 1000]
还有另一种方法可以使用 Python 的理解能力来创建列表。它让我们用一行代码创建一个列表。一开始可能看起来有点难以理解,因为语法可能令人生畏,但在这里我们将分解它以获得更简单的理解。
列表理解
List Comprehension 的一般语法类似于 集合生成器符号 。在集合论中,“集合构造符号是一种数学符号,它通过列出集合的所有元素或描述所有集合元素必须满足的性质来定义集合。”
这个定义的第二部分。根据集合的性质定义集合 在集合论中也叫集合理解。

作者图片
“|”的左边是一个输出表达式,右边是一个集合中所有元素都必须满足的规则。

集合论中的集合生成器符号:作者的图像
这里,输出表达式(n3)定义了集合的元素,规则由输入集合( n ϵ N )和过滤条件(n < =10 和 n!=0).上面的集合定义基本上映射了集合 N (0,1,2,…)中的所有自然数。)并用 n < =10 和 n 限制输入!=0.结果集是,
S1={8,64,216,512,1000}
S1 = { n3 ∣ n ϵ N,n < =10 和 n!=0}
类似地,列表理解的最基本语法是,

作者图片
让我们直接进入例子,
- 同 for 循环
Output:
-------
name_ltr[] = ['P', 'y', 't', 'h', 'o', 'n', ' ', 'P', 'r', 'o', 'g', 'r', 'a', 'm', 'm', 'i', 'n', 'g']With List-Comprehension = ['P', 'y', 't', 'h', 'o', 'n', ' ', 'P', 'r', 'o', 'g', 'r', 'a', 'm', 'm', 'i', 'n', 'g']
在上面的例子中,我们需要创建一个字符串的所有字符的列表。实现这一点的通常方法是定义一个空列表,并在一个 for 循环中使用 append( ) 方法将元素添加到我们的列表中。这里 for 循环正在迭代字符串文字 name 并且 append( ) 将字符串文字的所有单个字符添加到列表 name_ltr 中。使用列表理解可以获得相同的结果,甚至不用担心定义列表或使用 append()。

作者图片
如果我们检查语法,列表理解只不过是对循环的和输出表达式的重新排列。我们把输出表达式放在理解语法的开头,后面跟着循环。这很简单。

作者图片
- 用 if 语句
让我们进一步向它添加一个条件语句。在文章的开始,我们看到了创建偶数立方列表的四种不同方法,一种是使用进行循环。
Output:
-------
num_cube_floop[] = [8, 64, 216, 512, 1000]With List-Comprehension = [8, 64, 216, 512, 1000]
正如我们在代码块中看到的,使用 List Comprehension 时,四行代码减少到了一行。

作者图片
语法类似于我们在上一节中讨论的基本格式,条件性的【T2 if】-语句被添加到末尾,如下图所示。

作者图片
让我们再举一个例子,从一个给定的字符串中的一个特定字母开始创建一个单词列表。
Output:
-------
words[] = ['Python', 'Programming']With List-Comprehension = ['Python', 'Programming']
在这里, statement.split( ) 分隔(默认分隔符是空格)字符串文字中的所有单词,并且循环的将遍历它以过滤从字母‘P’开始的所有单词,结果将使用 words.append(w) 添加到列表中。使用 List Comprehension 时,输出表达式(总是放在开头) w 放在最前面, for 循环迭代字符串文字,后面是 if- 条件。

作者图片
- 带 if-else 语句
if-else 语句的位置与之前讨论的列表理解场景不同。当使用 if-else 条件块时,它被放置在输出表达式之后,如下图所示。

作者图片
让我们创建一个数字列表<10 where we have to calculate square for even numbers and cube for the odd.
Output:
-------
square_cube[] = [0, 1, 4, 27, 16, 125, 36, 343, 64, 729]With List-Conprehension = [0, 1, 4, 27, 16, 125, 36, 343, 64, 729]
The above code block filters the numbers based on condition num%2==0 ,并相应地执行square _ cube . append(num * * 2)或 square_cube.append(num**3)。使用时,列出与相关联的理解输出表达式,如果-语句放在前面,其他条件后面跟有 else 关键字。

作者图片
如何将常见的 for 循环转换成列表理解,这完全取决于我们。在另一个奇数/偶数的例子中,我在一个列表数字中创建了两个列表,代码块是不言自明的。
Output:
-------
numbers[] = [([0, 4, 16, 36, 64], [1, 27, 125, 343, 729])]With List-Conprehension = [[0, 4, 16, 36, 64], [1, 27, 125, 343, 729]]
区别在于如何使用理解来构造列表,为了在一个列表中创建两个列表,定义了两个单独的表达式。

作者图片
- 带有嵌套循环
在总结清单理解之前,让我们再举一个例子。它非常包容,大多数时候可以灵活地用于嵌套循环。我们需要创建两个现有列表 char_list 和 int_list 的笛卡尔积的列表。用于循环的一个在 char_list 上迭代,另一个在 int_list 上迭代。使用 cartesian.append((x,y)) 将两个列表中的元素作为元组添加到结果列表 cartesian 中。
Output:
-------
cartesian[] = 
 [('a', 0), ('a', 1), ('a', 2), ('a', 3), ('b', 0), ('b', 1), ('b', 2), ('b', 3), ('c', 0), ('c', 1), ('c', 2), ('c', 3)]With List-Conprehension = 
 [('a', 0), ('a', 1), ('a', 2), ('a', 3), ('b', 0), ('b', 1), ('b', 2), ('b', 3), ('c', 0), ('c', 1), ('c', 2), ('c', 3)]
输出表达式是一个元组,因此要加上括号。在列表理解中,嵌套的 for 循环按照预期的执行顺序放置。

作者图片
在下面的代码块中, person 是由两个字典组成的列表。他们每个人都有两把钥匙,名字和语言。现在我们必须创建一个与键语言相关的值列表。第一个 for 循环是遍历一个列表 person 的长度,第二个 for 循环被设置为获取给定索引处的键、语言的值。
Output:
-------
person[] = ['Python', 'Java', 'C++', 'C#']With List-Conprehension = ['Python', 'Java', 'C++', 'C#']
通过在一行中重新排列循环的,使用列表理解可以获得相同的结果。

作者图片
集合理解
集合理解的功能与列表理解的功能相同,只是它返回一个集合,并且使用花括号{ }代替方括号[ ]。当它返回一个集合时,它将具有唯一的元素(Set 属性)。
考虑我们之前讨论过的同一个例子,一个由三个字典条目组成的列表 person 。如果我们在代码块中对比 List 和 Set Comprehension 的语法,区别只是方括号和花括号。虽然输出不同,但与列表理解不同,集合理解返回唯一的值。
Output:
-------
With List-Conprehension = ['Python', 'Java', 'C++', 'Python', 'Python']With Set-Conprehension = {'Python', 'C++', 'Java'}
词典理解
与列表和集合理解不同,当数据预期为键值配对格式时,使用字典理解。让我们继续我们的数字立方体例子的遗产。
Output:
-------
cubes = {1: 1, 2: 8, 3: 27, 4: 64, 5: 125, 6: 216, 7: 343, 8: 512, 9: 729, 10: 1000} 
With Dictionary-Comprehension = {1: 1, 2: 8, 3: 27, 4: 64, 5: 125, 6: 216, 7: 343, 8: 512, 9: 729, 10: 1000}
使用我们前面讨论过的列表理解创建了两个列表。
numbers=[num for num in range(1,11)]  
#numbers=[1,2,3,4,5,6,7,8,9,10] num_cube=[n**3 for n in numbers]
#num_cube=[1,8,27,64,125,216,343,512,729,1000]
我们需要创建一个字典,其中一个键的值是来自列表数字的数字,而值来自第二个列表 num_cubes。for 循环在 zip(numbers,num_cube) 上迭代,以获取元组形式的键和值(key,value)。
zip(iterables) —映射多个容器的相似索引,并返回一个迭代器,该迭代器是一系列元组,包含参数中传递的每个 iterable 的元素。
print(list(zip(numbers,num_cube)))Output:
-------
[(1, 1), (2, 8), (3, 27), (4, 64), (5, 125), (6, 216), (7, 343), (8, 512), (9, 729), (10, 1000)]
输出表达式 key:value,映射从 zip(numbers,num_cube) 返回的值,如下图所示。

作者图片
生成器表达式
生成器表达式的语法类似于列表理解,只是它使用括号( )而不是方括号[ ]。生成器是 Python 中的特殊迭代器,它返回生成器对象。使用它的目的是生成一个项目序列,而不必将它们存储在内存中,这就是为什么你只能使用一次生成器。
这里,我们使用列表理解创建了一个列表 num_cube_lc ,生成器表达式定义为 num_cube_generator 。
Output:
-------
List Comprehension = [8, 64, 216, 512, 1000]Generator Comprehension = <generator object <genexpr> at 0x00000238925E7848>Sum = 1800
生成器表达式的输出不像列表理解的输出;但是,当与 sum( ) 一起使用时,它会传递从表达式中生成的值。
结论
一般来说,理解肯定是减少代码行的有效方法。代码大部分时间更容易阅读和理解;然而,如果你的程序逻辑中有多个嵌套循环,使用理解会牺牲可读性。尽管有些人认为理解是一种更 pythonic 化的编码方式,但真正决定使用或避免它的最佳情况还是由您自己来决定。该主题的一些有用资源是,
本文使用的代码可以从我的 GitHub 库中获得。
Kaggle &泰坦尼克号生存预测竞赛综合初学者指南
我对 Kaggle 上泰坦尼克号生存预测比赛的解答与分析

如果你认识我,我是 Kaggle 的超级粉丝。天知道我在之前的文章中在 Medium 上提到过 Kaggle 多少次。
但是我在卡格尔的旅程并不总是充满玫瑰和阳光,尤其是在开始的时候。由于缺乏经验,我最初很难理解机器学习项目背后的工作流程以及所使用的不同术语。
经过几个月的网上资源筛选,包括许多技术文章、文档和教程视频,我才慢慢开始学习标签编码、交叉验证和超参数调整等概念。
我当时希望有一个一站式商店,在那里我不仅可以学习机器学习项目背后的步骤,更重要的是学习这些步骤背后的原理。所以我想,为什么不利用我在过去几个月里积累的知识来创建一个一站式商店,以帮助那些可能和我经历同样事情的人呢?
在本文中,我将解释什么是机器学习问题,以及端到端机器学习项目背后的步骤,从导入和读取数据集到参考 Kaggle 上最受欢迎的初学者比赛之一,即泰坦尼克号生存预测比赛,建立预测模型。
这个项目将我引入了机器学习的世界,也是我在 Kaggle 上的第一次比赛。
什么是机器学习?
实际上,在我们进入机器学习之前,我认为重要的是,我们首先要理解建立模型背后的目的。
什么是模型,为什么它很重要?
模型是对现实世界的模拟和简化。
无论你是建造摩天大楼的工程师,还是挑选股票的对冲基金分析师,甚至是正在竞选的政治家,我们都要应对这个世界的不确定性。一个模型可以让我们最小化这种不确定性,做出更明智的决策。
现在,一个模型并不完美,它不会帮助我们持续预测一个特定事件的实际结果,但我们肯定可以非常接近它。有了大量的数据,再加上人工智能的力量,人类已经越来越擅长使用模型进行高质量、准确的预测。
这就是机器学习的用武之地。
机器学习是指计算机学会推导数据中存在的趋势和模式。机器学习模型是使用历史数据“训练”的。一旦模型被建立和测试,我们就可以用它来预测其他数据点的未来。
机器学习模型不同于统计模型,因为它们需要最少的人力,但更重要的是,机器学习模型比统计模型需要更少的假设。统计模型是数学密集型的,并且基于系数估计。它要求建模者理解不同变量之间的关系,然后才能将它们包含在模型中。
由于上述原因,机器学习模型不仅做出更准确的预测,而且做出更稳健的预测,远远超过任何普通模型的能力。
监督与非监督学习
为了让这篇文章对初学者尽可能全面,我还想强调机器学习的两个主要分支,即监督学习和非监督学习。理解这两个分支之间的区别将有助于我们更仔细地思考我们试图使用机器学习来解决的问题类型。
监督学习是使用标记数据训练模型。换句话说,我们明确地告诉我们的模型我们预测的样本结果。
监督学习可以进一步分为分类和回归。分类问题是当我们的预测的结果是离散的和绝对的。另一方面,回归具有连续分布的预测。
为了说明分类问题,假设您正在构建一个垃圾邮件分类器,帮助您将电子邮件分类为垃圾邮件或非垃圾邮件。你要做的是准备一个训练集,其中包含你的电子邮件的不同特征,如字数,特定单词和标点符号的使用,最重要的是它们的标签,垃圾邮件或非垃圾邮件。然后,您可以使用该训练集训练机器学习模型,该模型将开始学习特征及其标签之间的关系。一旦模型建立到你想要的精度,你可以使用这个分类器来分类你的其他邮件。
回归与分类的不同之处在于,回归问题的结果是连续的,而不是有一个离散的目标变量,如垃圾邮件或非垃圾邮件。回归的一个完美例子是预测房价。在这里,房价是一个连续变量。同样,我们需要准备一个训练集,其中包含房屋观察以及它们的特征,如卧室数量、离城市的距离、客厅面积,当然还有它们的销售价格。然后,模型将了解不同的房屋特征如何与最终销售价格相关联。随后,我们可以使用这个模型对其他房屋进行预测。
现在,让我们转到无监督学习。
正如我们在垃圾邮件分类器和房价预测示例中看到的那样,我们将样本结果(垃圾邮件与非垃圾邮件以及最终销售价格)作为训练集的一部分,以训练我们的机器学习模型。
在无监督学习中,我们不包括任何样本结果,而是简单地让模型导出数据中存在的任何潜在模式,并相应地对它们进行分组。
由于大多数 Kaggle 比赛都使用监督学习,所以我不会在本文中过多详细地讨论无监督学习。
本次比赛的目的
这个比赛的目的是建立一个机器学习模型,帮助我们预测泰坦尼克号上乘客的生存结果。
这是监督学习中二元分类问题的一个例子,因为我们将乘客的结果分为两类,泰坦尼克号幸存者或幸存者。
更具体地说,我们想调查不同的乘客特征,如他们的年龄、性别、机票等级等如何影响他们的生存结果。
这个竞争的评估标准是测试集中被我们的分类器正确预测的乘客的百分比。
笔记本漫游
在这一节中,我将简要讨论我分析竞争的步骤和结果。你可以在我的 GitHub 上找到这个项目的完整代码。
数据集
Kaggle 竞赛中有三种类型的数据集。在更高级的比赛中,您通常会发现更多也更复杂的数据集,但一般来说,它们属于三类数据集之一。
- 训练集:这是我们将执行大部分数据操作和分析的数据集。关于泰坦尼克号生存预测比赛,我们希望分析和/或创造能够帮助我们预测乘客生存结果的特征。此外,顾名思义,这是我们将用来训练我们的机器学习模型的数据集。
- 测试集:一旦我们的分类器建立起来,我们希望确保它不仅适合我们的训练数据,更重要的是能够对样本外数据进行预测。回想一下,建模的主要目的是预测未用于训练我们的模型的真实世界数据。如果模型仅在训练数据上表现良好,而无法对新数据进行预测,则该模型本质上是无用的。Kaggle 将使用测试集来评估我们模型的准确性,即我们的分类器在对我们的分类器以前没有见过的乘客数据进行分类时有多准确。测试集比训练集少一列,训练集是响应(目标)变量,在我们的例子中,是幸存的一列。
- 样本提交:这是我们向 Kaggle 提交最终解决方案的格式。我们最终的数据框架需要与样本提交数据框架具有相同的形状(相同的行数和列数)以及相同的列标题。
数据描述
在这里,我将概述 dataset 中列的定义。您可以在竞赛页面的数据选项卡下找到这些信息。
- 幸存: 0 =没有幸存,1 =幸存
- Pclass: 客票等级,其中 1 =头等,2 =二等,3 =三等
- 性别:男或女
- 年龄:以年为单位的年龄
- 泰坦尼克号上兄弟姐妹或配偶的数量
- Parch: 泰坦尼克号上父母或子女的数量
- 车票:旅客车票号码
- 票价:客运票价
- 客舱:客舱号
- 登船:登船地点,C =瑟堡,Q =皇后镇,S =南安普敦
探索性数据分析
探索性数据分析是可视化和分析数据以获取洞察力的过程。我们将主要使用熊猫图书馆来完成这项任务。我有大量关于熊猫的教程,你可以在这里查阅。
在这一部分,我将讨论我的 EDA 的关键结果。同样,你可以在我的笔记本上找到完整的分析。
我首先将数据集中的所有特征分为分类变量和数字变量,并对它们进行单独分析,以了解它们与存活率之间的关系。
执行特征分析时,区分分类变量和数值变量非常重要,因为这有助于我们更恰当地构建分析。例如,我们不能计算性别等分类变量的平均值,因为平均值只能应用于具有连续值分布的数值变量。
训练集中的分类变量是性别、p 类别和上船。另一方面,数字变量包括 SibSp、Parch、Age 和 Fare。
以下是我从 EDA 过程中收集的一些见解:
- 女乘客生还的可能性远远大于男乘客。如果我没记错的话,在泰坦尼克号的疏散过程中,妇女和儿童是优先考虑的,所以女性比男性有更高的生还机会是有道理的。

女性乘客比男性乘客更有可能生还
- 头等舱乘客比二等舱乘客更有可能生还,二等舱乘客比三等舱乘客更有可能生还。头等舱乘客是社会地位、影响力和财富都很高的个人。如果他们在疏散过程中比其他乘客优先,我不会感到惊讶。

头等舱乘客的生存概率最高;三等舱的乘客生还概率最低
- 费用是与存活率最(正)相关的数字特征。乘客支付的机票越多,他/她生还的可能性就越大。有道理!参考上面关于头等舱乘客的圆点点。

在 0.26 处,费用与存活率最相关
- 年龄较小的乘客,尤其是儿童,比其他乘客有更高的生存概率。再次强调,疏散时“妇女和儿童优先”。

年龄较小的乘客尤其是儿童更有可能幸存
数据预处理
数据预处理是为模型训练准备好训练集的过程。在这里,我们处理缺失值,应用数据转换,进行特征工程以及标签编码。
我现在将简要地介绍一下这些步骤,但我强烈建议您参考我的笔记本,以便更好地理解这里正在讨论的内容。
- 缺失值:有两种处理缺失值的方法,丢弃或者填充。用替代值填充缺失值有时称为插补。每种方法在方便性和准确性之间都有自己的权衡。具体地说,从我们的数据框架中删除缺失值是处理缺失数据的最简单和最方便的方法。然而,这通常是以丢失数据集中潜在的有用信息为代价的。另一方面,插补需要更多的时间和考虑才能有效执行。因此,您应该考虑数据集中缺失数据的状态,以便决定最合适的处理方法。根据经验,如果某个列或特性严重缺失,我们可以安全地将其从数据集中删除。关于泰坦尼克号的比赛,我决定去掉客舱特性,而填充上船、票价和年龄特性。
- 数据转换:该步骤特定于数据集中的 Fare 列。票价分布具有很高的正偏度,即分布向左侧倾斜。偏斜是指数值集中在分布的一端。这可能会对我们的模型做出准确预测的能力产生不利影响。因此,通过数据转换来解决这个问题至关重要。更具体地说,我对 Fare 列应用了一个对数变换,这导致偏斜度从 4.51 急剧下降到 0.57。

对数转换前的乘客票价

对数转换后的客运票价
- 特征工程:特征工程是从现有特征中创建新特征的过程,以更好地向预测模型表示潜在问题。这可以说是机器学习中最重要的艺术。之所以称之为艺术,是因为特征工程通常伴随着经验和领域专业知识,即特定问题或行业的知识。在我的笔记本上,我创建了三个新的功能,它们是标题,孤独和年龄。
- 标签编码:机器学习模型要求所有的输入输出变量都是数值。因此,我们需要在将分类数据拟合到我们的模型之前,对它们进行编码。
模特培训
比赛最精彩的部分来了,模特!
如果你正在用 Python 语言编码,Scikit-learn 是最流行的机器学习库之一。他们也有一个关于工具的全面的文档,这些工具包含在用于数据预处理、建模、模型评估和超参数调整的库中。
在我们可以将训练集拟合到我们的模型之前,我们需要首先将训练集分成预测变量和响应变量。
我已经选择将训练集适合于十个不同的分类器,它们是:
- 逻辑回归
- 支持向量分类器
- k-最近邻
- 高斯朴素贝叶斯
- 感知器
- 线性随机向量分类器
- 随机梯度下降
- 决策图表
- 随机森林
- CatBoost
在 Scikit-learn 中建模需要三个简单的步骤。首先,我们需要实例化我们的模型,也就是简单地声明一个模型并将它赋给一个变量。接下来,我们需要使模型符合我们的训练集,包括预测变量和响应变量。最后,我们可以使用这个模型对测试集进行预测。
模型评估
模型评估中的一个重要概念是交叉验证。回想一下,一个只适合训练数据,但无法对新数据进行预测的模型基本上是没有用的。交叉验证为我们测试模型预测新数据的准确性提供了一种方法。
交叉验证是这样一个过程:我们只使用训练集的一个子集来重复训练我们的模型,剩余的数据留待以后测试。保留的数据有时称为维持集。
交叉验证提供了比仅仅训练准确性更准确的模型准确性评估。这是因为训练精度忽略了过拟合的问题。过度拟合是指我们的模型学习数据中的噪声而不是信号。坚持集背后的想法是,我们的模型可以根据它对未经训练的数据进行预测的能力进行评估。
在我的笔记本中,我选择了其他模型中的支持向量分类器,因为它具有最高的交叉验证均值。

支持向量分类器具有最高的交叉验证均值
超参数调谐
超参数调整是调整模型参数的过程。使用 GridSearchCV,我设法调整了我的支持向量分类器的参数,并看到模型精度略有提高。
决赛成绩
使用我新调好的支持向量分类器,我对测试集进行了预测,并在提交给 Kaggle 时获得了 0.77511 的提交分数。换句话说,我成功预测了测试集中 77.5%的乘客数据。
提高模型准确性的可能扩展
我几个月前做了这个项目,我还没有机会修改它,提高我的模型精度。然而,这里我有几个可能的扩展,您可以潜在地添加到您的项目中,使它比我的更好:
- 分析机票和客舱列,而不是删除它们
- 在功能工程中提出比我现有的功能更好的替代功能
- 删除不太重要的特征以减少过度拟合
- 尝试集成建模,它结合了各种机器学习分类器的结果
结论
泰坦尼克号生存预测比赛是机器学习中分类问题的一个例子。
在这个项目中,我们分析了泰坦尼克号上乘客的不同特征,并随后建立了一个机器学习模型,可以将这些乘客的结果分为幸存或未幸存。
在将十个不同的分类器拟合到我的训练数据之后,支持向量分类器显示出最有希望和最准确的预测结果。因此,我选择了这个分类器作为我的选择模型,并获得了 0.77511 的提交分数,也就是说,我正确预测了测试集中 77.5%的乘客数据。
我希望这篇文章和我的笔记本可以帮助你绕过我刚开始学习机器学习时的最初驼峰,更重要的是,激励你在未来参加更多的 Kaggle 比赛。
你可以在我的 GitHub 这里找到完整的笔记本。如果你有任何问题,请随时联系我。
快乐学习!
视频教程
如果你喜欢通过视频学习,我的 YouTube 频道上有两个视频,详细介绍了这个项目。如果你感兴趣,一定要去看看!
第 1 部分:探索性数据分析
第 2 部分:数据预处理和建模
全面的流失预测和分析
我们的模型能准确检测客户流失以帮助留住这些客户吗?

客户流失,也称为流失,发生在客户停止与公司做生意的时候。理解和发现客户流失是留住这些客户和改进公司产品的第一步。
电信数据集
我们将在电信公司-客户-流失数据集上训练我们的流失模型,以预测客户离开虚拟电信公司 Telco 的可能性。这个合成数据集是由 IBM 整理的,包括一个标签,指示客户是否在上个月离开。
目标:根据人口统计和服务信息预测客户是否会流失。
数据探索
探索和建模将使用 Jupyter 笔记本进行。我们首先加载所需的库,并将数据作为 DataFrame 导入。
索引第一行向我们展示了数据集中的列和一个样本记录。

检查数据集
这个数据集相当干净,没有遗漏条目。总共有 7043 个观察值。大约有 27%的客户表示不满(这对电信公司来说不是一个好兆头)。

我们可以使用 pandas 的info(), describe()方法快速查看数据完整性。数据集没有任何缺失值。虽然熊猫把SeniorCitizen解读为连续变量,但实际上是一个二元指标。

连续变量
我们可以使用 seaborn 库来进一步可视化和检查数据集。让我们先来考察连续变量tenure, MonthlyCharges。由于TotalCharges可以近似为这两个变量的函数,我们将把它从图中排除。
Seaborn 的 pairplot 功能绘制数据中的成对关系。
解释配对图:
- 对角线轴是特定变量的直方图,y 轴测量出现的次数
- 除了轴被翻转之外,图的左下方三角形和右上方三角形捕捉相同的信息
直方图告诉我们,流失客户的任期分布是右偏的,而流失客户的任期分布更加均匀。它还显示,拥有更高MonthlyCharges的客户会看到更高的流失率。
其他两个图表捕捉到非常相似的信息,橙色的“流失”和蓝色的“未流失”之间有明显的区别。

接下来,我们使用箱线图来说明流失客户和未流失客户之间的分位数差异。
第一个方框图比较了两组之间的任期分位数。流失客户的平均任期比未流失客户短得多。75%最终离开电信公司的客户都是在头 30 个月内离开的。在电信公司工作了 70 个月后,出现了一些异常情况。
第二个方框图比较了月度费用和流失率。流失客户的月平均费用明显高于没有流失的客户。这表明折扣和促销可能是吸引顾客留下来的一个原因。

人口统计变量
接下来,我们可以检查分类变量,从人口统计变量开始:性别、老年人、伴侣和家属。
- 左上图统计了性别和流失之间的交集。男性和女性客户之间的流失率差异很小。
- 在SeniorCitizen(右上图)、没有Partners(左下图)和没有dependents(右下图)的客户中,流失率较高。

第二个发现可能会引起业务涉众的兴趣。没有伴侣和家属的非老年人描述了一个特殊的顾客群体。与其他指标不同的是,这一人群中较低的人员流动频率对他们的终身价值有积极意义。
其他分类变量
其他分类变量可以在服务和计费信息之间划分。
这些图表捕获了服务变量。没有在线安全、在线备份、设备保护和技术支持的客户流失比例更高。基于我对电信服务相当有限的了解,我认为大多数人不会经常为这些服务付费。这表明那些对电信服务越来越信赖的人,或者那些需要他们的设备用于特殊用途的人,倾向于减少流失。

接下来,我们研究计费方法和客户流失之间的交集。左上方的图表比较了不同合同类型的客户流失情况。不足为奇的是,那些计划越短(逐月)的人流失率越高。那些有长期计划的人在提前取消时会面临额外的障碍。
令人惊讶的是,那些选择不使用纸质账单的人和那些使用电子支票支付的人一样更频繁地流失。其中一些行为可能会与其他变量混淆(例如,老年客户可能更喜欢纸质账单)。

训练预测模型
这里是我们将要用来构建模型的库。我正在使用一个新的笔记本,所以我将再次加载数据。
在TotalCharges列中,11 条记录是一个空字符串。所有这些记录都有一个有效的MonthlyCharges值,但是期限为 0。我们可以用 0 来估算这些值。
为建模准备数据
首先,我们对分类变量进行热编码,并在训练和测试之间随机划分数据。在划分数据集之后,除了评估目的,我们不接触测试集。
特征选择
先前的探索已经给出了许多关于哪些特性是重要的信息。我们可以结合卡方检验来做出更明智的决定。
注意:卡方检验只能用于评估分类变量。
我们需要理解 p 值来解释卡方输出。
如果零假设为真,并且我们多次重复相同的实验,P 值表示更多“极端”观察的百分比。这里的零假设是该特征对目标没有影响(即,我们不应该将其用作预测器)。低 p 值表明我们应该拒绝我们的零假设——换句话说,拒绝特征没有效果的说法。
大多数要素的 p 值都非常低。只有gender, MultipleLines, PhoneService的 p 值>为 0.05,这意味着我们没有足够的证据来否定这些特征没有影响的说法。这与图表中的故事非常吻合。
为了更直观,让我们用 p 值> 0.05 来绘制所有分类变量。

我们还将删除TotalCharges,因为同样的信息被MonthlyCharges, Tenure捕获,以进一步减少维度(其他两列似乎比这一列更可靠,这一列有数据问题)。
模特培训
我们将使用逻辑回归模型来预测流失。这可能不一定是性能最好的算法或分离数据最好的算法(找出答案的唯一方法是尝试),但让我们假设它对于简化来说足够好。逻辑回归的另一个优点是易于解释。
我们可以利用 Sklearn 的GridSearchCV函数进行超参数调谐。。由于我们不想在最后才触及我们的测试集,我们将进一步把我们的训练集划分为训练和验证。
我们将使用网格搜索来确定这些超参数的最佳值:—我们是否应该使用 L1 或 L2 罚值—C 值—我们是否应该拟合截距类权重(我们是否应该更加强调流失类而不是非流失类)。
在幕后,GridSearchCV 将根据验证集训练和评估众多模型,以确定哪些超参数产生最佳性能。
注意:虽然我在这里没有这么做,但是GridSearchCV可以用来测试候选算法。
我们可以使用grid.best_params_找到最佳超参数值,在本例中为:
{'C': 1, 'fit_intercept': False, 'penalty': 'l1'}
有了这些信息,我们就可以正式训练我们的模型了。我们将在训练集和验证集上训练模型。
评估模型
让我们用我们的测试集来测试这个模型。分类报告给出了精度、召回率和 f1 值(支持是每一类中的观察数量)。微观 f1 分数为 0.81,而宏观 f1 分数为 0.74。

我们可以用混淆矩阵来形象化这个结果。

对于一个初始模型来说还不错。这个合成数据集非常简单:真实世界的电信公司可能会有超过 7000 个数据点。其他变量,如客户与支持人员的互动,可能有助于预测流失。
然而,这个数据集令人难以置信地干净,并带有一个我们在现实世界中经常找不到的良好的流失指示器。
感谢您的阅读!
如果你喜欢这篇文章,可以看看我关于数据科学、数学和编程的其他文章。通过 Medium 关注我的最新动态。😃
作为一个业余爱好项目,我还在www.dscrashcourse.com建立了一套全面的免费数据科学课程和练习题。
如果你想支持我的写作,下次你报名参加 Coursera 课程时,可以考虑使用我的会员链接。完全公开—我从每一次注册中获得佣金,但不会对您产生额外费用。
再次感谢您的阅读!📕
使用 Matplotlib 实现全面的数据可视化
深入研究电影镜头数据集

介绍
数据可视化是数据科学家工具包中的基本技能之一。有了正确的数据,拥有讲述令人信服的数据故事的能力可以为任何组织打开一个机会的金矿,为他们服务的任何人创造价值——别忘了让员工更有效率。
过去,我写过一些实现有效数据可视化的技巧,然而,在那篇文章中,我没有使用单一数据集来探索所有被分享的想法。因此,在这篇文章中,我们将使用我分享的技巧进行一些可视化,并深入研究 MovieLens 数据集。
编辑描述
towardsdatascience.com](/effective-data-visualization-ef30ae560961)
要获得这篇文章中使用的全部代码,请访问我的 Github 库。
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/kurtispykes/recommender_system/blob/master/notebooks/01_kpy_explorations.ipynb)
数据
如前所述,我们将使用 MovieLens 数据集。具体来说,我们将使用 MovieLens 100K 电影评级数据集,该数据集由 1700 部电影的 1000 名用户组成。这些数据是通过 MovieLens 网站在 1997 年 9 月 19 日至 1998 年 4 月 22 日的 7 个月期间收集的。该数据已被清理—评分少于 20 或没有完整人口统计信息的用户已从该数据集中删除。
MovieLens 100K 电影分级。稳定的基准数据集。1000 个用户对 1700 部电影的 100,000 次评分。已发布…
grouplens.org](https://grouplens.org/datasets/movielens/100k/)
为了有效地执行我们的可视化,我们关注收集的 3 个特定数据集:
- u.data—包含完整的数据集,943 名用户对 1682 个项目的 100000 个评分。
- u.item—项目信息(电影)
- u.user—用户的人口统计信息
在这个项目中,我使用了流行的数据科学库,如 Pandas 用于数据操作,Matplotlib 用于数据可视化,NumPy 用于处理数组。此外,我将 Python 的 datetime 模块用于一般的日历相关函数,将 IPython 用于交互式计算。
我们首先简单地导入框架并使用 Pandas read_csv加载数据——参见文档。
**import** numpy as np
**import** pandas as pd
**import** matplotlib.pyplot as plt 
**from** datetime **import** datetime
**from** IPython.display **import** IFrame
**import** warnings 
warnings.filterwarnings("ignore")# read data
rating_df= pd.read_csv("../data/u.data", sep="\t", names=["user_id", "item_id", "rating", "timestamp"])
item_df = pd.read_csv("../data/u.item", sep="|",encoding="latin-1", 
                      names=["movie_id", "movie_title", "release_date", "video_release_date",
                             "imbd_url", "unknown", "action", "adventure", "animation",
                             "childrens", "comedy", "crime", "documentary", "drama", "fantasy", 
                             "film_noir", "horror", "musical", "mystery", "romance", 
                             "sci-fi", "thriller", "war", "western"])
user_df = pd.read_csv("../data/u.user", sep="|", encoding="latin-1", names=["user_id", "age", "gender",
                                                                            "occupation", "zip_code"])
取提供给我们的 3 个数据帧:u.data、u.item和u.user,我们将它们转换成熊猫数据帧并存储在变量中,如下所示:
- rating_df—保存用户给出的所有评级的完整 u 数据集
- item_df—物品信息(电影)
- user_df—关于用户的人口统计信息
交叉核对数据
我袖手旁观的一般经验法则是总是检查我被告知我被给予的正是已经被提供的。Pandas 通过df.info()和df.head()(或df.tail())函数使识别这些事情变得容易,这些函数给我们提供了关于数据帧的更多信息,并允许我们看到数据的预览。
首先,我先看一下rating_df,我们预计会有 943 位用户对 1682 件商品给出 100000 个评价。
# peak at ratings_df
**print**(rating_df.info())
rating_df.head()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000 entries, 0 to 99999
Data columns (total 4 columns):
 #   Column     Non-Null Count   Dtype
---  ------     --------------   -----
 0   user_id    100000 non-null  int64
 1   item_id    100000 non-null  int64
 2   rating     100000 non-null  int64
 3   timestamp  100000 non-null  int64
dtypes: int64(4)
memory usage: 3.1 MB
None

我们可以看到我们有 100000 个评级,但我们希望确保有 943 个用户和 1682 个项目。
# checking unique users
**print**(f"# of Unique Users: {rating_df['user_id'].nunique()}")# checking number of items
**print**(f"# of items: {rating_df['item_id'].nunique()}")# of Unique Users: 943
# of items: 1682
很好。我们可以确认rating_df确实拥有它所说的东西。然而,经过进一步检查,我注意到我们有一个时间戳变量,但它目前显示为一个int64数据类型。从自述文件中,我发现这个数据帧的时间戳列是从 1970 年 1 月 1 日开始的 unix 秒。因此,我们使用Datetime(一个 Python 内置的)将timestamp列的 Dtype 转换为datetime64。
# convert timestamp column to time stamp 
rating_df["timestamp"] = rating_df.timestamp.apply(lambda x: datetime.fromtimestamp(x / 1e3))
# check if change has been applied 
**print**(rating_df.info())
rating_df.head()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000 entries, 0 to 99999
Data columns (total 4 columns):
 #   Column     Non-Null Count   Dtype         
---  ------     --------------   -----         
 0   user_id    100000 non-null  int64         
 1   item_id    100000 non-null  int64         
 2   rating     100000 non-null  int64         
 3   timestamp  100000 non-null  datetime64[ns]
dtypes: datetime64[ns](1), int64(3)
memory usage: 3.1 MB
None

您现在可以在终端打印输出中看到timestamp列现在是数据类型datetime64[ns]。
现在我对rating_df已经很舒服了,我可以继续探索item_df了,我们希望它能给我们更多关于这部电影的信息。
# peak at items_df 
**print**(item_df.info())
item_df.head()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1682 entries, 0 to 1681
Data columns (total 24 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   movie_id            1682 non-null   int64  
 1   movie_title         1682 non-null   object 
 2   release_date        1681 non-null   object 
 3   video_release_date  0 non-null      float64
 4   imbd_url            1679 non-null   object 
 5   unknown             1682 non-null   int64  
 6   action              1682 non-null   int64  
 7   adventure           1682 non-null   int64  
 8   animation           1682 non-null   int64  
 9   childrens           1682 non-null   int64  
 10  comedy              1682 non-null   int64  
 11  crime               1682 non-null   int64  
 12  documentary         1682 non-null   int64  
 13  drama               1682 non-null   int64  
 14  fantasy             1682 non-null   int64  
 15  film_noir           1682 non-null   int64  
 16  horror              1682 non-null   int64  
 17  musical             1682 non-null   int64  
 18  mystery             1682 non-null   int64  
 19  romance             1682 non-null   int64  
 20  sci-fi              1682 non-null   int64  
 21  thriller            1682 non-null   int64  
 22  war                 1682 non-null   int64  
 23  western             1682 non-null   int64  
dtypes: float64(1), int64(20), object(3)
memory usage: 315.5+ KB
None

我们已经知道从rating_df开始我们的数据中有 1682 个唯一的条目,所以看到movie_id和movie_title列中有 1682 个非空条目让我不寒而栗。然而,video_release_date是完全空的,这意味着它没有为我们提供任何关于电影的信息,这意味着我们可以删除这个专栏。
我注意到release_date和imbd_url也丢失了一些值,但不足以让我们删除该列——如果最坏的情况发生,我们可以通过访问 IMBD 网站并使用电影标题找到imbd_url和release_date来手动估算这些值。
我的另一个所谓的“习惯”是,当我在读取数据时,考虑什么样的数据类型是期望的。我期望release_date是datetime64的数据类型,但是经过检查,它的数据类型是 object,所以我按照必要的处理步骤将一个对象转换为 datetime。
# drop empty column 
item_df.drop("video_release_date", axis=1, inplace= True)
# convert non-null values to datetime in release_date
item_df["release_date"] = item_df[item_df.release_date.notna()]["release_date"].apply(lambda x: datetime.strptime(x, "%d-%b-%Y"))
# check if change is applied
print(item_df.info(), item_df.shape)
item_df.head()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1682 entries, 0 to 1681
Data columns (total 23 columns):
 #   Column        Non-Null Count  Dtype         
---  ------        --------------  -----         
 0   movie_id      1682 non-null   int64         
 1   movie_title   1682 non-null   object        
 2   release_date  1681 non-null   datetime64[ns]
 3   imbd_url      1679 non-null   object        
 4   unknown       1682 non-null   int64         
 5   action        1682 non-null   int64         
 6   adventure     1682 non-null   int64         
 7   animation     1682 non-null   int64         
 8   childrens     1682 non-null   int64         
 9   comedy        1682 non-null   int64         
 10  crime         1682 non-null   int64         
 11  documentary   1682 non-null   int64         
 12  drama         1682 non-null   int64         
 13  fantasy       1682 non-null   int64         
 14  film_noir     1682 non-null   int64         
 15  horror        1682 non-null   int64         
 16  musical       1682 non-null   int64         
 17  mystery       1682 non-null   int64         
 18  romance       1682 non-null   int64         
 19  sci-fi        1682 non-null   int64         
 20  thriller      1682 non-null   int64         
 21  war           1682 non-null   int64         
 22  western       1682 non-null   int64         
dtypes: datetime64[ns](1), int64(20), object(2)
memory usage: 302.4+ KB
None (1682, 23)

在我们的处理步骤之后,我们可以看到不再有video_release_date列,并且release_date现在显示为datetime64数据类型。
因为我们被提供了一些 URL,我认为利用这一点并使用来自Ipython库的IFrame查看imbd_url中的一些 URL 可能会很酷。
注意:在
imbd_url列中的 URL 可能会永久移动到一个新地址,或者在实施时关闭。此外,当我手动输入电影的 url 时,我无法连接到 IMBD 网页(即,我在 IFrame 中手动输入 copycat (1995) url,它返回拒绝连接-我还没有找到解决方法,但一旦找到,我会更新笔记本。与此同时,我只是简单地使用了 IMBD 主页的 url 来说明它是如何工作的——本质上,我们可以从笔记本上完全访问该网页。
# viewing random imbd_urls
IFrame("https://www.imdb.com", width=800, height=400)
[## IMDb:收视率,评论,以及在哪里看最好的电影和电视节目
IMDb 是世界上最受欢迎和最权威的电影、电视和名人内容来源。查找评分和评论…
www.imdb.com](https://www.imdb.com)
最后但同样重要的是,我们有user_df。如果您没记错的话,这是关于用户的人口统计信息,因此我预计会有 943 行,因为(特别是在user_id列中)我们已经确认数据中有 943 个唯一用户。
# peak at user data
print(user_df.info())
user_df.head()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 943 entries, 0 to 942
Data columns (total 5 columns):
 #   Column      Non-Null Count  Dtype 
---  ------      --------------  ----- 
 0   user_id     943 non-null    int64 
 1   age         943 non-null    int64 
 2   gender      943 non-null    object
 3   occupation  943 non-null    object
 4   zip_code    943 non-null    object
dtypes: int64(2), object(3)
memory usage: 37.0+ KB
None

很好,我们可以确认我们有 943 个用户:总之,我们有 943 个用户对 1682 部电影的 10 万个评分。为了使笔记本中不同点的数据可视化简单,我决定将我们拥有的数据帧组合在一起——我将在 PyTrix 系列中讨论如何在组合数据中更好地实现这一点。
编辑描述
towardsdatascience.com](/pandas-combining-data-b190d793b626)
# store full dataframe 
full_df = pd.merge(user_df, rating_df, how="left", on="user_id")
full_df = pd.merge(full_df, item_df, how="left", right_on="movie_id", left_on="item_id")
full_df.head()

太棒了。我们已经成功地确认了预期的数据正是我们所拥有的。这些信息足以让我们更深入地研究数据并获得更好的理解。
提问,用数据回答
遵循有效数据可视化的协议,我的下一步是思考一些问题,让我更深入地了解手头的数据,然后确定可视化问题答案的最佳方法——最佳方法可以定义为表达问题答案的最简单明了的方式。
注:在这一部分,当我查询数据时,我的想法通常会跳跃。因此,在浏览数据时,我更喜欢使用 Jupyter 笔记本。
收视率最高的 10 部电影有哪些?
# return number of rows associated to each title
top_ten_movies = full_df.groupby("movie_title").size().sort_values(ascending=False)[:10]
# plot the counts
plt.figure(figsize=(12, 5))
plt.barh(y= top_ten_movies.index,
         width= top_ten_movies.values)
plt.title("10 Most Rated Movies in the Data", fontsize=16)
plt.ylabel("Moive", fontsize=14)
plt.xlabel("Count", fontsize=14)
plt.show()

在我们的数据集中,星球大战(1977) 是评分最高的电影。这些信息非常有价值,因为我们可以决定使用我们数据集中评级最高的电影来推荐给新用户,以克服 冷启动问题 。我们可以从这个问题中进一步研究我们的数据,并开始思考哪些类型的电影与评分最高的电影相关联——在这种情况下,我们只查看了与《星球大战》相关联的类型。
genres= ["unknown", "action", "adventure", "animation", "childrens", "comedy", "crime", "documentary", "drama", "fantasy", "film_noir", "horror", "musical", "mystery", "romance", "sci-fi", "thriller", "war", "western"]
full_df[full_df.movie_title == "Star Wars (1977)"][genres].iloc[0].sort_values(ascending=False)action         1
sci-fi         1
romance        1
adventure      1
war            1
western        0
documentary    0
animation      0
childrens      0
comedy         0
crime          0
fantasy        0
drama          0
film_noir      0
horror         0
musical        0
mystery        0
thriller       0
unknown        0
Name: 204, dtype: int64
我不是《星球大战》的主要粉丝,尽管我已经看过很多部,但我提到这一点只是为了确认将动作、科幻、冒险、战争和浪漫等类型联系起来对这部电影来说是正确的。
这个问题完全忽略了评分最低的电影,但是如果我们正在构建一个推荐系统,我们不能忽略评分较低的电影,因为可能有许多原因导致电影没有得到很多评分。让我们来看看数据集中评分最低的几部电影。
# the least rated movies 
least_10_movies = full_df.groupby("movie_title").size().sort_values(ascending=False)[-10:]
least_10_moviesmovie_title
Coldblooded (1995)                            1
MURDER and murder (1996)                      1
Big Bang Theory, The (1994)                   1
Mad Dog Time (1996)                           1
Mamma Roma (1962)                             1
Man from Down Under, The (1943)               1
Marlene Dietrich: Shadow and Light (1996)     1
Mat' i syn (1997)                             1
Mille bolle blu (1993)                        1
Á köldum klaka (Cold Fever) (1994)            1
dtype: int64
《生活大爆炸》对我来说是一个惊喜,除了我不熟悉其他电影——反正我不是一个电影人,所以这并不意味着什么。
一个用户评价电影的最大/最小数量是多少?
从数据集提供的自述文件中,我们被告知单个用户评级的电影的最小数量是 20,但是我们不知道单个用户评级的电影的最大数量。
movies_rated = rating_df.groupby("user_id").size().sort_values(ascending=False)
print(f"Max movies rated by one user: {max(movies_rated)}\nMin movies rated by one user: {min(movies_rated)}")Max movies rated by one user: 737
Min movies rated by one user: 20rating_df.user_id.value_counts().plot.box(figsize=(12, 5))
plt.title("Number of Movies rated by a Single user", fontsize=16)
plt.show()

在数据集中,单个用户评级的电影的最大数量是 737 部——不管是谁,都是非常忠诚的电影观众和评级者——而某人评级的电影的中间数量是 70 部。有很多离群值已经评级超过 320 部电影,这是我从上面的情节中近似得出的极值。
每年发行多少部电影?
# create the year column from Movie title 
full_df["year"] = full_df["movie_title"].str.extract("\((\d{4})\)", expand=True)
# return number of rows by the year 
year_counts = full_df[["movie_title", "year"]].groupby("year").size()
fig, ax = plt.subplots(figsize=(12, 5)) 
ax.plot(year_counts.index, year_counts.values)
ax.xaxis.set_major_locator(plt.MaxNLocator(9)) # changes the number of xticks we see
plt.title("Number of movies per Annum", fontsize=16)
plt.xlabel("Year", fontsize= 14)
plt.ylabel("# of Movies Released", fontsize=14)
plt.show()

很难忽略 1988-1998 年间的大幅上涨和下跌。值得做一些研究,并向领域专家提问,以确定在此期间可能会发生什么。
有多少男人/女人评价的电影?
# count the number of male and female raters
gender_counts = user_df.gender.value_counts()
# plot the counts 
plt.figure(figsize=(12, 5))
plt.bar(x= gender_counts.index[0], height=gender_counts.values[0], color="blue")
plt.bar(x= gender_counts.index[1], height=gender_counts.values[1], color="orange")
plt.title("Number of Male and Female Participants", fontsize=16)
plt.xlabel("Gender", fontsize=14)
plt.ylabel("Counts", fontsize=14)
plt.show()

在这个样本中,男性明显比女性多得多,这可能会对所看电影的类型产生重大影响。
最受男性和女性欢迎的电影类型是什么?
full_df[genres+["gender"]].groupby("gender").sum().T.plot(kind="barh", figsize=(12,5), color=["orange", "blue"])
plt.xlabel("Counts",fontsize=14)
plt.ylabel("Genre", fontsize=14)
plt.title("Popular Genres Among Genders", fontsize=16)
plt.show()

令我惊讶的是,男性和女性确实喜欢相似的类型。男女最受欢迎的类型是戏剧,其次是喜剧。当然,我们考虑到在这个数据集中男性比女性多,当我们考虑建立我们的推荐系统时,我们也必须考虑到这一点。
需要知道的是,当我们对评分者的年龄加以限制时,兴趣是否会发生变化。
按性别划分,最受孩子欢迎的电影类型有哪些?
注:根据英国标准,成年人可以定义为> = 18 岁的人,因此儿童应该是< 18 岁。
full_df[full_df["age"] < 18][genres + ["gender"]].groupby("gender").sum().T.plot(kind="barh", figsize=(12, 5), color=["orange", "blue"])
plt.xlabel("Counts",fontsize=14)
plt.ylabel("Genre", fontsize=14)
plt.title("Popular Genres Among Children by Gender", fontsize=16)
plt.show()

戏剧仍然很受 18 岁以下男性的欢迎,但更多 18 岁以下的男性更喜欢喜剧和动作片。另一方面,18 岁以下的女性几乎没有改变,仍然是戏剧和喜剧。
这些数字很有趣,但我想知道戏剧和喜剧在男性和女性中的流行是因为这些类型的电影通常被认为是最好的电影类型(因此它们获得了最多的观看和评级),还是因为这些标签与最多的电影相关联。
什么类型的电影最受欢迎?
注:一部电影可以有多种类型(例如,一部电影可以是动画、儿童和喜剧)
# get the genre names in the dataframe and their counts
label= item_df.loc[:, "unknown":].sum().index
label_counts= item_df.loc[:, "unknown":].sum().values# plot a bar chart
plt.figure(figsize=(12, 5))
plt.barh(y= label, width= label_counts)
plt.title("Genre Popularity", fontsize=16)
plt.ylabel("Genres", fontsize=14)
plt.xlabel("Counts", fontsize=14)plt.show()

正如我所想,戏剧和喜剧标签与样本中的大多数电影相关联。也许电影制作人意识到我们需要笑声和一些戏剧,因此他们利用了这一点——这是我们可以研究的。
接下来,我们观察每个流派的平均收视率…
各流派的收视率分布如何?
注:密度图用于观察数据集中变量的分布。
# https://github.com/HarilalOP/movielens-data-exploration/blob/master/src/main/code/exploratory_analysis.ipynb
df_temp = full_df[['movie_id','rating']].groupby('movie_id').mean()# Histogram of all ratings
df_temp.hist(bins=25, grid=False, edgecolor='b', density=True, label ='Overall', figsize=(15,8))# KDE plot per genre
for genre in genres:
    df_temp = full_df[full_df[genre]==True][['movie_id','rating']].groupby('movie_id').mean()
    df_temp.rating.plot(grid=True, alpha=0.9, kind='kde', label=genre)
plt.legend()
plt.xlim(0,5)
plt.xlabel('Rating')
plt.title('Rating Density plot')
plt.show()

对于大多数类型来说,情节主要是向左倾斜的——这可能是因为用户更愿意评价他们喜欢的电影,因为如果他们不喜欢电影,人们不会真的看电影。我们必须进行一些研究,看看我们的情况是否如此。
好吧,最后一个情节比较复杂。我们可以通过更具体地观察用户来再次简化事情。
按性别划分的年龄分布是怎样的?
# creating new variable for ages of all males and females
female_age_dist = user_df[user_df["gender"] == "F"]["age"]
male_age_dist = user_df[user_df["gender"] == "M"]["age"]
# plotting boxplots 
plt.figure(figsize=(12,5))
plt.boxplot([female_age_dist, male_age_dist])
plt.xticks([1, 2], ["Female", "Male"], fontsize=14)
plt.title("Age Distribution by Gender", fontsize=16)
plt.show()

男性年龄分布有一些异常值,女性年龄中位数略高于男性。此外,女性年龄分布框比男性年龄分布框长,这意味着女性年龄分布比男性年龄分布更分散。
用户中最常见的职业是什么?
# creating the index and values variables for occupation
occ_label= user_df.occupation.value_counts().index
occ_label_counts = user_df.occupation.value_counts().values
# plot horizontal bar chart
plt.figure(figsize=(12,5))
plt.barh(y=occ_label, width=occ_label_counts)
plt.title("Most common User Occupations", fontsize=16)
plt.show()

不足为奇的是,数据集中的大多数人是学生。我们来看看各职业给出的平均评分。
给定职业的平均评分是多少?
# creating a empty df to store data
df_temp = pd.DataFrame(columns=["occupation", "avg_rating"])
# loop through all the occupations 
for idx, occ in enumerate(occ_label):
    df_temp.loc[idx, "occupation"] = occ 
    df_temp.loc[idx, "avg_rating"] = round(full_df[full_df["occupation"] == occ]["rating"].mean(), 2)
# sort from highest to lowest
df_temp = df_temp.sort_values("avg_rating", ascending=False).reset_index(drop=True)
df_temp

包裹
在这一点上停下来是困难的,因为我们可以从这些数据中提取更多的见解。我个人认为数据可视化并没有真正的尽头,所以应该由做可视化的人来决定何时停止。一个好的指标可能是当我们相信我们已经对数据有了足够的了解,可以开始建立一个有效的基线模型(如果我们还没有的话)。在构建模型时,我们可以随时返回并迭代我们的可视化,以基于我们的模型预测从我们的数据中获得更多的洞察力。
本文使用jupyter_to_medium制作。
在媒体上创建技术职位的更有效方法
towardsdatascience.com](/publishing-to-medium-from-jupyter-notebooks-53978dd21fac)
让我们继续 LinkedIn 上的对话…
[## Kurtis Pykes -人工智能作家-走向数据科学| LinkedIn
在世界上最大的职业社区 LinkedIn 上查看 Kurtis Pykes 的个人资料。Kurtis 有两个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/kurtispykes/)
监控二元类最大似然预测模型
回顾 ML 模型的稳定性和性能

克里斯·利维拉尼在 Unsplash 上的照片
简介:
随着技术和技能的进步,越来越多的公司开始对机器学习(ML)模型表现出信心。这反过来意味着越来越多的组织已经开始在他们的各种业务功能中使用 ML 模型。它在 ML 管道中开辟了另一条工作线,叫做“模型监控和模型评审”。ML 渠道中的这一系列工作对于具有高再培训周转时间的模型变得极其重要,银行领域的 ML 模型就是一个例子。在这种情况下,模型被训练一次,并被用于定期预测,例如,被训练一次的信用卡使用模型可以被营销团队用于每周/每月预测一组新的观察值。那么问题来了,我们如何知道 6 个月前训练好的模型仍然足够好?这种模式有多“稳健”?在本文中,我将尝试为二元类预测模型回答这些问题。
在本文中,我们将以贷款占用预测模型为例。该模型预测每个客户为 1 或 0。
1:客户将在未来 30 天内接受贷款,
0:客户在未来 30 天内不会接受贷款
车型回顾:
该模型评审框架不知道用于构建该模型的 ML 技术。
总的来说,模型评审框架可以分为两组指标,“稳定性指标”和“性能指标”。
- 稳定性指标:
答:人口稳定指数
b .特征稳定性指数(CSI)
2。绩效指标:
a .基尼系数和基尼系数——统计数据
b .增益矩阵:增益图,等级排序
数据集:
对于审查过程,我们需要两个数据集:
A .开发数据集:构建模型的原始数据集。该数据集中需要的字段有:
i .唯一观察标识符,如果模型是为客户建立的,它可以是客户 ID
二。实际因变量标志(实际 0 和 1)表示客户是否将在未来 30 天内贷款
三世。模型开发期间的预测概率值
B .查看数据集:要查看模型的数据集。例如,我们假设贷款占用模型是基于 2019 年 1 月的一些数据和一组变量构建的,您希望检查 2019 年 6 月的模型有多稳健,那么在这种情况下,审查数据集将具有模型对 6 月做出的预测。该数据集需要的字段有:
一、唯一标识符
二世。实际因变量标志(实际 0 和 1)表示客户是否将在未来 30 天内贷款
三。预测概率值
指标:
为了检查模型性能有多好,我们将跨开发和审查数据集计算以下指标。
稳定性指标:
稳定性度量测量模型相对于时间的稳定性。这些指标主要评估预测概率的分布在多个预测中是否具有可比性。
1。PSI:
PSI 代表人口稳定指数。它基本上将回顾数据集中的预测概率分布与开发数据集中的预测概率分布进行比较。这里的想法基本上是检查,“评审概率如何与开发数据集的概率相比较”。
PSI = [(基于审查数据集(R)中预测概率的记录百分比)–
(基于开发数据集(D)中预测概率的记录百分比)】 ln(R/D)*
计算 PSI 的步骤:
- 按照预测概率降序排列开发数据集
- 将数据集分成 10 或 20 组(十分位数)
- 计算每组观察值的百分比(D)
- 对于步骤(2)中形成的每个组,找到概率值的最小值和最大值,基本上计算开发数据集中每个组的边界值
- 用这些概率边界值在审查数据集中形成 10 组,计算每组审查数据集中的观察值百分比(R)
- 计算步骤(5)和步骤(3)之间的差值,即(R-D)
- 取步骤(v)/步骤(iii)的自然对数,即 ln(R/D)
- 将步骤(6)和(7)相乘
- 对步骤(8)中得到的所有行的值求和,求和值为 PSI
从 PSI 推断:
- 如果PSI<0.1则没有变化,模型是健壮的,可以继续使用现有的模型
- 如果PSI>= 0.1和< 0.2,则需要对型号稍加改动
- 如果PSI>= 0.2那么你应该重新训练该型号

十分位数人口稳定指数表。
2。老何:
CSI 代表特征稳定性指数。如果 PSI 超出可接受的值范围,CSI 可以帮助我们了解哪些变量导致了审查数据集中概率分布的变化。CSI 的计算方法与 PSI 完全相同。我们不再考虑模型预测的概率,而是一次取一个独立变量,以完全相同的方式计算 CSI。
性能指标:
1。基尼系数和基尼系数——统计:
基尼:基尼系数或基尼指数衡量一个变量的值之间的不平等。指数值越高,数据越分散。因此,根据预测概率计算的基尼系数给出了基尼系数的离散程度。可以计算发展和审查数据集的基尼系数。两个基尼值之间的差异应该尽可能小。基尼系数的详细解释和计算方法在这里有解释。预测概率的基尼系数可以通过为两个十进制舍入概率值(即 0.00、0.01、0.02、0.03)创建 101 个相等的观察组来计算,然后遵循这里提到的步骤。
KS 统计: KS 统计代表 Kolmogorov Smirnov 统计。这是一种衡量积极因素(事件或 1)与消极因素(非事件或 0)的区分程度。如果 KS 值为 100%(或 1),这意味着模型能够将群体分成两组,其中一组包含所有阳性,另一组包含所有阴性。在这个模型评审框架中,为开发和评审数据集计算 KS 统计。KS 值的差异应该尽可能小。详细的分步计算可以在这里找到。
从基尼和 KS 推断:
开发和评审数据集之间的 Gini 和 KS 之差应小于 10%。如果该值大于 10%,我们需要重新训练模型。
2。增益矩阵:
增益和提升图是众所周知且易于使用的模型评估框架。它们衡量的是,与没有模型的情况相比,使用预测模型的情况会好到什么程度。创建收益矩阵的步骤计算如下(遵循开发和审查数据的步骤,一次一个)
- 按照贷款吸收预测概率值的降序对数据集进行排序
- 将数据集分成 10 等份
- 计算每十分位数的观察次数
- 计算每十分位数中实际阳性的数量(事件或 1)
- 计算每十分位数中实际阳性的累积
- 计算每十分位数中实际事件的百分比,获得分数
- 计算事件发生率,即每十分位数/观察总数中的累积事件
你可以在这里找到详细的步骤和 Python 代码来计算同样的。
执行上述开发和审查数据集的步骤。
a .增益分数 : 开发和评审数据集的绘图增益分数与累积人口百分比。这两种趋势应该是相似的,相互接近的。
b .等级排序 : 地块事件发生率与累计人口百分比,再次为开发和评审数据集,且趋势应相互接近。

从增益矩阵推断:
增益得分和等级排序图在开发和审查数据集中应具有相同的趋势。我们得出的每组 10 个十分位数之间的差值应该在 5%的范围内。如果这些值相差超过 5%,则需要再次训练该模型。
结论:
PSI、基尼系数、KS 统计和增益矩阵给出了如何在不同的预测概率得分值之间分布观察值的概念。如果评审数据集中的这些度量反映了它们在开发数据集中的值,那么模型就像预期的那样执行。或者,如果评审数据集中的这些度量与开发数据集中的不同,您需要重新训练您的模型。
参考资料:
- https://www . LinkedIn . com/pulse/credit-risk-score card-monitoring-tracking-shailendra
- https://towards data science . com/Gini-coefficient-and-Lorenz-curve-f 19 bb 8 f 46d 66
- https://www . analyticsvidhya . com/blog/2019/08/11-重要-模型-评估-错误-度量/
- https://towards data science . com/how-to-determine-the-best-model-6b 9 c 584d 0 db 4
- https://unsplash.com/photos/dBI_My696Rk
基于项目的协同过滤综合指南
关于基于项目的推荐系统如何工作以及如何在实际工作环境中实现它的详细指南。

推荐系统已经存在很长时间了。Youtube、脸书、亚马逊和许多其他网站向他们的用户提供某种推荐。这不仅有助于他们向用户展示相关产品,还能让他们在竞争对手面前脱颖而出。

个性化体验对业务 KPI 的影响(来源 bluevenn
一种向用户推荐项目的技术是基于项目的推荐系统,也称为项目-项目协同过滤或 IBCF。在本指南中,我们将介绍该算法的所有细节,其背后的实际数学将在 R 中实现,首先不使用任何库,这将有助于理解该算法如何适应数据集,然后使用 recommenderlab(一个 R 包)实现该算法,以展示在真实环境中如何工作。
如果你也对评估推荐系统的性能感兴趣,看看 这篇文章解释了如何评估推荐系统的性能 。
项目—项目协同过滤
项目-项目协同过滤是一种基于用户已经喜欢或积极互动的项目寻找相似项目的推荐方法。它是亚马逊在 1998 年开发的,对亚马逊的成功起了很大的作用。

Sidharth 在 spatnaik77 上拍摄的照片
IBCF 的工作原理是,它根据用户以前消费过的商品来推荐商品。它查找用户已经消费过的商品,然后找到与消费过的商品相似的其他商品并相应地推荐。
我们用一个例子来理解这个。假设我们的用户 Jhone 想要购买一部电影 DVD。我们的工作是根据他过去的喜好给他推荐一部电影。我们会先搜索 Jhone 看过或者喜欢过的电影,我们姑且把那些电影称为‘A’、‘B’、‘C’。接下来,我们将搜索与三部电影相似的其他电影。假设我们发现电影“D”与“C”高度相似,因此,约翰很有可能也会喜欢电影“D ”,因为它与约翰已经喜欢过的电影相似。因此,我们将向约翰推荐电影《D》。
因此,IBCF 的核心就是寻找与用户已经喜欢的商品相似的商品。但是如何找到相似的物品呢?如果有多个相似的项目,那么应该先推荐哪个项目呢?为了理解这一点,让我们首先理解过程背后的直觉,这将帮助我们理解 IBCF 推荐过滤背后的数学原理。
如果你想更深入地了解基于项目的过滤,我建议从 这个课程开始 。导师不仅解释了算法的所有细节,还用真实世界的例子解释了它的应用,这对任何想在推荐系统领域进步的人都是有价值的。
查找项目之间的相似性
假设我们有一个用户和他们对电影的评分表(链接):

评级数据集(由 Muffaddal 提供)
让我们挑选两部电影(Id 1:玩具总动员和 Id 2:星球大战),我们必须计算它们的相似度,即这两部电影在用户相似度方面有多少可比性。为了计算这一点,我们将:
首先将两部电影的多个评分互相比较,然后将结果相加。让我们称这个值为' A' 。

两个用户评级的乘积(由 Muffaddal)
其次,我们将对电影评分的平方求和,然后求它们的平方根。因此,平方所有电影 1 的评级,将它们相加,然后取平方根得到最终值(对电影 2 做同样的操作)。这样做将得到两个值,即电影 1 和电影 2 的平方根值。将两个值相乘。我们称这个最终值为' B'

等级平方和的平方根(由 Muffaddal)
第三,将 A 和 B 分开,这将得到一个分数,表明电影 1 和电影 2 彼此有多接近(链接)。

电影 1 和 2 的相似之处(穆法达尔)
对所有电影重复上述过程将产生一个表格,其中包含每部电影之间的相似之处(通常我们称之为项目)。
下面是如何用数学形式描述上述过程的。

相似性方程(Muffaddal)
不要被这个公式看起来有多疯狂所压倒。这真的很简单,也正是我们在上面的 excel 练习中所做的。让我们一点一点的分解,来理解这些怪异的符号是什么意思。
在字母上加一些标签将有助于理解等式的每一部分。

余弦相似方程(Muffaddal)
从标签 1(左边的蓝色)开始,上面说明了为了计算项目“I”和项目“j”之间的相似性(将“I”和“j”视为电影 id 1 和 2),将用户“u”给出的项目“I”和“j”的所有评级相乘并求和。将结果除以用户“u”给出的各个项目的评分平方和的平方根乘积。

余弦相似方程(Muffaddal)
这正是我们在上面的 excel 练习中所做的。对以上所有项目进行迭代将产生一个值列表,该列表将指示其他项目与我们的主项目“I”的接近程度。这种方法也被称为余弦相似度。它有助于计算两个向量之间的距离。
余弦相似性是可行的,但是它没有考虑用户的乐观行为。不同的用户可以根据他们的乐观程度对相同的项目做出不同的评价。在 5 的尺度上,一个人可以给一个项目评分 5,而另一个人可以给 3,即使他们都非常喜欢这个项目。为了说明这一点,我们必须对我们的相似性公式做一个小小的改变。它看起来是这样的:

调整后的余弦相似性方程(Muffaddal)
用用户的平均评分减去给定项目的用户评分,将评分标准化到相同的等级,并有助于克服乐观主义问题。我们称之为调整余弦相似度。
还有一种类似的方法,不是减去用户的平均评分,而是减去项目的平均评分。这有助于了解给定用户评分与平均项目评分的偏差。这种技术被称为皮尔逊相似度。余弦和皮尔逊都是广泛使用的计算相似性的方法。
将调整后的余弦相似性方程应用于项目评级将生成一个表格或矩阵,显示一个项目与另一个项目的相似程度。它应该是这样的:

调整后的相似性矩阵(由 Muffaddal)
计算推荐得分
一张相似物品的桌子已经完成了一半的工作。我们知道哪些项目是可比较的,但我们还没有解决从相似项目列表中向用户推荐哪些项目的问题。为此,必须将我们的相似度矩阵与用户过去的评分项目历史结合起来,以生成一个推荐。这很容易通过应用 IBCF 方程来实现。

基于项目的推荐系统方程(Muffaddal)
u =我们正在为其生成推荐的用户
i =考虑中的项目,即该项目是否应该被推荐
score(u,i) =生成一个分数,该分数将指示项目‘I’对我们的用户‘u’的推荐有多强。
j =与主要项目 I 相似的项目
上面的等式得出,为了计算用户“u”的项目“I”的推荐分数,将项目“I”和“j”的相似性与用户“u”对项目“j”给出的评级和项目“j”的平均评级之差的乘积求和。将结果除以项目“I”和“j”的相似度之和,将输出与用户“u”的平均评分相加。

基于项目的推荐系统方程(Muffaddal)
这样做将为用户和可用项目生成一个得分矩阵。可以向用户推荐得分最高的项目。

用户方程的推荐分数矩阵(Muffaddal)
使用 R 实现 IBCF
让我们看看上面使用 R ( 代码链接)的操作。这将使我们进一步了解 IBCF。
注意:我们将使用循环和数据帧来实现 IBCF,因为我们知道 R 在循环中相当慢,而矩阵的行/列乘法比数据帧快。原因是这个练习的主要目的是帮助理解 IBCF 是如何使用编程实现的,我们对编写健壮的代码不感兴趣。将在本文的第三部分展示一种更快的替代方法。
说到这里,让我们开始加载所需的库和电影分级数据集。

R 中的评级数据集(由 Muffaddal)
为了根据电影评分计算 IBCF,首先,我们将通过用用户的平均评分减去项目评分来计算标准化评分。这将使评级标准化到相同的等级。

R 中的归一化评级矩阵(由 Muffaddal)
接下来,我们将通过将实际评分和标准化评分传递给我们的“calCosine”函数来计算每个项目的相似度。

R 中调整的相似矩阵(由 Muffaddal)
现在是时候通过迭代每个用户和项目来计算每个用户的推荐分数了。在每次迭代中,首先,我们将计算用户的平均评分,这将用于计算分数,其次,我们检查项目是否由用户评级,如果评级,那么我们存储-1,因为我们只对推荐用户没有评级或不喜欢的项目感兴趣。
如果该项目没有评级,然后我们抓住前 10 个项目类似于我们的给定项目使用相似性矩阵,我们计算以上。然后,我们获取用户对这些相似项目的评分。我们还将计算类似项目的平均评级,以便将其与项目的评级相减。
最后,我们将传递一个类似的项目列表,类似项目的用户评级历史列表,以及类似项目的平均评级到我们的' calScore '分数函数。结果将添加到用户的平均评分中。这样做将产生分数,该分数将指示向哪个用户推荐哪个项目。分数越高,用户购买该商品的可能性越大。

R 中的推荐分数(由 Muffaddal 提供)
预测推荐分数有助于理解和建议项目,但我们可以通过用项目名称替换分数来进一步改善结果的显示方式。下面是怎么做的。

R 中的推荐项目(Muffaddal)
实现以上内容将增强我们对推荐系统如何工作的理解。但是上面的代码效率不高,而且相当慢。有许多 R 包,使用它们我们可以毫无困难地实现推荐过滤。一个这样的包是推荐者实验室。
利用推荐实验室实现 IBCF
现在,让我们使用推荐者实验室来实现 IBCF。
recommenderLab 在“真实评级矩阵”数据集上工作,因此我们必须首先将我们的数据框架转换成它。
接下来,我们将数据集分为训练集和测试集。我们将使用训练集来为 IBCF 建立模型,并在测试集上评估其结果。
是时候使用 recommenderLab 实现项目-项目推荐系统了
就是这样。这一行代码计算了我们用自己的 R 代码实现的所有内容。
当我们提供训练数据时,这是我们的模型所预测的

使用 recommenderLab 推荐的项目(Muffaddal)
结论
项目-项目协同过滤是一种推荐系统,它基于使用用户给项目的评级计算的项目之间的相似性。它有助于解决基于用户的协作过滤器所遇到的问题,例如当系统有许多项目而评价的项目较少时。IBCF 的计算强度也不如 UBCF。实现 IBCF 有助于构建一个强大的推荐系统,可以用来向用户推荐商品。
相似读取
本文解释了一些评估推荐系统性能的技术。
towardsdatascience.com](/an-exhaustive-list-of-methods-to-evaluate-recommender-systems-a70c05e121de) [## 使用 BigQuery ML 进行 RFM 分析
使用 BigQuery ML 中的 RFM 分析和 Data Studio 中的可视化进行用户细分。
towardsdatascience.com](/rfm-analysis-using-bigquery-ml-bfaa51b83086)
参考
[1]评级数据:
收视率用户,1:玩具总动员(1995),2:星球大战:第六集-绝地归来(1983),356:阿甘正传(1994),318…
docs.google.com](https://docs.google.com/spreadsheets/d/1ylQyvq7slR0Vjj9hX4VkpFAnidaPRhnv/edit#gid=602195593)
[2]代码链接:
R. Contribute 中基于项目的协作系统代码 muffaddal 52/基于项目的协作推荐系统…
近似最近邻算法综合指南
实践中的搜索-近似最近邻

最近邻动机
如今,随着用户在短时间内从互联网上获取越来越多的信息,对高效的搜索方式的需求也越来越大。这也是为什么“最近邻居”成为一个热门的研究课题,以增加用户在合理的时间内找到所要找信息的机会。
“最近邻”的用例是无穷无尽的,它在许多计算机科学领域都有使用,比如图像识别、机器学习和计算语言学( 1 、 2 等等)。

在无止境的使用案例中,有网飞的推荐、Spotify 的推荐、Pinterest 的视觉搜索,以及更多令人惊叹的产品。亚历山大·沙托夫在 Unsplash 上拍摄的照片
为了计算精确的最近邻,存在以下技术:
- 穷举搜索- 将每个点与每隔个点的进行比较,这将需要线性查询时间(数据集的大小)。
- 网格技巧- 将空间细分成网格,这将需要指数空间/时间(在数据集的维度上)。
 因为我们在谈论高维数据集,这是不切实际的。
穷举搜索用法
我将向展示如何找到相似的向量,并将使用movie lens数据集来完成(包含 100k 行),方法是使用数据集的丰富版本(已经包含电影标签及其语义表示)。本文的全部代码可以在 Jupyter 笔记本这里找到。
首先,我们将加载我们的数据集,它已经由电影标签和它们的语义表示组成,在这里计算。
import pickle
import faissdef load_data():
    with open('movies.pickle', 'rb') as f:
        data = pickle.load(f)
    return datadata = load_data()
data

正如我们所看到的,数据实际上是一个字典,name 列由电影的名称组成,vector 列由电影的向量表示组成。
我将展示如何使用faiss进行彻底的搜索。我们首先要创建索引类。
class ExactIndex():
    def __init__(self, vectors, labels):
        self.dimension = vectors.shape[1]
        self.vectors = vectors.astype('float32')
        self.labels = labels    
     def build(self):
        self.index = faiss.IndexFlatL2(self.dimension,)
        self.index.add(self.vectors)
    def query(self, vectors, k=10):
        distances, indices = self.index.search(vectors, k) 
        # I expect only query on one vector thus the slice
        return [self.labels[i] for i in indices[0]]
在定义了 index 类之后,我可以使用下面的代码片段用我的数据集构建索引。
index = ExactIndex(data["vector"], data["name"])
index.build()
现在很容易搜索,假设我想搜索与“玩具总动员”最相似的电影(它位于索引号 0 中),我可以编写以下代码:
index.query(data['vector'][0])

就这样,我们已经做了精确的搜索,我们现在可以去午睡了:)。

Lidya Nada 在 Unsplash 上拍摄的照片
但不全是彩虹和独角兽:
不幸的是,大多数现代应用程序都有大量高维(数百或数千)数据集,因此线性扫描需要一段时间。如果这还不够,通常还会有额外的限制,比如合理的内存消耗和/或低延迟。
值得注意的是,尽管最近在这个主题上取得了很多进展,但是保证检索精确最近邻居的唯一可用方法是穷举搜索(由于维度的 T2 诅咒)。)
这使得精确最近邻变得不切实际,甚至允许 【近似最近邻】 (安)进入游戏。如果我们愿意牺牲一些准确性,相似性搜索可以快几个数量级。

照片由 Niklas Kickl 在 Unsplash 上拍摄
近似最近邻游戏攻略
为了给出为什么近似最近邻可能足够好的小直觉,我将给出两个例子:
- 视觉搜索:作为一个用户,如果我寻找一张蜜蜂图片,我不介意从这三张图片中得到哪一张。
- 推荐:作为一个用户,我并不真的介意最近邻的顺序或者即使我只有十个最佳候选中的八个。
近似最近邻技术通过将数据预处理成有效的索引来加速搜索,并且通常使用以下阶段来解决:
- 向量变换 —在向量被索引之前应用于向量,其中包括维度缩减和向量旋转。为了这篇文章结构良好又有几分简洁,我就不讨论这个了。
- 矢量编码 —应用于矢量,以构建实际的搜索索引,其中包括基于数据结构的技术,如树、LSH 和量化,这是一种将矢量编码为更紧凑形式的技术。
- 无穷举搜索组件 —应用于矢量以避免穷举搜索,在这些技术中有倒排文件和邻域图。
使用树的矢量编码
介绍和直觉
当谈到人工神经网络时,基于树的算法是最常见的策略之一。他们通过将数据集分成子集来构建森林(树的集合)作为他们的数据结构。
最突出的解决方案之一是,它使用树木(更准确地说是森林)来实现 Spotify 的音乐推荐。既然有综合解释我这里只提供背后的直觉,应该怎么用,利弊。
在这种情况下,为了构造索引,我们创建了一个森林(也称为许多树),每棵树都是以如下方式构造的,我们随机选取两个点,并通过它们的超平面将空间一分为二,我们不断递归地分裂成子空间,直到与一个节点相关的点足够小。
 ****
****
为了搜索构建的索引,遍历森林以获得一组候选点,从这些候选点返回最接近查询点的点。
讨厌的用法
我们将像以前一样创建一个索引类。我们要用惹恼库。可以想象,大多数逻辑都在构建方法(索引创建)中,其中准确性-性能的权衡由以下因素控制:
- number_of_trees —我们构建的二叉树的数量,一个较大的值会给出更准确的结果,但是索引也更大。
- search_k —我们对每个点搜索的二叉树的个数,值越大会给出更准确的结果,但返回的时间会更长。
**class** Annoy**Index**():
    **def** __init__(self, vectors, labels):
        self.dimension = vectors.shape[1]
        self.vectors = vectors.astype('float32')
        self.labels = labels    
    **def** build(self, number_of_trees=5):
        self.index = annoy.AnnoyIndex(self.dimension)
        **for** i, vec **in** enumerate(self.vectors):
            self.index.add_item(i, vec.tolist())
        self.index.build(number_of_trees)
    **def** query(self, vector, k=10):
        indices = self.index.get_nns_by_vector(
              vector.tolist(), 
              k, 
              search_k=search_in_x_trees)                                           
        **return** [self.labels[i] **for** i **in** indices]
在我定义了烦人的索引类之后,我可以使用下面的代码片段用我的数据集构建索引。
index = AnnoyIndex(data["vector"], data["name"])
index.build()
现在搜索相当容易,假设我想搜索与“玩具总动员”最相似的电影(它位于索引号 0)。
index.query(data['vector'][0])

就是这样,我们已经有效地搜索了类似“玩具总动员”的电影,并得到了近似的结果。
值得注意的是,我将针对每种实现而不是每种技术来说明利弊。
惹恼专业人士
- 将索引创建与加载分离开来,这样就可以将索引作为文件传递,并快速将它们映射到内存中。
- 我们可以调整参数来改变精度/速度的折衷。
- 它有能力使用静态文件作为索引,这意味着你可以跨进程共享索引。
惹怒弊
- 精确的最近邻可能跨越边界到达相邻单元之一。
- 不支持 GPU 处理。
- 不支持批处理,所以为了增加吞吐量“需要进一步的黑客攻击”。
- 不能逐渐增加点数(烦恼 2 试图解决这个问题)。
使用 LSH 的矢量编码
介绍和直觉
就人工神经网络而言,基于 LSH 的算法是最常见的策略之一。它们通过将附近的点映射到同一个桶中来构建哈希表作为它们的数据结构。
****最突出的实现之一是 facebook 的【Faiss】。由于有大量的 LSH 解释,我在这里只提供其背后的直觉,应该如何使用,利弊。
在 LSH 中,为了构造索引,我们应用多个哈希函数将数据点映射到桶中,使得彼此靠近的数据点以高概率位于相同的桶中,而彼此远离的数据点很可能落入不同的桶中。

来源:https://brc7.github.io/2019/09/19/Visual-LSH.html
为了搜索所构建的索引,查询点被散列以获得最接近的桶(一组候选点),从该桶返回最接近查询点的桶。
重要的是要注意,有一些改进我还没有检查,如飞行算法和GPU 上的 LSH。
LSH 用法
我将展示如何使用 faiss ,来“使用 LSH 近似最近邻”。
我们将创建 index 类,正如您所见,大多数逻辑都在 build 方法中(索引创建),您可以在其中控制:
- num_bits —较大的值将给出更准确的结果,但索引也更大。
class LSHIndex():
    def __init__(self, vectors, labels):
        self.dimension = vectors.shape[1]
        self.vectors = vectors.astype('float32')
        self.labels = labels    
     def build(self, num_bits=8):
        self.index = faiss.IndexLSH(self.dimension, num_bits)
        self.index.add(self.vectors)
    def query(self, vectors, k=10):
        distances, indices = self.index.search(vectors, k) 
        # I expect only query on one vector thus the slice
        return [self.labels[i] for i in indices[0]]
在定义了 LSH 索引类之后,我可以使用下面的代码片段用我的数据集构建索引。
index = LSHIndex(data["vector"], data["name"])
index.build()
现在搜索相当容易,假设我想搜索与“玩具总动员”最相似的电影(它位于索引号 0)。
index.query(data['vector'][0])

就是这样,我们已经有效地搜索了类似“玩具总动员”的电影,并得到了近似的结果。
像以前一样,我将声明每个实现的优点和缺点,而不是每个技术。
LSH 的优点
- 生成这些随机散列函数不需要诸如数据分布之类的数据特征。
- 可以调整近似搜索的准确性,而无需重建数据结构。
- 良好的亚线性查询时间的理论保证。
LSH 弊
- 实际上,该算法可能比线性扫描运行得慢。
- 不支持 GPU 处理。
- 需要大量内存。
使用量化的矢量编码
量化动机
尽管我们通过构建索引来提高查询性能,但是我们没有考虑额外的约束。
像许多工程师一样,我们认为线性存储“不成问题”(由于像 S3 这样的系统)。然而在实践中,线性存储可能会变得非常昂贵非常快,一些算法需要将它们加载到 RAM 中,并且许多系统没有分离存储/计算,这一事实肯定不会改善这种情况。

这张图片显示了支持 X 张图片所需的价格来源:https://www.youtube.com/watch?v=AQau4-VF64w
这就是为什么当谈到人工神经网络时,基于量化的算法是最常见的策略之一。
****量化是一种通过定义一个函数(量化器)将我们的数据编码成一个紧凑的近似表示来减少数据集大小(从线性的)的技术。

给定一个数据集,构造表示我们的数据集的数字向量,然后将向量压缩为近似表示来源:https://medium . com/code-heroku/building-a-movie-recommendation-engine-in-python-using-sci kit-learn-c 7489d 7 CB 145
量子直觉
这种方法的直觉如下,我们可以通过在编码阶段用矢量的更精简的近似表示法替换每个矢量来减小数据集的大小。
实现更精简的近似表示的一种方式是给相同的表示提供相似的向量。这可以通过聚类相似的向量并以相同的方式表示每个向量来实现(质心表示),最流行的方法是使用 k-means 。

一个演示 k-means 内部工作原理的动画
由于 k-means 将空间中的向量分成 k 个簇,所以每个向量可以表示为这 k 个质心中的一个(最相似的一个)。
这将允许我们以更有效的方式来表示每个向量,每个向量 log(k)比特,因为每个向量可以用质心的标签来表示。

在我们的例子中,每个向量由一个质心表示。因为我们有 2042 个质心,所以我们可以用 11 位来表示每个向量,而不是 4096 ( 1024*32)。
但是,这种惊人的压缩带来了巨大的成本,我们失去了准确性,因为我们现在不能从质心分离原始向量。
产品量化直觉
我们看到,使用像 k-means 这样的量化器是有代价的,为了提高矢量的精度我们需要大幅增加质心的数量,这使得量化阶段在实践中不可行。
这就是乘积量化的起源,我们可以通过将每个矢量分成许多矢量来大幅增加质心的数量,并对所有这些矢量运行量化器,从而提高精度。

在我们的例子中,每个向量由 8 个子向量表示,这 8 个子向量可以由质心之一表示。由于我们有 256 个质心,我们可以用 1 个字节表示每个矩阵,与 4096 ( 1024*32)相比,向量表示只有 8 个字节。
虽然与常规量化器相比,它增加了一点向量的大小,但它仍然是 O(log(k)),允许我们大幅提高精度,并且在实践中仍然有效。
不幸的是,在搜索方面,尽管我们可以使用查表和一些加法更有效地计算距离。我们仍将进行彻底的搜索。
使用以下算法完成搜索:
- 用计算出的每个子向量和该子向量的每个质心之间的距离构建一个表。
- 计算数据集中每个向量的近似距离值时,我们只需使用这些质心 id 在表中查找部分距离,并将它们相加!

在我们的例子中,这意味着构建一个具有 256 行(每个质心一行)和 8 列(每个子向量一列)的子向量距离表。记住,每个数据库向量现在只是 8 个质心 id 的序列。
- 精确的最近邻可能跨越边界到达相邻单元之一。
倒排文件索引直觉
该算法的直觉是,如果我们以这样一种方式分割我们的数据集,我们可以避免穷举搜索,即在搜索时,我们只查询相关的分区(也称为 Voronoi 单元)。这在实践中效果很好的原因是因为许多数据集实际上是多模态的。
然而,以这种方式划分数据集再次降低了准确性,因为如果查询向量落在最近的聚类的外围,那么它的最近邻居很可能位于多个附近的聚类中。
这个问题的解决方案很简单,就是搜索多个分区(这也称为探测),搜索多个附近的分区显然需要更多的时间,但它给了我们更好的准确性。
因此,正如我们现在看到的这完全是关于折衷,可以调整分区的数量和要搜索的分区的数量,以找到时间/精度折衷的最佳点。
值得注意的是,除了量化之外,倒排文件索引是一种可以与其他编码策略一起使用的技术。
编码残差直觉
算法的直觉是,我们希望相同数量的质心给出更精确的表示。如果矢量没有以前那么明显,这是可以实现的。
为了做到这一点,对于每个数据库向量,不是使用 PQ 来编码原始数据库向量,而是编码向量相对于其分区质心的偏移。
然而,用它们的偏移量替换向量将** 再次增加搜索时间,因为我们将需要为我们探测的每个分区计算单独的距离表,因为每个分区的查询向量是不同的。**
显然,这种权衡是值得的,因为 IVFPQ 在实践中运行得相当好。
具有倒排文件索引的产品量化
算法如下,我们用 k-means 聚类提前对数据集进行分区,以产生大量的数据集分区(倒排索引)。然后,对于每个分区,我们运行常规乘积量化。

然后,对于每个分区,我们将把它的每个向量分解成 D/M 个子向量,这将把我们的 N×D 矩阵转换成 N×M 的 D/M 矩阵。

在我们的示例中,我们将把每个 1024 个向量分成 8 个 128 个向量,因此我们将数据集视为大小为 10k x 128 的 8 个矩阵。
然后我们将在每个子矩阵上运行 k-means 算法,这样每个子向量(行)将连接到 k 个质心之一。

在我们的例子中,我们将在 k=256 的 8 个矩阵上运行 k 均值。这意味着,我们的每一行都连接到每个矩阵上的这 256 个行中的一个(原始矩阵中的每一行都连接到 8 个质心)。
我们将用最接近的匹配质心的 id 替换每个子向量。这就是我们所期待的,因为在前面的部分之后我们已经重复了元素,我们现在可以用一个非常小的标签来表示它们中的每一个,并且只保留一次实际值。
如果你想得到更多的理论,你可以看这个令人惊奇的视频。
使用倒排索引的乘积量化
我们将创建 index 类,正如您可以看到的,大多数逻辑都在 build 方法(索引创建)中,您可以在其中控制:
- 子向量大小 —子向量的目标大小(产品量化阶段)。
- number_of_partitions —用来划分数据集的分区数量(反向文件索引阶段)。
- search_in_x_partitions —要搜索的分区数量(反向文件索引阶段)。
class IVPQIndex():
    def __init__(self, vectors, labels):
        self.dimension = vectors.shape[1]
        self.vectors = vectors.astype('float32')
        self.labels = labels    def build(self, 
              number_of_partition=8, 
              search_in_x_partitions=2, 
              subvector_size=8):
        quantizer = faiss.IndexFlatL2(self.dimension)
        self.index = faiss.IndexIVFPQ(quantizer, 
                                      self.dimension, 
                                      number_of_partition, 
                                      search_in_x_partitions, 
                                      subvector_size)
        self.index.train(self.vectors)
        self.index.add(self.vectors)
    def query(self, vectors, k=10):
        distances, indices = self.index.search(vectors, k) 
        # I expect only query on one vector thus the slice
        return [self.labels[i] for i in indices[0]]
在定义了 IVPQIndex 类之后,我可以使用下面的代码片段用我的数据集构建索引。
index = IVPQIndex(data["vector"], data["name"])
index.build()
现在很容易搜索,假设我想搜索与“玩具总动员”最相似的电影(它位于 0 索引中)。
index.query(data['vector'][0:1])

就这样,我们使用 IVPQ 有效地搜索了类似“玩具总动员”的电影,并得到了近似的结果。
带倒排文件优点的产品量化
- 具有子线性空间、大压缩比(每个向量 log(k)比特)的唯一方法。
- 我们可以调整参数来改变精度/速度的折衷。
- 我们可以调整参数来改变空间/精度的权衡。
- 支持批量查询。
带反向文件 Cons 的产品量化
- 精确的最近邻可能跨越边界到达相邻单元之一。
- 不能逐渐增加点数。
- 精确的最近邻可能跨越边界到达相邻单元之一。
分层可导航小世界图
这种方法的直觉如下,为了减少图上的搜索时间,我们希望我们的图有一个平均路径。
这与著名的“六个握手规则”的说法紧密相连。
"你和地球上的任何人之间最多有 6 度的距离."— 弗里吉斯·卡琳蒂
平均而言,许多真实世界的图是高度聚集的,并且倾向于具有彼此靠近的节点,这在形式上被称为小世界图:
- 高度可传递性(社区结构)通常是分等级的。
- 小平均距离~log(N)。
为了进行搜索,我们从某个入口点开始,迭代遍历图。在遍历的每一步,该算法检查从查询到当前基节点的邻居的距离,然后选择距离最小的相邻节点作为下一个基节点,同时不断跟踪最佳发现的邻居。当满足某个停止条件时,搜索终止。
分层可导航小世界图用法
我将展示如何使用 nmslib ,来“使用 HNSW 近似最近邻”。
我们将创建 index 类,正如您所看到的,大多数逻辑都在 build 方法中(索引创建)。
class NMSLIBIndex():
    def __init__(self, vectors, labels):
        self.dimention = vectors.shape[1]
        self.vectors = vectors.astype('float32')
        self.labels = labelsdef build(self):
        self.index = nmslib.init(method='hnsw', space='cosinesimil')
        self.index.addDataPointBatch(self.vectors)
        self.index.createIndex({'post': 2})
    def query(self, vector, k=10):
        indices = self.index.knnQuery(vector, k=k)
        return [self.labels[i] for i in indices[0]]
在定义了 NMSLIB 索引类之后,我可以使用下面的代码片段用我的数据集构建索引。
index = NMSLIBIndex(data["vector"], data["name"])
index.build()
现在搜索相当容易,假设我想搜索与“玩具总动员”最相似的电影(它位于索引号 0)。
index.query(data['vector'][0])

就是这样,我们已经有效地搜索了类似“玩具总动员”的电影,并得到了近似的结果。
像以前一样,我将声明每个实现的优点和缺点,而不是每个技术。
分层可导航小世界图优点
- 我们可以调整参数来改变精度/速度的折衷。
- 支持批量查询。
- NSW 算法具有多对数时间复杂度,并且在许多真实数据集上可以优于竞争对手的算法。
分层可导航小世界图 Cons
- 精确的最近邻可能跨越边界到达相邻单元之一。
- 不能逐渐增加点数。
- 需要相当多的内存。
选择正确的近似最近邻算法
什么会影响我们的决定
评估应该使用哪些算法以及何时使用取决于用例,并且会受到以下指标的影响:
- 速度- 指标创建和指标构建。
- 硬件和资源- 磁盘大小,RAM 大小,以及我们是否有 GPU。
- 精度要求。
- 访问模式— 查询的数量,是否批处理,以及我们是否应该更新索引。
重要的是要注意,没有完美的算法,所有的都是关于权衡,这就是为什么这个主题如此有趣。
我们如何挑选
我创建了一个有点天真的流程图,以便允许人们选择应该为他的用例以及我们上面定义的度量选择哪种技术和实现。

每种实现都有自己的参数,这些参数会影响精度/速度权衡或空间/精度权衡。
遗言
本文一开始,我们展示了最近邻算法提供的价值,然后我列出了在现代应用中使用这些算法的问题,这些问题导致了“近似最近邻技术的诞生”。
然后,我解释了领先技术背后的直觉以及如何在实践中使用它们,这些技术允许我们在存储/准确性和速度/准确性之间进行权衡。
由于主题的范围,有许多事情我没有涉及,比如某些算法对 GPU 的使用。
我希望我能够分享我对这个迷人的话题的热情,并且你会发现它是有用的,并且我一如既往地欢迎任何建设性的反馈。
哈伯曼生存数据集探索性数据分析综合指南
探索性数据分析
一个详尽和广泛的方法,处理探索性数据分析的每个方面。

图片提供:https://pixabay.com/users/geralt-9301/(来自 Pixabay 的 Gerd Altmann)
探索性数据分析
探索性数据分析(EDA)是一个数据分析过程,主要目的是使用统计工具、绘图工具、线性代数和其他技术来挖掘隐藏在数据集中的信息。它有助于更好地理解数据,并突出其主要特征,这可能有助于做出对未来有影响的预测和预报。
理解数据是数据科学的核心。因此,EDA 对于生成精确的机器学习模型是必不可少的。考虑 Haberman 的生存数据集使用 Python 对其执行各种 EDA 过程。该数据集包含 1958 年至 1970 年间在芝加哥大学比林斯医院进行的一项关于乳腺癌手术患者存活率的研究。
数据集的各种属性是:
- 手术时患者的年龄(数字)
- 患者手术年份(1958 年至 1970 年之间的年份,数字)
- 检测到的阳性腋窝淋巴结数(数字)
- 生存状态(类属性)表示为:
- 1 —如果患者存活了 5 年或更长时间
- 2 —如果患者在 5 年内死亡
正如患者的医疗诊断在患者的治疗生命周期中起着关键作用一样,EDA 在数据评估和创建精确模型中也起着至关重要的作用。
导入必备的 Python 库
选择 Python 是因为它最好的 AI 包和机器学习库。在这里,我们导入执行数据分析和绘图所需的库:
- 熊猫(Python 数据分析库)
- Numpy(用于科学计算的 Python 包)
- Matplotlib(Python 绘图库)
- Seaborn(Python 统计数据可视化库)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
加载数据集
哈伯曼的生存数据集是一个逗号分隔值(csv)文件。 Pandas 的 read_csv() 函数用于将 csv 文件(haberman.csv)读入名为 haberman 的数据帧中。Dataframe 是一个大小可变的二维结构,具有潜在的异构表格数据。
haberman = pd.read_csv('haberman.csv', header=0, names=['Age of Patient', 'Year of Operation', \                                                                          'Positive Axillary Nodes', 'Survival Status'])
一瞥数据集
让我们通过做一些初步的数据分析来熟悉数据集。首先,我们来看看数据集是什么样子的。在下面的代码片段中,iterrows()用于遍历 DataFrame 的每一行。
for i,j in haberman.iterrows():
    print(j)
    print()

图片提供:iambipin
数据集的所有属性都是不言自明的。患者的年龄意味着患者的年龄。操作年份指执行操作的年份。阳性腋窝淋巴结表示患者中阳性腋窝淋巴结(淋巴结)的数量(存在或不存在)。阳性腋窝淋巴结是受癌细胞影响的淋巴结。最后,生存状态提供了关于患者 5 年或更长时间生存的信息。
观察:
- csv 文件包含 306 行和 4 列,这意味着数据集包含关于 306 名接受乳腺癌手术的患者的信息。考虑到数据量,数据集很小。
- 患者的诊断是基于患者表现出的症状。由于除了阳性腋窝淋巴结之外,没有其他数据集属性属于症状类别,我们可以假设阳性腋窝淋巴结的存在是乳腺癌的主要催化剂(原因)。根据 BreastCancer.org 的说法,为了去除浸润性乳腺癌,医生(在手术前或手术中)切除一个或多个腋下淋巴结,以便在显微镜下检查癌细胞。癌细胞的存在被称为淋巴结受累。
- 在数据集中出现另一个症状会造成混乱,不知道在数据分析中应该优先考虑什么变量。因此在初步分析中,阳性腋窝淋巴结似乎是最重要的变量。
head()函数可以看到数据集的前五行。
haberman.head()

图片提供:iambipin
现在让我们通过使用熊猫形状属性来查找数据点的总数和数据集的特征(属性)。数据点是属性或特征的集合。因此这是一个完整的记录。在给定的数据集中,数据点由涉及四个属性的数据组成(数据帧的行)。shape 属性返回表示数据帧维度的元组(数据帧将行数和列数存储为元组)。
print(haberman.shape)Output:
(306, 4)
数据集有 306 个数据点(行)和 4 个属性(列)。通过熊猫的列属性可以知道数据集的属性。
print(haberman.columns)Output:
Index(['Age of Patient', 'Year of Operation', 'Positive Axillary Nodes', 'Survival Status'],
      dtype='object')
这里的 dtype 指的是熊猫的数据类型。Pandas 中的对象数据类型相当于 Python 中的字符串数据类型。
生存状态属性(因变量)包含非分类类型的整数数据类型。因此需要转换成分类类型。
haberman['Survival Status'] = haberman['Survival Status'].apply(
    lambda x: 'Survived' if x == 1 else 'Died')
让我们验证转换是否已经发生。
print(haberman.head(10))

我们现在可以看到,存活状态有标记为存活或死亡的字段。
info 方法可以显示数据集的简明摘要。这个方法打印关于数据帧的信息,包括索引数据类型和列数据类型、非空值和内存使用情况。
print(haberman.info())Output:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 306 entries, 0 to 305
Data columns (total 4 columns):
Age of Patient             306 non-null int64
Year of Operation          306 non-null int64
Positive Axillary Nodes    306 non-null int64
Survival Status            306 non-null object
dtypes: int64(3), object(1)
memory usage: 9.7+ KB
None
观察:
- 前三列即患者年龄、手术年份和腋窝淋巴结阳性的数据类型是整数。生存状态有一个对象数据类型。数据集有四个数据列。
- 数据集没有空值。
- 数据集使用的内存约为 9.7 KB
Pandas description方法生成描述性统计数据,其中包括汇总数据集分布的集中趋势、分散性和形状的信息,但不包括 NaN 值。它分析数字和对象序列,以及混合数据类型的 DataFrame 列集。根据所提供的内容,输出会有所不同。
print(haberman.describe(include='all'))Output:
Age of Patient  Year of Operation  Positive Axillary Nodes \
count       306.000000         306.000000               306.000000   
unique             NaN                NaN                      NaN   
top                NaN                NaN                      NaN   
freq               NaN                NaN                      NaN   
mean         52.457516          62.852941                 4.026144   
std          10.803452           3.249405                 7.189654   
min          30.000000          58.000000                 0.000000   
25%          44.000000          60.000000                 0.000000   
50%          52.000000          63.000000                 1.000000   
75%          60.750000          65.750000                 4.000000   
max          83.000000          69.000000                52.000000Survival Status  
count              306  
unique               2  
top           Survived  
freq               225  
mean               NaN  
std                NaN  
min                NaN  
25%                NaN  
50%                NaN  
75%                NaN  
max                NaN
观察:
- 该方法给出每个属性的总计数
- 对于数字数据(如患者年龄、手术年份、阳性腋窝淋巴结等变量),该方法提供了关于标准差(std)、平均值、百分位数(25%、50%和 75%)、最小值和最大值的有价值信息。第 50 百分位(50%)是中位数。因此,获得了集中趋势(平均值和中值)和分散(标准偏差)的汇总。
- 使用最小值和最大值,可以得出以下推论:
- 患者的最大年龄为 83 岁,最小年龄为 30 岁。****
- 经营年份从 58(1958)到 69(1969) 开始。
- 一名或多名患者有 52 个阳性腋窝淋巴结,一名或多名患者有零个阳性腋窝淋巴结。
4.对于对象数据类型(生存状态),结果将包括 unique,top 和 freq(频率)。数值数据类型的变量将在相应的字段中被赋予 NaN。生存状态有两个唯一值(存活和死亡)。顶部是最常见的值。因此幸存是最常见的生存状态。freq 是最常见的值的频率,这里的值是 225。所以存活下来的患者总数是 225 。
我们可以通过 value_counts()方法确定存活的患者总数。
print(haberman['Survival Status'].value_counts())Output:
Survived    225
Died         81
Name: Survival Status, dtype: int64
因此,我们可以得出这样的结论:从乳腺癌中幸存下来的患者比死于乳腺癌的患者多(81)。因此,数据集是不平衡的。****
目标
EDA 的主要目的是根据患者的年龄、手术年份和阳性腋窝淋巴结来确定患者是否能存活 5 年或更长时间。
不同层次的分析
现在让我们更深入地研究数据集。为此,必须考虑现有的不同层次的分析。它们是:
- 单变量分析
- 双变量分析
- 多元分析
数据分析技术的选择最终取决于变量的数量、数据类型和统计调查的重点。
单变量分析
单变量分析是只处理一个变量的最简单的数据分析技术。作为一个单一的变量过程,它并没有给出因果关系的见解。单变量分析的主要目的是简单地描述数据,找出数据中的模式。正在考虑的单变量分析方法有:
- 一维散点图
- 概率密度函数
- 累积分布函数
- 箱形图
- 小提琴情节
双变量分析
双变量分析是建立两个变量之间相关性的过程。双变量分析比单变量分析更具分析性。如果数据似乎符合一条直线或曲线,那么这两个变量之间存在关系或相关性。正在考虑的双变量分析方法有:
- 二维散点图
- 配对图
多变量分析
多元分析是一种更复杂的统计分析。它是涉及三个或更多变量的分析,在需要了解它们之间关系的场景中实施。正在考虑的多变量分析方法是:
- 等值线图表
作案手法
分析将从双变量分析开始。将首先绘制二维散点图,并对其进行观察。然后我们将转到配对图,看看单个变量的分布和两个变量之间的关系。之后,将进行单变量和多变量分析。
二维散点图
二维散点图有助于使用笛卡尔坐标可视化两个变量之间的相关性。一个变量的值将沿 x 轴绘制,另一个变量的值将沿 y 轴绘制。数据将作为有序对(x,y)绘制在结果象限中,其中 x 与 x 轴上的值相关,y 与 y 轴上的值相关。
sns.set_style('whitegrid')
sns.FacetGrid(haberman, hue ='Survival Status', size = 8) \
.map(plt.scatter, 'Age of Patient', 'Positive Axillary Nodes') \
.add_legend()
plt.show()

图片提供:iambipin
FacetGrid 是一个多绘图网格,用于绘制条件关系。FacetGrid 对象将一个 DataFrame 作为输入,并将形成网格的行、列或色调维度的变量的名称作为输入。变量应该是分类的,变量的每个级别的数据将用于沿该轴的一个方面。map()方法负责在网格的每个空间上重复相同的绘图。它对每个方面的数据子集应用绘图功能。add_legend()方法创建绘图的图例。
观察:
- 30-40 岁年龄组的大多数患者已经从乳腺癌中存活下来,因为这里很少有橙色点。
- 非常罕见患者的阳性腋窝淋巴结超过 25 个(或者说 30 个)
- 当没有阳性腋窝淋巴结时,几乎年龄组50-60 的所有患者都存活了。我们可以假设在 50 和 60 之间没有橙色的点。
- ****所有 80 岁以上的患者都在手术后五年内死亡,因为这里没有蓝点。
- 少数具有较高数量的阳性腋窝淋巴结(大于 10)** 的患者也存活于乳腺癌(沿着阳性腋窝淋巴结出现蓝点> 10)。**
确定观察值:
我们可以通过在 haberman 数据帧上执行以下操作来确定 1 号观察值。
df_3040 = haberman.loc[(haberman['Age of Patient']<=40) & (haberman['Age of Patient']>=30)]
#print(df_3040)df_3040_survived = df_3040.loc[df_3040['Survival Status']=='Survived']
print('No. of patients in the age group 30-40 survived: {0}' .format(len(df_3040_survived)))df_3040_died = df_3040.loc[df_3040['Survival Status']=='Died']
print('No. of patients in the age group 30-40 died: {0}' .format(len(df_3040_died)))Output:
No. of patients in the age group 30-40 survived: 39
No. of patients in the age group 30-40 died: 4
****该输出验证了 1 号观察结果。
我们可以通过以下方式确定 2 号观察值中提到的值 25(由 20 和 30 中点的蓝点表示)😗***
ax_node = haberman['Positive Axillary Nodes'].unique() #unique values of axillary node
ax_node.sort() #sorted the list
print(ax_node)Output:
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 28 30 35 46 52]
该列表的值为 25。所以我们可以安全地假设蓝点的值是 25。
我们可以通过以下操作确定4 号观察值:
age = haberman['Age of Patient']
count = 0
print(len(age))
for i in age:
    if(i >= 80):
        count += 1
print('No. of patients whose age is greater than or equal to 80: {0}' .format(count))Output:
306
No. of patients whose age is greater than or equal to 80: 1
因此,只有一名患者的年龄大于或等于 80 岁。80 后的橙色圆点一定代表这个病人。
配对图
配对图绘制数据集中的成对关系。它将创建一个轴网格,这样数据中的每个数值变量将在 y 轴的一行中共享,在 x 轴的一列中共享。对角线轴不同于轴网格的其余部分,它们显示该列中变量数据的单变量分布。
sns.set_style('whitegrid')
sns.pairplot(haberman, hue = 'Survival Status', size = 4)
plt.show()

图片提供:iambipin
在数据集中,有 3 个定量变量(患者年龄、手术年份、阳性腋窝淋巴结)和 1 个分类变量(生存状态)。只有数值(整数或浮点)连续值才会绘制在配对图中。因此,在上面显示的配对图中,我们有 3C2 图(即 3 个独特的图)。对角线两边有相等数量的图,它们是彼此的镜像。对角线图(图 1、图 5 和图 9)** 展示了展示单个变量分布的直方图。**
观察:
- 任何图中都没有线性分离
- 每个图中的数据有相当多的重叠。
- 在图 2** 中,x 轴表示手术年份,y 轴表示患者年龄。有大量的重叠,很难根据剧情进行分类。观察到的一个有趣的事实是,在 1961 年和 1968 年期间接受手术的大多数患者已经存活了 5 年或更长时间(由于与其他年份相比橙色点非常少)。我们甚至可以把它重新表述为 1961 年和 1968 年接受乳腺癌手术的患者死亡人数最少**
- 在图 3** 中,x 轴为阳性腋窝淋巴结,y 轴为患者年龄。即使有重叠的点,也有可区分的模式使我们能够做出推论。图 3(和图 7)似乎比其余的图更好(我们已经在二维散点图中深入分析了相同的图)。因此,阳性腋窝淋巴结和患者年龄是识别患者生存状态最有用的特征。**
- 在图 6** 中,x 轴为阳性腋窝淋巴结,y 轴为手术年份。该图有个重叠最多的点。因此,它不会导致任何有意义的结论或分类。**
- 地块 4 是地块 2 的镜像。图 7 是图 3 的镜像。图 8 是图 6 的镜像
- 最后,地块 7 和地块 3 是数据分析考虑的最佳地块。
确定观测值:
我们可以通过对哈伯曼数据帧进行以下操作来确定地块 2 的3 号观测值:
df_1961 = haberman.loc[haberman['Year of Operation']==61]
df_1968 = haberman.loc[haberman['Year of Operation']==68]
#print(df_1961)df_1961_survived = df_1961.loc[df_1961['Survival Status']=='Survived']
print('No. of patients survived during 1961: {0}' .format(len(df_1961_survived)))df_1961_died = df_1961.loc[df_1961['Survival Status']=='Died']
print('No. of patients died during 1961: {0}' .format(len(df_1961_died)))aster = '*'
print(aster*45)df_1968_survived = df_1968.loc[df_1968['Survival Status']=='Survived']
print('No. of patients survived during 1968: {0}' .format(len(df_1968_survived)))df_1968_died = df_1968.loc[df_1968['Survival Status']=='Died']
print('No. of patients died during 1968: {0}' .format(len(df_1968_died)))Output:
No. of patients survived during 1961: 23
No. of patients died during 1961: 3
*********************************************
No. of patients survived during 1968: 10
No. of patients died during 1968: 3
一维散点图
使用单个变量进行推断的散点图是一维散点图。此处变量将位于 x 轴上,y 轴上的值为零(因为没有两个轴就无法绘图)。很明显,是单变量分析。
df_survived = haberman.loc[haberman['Survival Status'] == 'Survived'] 
df_died = haberman.loc[haberman['Survival Status'] == 'Died']
plt.plot(df_survived['Positive Axillary Nodes'], np.zeros_like(df_survived['Positive Axillary Nodes']), 'o')
plt.plot(df_died['Positive Axillary Nodes'], np.zeros_like(df_died['Positive Axillary Nodes']), 'o')
plt.show()

图片提供:iambipin
在上面的代码中, haberman.loc[ ] 用于从 haberman 数据帧中挑选与特定索引相关联的数据点,这些数据点又存储在另一个数据帧中。 np.zeros_like() 方法将创建一个零数组。“O”是字母 O 的小写字母,用于使图中的点更大且可见。
观察:
- 基于一个特征(阳性腋窝淋巴结)的一维散点图
- 数据有很大的重叠,这妨碍了进行任何有意义的观察。
柱状图
直方图是由卡尔·皮尔逊首先介绍的数字数据分布的精确表示。它给出了连续变量概率分布的估计。直方图是单变量分析,因为它只涉及一个变量。
构建直方图的第一步是“bin”(或桶)值的范围。“入库”意味着将整个值范围分成一系列区间,然后计算属于每个区间的值的数量。箱通常是变量的连续的、不重叠的区间。箱子必须相邻且大小相等(不要求)。除了最后一个箱子(最右边)外,所有箱子都是半开的。
对于相同大小的箱,在箱上方竖立一个矩形,其高度与每个箱中的病例数(频率或计数)成比例。
Matplotlib 用于绘制直方图,Numpy 用于计算计数和面元边缘。
import matplotlib.pyplot as plt
import numpy as npdf_axnodes = haberman['Positive Axillary Nodes'] #DataFrame of Positive Axillary Nodes
count, bin_edges = np.histogram(df_axnodes, bins=25)print('Bin Edges: ', bin_edges)
print('Counts per Bin: ', count)
plt.hist(df_axnodes, bins=25, color='skyblue', alpha=0.7) 
plt.xlabel('Positive Axillary Nodes', fontsize=15)
plt.ylabel('Frequency', fontsize=15)Output:
Bin Edges:  [ 0\.    2.08  4.16  6.24  8.32 10.4  12.48 14.56 16.64 18.72 20.8  22.88 24.96 27.04 29.12 31.2  33.28 35.36 37.44 39.52 41.6  43.68 45.76 47.84 49.92 52\.  ]
Counts per Bin:  [197  33  13  14   9   6   9   4   2   5   4   4   1   1   1   0   1   0   0   0   0   0   1   0   1]Text(0,0.5,'Frequency')

图片提供:iambipin
Matplotlib 使用 plt.hist()绘制直方图,该函数将 DataFrame 作为输入。bin_edges 给出容器边缘(第一个容器的左边缘和最后一个容器的右边缘)。color 参数设置条形的颜色,alpha 参数设置条形的透明度。plt.xlabel 和 plt.y-label 分别用于设置 x 轴和 y 轴的标签。
观察结果
- 306 名患者中的 197 名患者具有小于 2.08 的阳性腋窝淋巴结。因此大多数(64.37%)患者有少量阳性腋窝淋巴结。
概率密度函数
PDF 用于指定随机变量落在特定值范围内的概率。它是用来描述连续概率分布的概率函数。PDF 用于处理具有连续结果的随机变量的概率。从人群中任意选择一个人的身高就是一个典型的例子。
PDF 是直方图的平滑版本。直方图的平滑是使用核密度估计(KDE)来完成的。PDF(曲线)下的面积总和始终为 1。PDF 是单变量分析。
下面显示的代码片段将绘制 PDF。
基于患者年龄的 PDF
sns.set_style('whitegrid')
sns.FacetGrid(haberman, hue='Survival Status', size=8) \
    .map(sns.distplot, 'Age of Patient') \
    .add_legend()
plt.show()

图片提供:iambipin
条形(橙色和蓝色)是直方图,曲线代表 PDF。
观察:
- 数据有很大的重叠,相当于模糊不清
- 30-40 岁年龄段的患者比其他年龄段的患者有更多的生存机会。
- 40-60 岁年龄组的患者存活的可能性更小。
- 40-45 岁年龄组的死亡人数最多(存活的可能性最小)。
- 我们不能根据“患者年龄”这一属性对患者的生存机会做出最终结论。
基于运营年份的 PDF
sns.set_style('whitegrid')
sns.FacetGrid(haberman, hue='Survival Status', size=8) \
    .map(sns.distplot, 'Year of Operation') \
    .add_legend()
plt.show()

图片提供:iambipin
观察:
- 可以观察到大量重叠。
- 该图提供了关于成功手术(患者存活)和不成功手术的数量的信息。一次行动的成功不能以一年作为一个因素。
- 大多数不成功的手术都是在 1965 年进行的,然后是 1960 年。
- 最成功的手术是在 1961 年进行的。
基于阳性腋窝淋巴结的 PDF
sns.set_style('whitegrid')
sns.FacetGrid(haberman, hue='Survival Status', size=8) \
    .map(sns.distplot, 'Positive Axillary Nodes') \
    .add_legend()
plt.show()

图片提供:iambipin
观察:
- 阳性腋窝淋巴结(淋巴结受累)的存在可以是乳腺癌的明显表现。BreastCancer.org 在其网站上将它列为一个重要症状。因此,正腋淋巴结比其他属性更重要。
- ****腋窝淋巴结阴性的患者比腋窝淋巴结阳性的患者有更高的存活几率。
- ****单个腋窝淋巴结阳性的患者也有很好的生存机会。
- ****乳腺癌存活的可能性随着阳性腋窝淋巴结数量的增加而降低。
- 只有一小部分患者腋窝淋巴结阳性超过 25 个。
- ****阳性腋窝淋巴结是做数据分析的首选属性。
确定观察值:
我们可以通过对哈伯曼数据帧执行以下操作来确定观察值:
df_one = haberman.loc[haberman['Positive Axillary Nodes']<=1]
df_less = haberman.loc[(haberman['Positive Axillary Nodes']<=25) & (haberman['Positive Axillary Nodes']>1)]
df_more = haberman.loc[haberman['Positive Axillary Nodes']>25]df_one_survived = df_one.loc[df_one['Survival Status']=='Survived']
print('No. of patients survived(with one or no positive nodes): {0}' .format(len(df_one_survived)))df_one_died = df_one.loc[df_one['Survival Status']=='Died']
print('No. of patients died(with one or no positive nodes): {0}' .format(len(df_one_died)))aster = '*'
print(aster*65)df_less_survived = df_less.loc[df_less['Survival Status']=='Survived']
print('No. of patients survived(1<positive nodes<=25): {0}' .format(len(df_less_survived)))df_less_died = df_less.loc[df_less['Survival Status']=='Died']
print('No. of patients died(1<positive nodes<=25): {0}' .format(len(df_less_died)))print(aster*65)df_more_survived = df_more.loc[df_more['Survival Status']=='Survived']
print('No. of patients survived(25<positive nodes<=52): {0}' .format(len(df_more_survived)))df_more_died = df_more.loc[df_more['Survival Status']=='Died']
print('No. of patients died(25<positive nodes<=52): {0}' .format(len(df_more_died)))Output:
No. of patients survived(with one or no positive nodes): 150
No. of patients died(with one or no positive nodes): 27
*****************************************************************
No. of patients survived(1<positive nodes<=25): 72
No. of patients died(1<positive nodes<=25): 52
*****************************************************************
No. of patients survived(25<positive nodes<=52): 3
No. of patients died(25<positive nodes<=52): 2
输出已经确定了观察值。我们可以得出以下结论:
- 有个或零个腋窝淋巴结阳性的患者中,有 85%乳腺癌存活。
- 腋窝淋巴结阳性小于 25 且大于 1 的患者中有 58%存活了 5 年或更长时间
- 腋窝淋巴结阳性大于 25 的患者,有 60%的乳腺癌存活。
- 这些统计数据证明,如果阳性腋窝淋巴结的数目是 1 或 0,患者的存活机会是相当高的。如果这个数字大于 1,那么存活几率在 58%到 60%之间。
累积分布函数
实值随机变量 X 的累积分布函数(CDF)是该变量取值小于或等于 X 的概率。
F(x) = P(X < = x)
其中右手边代表随机变量 X 取值小于或等于 X 的概率。X 位于半闭区间(a,b)的概率,其中 a < b 因此:
P(a < X <
概率密度函数的积分给出了 CDF。CDF 也是单变量分析。
使用选择的变量“阳性腋窝淋巴结”绘制 CDF。
**df_axnodes_survived = haberman.loc[haberman['Survival Status']=='Survived']
counts1, bin_edges1 = np.histogram(df_axnodes_survived['Positive Axillary Nodes'], bins=10, density=True)
pdf1 = counts1/(sum(counts1))
print('PDF of patients survived 5 years or longer:', pdf1)
print('Bin Edges: ', bin_edges1)
cdf1 = np.cumsum(pdf1)aster = '*'
print(aster * 60)df_axnodes_died = haberman.loc[haberman['Survival Status']=='Died']
counts2, bin_edges2 = np.histogram(df_axnodes_died['Positive Axillary Nodes'], bins=10, density=True)
pdf2 = counts2/(sum(counts2))
print('PDF of patients died within 5 years:', pdf2)
print('Bin Edges: ', bin_edges2)
cdf2 = np.cumsum(pdf2)print(aster * 60)line1, = plt.plot(bin_edges1[1:], pdf1, label='PDF_Survived')
line2, = plt.plot(bin_edges1[1:], cdf1, label='CDF_Survived')
line3, = plt.plot(bin_edges2[1:], pdf2, label='PDF_Died')
line4, = plt.plot(bin_edges2[1:], cdf2, label='CDF_Died')
plt.legend(handles=[line1, line2, line3, line4])plt.xlabel('Positive Axillary Nodes', fontsize=15)
plt.show()Output:
PDF of patients survived 5 years or longer: [0.83555556 0.08       0.02222222 0.02666667 0.01777778 0.00444444 0.00888889 0\.         0\.         0.00444444]
Bin Edges:  [ 0\.   4.6  9.2 13.8 18.4 23\.  27.6 32.2 36.8 41.4 46\. ]
************************************************************
PDF of patients died within 5 years: [0.56790123 0.14814815 0.13580247 0.04938272 0.07407407 0\. 0.01234568 0\.         0\.         0.01234568]
Bin Edges:  [ 0\.   5.2 10.4 15.6 20.8 26\.  31.2 36.4 41.6 46.8 52\. ]
**************************************************************

图片提供:iambipin
Matplotlib 用于绘制直方图,Numpy 用于计算计数和 bin 边缘。Matplotlib 使用 plt.hist()绘制直方图,该函数将 DataFrame 作为输入。bin_edges 给出容器边缘(第一个容器的左边缘和最后一个容器的右边缘)。np.cumsum()是一个计算累积和的 numpy 方法。plt.legend()是一个 Matplotlib 方法,用于生成图形的图例。plt.xlabel()是另一个标记 x 轴的 Matplotlib 方法
观察:
- 甚至有更多阳性腋窝淋巴结的患者也能从乳腺癌中存活下来。与此相反,没有阳性腋窝淋巴结的患者在接受手术后死亡。
- 癌症存活患者腋窝淋巴结阳性的最大数量为 46 个
- 83.55% 癌症存活患者腋窝淋巴结在0 ~ 4.6范围内阳性。****
- 死亡患者中有 56.79% 的腋窝淋巴结在0 ~ 5.2范围内。****
确定观察值:
我们可以通过对哈伯曼数据帧执行以下操作来确定 1 号观察值:
**df_axnodes_died = haberman.loc[haberman['Survival Status']=='Died']
df_no_axnodes_died = df_axnodes_died.loc[df_axnodes_died['Positive Axillary Nodes']==0]
print('No. of patients died with zero Positive Axillary Node: ', len(df_no_axnodes_died))df_axnodes_survived = haberman.loc[haberman['Survival Status']=='Survived']
df_high_axnodes_survived = df_axnodes_survived.loc[df_axnodes_survived['Positive Axillary Nodes']>=20]
print('No. of patients survived with high Positive Axillary Nodes(>=20): ', len(df_high_axnodes_survived))Output:
No. of patients died with zero Positive Axillary Node:  19
No. of patients survived with high Positive Axillary Nodes(>=20):  7
     Age of Patient  Year of Operation  Positive Axillary Nodes  \
7                34                 59                        0   
34               39                 66                        0   
44               41                 64                        0   
45               41                 67                        0   
54               42                 59                        0   
64               43                 64                        0   
65               43                 64                        0   
81               45                 66                        0   
97               47                 62                        0   
98               47                 65                        0   
114              49                 63                        0   
125              50                 64                        0   
224              60                 65                        0   
230              61                 65                        0   
239              62                 58                        0   
258              65                 58                        0   
268              66                 58                        0   
285              70                 58                        0   
293              72                 63                        0Survival Status  
7              Died  
34             Died  
44             Died  
45             Died  
54             Died  
64             Died  
65             Died  
81             Died  
97             Died  
98             Died  
114            Died  
125            Died  
224            Died  
230            Died  
239            Died  
258            Died  
268            Died  
285            Died  
293            Died  
     Age of Patient  Year of Operation  Positive Axillary Nodes  \
9                34                 58                       30   
59               42                 62                       20   
174              54                 67                       46   
188              55                 69                       22   
227              60                 61                       25   
252              63                 61                       28   
254              64                 65                       22Survival Status  
9          Survived  
59         Survived  
174        Survived  
188        Survived  
227        Survived  
252        Survived  
254        Survived**
箱形图
箱线图是基于五个数字汇总的数据分布的直观表示。这五个数字是最小或最小的数字、第一个四分位数(Q1)或第 25 个百分位数、中间值(Q2)或第 50 个百分位数、第三个四分位数(Q3)或第 75 个百分位数以及最大或最大的数字。Q1 是最小值和中间值之间的中间数。Q3 是中间值和最大值之间的中间值。四分位数间距(IQR)是第一个四分位数和第三个四分位数之间的差值。
IQR = Q3 — Q2
方框图的高度代表 IQR。方框的顶线和底线分别代表第一个四分位数和第三个四分位数。盒子顶线和底线之间的线代表中间值。从方框平行延伸的线被称为“胡须”,用于指示上下四分位数之外的可变性。异常值有时被绘制成与须状物成直线的单个点。异常值是与其他观察值显著不同的数据点。它不属于总的分配模式。
盒须图是数学家约翰·图基在 1969 年首次提出的。箱线图可以垂直或水平绘制。虽然与直方图或密度图相比,箱线图可能看起来很原始,但它们具有占用空间少的优点,这在比较许多组或数据集之间的分布时很有用。
Python 统计数据可视化库 Seaborn 用于绘制箱线图。
**sns.boxplot(x='Survival Status', y='Positive Axillary Nodes', data=haberman)
plt.show()**

图片提供:iambipin
小提琴情节
小提琴图用于可视化数据的分布及其概率密度。它是箱形图与两侧的旋转核密度图的组合,用于显示数据的分布形状。中间的白点是中间值,中间粗黑条代表四分位数范围。从它延伸出来的黑色细线代表数据中的最大值和最小值。小提琴图类似于箱线图,只是它们也显示不同值的数据的概率密度,通常由核密度估计器平滑。结合两个世界最好的(直方图和 PDF,方框图)给出了小提琴图。小提琴剧情是单变量分析。
**sns.violinplot(x='Survival Status', y='Positive Axillary Nodes', data=haberman, size=8)
plt.show()**

图片提供:iambipin
观察:
- IQR 是衡量谎言价值的标准。因此,幸存于的患者的阳性腋窝淋巴结少于 3 个。同样,死亡的患者,腋窝淋巴结阳性大于 2 个。
- 晶须外点的存在表明异常值的存在。存活类别(存活 5 年或更长时间的患者)中异常值的数量大大高于死亡类别(5 年内死亡的患者)。****
- 幸存类别的 Q1 和中位数几乎相同。死亡类别的中位数和幸存类别的 Q3 显然在同一条线上。因此存在重叠,这可能导致至少 15%到 20% 的误差。因此,很难设定一个阈值来区分患者的生存机会。
- 大多数没有阳性腋窝淋巴结的患者在乳腺癌中存活。类似地,大多数有大量阳性腋窝淋巴结的患者死亡。
- 每条规则都有例外。它也适用于这里。因为具有大量阳性腋窝淋巴结的患者很少存活,并且没有阳性腋窝淋巴结的患者很少死亡。
等值线图表
等值线图是一种多变量分析。等高线图不是一种标准化技术,而是一种通过绘制二维格式的称为等高线的常量 z 切片来表示三维表面的图形技术。Seaborn 习惯于绘制等高线图。****
sns.jointplot(x='Age of Patient', y='Positive Axillary Nodes', data=haberman, kind='kde')
plt.show()

图片提供:iambipin
观察:
- 在所有腋窝淋巴结阳性少于或等于 2 个的患者中,大多数患者的年龄在50–56 岁之间。****
确定观察值:
我们可以通过对哈伯曼数据帧执行以下操作来确定观察值:
df_axnodes_zero = haberman.loc[haberman['Positive Axillary Nodes']<=2]
print('No. of patients with positive axillary nodes<=2: ', len(df_axnodes_zero))
df_axnodes_zero_50 = df_axnodes_zero.loc[(df_axnodes_zero['Age of Patient']>=50) & (df_axnodes_zero['Age of Patient']<=56)]
print('No. of patients in the age group 50-56 who have positive axillary nodes<=2: ', len(df_axnodes_zero_50))Output:
No. of patients with positive axillary nodes<=2:  197
No. of patients in the age group 50-56 who have positive axillary nodes<=2:  40
因此,在 50-56 岁年龄组中,20.30%腋窝淋巴结阳性小于或等于两个淋巴结的患者。
让我们总结一下我们在探索性数据分析过程中所做的重要观察。
结论:
- 30-40 岁年龄段的大多数患者都从乳腺癌中幸存下来。
- 在 1961 年至 1968 年期间接受手术的大多数患者在手术后存活了 5 年或更长时间。
- 出现阳性腋窝淋巴结(淋巴结受累)可以是乳腺癌的明显表现。一般来说,乳腺癌患者的生存机会与阳性腋窝淋巴结的数量成反比。
- ****腋窝淋巴结无阳性的患者比腋窝淋巴结有阳性的患者有更高的存活几率。
- 少数有大量阳性腋窝淋巴结的患者存活,少数无阳性腋窝淋巴结的患者死亡。所以没有阳性腋窝淋巴结并不能预示着绝对的生存保证。
- 只有一小部分患者腋窝淋巴结阳性超过 25 个。
- 所以基于探索性数据分析,我们可以提出一个关于乳腺癌患者生存几率的假设。
参考文献:
- https://www.breastcancer.org/symptoms/diagnosis/lymph_nodes
- https://www.kaggle.com/gilsousa/habermans-survival-data-set
- https://en.wikipedia.org/wiki/Exploratory_data_analysis
- https://matplotlib . org/API/_ as _ gen/matplotlib . py plot . hist . html
- https://matplotlib . org/tutorials/intermediate/legend _ guide . html
- https://seaborn.pydata.org/generated/seaborn.FacetGrid.html
- https://pandas . pydata . org/pandas-docs/stable/reference/API/pandas。DataFrame.dtypes.html
- https://www.appliedaicourse.com/
- https://en . Wikipedia . org/wiki/Cumulative _ distribution _ function
- https://datavizcatalogue.com/methods/box_plot.html
- https://datavizcatalogue.com/methods/violin_plot.html
- https://pixabay.com/users/geralt-9301/(来自 Pixabay 的 Gerd Altmann 的第一张背景图片)
- 我创建的所有其他图像(除了第一个背景图像)。
使用机器学习对图像进行压缩、搜索、插值和聚类
如何使用图像嵌入进行压缩、搜索、插值和聚类
机器学习中的嵌入提供了一种创建复杂、非结构化数据的简明、低维表示的方法。自然语言处理中通常使用嵌入来将单词或句子表示为数字。
在之前的一篇文章中,我展示了如何创建一个 1059x1799 HRRR 图像的简洁表示(50 个数字)。在本文中,我将向您展示嵌入有一些很好的属性,您可以利用这些属性来实现用例,如压缩、图像搜索、插值和大型图像数据集的聚类。
压缩
首先,嵌入是否捕捉到了图像中的重要信息?我们能把嵌入的图像解码成原始图像吗?
嗯,我们将无法恢复原始图像,因为我们获取了 200 万像素的值,并将它们放入一个长度=50 的向量中。尽管如此,嵌入是否捕捉到了天气预报图像中的重要信息?
以下是世界协调时 2019 年 9 月 20 日 05:00 HRRR 的原始预报:

2019 年 9 月 20 日天气
我们可以获得时间戳的嵌入并如下解码(完整代码在 GitHub 上)。首先,我们通过加载 SavedModel、找到嵌入层并重建所有后续层来创建解码器:
import tensorflow as tf
def create_decoder(model_dir):
    model = tf.keras.models.load_model(model_dir)
    decoder_input = tf.keras.Input([50], name='embed_input')
    embed_seen = False
    x = decoder_input
    for layer in model.layers:
        if embed_seen:
            x = layer(x)
        elif layer.name == 'refc_embedding':
            embed_seen = True
    decoder = tf.keras.Model(decoder_input, x, name='decoder')
    print(decoder.summary())
    return decoderdecoder = create_decoder('gs://ai-analytics-solutions-kfpdemo/wxsearch/trained/savedmodel')
一旦我们有了解码器,我们就可以从 BigQuery 中提取时间戳的嵌入:
SELECT *
FROM advdata.wxembed
这给了我们一个这样的表格:

然后,我们可以将上表中的“ref”值传递给解码器:
import tensorflow as tf
import numpy as np
embed = tf.reshape( tf.convert_to_tensor(df['ref'].values[0], 
                     dtype=tf.float32), [-1, 50])
outimg = decoder.predict(embed).squeeze() * 60
plt.imshow(outimg, origin='lower');
请注意,TensorFlow 希望看到一批输入,因为我们只传入一个,所以我必须将其重新整形为[1,50]。类似地,TensorFlow 返回一批图像。我在显示它之前挤压它(删除虚拟尺寸)。结果呢?

如你所见,解码图像是原始 HRRR 的模糊版本。嵌入确实保留了密钥信息。它起到压缩算法的作用。
搜索
如果嵌入是一个压缩的表示,嵌入空间的分离程度会转化为实际预测图像的分离程度吗?
如果是这种情况,搜索过去与现在的一些情况“相似”的天气情况就变得容易了。在 200 万像素的图像上寻找相似物可能会很困难,因为风暴可能会略有不同,或者大小有所不同。
由于我们在 BigQuery 中有嵌入,所以让我们使用 SQL 来搜索类似于 2019 年 9 月 20 日 05:00 UTC:
WITH ref1 AS (
SELECT time AS ref1_time, ref1_value, ref1_offset
FROM `ai-analytics-solutions.advdata.wxembed`,
     UNNEST(ref) AS ref1_value WITH OFFSET AS ref1_offset
WHERE time = '2019-09-20 05:00:00 UTC'
)SELECT 
  time,
  **SUM( (ref1_value - ref[OFFSET(ref1_offset)]) * (ref1_value - ref[OFFSET(ref1_offset)]) ) AS sqdist** 
FROM ref1, `ai-analytics-solutions.advdata.wxembed`
GROUP BY 1
ORDER By sqdist ASC
LIMIT 5
基本上,我们计算在指定时间戳(refl1)的嵌入和每隔一个嵌入之间的欧几里德距离,并显示最接近的匹配。结果是:

这很有道理。前一小时/后一小时的图像最相似。然后,从+/- 2 小时开始的图像等等。
如果我们想找到不在+/- 1 天内的最相似的图像呢?由于我们只有 1 年的数据,我们不打算进行大的类比,但让我们看看我们得到了什么:
WITH ref1 AS (
SELECT time AS ref1_time, ref1_value, ref1_offset
FROM `ai-analytics-solutions.advdata.wxembed`,
     UNNEST(ref) AS ref1_value WITH OFFSET AS ref1_offset
WHERE time = '2019-09-20 05:00:00 UTC'
)SELECT 
  time,
  SUM( (ref1_value - ref[OFFSET(ref1_offset)]) * (ref1_value - ref[OFFSET(ref1_offset)]) ) AS sqdist 
FROM ref1, `ai-analytics-solutions.advdata.wxembed`
**WHERE time NOT BETWEEN '2019-09-19' AND '2019-09-21'**
GROUP BY 1
ORDER By sqdist ASC
LIMIT 5
结果有点出人意料:1 月 2 日和 7 月 1 日是天气最相似的日子;

好吧,让我们来看看这两个时间戳:


我们看到 9 月 20 日的图像确实落在这两张图像之间。两张图片中都有墨西哥湾沿岸和中西部北部的天气情况。就像 9 月 20 日的照片一样。
如果我们有(a)不止一年的数据,( b)在多个时间步加载 HRRR 预报图像,而不仅仅是分析场,以及(c)使用更小的切片来捕捉中尺度现象,我们可能会得到更有意义的搜索。这是留给感兴趣的气象学学生的练习
插入文字
回想一下,当我们寻找与 05:00 的图像最相似的图像时,我们得到了 06:00 和 04:00 的图像,然后是 07:00 和 03:00 的图像。在嵌入空间中,到下一个小时的距离大约为 sqrt(0.5)。
给定搜索用例中的这种行为,一个自然的问题是我们是否可以使用嵌入在天气预报之间进行插值。我们能对 t-1 和 t+1 的嵌入进行平均以得到 t=0 的嵌入吗?有什么错误?
WITH refl1 AS (
SELECT ref1_value, idx
FROM `ai-analytics-solutions.advdata.wxembed`,
     UNNEST(ref) AS ref1_value WITH OFFSET AS idx
WHERE time = '2019-09-20 05:00:00 UTC'
),...SELECT SUM( (ref2_value - (ref1_value + ref3_value)/2) * (ref2_value - (ref1_value + ref3_value)/2) ) AS sqdist
FROM refl1
JOIN refl2 USING (idx)
JOIN refl3 USING (idx)
结果呢?sqrt(0.1),比 sqrt(0.5)少很多。换句话说,嵌入确实起到了方便的插值算法的作用。
为了将嵌入作为一种有用的插值算法,我们需要用远远超过 50 个像素的像素来表示图像。丢失的信息不可能这么高。同样,这是留给感兴趣的气象学家的练习。
使聚集
鉴于嵌入在可交换性和可加性方面看起来工作得非常好,我们应该期望能够对嵌入进行聚类。
让我们使用 K-Means 算法,要求五个聚类:
CREATE OR REPLACE MODEL advdata.hrrr_clusters
OPTIONS(model_type='kmeans', num_clusters=5, KMEANS_INIT_METHOD='KMEANS++')
ASSELECT arr_to_input(ref) AS ref
FROM `ai-analytics-solutions.advdata.wxembed`
生成的质心形成一个 50 元素的数组:

我们可以继续绘制五个质心的解码版本:
for cid in range(1,6):
    **embed = df[df['centroid_id']==cid].sort_values(by='feature')['numerical_value'].values**
    embed = tf.reshape( tf.convert_to_tensor(embed, dtype=tf.float32), [-1, 50])
    outimg = decoder.predict(embed).squeeze() * 60
    axarr[ (cid-1)//2, (cid-1)%2].imshow(outimg, origin='lower');
12.15992
以下是 5 个群集的结果形心:

当嵌入被聚类成 5 个簇时,这些是质心
第一个好像是你们班中西部风暴。第二个是芝加哥-克利夫兰走廊和东南部的大范围天气。第三个是第二个的强大变体。第四个是穿越阿巴拉契亚山脉的飑线。第五是内陆晴朗的天空,但是沿海的天气。
为了将聚类用作有用的预测辅助工具,您可能想要对更小的切片进行聚类,可能是 500 公里 x 500km 公里的切片,而不是整个美国。
在所有五个集群中,西雅图下雨,加利福尼亚阳光明媚。

后续步骤:
- 仔细阅读 GitHub 上的完整代码
- 阅读前两篇文章。一个是关于如何将 HRRR 文件转换成张量流记录,另一个是关于如何使用自动编码器来创建 HRRR 分析图像的嵌入。
- 我在华盛顿大学的科学研究所做了一个关于这个话题的演讲。看 YouTube 上的演讲:
compression vae——t-SNE 和 UMAP 的强大和多功能替代产品
介绍一种快速、易用、基于深度学习的降维工具

使用 CompressionVAE 的 MNIST 嵌入的风格化可视化,由Qosmo的程序员罗宾·荣格斯创建。
【TL;dr: CompressionVAE 是一个基于可变自动编码器思想的降维工具。它可以通过pip install cvae安装,使用方式与 scikit-learn 的 t-SNE 或 UMAP-learn 非常相似。完整的代码和文档可以在这里找到:https://github.com/maxfrenzel/CompressionVAE
降维是许多机器学习应用的核心。即使像 MNIST 这样简单的 28x28 像素黑白图像也要占据 784 维空间,而对于大多数感兴趣的真实数据来说,这可以轻松达到数万甚至更多。为了能够处理这种数据,第一步通常包括降低数据的维度。
这背后的一般思想是,不同的维度通常是高度相关和冗余的,并且不是所有的维度都包含等量的有用信息。降维算法的挑战是保留尽可能多的信息,同时用较少的特征描述数据,将数据从高维数据空间映射到低得多的维度潜在空间(也称为嵌入空间)。本质上是压缩的问题。一般来说,压缩算法越好,我们在这个过程中丢失的信息就越少(有一个小警告,根据应用程序的不同,并非所有信息都同样重要,因此最无损的方法不一定总是最好的)。
良好的降维技术允许我们创建更适合下游任务的数据,允许我们建立更紧凑的模型,并防止我们遭受维数灾难。它还使我们能够在二维或三维空间中可视化和分析数据。它可以发现大数据集中隐藏的潜在结构。
长期以来t-分布式随机邻居嵌入(t-SNE) ,特别是它作为 scikit-learn 的一部分的实现,一直是降维的主力(与 PCA 一起)。它很容易使用,有很多好的属性,特别是可视化。但是它有几个缺点。找到正确的参数并解释结果可能有点挑战性,并且它不能很好地扩展到高维空间(在输入和潜在维度中都是如此)。它的特性也使它不太适合非可视化的目的。
最近,均匀流形近似和投影(UMAP) 变得流行起来,并开始在许多应用中补充甚至取代 t-SNE,这要归功于它提供的几个优点。UMAP 要快得多,并且能更好地适应高维数据。它还更好地保留了全局结构,使其更适合可视化之外的许多应用。
在本文中,我想介绍这两种方法的替代方法:CompressionVAE(或简称为 CVAE)。CVAE 建立在 t-SNE 和 UMAP 实现的易用性基础上,但提供了几个非常可取的属性(以及一些缺点——它不是银弹)。
在其核心,CVAE 是一个变分自动编码器(VAE) 的 TensorFlow 实现,包装在一个易于使用的 API 中。我决定将其命名为 CompressionVAE,以赋予该工具一个独特的名称,但从根本上来说,它只是一个 VAE(可选地添加了反向自回归流层,以使学习到的分布更具表现力)。
因此,它基于与 VAE 相同的强大理论基础,并且可以很容易地扩展以考虑该领域中许多正在进行的发展。如果你想了解更多关于 VAEs 的一般知识,我写了一个关于这个主题的深入的三部分系列,从一个双人游戏的独特角度解释它们。
我们将在下面的示例中更深入地探讨 CVAE 的一些优点(和缺点),但这里只是一个总体概述:
- CVAE 比 SNE 霸王龙和 UMAP 都快
- CVAE 倾向于学习一个非常平滑的嵌入空间
- 学习到的表征非常适合作为下游任务的中间表征
- CVAE 学会了从数据到嵌入空间的确定性和可逆的映射(注意:UMAP 的最新版本也提供了一些可逆性)
- 该工具可以集成到实时系统中,无需重新培训即可处理以前未见过的示例
- 经过训练的系统是完整的生成模型,可用于从任意潜在变量生成新数据
- CVAE 很好地扩展到高维输入和潜在空间
- 原则上,它可以扩展到任意大的数据集。它既可以像 t-SNE 和 UMAP 实现那样获得全部训练数据,也可以一次只将一批训练数据加载到内存中。
- CVAE 是高度可定制的,即使在其当前的实现中,也提供了许多可控制的参数(同时也为缺乏经验的用户提供了相当健壮的默认设置)
- 除了当前的实现,它是高度可扩展的,并且将来的版本可以提供例如卷积或递归编码器/解码器,以允许除简单向量之外的更多问题/数据特定的高质量嵌入。
- VAEs 有一个非常强大和研究良好的理论基础
- 由于优化的目标,CVAE 往往不会得到一个非常强的集群之间的分离。非常手动地,在拥有平滑的潜在空间和获得强的团簇分离之间有一个权衡,VAE 更加平滑并且保留了全局结构。这可能是一个问题,也可能是一个优势,取决于具体的应用。
- 最大的缺点:和几乎任何深度学习工具一样,它需要大量的数据来训练。t-SNE 和 UMAP 在小数据集上工作得更好(当 CVAE 变得适用时没有硬性规定,但是通常通过运行pip install cvae(或克隆库并从那里安装), CVAE 就可以使用了。在自述文件中可以找到更广泛的代码示例,但在其最基本的用例中,我们可以使用以下几行代码在数据阵列 X 上训练 CVAE 模型:
默认情况下,初始化 CompressionVAE 对象会创建一个具有二维潜在空间的模型,将数据 X 随机分为 90%的训练数据和 10%的验证数据,应用特征规范化,并尝试将模型架构与输入和潜在特征维度相匹配。它还将模型保存在一个临时目录中,下次在那里创建新的 CVAE 对象时,该目录将被覆盖。所有这些都可以定制,但我建议您参考自述文件了解详细信息。
训练方法应用基于验证数据丢失的自动学习率调度,并且在模型收敛时(由某些可调整的标准确定)或者在 50k 训练步骤之后停止。我们也可以通过键盘中断(ctrl-c 或 Jupyter notebook 中的‘中断内核’)来提前停止训练过程。此时模型将被保存。也可以停止训练,然后使用不同的参数重新开始(更多详细信息,请参见自述文件)。
一旦我们有了一个训练好的模型,我们就可以使用工具的 embed 方法来嵌入数据。例如,为了嵌入整个数据集 X,我们可以运行
# Import CVAE 
from cvae import cvae# Initialise the tool, assuming we already have an array X containing the data 
embedder = cvae.CompressionVAE(X)# Train the model 
embedder.train()
在这种情况下,我们嵌入了模型在训练期间已经看到的数据,但是我们也可以向 embed 方法传递新的示例。
可视化嵌入
对于二维潜在空间,CVAE 自带可视化的内置方法。让我们假设我们在 MNIST 数据上训练我们的模型,并且最初以如下方式定义 X:
z = embedder.embed(X)
然后,我们可以通过以下方式绘制上面创建的嵌入
下面是一个这样的例子。
我们也可以扫描潜在空间,并使用 VAE 的生成器部分来可视化学习空间。
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1, cache=True)
X = mnist.data
同样,您可以在自述文件中找到更多的详细信息和选项。
embedder.visualize(z, labels=[int(label) for label in mnist.target])
速度比较

CVAE 的好处之一是训练速度快。下面是在两个不同规模的流行数据集上与 UMAP 和 t-SNE 的一些比较:MNIST (70,000 个样本)和 Kuzushiji-49 (270,912 个样本)。
embedder.visualize_latent_grid()

为了进行比较,我使用了 scikit-learn 提供的 t-SNE 的基本版本,而不是任何多核版本。使用 UMAP 时,我收到一个“NumbaPerformanceWarning”,通知我不能使用并行化。所以这两种方法都有可能被加速,但是下面仍然是大多数用户会遇到的最普通的用例的一个很好的比较。所有测试都是在我的 MacBook Pro 上进行的,没有使用任何 GPU。
MNIST:
t-SNE: 5735 秒(单次)
UMAP:611±1 秒(10 次运行的平均值和标准偏差)
CVAE:121±39 秒(10 次运行的平均值和标准偏差)
- Kuzushiji-49:
- t-SNE:甚至没有尝试
- UMAP: 4099 秒(单次)
CVAE:235±70 秒(10 次运行的平均值和标准偏差)
- 我们看到,在这些数据集规模上,CVAE 在收敛时间方面明显更胜一筹,而且这种优势只会在更大的数据集上增强。请注意,CVAE 持续时间是由默认学习率计划和停止标准确定的收敛时间。
- 更详细的外观
- 下面的动画展示了一个在 MNIST 进行训练的例子。我们可以看到,CVAE 很快就接近了它的最终解,然后花了大部分时间来完善它。这表明,如果训练速度是一个关键问题,早期停止可能仍然会提供足够的结果,这取决于用例。自从我禁用了学习率衰减,这个特殊的模型实际上从未完全收敛。]
我们还在上面的速度数字中看到,由于当前版本对模型的初始化相当敏感,CVAE 在收敛时间上有相当大的差异。这种灵敏度既影响收敛速度,也影响最终结果。尝试几种模型并比较结果可能是值得的。作为一个例子,下面的潜在空间的三个不同的实例训练相同的 MNIST 数据。
然而,在所有情况下,我们看到 CVAE 倾向于实现一个非常平滑的潜在空间,并很好地保存全球结构。与此形成对比的是,例如,UMAP 的强聚类将相似的数字非常明显地分开,而对 CVAE 来说,它们平滑地流入彼此。
MNIST 嵌入和 UMAP 潜在空间的可视化,摘自 UMAP 文件。
上面的图也显示了 UMAP 逆变换的结果。与上面的 CVAE 版本相比,我们再次看到 CVAE 的潜在空间更加平滑。要明确的是,我在这里提出的“平滑”论点是非常定性的。如果你对更具体的东西感兴趣,我不久前给写了一篇关于这个主题的论文,但是我没有在这里的任何测试中应用这些更精确的指标。

关于用 CVAE 解码数据的一个注意事项:如果解码是主要的兴趣,我们可能应该有一个更专业的解码器。现在,解码器完全不受约束,例如,MNIST 模型可以输出值< 0 and > 255,即使它们作为像素值没有意义。我希望 CVAE 尽可能的通用和数据不可知。然而,在未来的版本中提供更多的选项来更加专门化这一点可能是值得的。例如,在 MNIST,通常的做法是通过 sigmoid 函数和适当的损耗将值限制在适当的范围内。

时尚 MNIST 是另一个简单而有用的玩具数据集,用于探索该工具的功能和输出。首先,让我们在 UMAP 的观想中寻找对比。
时尚 MNIST 的嵌入和 UMAP 的潜在空间的可视化,摘自 UMAP 文献。
与此相比,这是一个经过训练的 CVAE 模型的结果。
我们再一次看到了与上面非常相似的结果,CVAE 的星团分离不那么明显,但是过渡更加平滑。

这个例子也很好地展示了 CVAE 是如何抓住本地和全球结构的。
在全球范围内,我们看到“踝靴”、“运动鞋”和“凉鞋”类别都聚集在一起,无缝地相互融合。我们观察到从“连衣裙”,到“衬衫”和“t 恤/上衣”(几乎完全重叠),到“套头衫”和“外套”的类似过渡。例如,我们在“裤子”和更大的鞋类集群之间看到了更强烈的区分(和不明确的重建)。有趣的是,包包几乎可以无缝地变成套头衫。

在局部我们也发现了很多结构,比如鞋子随着 y 的降低脚踝高度越来越高,随着 x 的增加脚踝高度越来越高。
所有这些都表明,模型已经清楚地学习/发现了数据集的一些底层潜在结构。
我们还可以玩数据插值。让我们随机挑选两个真实的例子,创建五个中介“假”数据点。
注意,这段代码假设embedder是一个 CVAE 实例,它是根据时尚 MNIST 数据X_fashion训练的。在做了一些整形和显示后,我们可以得到如下结果。
虽然这里讨论的所有示例都只是没有任何实际应用的玩具数据集,但 CVAE 可以应用于许多现实世界的问题,也可以用于生产环境。
在我现在的公司 Qosmo ,我们在几个项目中使用 CVAE 的变体,从纯粹的艺术到商业。我们开发的各种音乐推荐和播放列表生成解决方案就是一个例子。
import random
import numpy as np# Get two random data points
X1 = np.expand_dims(random.choice(X_fashion), axis=0)
X2 = np.expand_dims(random.choice(X_fashion), axis=0)# Embed both
z1 = embedder.embed(X1)
z2 = embedder.embed(X2)# Interpolate between embeddings in five steps
z_diff = (z2 - z1) / 6 
z_list = [z1 + k*z_diff for k in range(7)]
z_interp = np.concatenate(z_list)# Decode
X_interp = embedder.decode(z_interp)
在其中的一些中,我们首先使用专有系统将歌曲转换为固定大小的非常高维的矢量表示,然后使用 CVAE 进一步压缩矢量,使它们更适合下游任务,最后使用另一个定制的系统,该系统使用创建的歌曲嵌入来生成有意义和自然的声音播放列表,可能还会考虑某些附加标准和实时数据(例如,当前天气或一天中的时间)。

如果您有兴趣使用这些系统中的一个,或者想根据您的特定需求与我们合作定制解决方案,请访问我们的网站并与我们联系。
压缩阀仍处于非常早期的阶段。但是我鼓励你试着把它应用到你自己的问题上。
如果当前的基本版本不能完全满足您的需求,您可以根据自己的需要随意扩展和修改它。如果你愿意分享你的定制,并让它成为 CVAE 主发行版的一部分,那就更好了。虽然肯定有很大的改进空间,但我希望当前的实现提供了一个很好的框架和起点,使这个过程尽可能简单。
我期待着看到每个人的用例和贡献(希望也能修复我草率的代码、潜在的错误和不稳定性)。
CompressionVAE 无法取代其他各种维度缩减工具,但我希望它能为现有的工具库提供有价值的补充。
快乐压缩!
I’m looking forward to see everyone’s use cases and contributions (and hopefully also fixes to my sloppy code, potential bugs, and instabilities).
CompressionVAE won’t be able to replace the various other dimensionally reductions tools, but I hope it provides a valuable addition to the existing arsenal of tools.
Happy compressing!
计算物理学
使用数据科学工具理解激发电影《到来》的物理定律

来自电影的图像到达。静止不动的原理启发了这本书的作者。
在看到电影到来的几天后,当我在写我之前的论文微分编程时,我发现 JAX 的作者,一个实现自动微分的库,推荐阅读结构和经典力学解释。我立刻被他们解释静止动作原理的巧妙方式所打动,这启发了我的到来。
这本书已经由麻省理工学院出版,并重新使用了同样的标题计算机程序的结构和解释,也是由麻省理工学院出版的。两者都可以在网上免费获得!
计算机程序的结构与解释,又称向导书是一本著名的计算机科学书籍。我强烈推荐它的阅读。当我读得非常开心的时候,我被这本关于经典力学的新书吸引住了,这本书似乎遵循了同样的模式。
作为一名机械领域的前研究人员,我没有对这本书失望;至于 SICP,我真的很喜欢阅读 SICM 的作品。这本书的整体思想是使用计算机科学提供的工具接近经典力学:数值分析,符号计算,自动微分,…
在本文中,我们将看到如何使用我们熟悉的数学和数值工具来计算刚体轨迹,使用激发电影降临的物理学原理:静止作用原理。
牛顿第二定律
我们所熟悉的物理学本质上是基于牛顿的设想。在他的设想中,基于微分方法,运动和力通过加速度联系起来:对物体施加力会改变其速度,即物体的加速度与力成正比。物体质量越大,力对其速度的影响越小:加速度与质量成反比。
这个关系通常被他著名的第二定律所包含:

牛顿第二物理定律。图片由作者提供。
使用这个公式,或者稍微复杂一点的版本,完全可以模拟刚性或可变形物体的运动。
静止作用原理
然而,在本文中,我们将重点介绍另一种方法,该方法由法国数学家牟培尔堆开发,由杰出的英国数学家汉密尔顿完善,由俄罗斯数学家奥斯特罗夫斯基进一步扩展,并由法国科学家拉格朗日推广:最小作用的原理,推广为静止作用的原理。
另外,请注意,这一原则是美国作家姜峯楠写的引人入胜的书《你生活的故事》的故事基础。这本书启发了同样著名的电影降临。但是文化上的离题已经够多了。让我们回到数学和物理。
我不会给你这个原理的陈述,在任何一本好的力学教科书中都可以找到。相反,就像在 SICM 中一样,我将强调我们如何利用这一原则,使用我们首选的编程语言:Python,在候选轨迹中选择物理上可接受的运动。
事实上,这个原则本质上是说,任何物理上可接受的路径都会最小化特定的功能。或者更准确地说,可接受路径周围的任何微小变化都必须保持函数不变,即路径是静止的。
因此,为了找到正确的路径,我们所要做的就是对目标进行一些最小化。再简单不过了,这是我们的日常工作。
编程物理学
为了计算这些可接受的路径,我们只需要:
- 一种创建候选轨迹的方法。为此,我们需要一个返回位置的时间函数。普通的 python 和一些多项式魔法就能胜任这项工作。
- 从轨迹中提取特征的方法:通常是它们的位置、速度、加速度和可能的高阶导数。我们可以用自动微分来解决这个问题。
- 一种在候选轨迹中识别物理上更合理的轨迹的方法。我们知道,由于静止动作的原理,这是通过最小化由先前步骤提取的候选轨迹特性特征相对于候选轨迹的函数来实现的。我们把这个函数称为动作,它是拉格朗日函数的两个时间戳之间的积分,另一个函数执行我们想要模拟的物理定律。
让我们具体一点,给这些概念加上一些代码。
创建轨迹
有各种选项来定义轨迹。例如,我们可以使用样条、贝塞尔曲线和伯恩斯坦多项式。但是当我们处理一个物理问题时,我们将使用拉格朗日多项式。
这些多项式非常方便:它们通过强制曲线通过预定义的控制点来控制曲线。为了做到这一点,拉格朗日设计了他的多项式,使得每个点 x_i 之前的系数对于不同于 t_i 的任何时间 t_k 都是 0.0,并且对于 t_i 等于 1.0。
这是一个有 3 个控制点的曲线的第一个控制点 x_0 的系数公式的例子。

拉格朗日多项式系数的一个例子。图片由作者提供。
用 Python 写的,给出了:
拉格朗日多项式代码
我们可以如下应用该代码,以获得示例轨迹:
用拉格朗日多项式定义简单曲线
生成的轨迹如下所示:

穿过 4 个控制点的轨迹
请注意,曲线正好穿过控制点。
提取特征特性
既然我们有了一种使用参数定义曲线的方法,就像控制点一样容易处理,我们需要一种工具来提取这些曲线上任何一点的特征。
因为我们需要的特征是位置、速度或加速度,即曲线相对于时间的各阶导数,所以我们可以使用自动微分。
我们定义了下面的函数, local_state ,它采用任何时间函数,使用 AD 库 JAX 计算其特性:
local_state 计算由时间函数 f. 定义的轨迹特征
我们可以在上面定义的曲线上应用这个函数。看曲线告诉我们,导数在 t=1.5 时一定为零。让我们用这几行代码来检查一下:
数组的第三个元素,时间导数为零,如 t=1.5 时所预期的
一切都像魔咒一样管用。
识别正确的轨迹
创建候选轨迹现在就像选择一组控制点一样简单。我们还有自动计算微分几何特征的工具。
我们现在需要的是一种在候选人中选择正确轨迹的方法,或者更好的是,收敛到正确的轨迹。
正如上面写的几行,以及牟培尔堆的陈述,几个世纪以来,我们知道一个物理上可接受的轨迹可以最小化所谓的动作。

通过对作者的候选轨迹 f. Image 的拉格朗日积分,在时间 t0 和 t1 之间计算动作。
这里 l 代表拉格朗日量,γ是我们的函数 local_state,负责特征提取。
为了执行集成,我们将使用标准的 scipy integrate 方法。
作为第一次尝试,我们将计算自由落体的轨迹。在这种情况下,物体只受到重力加速度的作用。当以零初始速度下落物体时,其在时间 t 的 y 坐标为:

y,坐标一时间 t .作者图片
在这种物理设置下,拉格朗日被定义为动能和重力势能之差,即:

拉格朗日当物体只受重力作用时。图片由作者提供。
将所有这些放在一起,我们得到:
相对于受扰动的曲线,真实轨迹的作用实际上是最小的。
自由落体示例
现在让我们检查从任何候选轨迹开始,当最小化动作时,我们可以收敛到正确的轨迹。为此,我们将使用scipy . optimize . minimize函数,使用几何 Nelder-Mead 最小化方法。
这导致下面的代码:
正如所料,我们收敛到右边的曲线,显示在时间 t 的 y 坐标:

初始曲线以蓝色显示。最小化动作后,我们收敛到正确的轨迹。图片由作者提供。
自由运动示例
我们再举一个例子,要被说服。在这种情况下,我们不仅要考虑 y ,还要考虑 x 。
我们不仅在 t0 时刻,而且在 t1 时刻加强我们身体的位置。因此我们有两个边界条件:
- t0: (0.0,0.0)
- t1: (0.0,4.0)
这一次,我们插值 2D 向量,而不仅仅是浮点。为了处理这个问题,我们将对每个维度使用一个拉格朗日多项式。另一种选择是使用单个多项式并使用 2D 点作为控制点。
参见下面的代码:
真实轨迹的方程式在real_trajectory函数中定义。它用于显示一些检查点,以确保我们收敛到正确的运动,如下所示:

蓝色曲线,即使离正确的轨迹相当远,也成功地收敛到正确的运动。图片由作者提供。
再来一次,一切都按预期工作。
结论:用编程来教物理(和数学)
通过这些基本的例子,我们已经表明,使用我们用于数据科学的相同工具,有可能简化对相当复杂的物理理论的理解。
我们已经能够用几行代码说明经典力学的变分方法,因此使理解这些理论原理背后的概念变得更容易。
在物理教学中引入像 python 这样的编程语言,以其强大的功能和灵活性,是帮助掌握复杂学科的一个非常好的方法。有了它们,实验物理定律并理解它们的真正含义就简单了。如今测试一个假设,建造一个玩具模型,展示曲线,...
没有比创建自己的模拟器更好的方法来理解一个复杂的现象。编程应该是任何物理课堂都会用到的工具。
计算无法计算的| SVI 和艾尔波是如何工作的
贝叶斯建模与真实世界数据一起工作的一个原因。随机海洋中的近似灯塔。

照片由威廉·布特在 Un spash上拍摄
当你想对你的数据获得更多的洞察力时,你依赖于编程框架,它允许你与概率进行交互。开始时你所拥有的只是数据点的集合。它只是对数据来源的底层分布的一瞥。然而,您最终不仅仅想要简单的数据点。您想要的是精细的、健谈的密度分布,您可以用它来执行测试。为此,您使用概率框架,如张量流概率、 Pyro 或 STAN 来计算概率的后验概率。
正如我们将看到的,这种计算并不总是可行的,我们依靠马尔可夫链蒙特卡罗(MCMC)方法或随机变分推断(SVI)来解决这些问题。特别是对于大型数据集,甚至是每天的中型数据集,我们必须执行神奇的采样和推理来计算值和拟合模型。如果这些方法不存在,我们将被我们想出来的一个整洁的模型所困,但是没有办法知道它实际上是否有意义。
我们使用 Pyro 构建了一个预测模型,该模型使用的正是——SVI:
将一段时间内的点连接起来,并满怀信心地进行预测(-区间)。
towardsdatascience.com](/pyro-top-down-forecasting-application-case-4781eb2c8485)
在这个故事中,我们将从 Pyro 的角度来看计算。
简而言之,Pyro 并没有强行计算后验概率,而是做出了一个我们可以改进的估计。但是为什么我们不能计算整个事情呢?很高兴你问了。
从无穷大到常数
假设我们有一个数据集 D ,我们想要拟合一个模型(θ)来准确描述我们的数据。为此,我们需要一组变量。这些变量对应于随机事件,我们称之为潜在随机变量,例如 z.
现在来看看我们的模型对后验概率分布 P 的依赖程度。这是一个很好的起点,现在我如何计算这个 P?为了让这个工作,我们必须做这样的推论:

(1) —给定我们数据中的观察值 x,计算潜在随机变量 z 的后验概率。
精通数学的读者已经看到上面等式的右边部分通常是不可计算的。我们面对未知事物上的全能积分的恐惧。现在把 z 作为连续变量,它可能是无限的。那相当大。
我们能做的是用一个变分分布;姑且称之为 Q 。在一个理想的世界中,这个近似的 Q 应该尽可能的接近我们正在计算的 P。那我们怎么去呢?
我们首先需要的是 P 和 Q 相似程度的度量。输入 Kullback Leibler 散度——你可以在数理统计年鉴中找到 Kullback 和 Leibler 的传奇论文[1]。
我们用来比较分布的工具,Kullback Leibler 散度被
定义为:

(2) —定义库尔贝克-莱布勒散度
这个度量告诉我们分布 P 和 Q 有多相似。如果它们相似,KL 就低,如果它们非常不同,差异就大。
现在,这个度量要求我们知道分布 P 和 q,但是我们还不知道 P。毕竟,这是我们正在计算的,到目前为止,我们的 Q 是一个野生猜测。
经过一些重新表述,我们最终得到了这个(参见[5]的分步解决方案):

(3) —从 KL 导出 ELBO,作为与常数 log(P)的和
第一部分(-E[log P(z,x)-log Q(z|x)])就是所谓的证据下界或 ELBO 。第二部分(log(P))是一个常数。这对我们以后的计算更有利。我们现在要做的就是最大化 ELBO,使 P 和 q 之间的偏差最小。
Minimizing KL divergence corresponds to maximizing ELBO and vice versa.
用深度学习学习 Q
既然我们已经有了基本的理论,那么我们该如何处理我们的问题呢?我们可以通过使用某种形式的神经网络来估计 Q。我们已经有了 TensorFlow 或者 PyTorch 来处理 Pyro。与我们已经建立的模型相比,我们现在可以建立一些东西来描述我们的变分分布。一旦我们有了这个分布估计,我们就可以使用随机梯度下降来拟合我们的模型。所以让我们开始吧。
我们使用我们的数据作为模型和指南的输入。模型给了我们 P,而指南建议网络输出一个 Q。如果这是高斯分布,那么网络会给出平均值μ和标准差σ。如果我们用一个非常简单的形式来描绘这个过程,随着时间的推移,它看起来就像这样:

图 1 — 计算 n 个时间步上的后验概率时,用向导拟合模型的过程。
跟我说说 Pyro
让我们举一个简单的例子,给定潜在随机变量 z ,我们拟合一个模型函数 f 。该变量来自β分布(仅作为一个例子)。我们观察到的事件是伯努利随机发生的。这意味着事件要么发生,要么不发生。最后,事件是独立同分布的,我们写为烟火板符号。所以我们的模型是:
def model(self):
        # sample `z` from the beta prior
        f = pyro.sample("z", dist.Beta(10, 10))
        # plate notion for conditionally independent events given f
        with pyro.plate("data"):
            # observe all datapoints using the bernoulli distribution
            pyro.sample("obs", dist.Bernoulli(f), obs=self.data)
到目前为止,这是标准的烟火,你不必总是指定一个向导。Pyro 可以处理,但是会有一个向导在那里。通过指定向导,你可以完全控制你的建模过程。在这个例子中,向导的潜在 z 来自于此:
def guide(self):
        # register the two variational parameters with pyro
        alpha_q = pyro.param("alpha_q", torch.tensor(15),
                             constraint=constraints.positive)
        beta_q = pyro.param("beta_q", torch.tensor(15),
                            constraint=constraints.positive)
        pyro.sample("latent_z", NonreparameterizedBeta(alpha_q, beta_q))
最后,我们通过使用 SVI 对象来运行我们的推理。我们提供先前指定的模型和指定的指南。我们还依赖标准 PyTorch Adam optimizer 进行随机梯度下降。
def inference(self):
        # clear everything that might still be around
        pyro.clear_param_store()
        # setup the optimizer and the inference algorithm
        optimizer = optim.Adam({"lr": .0005, "betas": (0.93, 0.999)})
        svi = SVI(self.model, self.guide, optimizer, loss=TraceGraph_ELBO())
你都准备好了。现在我们来看一些输出。
上面的例子只是伪代码,为了获得正确的输出,我使用了一个简单的贝叶斯回归任务,并计算了模型函数的后验概率(具体细节请参见我的github)。
我们使用 Pyro SVI 和 Markov Chain Monte Carlo 程序以及 NUTS 采样器。
丑陋的真相|比较 SVI 和 MCMC
到目前为止,一切听起来都很有希望——好得令人难以置信。对于生活中的每一件事,你应该经常检查你的结果。下面的例子(图 2-3)显示了前面提到的回归任务的拟合后验分布,并同时使用 SVI 和 MCMC。

图 2——使用 SVI 和 MCMC 对具有两个变量的函数 f 进行贝叶斯回归。

图 3——模型拟合后,贝叶斯回归任务的密度分布与后验分布重叠的等高线图。两个模型变量 bF2 的相互作用和相互作用项 bF12。
先说好的一点:我们已经为 SVI 和麦克米伦安排了足够的后验概率。不太理想的是,它们明显不同。对马尔可夫方法和随机推理如何以及为什么不同的深入理解是各种研究的主题,深入的理解将是另一篇文章的主题。在这一点上,我们唯一能得出的结论是推理程序对我们最终的模型有明显的影响。
整洁——SVI 作品。关键点。
- 我们正视了一个看似不可行的问题,并研究了潜在过程的理论,以找出解决它的方法。
- 你可以使用深度学习框架,如 TF 和 PyTorch,通过近似另一个模型-分布来建立你的概率模型。
- 这一切的目标是最小化我们的两个近似后验之间的分歧(或最大化下界)。
- 我们应该总是看着我们的输出,问自己我们所看到的是否有意义。取样程序显然会影响我们的后验结果——所以我们应该小心。
总而言之,SVI 允许我们采用任何数据集,并用它进行贝叶斯建模。如果你问我,这是一个非常好的功能。
参考
- 南 Kullback 和 r . lei bler关于信息和充分性。1951 年在数理统计年鉴。
- D.温盖特和 t .韦伯。概率规划中的自动变分推理。2013 年月 arxiv 日。
- Pyro 文档— SVI 教程一
- 烟火文档— SVI 教程二
- 烟火文档— SVI 教程三
- 礼萨·巴巴纳扎德。随机变分推理。 UBC
- D.马德拉斯。 教程随机变分推理 。多伦多大学。2017.
完整的烟火例子
上面提到的例子只是作为一个关于如何建立一个 Pyro 类的粗略概述。功能代码可在 Pyro 文档中找到(参见[ 5 ])。
class BetaExample:
    def __init__(self, data):
        # ... specify your model needs here ...
        # the dataset needs to be a torch vector
        self.data = data def model(self):
        # sample `z` from the beta prior
        f = pyro.sample("z", dist.Beta(10, 10))
        # plate notion for conditionally independent events given f
        with pyro.plate("data"):
            # observe all datapoints using the bernoulli likelihood
            pyro.sample("obs", dist.Bernoulli(f), obs=self.data) def guide(self):
        # register the two variational parameters with pyro
        alpha_q = pyro.param("alpha_q", torch.tensor(15),
                             constraint=constraints.positive)
        beta_q = pyro.param("beta_q", torch.tensor(15),
                            constraint=constraints.positive)
        pyro.sample("latent_z", NonreparameterizedBeta(alpha_q, beta_q)) def inference(self):
        # clear the param store
        pyro.clear_param_store()
        # setup the optimizer and the inference algorithm
        optimizer = optim.Adam({"lr": .0005, "betas": (0.93, 0.999)})
        svi = SVI(self.model, self.guide, optimizer, loss=TraceGra
用一行代码计算你收入的概率
你有没有问过自己,你达到既定财务目标的概率是多少?
如果你是一个自由职业者,一个零售商或者仅仅是一个活跃的媒体作家,你可以在下面找到一个简单的公式来更好地预测你的收入机会。
下面的讨论是基于这样一个想法,即任何业务都有一定程度的随机性。尤其是在日冕危机时期,任何预测都是不确定的。
然而,即使在如此困难的情况下,我相信一些应用数学永远不会有坏处。下面,您可以欣赏随机过程理论应用于简单业务场景的一些概念。
必要前提:我的目的是展示一些工作中的数学,而不是给出任何理财建议。请不要忘记,数学公式只有在工作假设得到满足时才是可靠的。
出乎意料
让我们从简单的开始,考虑在你的生意中你只以固定的价格销售一种类型的产品。这可能是一个具体的对象、数字内容或任何服务。
例如,假设你是一名业余画家,你以每幅 100 美元的价格出售你的作品。为了简洁起见,我们将用字母 a 来表示这个价格。所以,a= 100 $/艺术品,意思是每件艺术品一百美元。很好。
假设你知道,平均来说,你一个月卖出 r =10 件艺术品。你也有一些固定支出,如颜色、画笔等。你每月花在绘画材料上的钱正好是 50 美元。
一年能赚多少?你打开你信任的计算器应用程序和数字
a×r×t–c×t=(100×10×12-50×12)$ = 11400 $,
其中时间 t 设置为 12 个月。不过,这只是你的 期望 。你不能确定今年能卖出r×t =120 件艺术品。相反,每月售出的艺术品数量将围绕平均值随机波动。
下面的图有助于形象化随机性的影响。每条绿线都是一个独立的随机模拟,代表了未来可能的净收益轨迹。直到年底,波动会显著增加并偏离预期!

画家一年净收入的随机轨迹。每一个都是作为艺术品销售的齐次泊松过程产生的,并且由于画家的成本而不断向下漂移。黑线显示的是平均趋势。—图片由作者提供。
那么,如何主张不仅仅是期望值呢?
在假定卖出率 r 不随时间变化的情况下,结果是某段时间 t 卖出的艺术品数量 N(t) 按照一个均值等于 r × t 的 泊松分布 分布。不出意外的话,平均值r×t =120与预期的销售额是一致的,但是现在我们知道的更多了。精确分布的知识允许我们计算概率。
更准确地说,我们想知道在时间 t 时,净收益 E(t) 增长超过目标 g 的概率。用数学的话来说,这个概率可以简洁地写成 Prob( E(t) > g )。

蓝色区域代表净收入超过财务目标的概率。—图片由作者提供。
我们现在可以做一些数学计算并获得

超越财务目标概率的推导。—图片由作者提供。
这就是公式。我们已经表明,赚取超过目标 g 的概率与计算售出艺术品数量 N(t) 大于(g+c×t)/a的可能性是完全相同的,这很容易通过泊松分布的累积分布函数的倒数获得。数字怎么弄?
在 Julia 中,使用软件包发行版:
***using** Distributions 
p = ccdf(Poisson(r*t),(g+c*t)/a)*
在 Python 中,使用包 scipy:
***import** scipy.stats **as** st 
p = 1 - st.poisson.cdf((g+c*t)/a,r*t)*
首先,让我们做一个理智检查。在画家的例子中,获得超过预期的 11400 美元的概率是 47.6%。这是有道理的,因为卖出多于或少于 120 件艺术品的机会是相当对称的。(注:如果你在问自己为什么这个数字不是 50%,那只是因为 mean 和 median 不一样,一般来说)。
为了更好地了解赚钱的机会,可以将 Prob(E(t) > g) 绘制成目标值 g 的函数。例如,大约有 90%的机会超过净收益 1 万美元,但只有 0.9%的机会超过 1.4 万美元。

该曲线显示了超过给定财务目标的概率,使用 painter 示例中提供的公式进行计算。画家最好在梦想过高之前知道几率!—图片由作者提供。
更高的价格还是更多的销售?
在研究价格和平均销售额时,观察收入分布的行为很有趣。例如,通过调整价格 a 和平均销售额 r 同时保持他们的产品不变,人们很快就会意识到增加销售数字有一个稳定的效果:赚钱的概率集中在期望值附近。相反,以更高的价格卖出更少的商品会导致更不确定的结果。

预期销售额减少意味着收益的随机性增加。相反,由于大数定律,更多的销售是可以预测的。注意:使用的数字不反映价格和需求的规律。—图片由作者提供。
这个事实只不过是大数定律的一个结果:你加起来的随机事物越多,你就越能更好地描述它们的总和。实际上,我喜欢反过来思考这句话:
数字越小,机会越重要。
两个方向都是如此。你可能表现不佳,也可能中了大奖。这只是运气。
销售几种类型的产品
现在让我们考虑更常见的零售几种产品的情况,每种产品都有一个给定的价格。例如,再次以画家为例,我们可以想象我们出售三种不同类型的艺术品,取决于大小:小型、中型和大型画布,价格分别为 50 美元、100 美元和 200 美元。
我们再次假设平均每月卖出 r =10 件艺术品和来知道油画通常卖出的比例。比如 40%小,40%中,20%大。请注意,这些数字是这样的,期望是不变的,相对于一个产品的情况。但是赚的概率呢?现在会有显著的不同吗?
这种具体情况下,不会有太大变化。只需将 a 设置为项目价格的加权平均值( a =0.4×50 美元+0.4×100 美元+0.2×200 美元),就可以使用前面的公式安全地再次估算收入概率。
然而,只要每件商品“经常”售出,这种方法就能可靠地发挥作用,因此不存在过于接近小数量不确定区域的风险。如果项目价格彼此相差不大,该公式尤其准确,因为平均值更有代表性。
如果情况不是这样,或者有些商品很少售出,那么为这种库存情况推导出一个特定的公式就很重要了。这可以通过泊松过程的 稀疏属性 来实现:以全局速率 r 和各个分数 f(i) 销售更多产品在数学上等价于以速率 r(i)=r×f(i)独立销售每一个项目 i 。无论如何,最终公式在概念上与前面显示的公式相似,只是在计算净收益 E(t) 时,需要对所有项目的回报求和。
估计中等收入
最后,让我们探索一下 Medium 上最有趣的案例。如何计算你写作回报的概率?
主要的复杂之处在于,我们无法将媒体报道的收益建模为我们事先知道价格的项目。相反,我们也可以通过引入回报* 分布来以概率的方式描述它们。*
我们本质上想要将总的中等收入建模为在给定时间内将要出版的随机数量的故事的随机回报的总和。
回报分布可以被认为是前面提到的价格分数的连续版本。它描述了你发布的一个故事以何种概率获得一定的收益。显然,这种分布对于每个媒体作者来说是特定的,因为它强烈地依赖于媒体上的经验和受欢迎程度。
为了简单起见,我在这里用指数分布来描述 post 回报,原因有两个。首先,这适用于许多媒体作家:大多数帖子回报适中,少数稍好,但没有暴涨。其次,指数分布很简单,因为它只有一个参数,即每篇文章的平均回报。(还是那句话,姑且称之为a)**
在这些设置中,总中等收入由一个 复合泊松过程 描述,比率为 r×t ,被加数为指数分布。具体是什么意思?首先,让我们看看平均值。
对于所考虑的复合过程,平均总收益等于 a×r×t ,标准差为 a×sqrt(2×r×t) 。标准差给出了波动大小的概念:作为中心极限定理的结果,收益将在期望值上下一个标准差的区间内以 68%的概率下降。然而,为了让中心极限定理体面地工作,经验法则是考虑至少 20 个以上的总销售额(即,中等故事,在这种情况下)。
例如,如果你每周发布一篇文章,平均每篇文章赚 25 美元,那么一年后你将会得到(1300 255)美元(52 周)。
如果你对达到某个目标的概率特别感兴趣,画家的公式在这种情况下也很好,即使只是作为一个代理。使用它仍然有效的原因是指数分布不太偏斜,就像画布价格的例子一样。
但是,请注意,获得的概率值的可信度取决于这些建模假设的满足程度:
- 你对你的出版率有一个很好的估计
- 你的岗位收入是相互独立的,并以已知的平均值 a 呈指数分布
- 在时间跨度 t 内,上述任何一项都不会改变
你可能想试试这个公式。相反,如果您不喜欢近似,可以通过直接模拟直接计算 Prob(E(t) > g) 。这也只是一行代码。
在朱莉娅身上,
***using** Distributions 
S = 10^6  # Number of simulations you want
p = length(findall([sum(rand(Exponential(a),rand(Poisson(r*t)))) for i=1:S] .> g)) / S*
或者用 Python
***import** numpy **as** np
S = 10**6  # Number of simulations you want
p = sum(map(lambda x : x > g, [sum(np.random.exponential(a,np.random.poisson(r*t))) for i in range(S)])) / S*
(附注:朱莉娅看起来好多了,而且速度快得惊人。我爱朱莉娅!)
上面的代码只是简单地将几次泊松分布的指数分布回报相加。然后,超过目标 g 的概率被简单地估计为总收益大于 g 的模拟的分数。
但是所有这些准确性真的有必要吗?但说实话,很可能不是。
应用于媒体的概率论的实际信息简单地证实了日常证据:确保你在媒体上成功的最强有力的方法是提高你的出版率和写出好东西。多一个多一个,多一个多一个,并且两者的时间都更长 t 。总之,更多的艺术。
即使我是一个学数学的人,我也相信生活有时会因为数字少而单词多而变得更加愉快。所以,写得好!
计算机辅助翻译系统与机器翻译
有什么区别,为什么重要?

照片由 Fotis Fotopoulos 在 Unsplash 上拍摄
我们都很熟悉谷歌翻译,可能在某个地方用过。但是我们真的知道其背后的原因吗?语言行业对自动化翻译工具有什么看法吗?如果你是个人或公司的利益相关者,考虑为你的文档或网站使用翻译服务,你尤其应该了解这些技术,以便做出明智的决定。作为一名德语到英语的翻译,我已经使用翻译技术二十年了,所以让我们来仔细看看吧!
什么是计算机辅助翻译工具,什么是翻译管理系统?
CAT(计算机辅助翻译)工具是支持译者准备翻译的软件。它转换要翻译的文本,对其进行分段,然后在自己的编辑器中对这些分段进行翻译。
CAT 工具由几个子系统组成。其核心是翻译记忆库(TM),它将各个翻译单元收集在一个数据库中。在翻译过程中,保存由一段源文本和目标语言中的对等译文组成的各个翻译单元。存储翻译单元可以确保翻译的一致性,以及在文本中使用相同的术语。这提高了输出文本的质量。
计算机辅助翻译工具将源文本分成句子、要点或标题等片段。然后在编辑器窗口中向翻译者显示各个片段。译者在源文本旁边或下面的相应部分输入他们的译文。这创建了存储在 TM 中的翻译单元,TM 是一个包含原始源内容和相应翻译的双语数据库。当翻译者将来翻译类似的文本时,程序会建议现有的、已保存的翻译。
因此,与机器翻译程序不同,CAT 工具或翻译记忆库不独立翻译,而是仅用于支持人工翻译并将创建的翻译存储在数据库中。事实上,研究表明,使用 CAT 工具时,翻译人员的工作速度提高了 28%。
计算机辅助翻译系统的优势
因此,卡特彼勒工具能够实现更快、更高质量的翻译,并确保翻译的一致性。此外,卡特彼勒工具可以准备和分割各种文件格式,如 Microsoft Word、Excel 和 PowerPoint 文件、Open Office 文件,甚至 XML 文件。这允许翻译人员处理某些文件类型,即使他们没有创建这些文件的原始软件,例如 InDesign。
如今,卡特彼勒工具已成为全球翻丨译丨公丨司和翻译人员使用的行业标准程序。
使用 CAT 工具和相关的翻译记忆库为将来的翻译打下基础,这将随着每次翻译的完成而增长。如果在 CAT 工具中打开一个新的源语言文件,并且使用了翻译记忆库,则在翻译该文件时会显示“新单词”、“100%匹配”(相同段)或“模糊匹配”(相似段)。然后,翻译员会对这些内容进行检查,如有必要,还会对其进行调整,以便能够接收目标片段。因此,轮子不必每次都重新发明。
此外,当一份文件的重复率很高时,翻译记忆系统的优势就很明显了。相应的片段只需翻译一次,然后可以通过工具自动插入文本中的所有其他实例。这使得翻译效率更高,并能为客户节省成本。
文本分析
CAT 工具通过将每个新文本与其翻译记忆库中存储的单位进行比较来分析每个新文本。然后,它评估有多少内容需要翻译,这些信息通常用于计算成本。分析通常分为以下几部分:
- 新单词:以前从未被翻译过且不在翻译记忆库中的片段
- 模糊匹配:与先前翻译过的内容相似的片段,这些片段存在于翻译记忆中,但需要译者进行编辑
- 100%匹配:在翻译记忆库中找到的相同片段
- 上下文匹配:出现在与翻译记忆库中完全相同的上下文中(即两次 100%匹配之间)的相同片段
- 重复:在每个文件中出现不止一次的相同片段;翻译人员只需翻译一次,该工具会自动在整个文档中复制已翻译的片段

照片由 Miguelangel Miquelena 在 Unsplash 拍摄
每种类型的片段都需要译者付出不同程度的努力,从从头开始的全新翻译到不加修改的审阅。
当译者第一次使用 CAT 工具时,他们的 TM 仍然是空的,所有片段将作为“新单词”返回。但是一旦存储了更多的文本,杠杆作用就会开始增加。
除了翻译记忆库之外,卡特彼勒工具还有许多其他功能,包括:
- 术语管理
- 质量保证特征
- 搜索和替换功能
- 索引搜索
- 文本对齐功能
如今,卡特彼勒工具有桌面版、基于服务器或基于云的版本。基于服务器的版本通常由大型语言服务提供商使用,因为它们允许多个翻译人员同时为一个项目工作。
市场上最受欢迎的卡特彼勒工具包括:
- memoQ
- SDL Trados Studio
- 穿过
- OmegaT
- Wordfast
- 智能猫
重要的是,我们不要将 CAT 工具与机器翻译程序混淆。
什么是机器翻译?
机器翻译应用程序是一种将文本输入计算机算法的程序,计算机算法会自动将文本翻译成另一种语言。所以没有人参与翻译过程。
然而现在,许多 CAT 工具都提供了机器翻译插件。有趣的是,一些译者出于道德原因拒绝使用这些方法,或者因为他们认为这会导致较差的翻译质量,并冲淡了技巧。
我们区分了三种类型的机器翻译方法:
1)基于规则的机器翻译:这种方法依赖于语言专家开发的语法和语言规则,以及高度可定制的词典。
2)统计机器翻译:这种方法不依赖语言学规则和单词;相反,它通过分析大量现有的人工翻译来学习。
3)神经机器翻译:这种方法通过使用大型神经网络来自学如何翻译;它越来越受欢迎,因为它提供了比其他两种方法更好的结果。例如,谷歌 MT 和微软 Translator 依赖于神经网络。
一些流行的机器翻译程序有:
- DeepL
- 亚马逊山
- 谷歌山
- 全能技术
- 蒂尔德山
- 微软翻译器
- 希斯特兰
如今,许多语言服务提供商部署了某种机器翻译程序,并由人工翻译或编辑(称为“后期编辑”)对输出进行修改,以改善结果,这些结果通常是不合格的。
自然,这些自动化程序的成本比人工翻译要低,但代价总是较低的质量。语言是高度动态和复杂的,尽管机器翻译技术多年来有了很大的改进,但它永远无法准确识别每种语言的细微差别,并像人类翻译一样用另一种语言传达它们。
因此,无论它们听起来多么相似,CAT 工具和机器翻译程序的工作方式不同,用于不同的目的。简单来说,CAT 工具是专业翻译人员使用的,不适合外行用户。当然,人类的产出是有限且昂贵的。这就是机器翻译的用武之地。任何人都可以低价甚至免费使用机器翻译程序,但其可靠性要低得多。语言公司经常将它们与人工审核员一起部署。
如果您需要翻译服务,现在您可以权衡您的选择,做出符合您需求的明智决定。
计算机视觉 101:用 Python 处理彩色图像

来源:佩克斯。com
了解处理 RGB 和实验室图像的基础知识,以促进您的计算机视觉项目!
每一个计算机视觉项目——无论是猫/狗分类器还是给旧图像/电影添加颜色——都涉及到图像处理。而最终,模型只能和底层数据一样好——垃圾入,垃圾出。这就是为什么在这篇文章中,我重点解释在 Python 中处理彩色图像的基础知识,它们是如何表示的,以及如何将图像从一种颜色表示转换成另一种颜色表示。
设置
在本节中,我们将设置 Python 环境。首先,我们导入所有需要的库:
import numpy as npfrom skimage.color import rgb2lab, rgb2gray, lab2rgb
from skimage.io import imread, imshowimport matplotlib.pyplot as plt
我们使用 scikit-image ,这是来自scikit-learn家族的一个库,专注于处理图像。有许多可供选择的方法,一些库包括matplotlib、numpy、 OpenCV 、 Pillow 等。
在第二步中,我们定义一个 helper 函数,用于打印出关于图像的信息摘要——它的形状和每一层中的值的范围。
该函数的逻辑非常简单,一旦我们描述了图像是如何存储的,维度切片就有意义了。
灰度等级
我们从最基本的情况开始,灰度图像。这些图像完全是由灰色阴影构成的。极端情况是黑色(对比度的最弱强度)和白色(最强强度)。
在遮光罩下,图像被存储为整数矩阵,其中一个像素的值对应于给定的灰度。灰度图像的数值范围从 0(黑色)到 255(白色)。下图直观地概述了这一概念。

在这篇文章中,我们将使用你已经看到的缩略图,彩色蜡笔的圆圈。选了这么一张有色彩的图不是偶然的:)
我们首先将灰度图像加载到 Python 中并打印出来。
image_gs = imread('crayons.jpg', as_gray=True)fig, ax = plt.subplots(figsize=(9, 16))
imshow(image_gs, ax=ax)
ax.set_title('Grayscale image')
ax.axis('off');

由于原始图像是彩色的,我们使用as_gray=True将其加载为灰度图像。或者,我们可以使用默认设置imread加载图像(这将加载一个 RGB 图像——在下一节介绍),并使用rgb2gray功能将其转换为灰度。
接下来,我们运行 helper 函数来打印图像摘要。
print_image_summary(image_gs, ['G'])
运行代码会产生以下输出:
--------------
Image Details:
--------------
Image dimensions: (1280, 1920)
Channels:
G : min=0.0123, max=1.0000
图像存储为 1280 行×1920 列的 2D 矩阵(高清晰度分辨率)。通过查看最小值和最大值,我们可以看到它们在[0,1]范围内。这是因为它们被自动除以 255,这是处理图像的常见预处理步骤。
RGB
现在是使用颜色的时候了。我们从 RGB 模型 开始。简而言之,这是一个加法模型,其中红色、绿色和蓝色(因此得名)的阴影以各种比例添加在一起,以再现广泛的颜色光谱。
在scikit-image中,这是使用imread加载图像的默认模式:
image_rgb = imread('crayons.jpg')
在打印图像之前,让我们检查一下摘要,以了解图像在 Python 中的存储方式。
print_image_summary(image_rgb, ['R', 'G', 'B'])
运行代码会生成以下摘要:
--------------
Image Details:
--------------
Image dimensions: (1280, 1920, 3)
Channels:
R : min=0.0000, max=255.0000
G : min=0.0000, max=255.0000
B : min=0.0000, max=255.0000
与灰度图像相比,这次图像存储为 3D np.ndarray。额外的维度表示 3 个颜色通道中的每一个。和以前一样,颜色的强度在 0-255 的范围内显示。它经常被重新调整到[0,1]范围。然后,任何层中的像素值为 0 表示该像素的特定通道中没有颜色。
一个有用的提示:当使用 OpenCV 的imread函数时,图像被加载为 BGR 而不是 RGB。为了使它与其他库兼容,我们需要改变通道的顺序。
是时候打印图像和不同的颜色通道了:
fig, ax = plt.subplots(1, 4, figsize = (18, 30))ax[0].imshow(image_rgb/255.0) 
ax[0].axis('off')
ax[0].set_title('original RGB')for i, lab in enumerate(['R','G','B'], 1):
    temp = np.zeros(image_rgb.shape)
    temp[:,:,i - 1] = image_rgb[:,:,i - 1]
    ax[i].imshow(temp/255.0) 
    ax[i].axis("off")
    ax[i].set_title(lab)plt.show()
在下图中,我们可以分别看到原始图像和 3 个颜色通道。我喜欢这幅图像的原因是,通过关注单个蜡笔,我们可以看到 RGB 通道中的哪些颜色以及哪些比例构成了原始图像中的最终颜色。

或者,我们可以绘制单独的颜色通道,如下所示:
fig, ax = plt.subplots(1, 4, figsize = (18, 30))ax[0].imshow(image_rgb) 
ax[0].axis('off')
ax[0].set_title('original RGB')for i, cmap in enumerate(['Reds','Greens','Blues']):
    ax[i+1].imshow(image_rgb[:,:,i], cmap=cmap) 
    ax[i+1].axis('off')
    ax[i+1].set_title(cmap[0])plt.show()
什么会生成以下输出:

我更喜欢这种绘制 RGB 通道的变体,因为我发现它更容易区分不同的颜色(由于其他颜色更亮和透明,它们更突出)及其强度。
在处理图像分类任务时,我们经常会遇到 RGB 图像。当将卷积神经网络(CNN)应用于该任务时,我们需要将所有操作应用于所有 3 个颜色通道。在这篇文章中,我展示了如何使用 CNN 来处理二值图像分类问题。
工党
除了 RGB,另一种流行的表示彩色图像的方式是使用 Lab 色彩空间(也称为 CIELAB)。
在进入更多细节之前,指出颜色模型和颜色空间之间的区别是有意义的。颜色模型是描述颜色的数学方法。颜色空间是将真实的、可观察的颜色映射到颜色模型的离散值的方法。更多详情请参考本答案。
Lab 颜色空间将颜色表示为三个值:
- L :从 0(黑色)到 100(白色)范围内的亮度,实际上是灰度图像
- a :绿-红色谱,数值范围为-128(绿色)~ 127(红色)
- b :蓝黄色光谱,数值范围为-128(蓝色)~ 127(黄色)
换句话说,Lab 将图像编码为灰度层,并将三个颜色层减少为两个。
我们首先将图像从 RGB 转换到 Lab,并打印图像摘要:
image_lab = rgb2lab(image_rgb / 255)
rgb2lab函数假设 RGB 被标准化为 0 到 1 之间的值,这就是为什么所有值都除以 255 的原因。从下面的总结中,我们看到实验室值的范围在上面规定的范围内。
--------------
Image Details:
--------------
Image dimensions: (1280, 1920, 3)
Channels:
L : min=0.8618, max=100.0000
a : min=-73.6517, max=82.9795
b : min=-94.7288, max=91.2710
下一步,我们将图像可视化——实验室一号和每个单独的通道。
fig, ax = plt.subplots(1, 4, figsize = (18, 30))ax[0].imshow(image_lab) 
ax[0].axis('off')
ax[0].set_title('Lab')for i, col in enumerate(['L', 'a', 'b'], 1):
    imshow(image_lab[:, :, i-1], ax=ax[i])
    ax[i].axis('off')
    ax[i].set_title(col)fig.show()

第一次尝试绘制实验室图像
嗯,第一次可视化 Lab 色彩空间的尝试远远没有成功。第一张图像几乎无法辨认,L 层不是灰度。根据本答案中的见解,为了正确打印,实验室值必须重新调整到【0,1】范围。这一次,第一个图层的重定比例与后两个不同。
#scale the lab image
image_lab_scaled = (image_lab + [0, 128, 128]) / [100, 255, 255]fig, ax = plt.subplots(1, 4, figsize = (18, 30))ax[0].imshow(image_lab_scaled) 
ax[0].axis('off')
ax[0].set_title('Lab scaled')for i, col in enumerate(['L', 'a', 'b'], 1):
    imshow(image_lab_scaled[:, :, i-1], ax=ax[i])
    ax[i].axis('off')
    ax[i].set_title(col)
fig.show()
第二次尝试要好得多。在第一幅图像中,我们看到了彩色图像的 Lab 表示。这一次, L 层是实际的灰度图像。仍然可以改进的是最后两层,因为它们也是灰度的。

第二次尝试绘制实验室图像
在最后一次尝试中,我们将彩色贴图应用到实验室图像的 a 和 b 层。
fig, ax = plt.subplots(1, 4, figsize = (18, 30))ax[0].imshow(image_lab_scaled) 
ax[0].axis('off')
ax[0].set_title('Lab scaled')imshow(image_lab_scaled[:,:,0], ax=ax[1]) 
ax[1].axis('off')
ax[1].set_title('L')ax[2].imshow(image_lab_scaled[:,:,1], cmap='RdYlGn_r') 
ax[2].axis('off')
ax[2].set_title('a')ax[3].imshow(image_lab_scaled[:,:,2], cmap='YlGnBu_r') 
ax[3].axis('off')
ax[3].set_title('b')
plt.show()
这一次结果令人满意。我们可以清楚的分辨出 a 和 b 图层中不同的颜色。仍然可以改进的是色彩映射表本身。为了简单起见,我使用了预定义的颜色贴图,它包含一种介于两种极端颜色之间的颜色(黄色代表图层 a ,绿色代表图层 b )。一个潜在的解决方案是手动编码彩色地图。

第三次尝试绘制实验室图像
在处理图像着色问题时,经常会遇到 Lab 图像,例如著名的去模糊。
结论
在本文中,我回顾了使用 Python 处理彩色图像的基础知识。使用所介绍的技术,你可以开始自己解决计算机视觉问题。我认为理解图像是如何存储的以及如何将它们转换成不同的表示是很重要的,这样你就不会在训练深度神经网络时遇到意想不到的问题。
另一个流行的色彩空间是 XYZ。scikit-image还包含将 RGB 或 Lab 图像转换成 XYZ 的功能。
您可以在我的 GitHub 上找到本文使用的代码。一如既往,我们欢迎任何建设性的反馈。你可以在推特上或者评论里联系我。
我最近出版了一本关于使用 Python 解决金融领域实际任务的书。如果你有兴趣,我贴了一篇文章介绍这本书的内容。你可以在亚马逊或者 T21 的网站上买到这本书。
参考
[1]https://ai.stanford.edu/~syyeung/cvweb/tutorial1.html
https://github.com/scikit-image/scikit-image/issues/1185
计算机视觉——当计算机能够看见和感知的时候!!!
电脑能够看、听和学习。欢迎来到未来”——戴夫·沃尔特斯

众所周知,计算机的计算能力远远超过人类。因此,自 20 世纪中期以来,已经尝试通过利用计算机来逐渐自动化计算繁重的活动。随着机器学习(ML)应用的出现和日益熟练,预测模型已经占据了优先地位。ML 接管的一些例子如下
- 通过主动识别可能终止订阅的客户来留住客户
- 销售、需求、收入等的预测。对于任何行业
- 识别任何行业中交叉销售的客户
- 识别可能拖欠按揭付款的客户
- 通过识别可能发生故障的机器进行预防性维护
然而,历史上需要人类感知来解决的问题超出了经典(静态)ML 算法的范围。好消息是,随着 2010 年代初以来深度学习(DL)的大规模突破,在解决传统上需要人类直觉的问题时,可以观察到接近人类的表现。前面提到的感知问题需要处理声音和图像的技能。因此,通过听觉和视觉进行感知和处理的能力似乎是促进解决此类问题的主要技能。这些技能对人类来说是自然而直观的,但对机器来说却是难以捉摸的。下面给出了一些通过应用 DL 解决感知问题的例子
- 接近人体水平的图像分类和目标检测
- 接近人类水平的语音识别
- 接近人类水平的笔迹转录
- 改进的机器翻译
- 改进的文本到语音转换
- Google Now、亚马逊 Alexa 等数字助手
- 接近人类水平的自动驾驶
- 回答自然语言问题的能力
然而,必须指出的是,这仅仅是一个开始,我们仅仅触及到所能取得的成就的表面。也有正在进行的对形式推理领域的研究。如果成功,这可能在科学、心理学、软件开发等领域帮助人类。
我想到的问题是,机器如何做到这一点,即获得感知能力来解决需要人类直觉的问题。数字处理,解决处理数字和/或类别的问题,应用监督学习(或无监督学习)属于具有巨大计算能力的机器领域。能够看到和识别图像的组成部分是一项新技能,本文的重点是关注现在赋予计算机像人类一样解决图像问题的能力的过程。本文的内容包括以下内容
- 人工智能(AI)和深度学习(DL)简介
- 通过应用全连接层(FCN)即多层感知器(MLP)进行图像识别
- 卷积神经网络介绍(CNN)
- 为什么 CNN 在解决图像相关问题时比 MLPs 更常用
- CNN 的工作
- MLP 和 CNN 的表现对比
人工智能和 DL 简介
人工智能是一个通过应用逻辑、if-then 规则、决策树和 ML(包括 DL)使计算机能够模仿人类智能的领域。人工神经网络是人工智能的一个分支。以下图示将进一步阐明 AI 的子集,即 ML 和 DL。数据科学跨越了人工智能的所有层面。

图 1:人工智能子集(图片由作者提供)
任何人工神经网络架构的基础都始于这样一个前提,即模型需要经历多次迭代,考虑到错误,从错误中学习,并在学习足够多的时候实现拯救或饱和,从而产生等于或类似于现实的结果。下图向我们简要介绍了人工神经网络,并介绍了 DL 神经网络。

图 2:浅层网络与 DLNN 的比较(图片来自作者的早期文章
人工神经网络本质上是一种由多层处理单元(即神经元)组成的结构,这些处理单元获取输入数据,并通过连续的层对其进行处理,以获得有意义的表示。深度学习中的 deep 这个词代表了这种连续层表示的思想。有多少层对一个数据模型有贡献,称为模型的深度。上图更好地说明了结构,因为我们有一个只有一个隐藏层的简单 ANN 和一个有多个隐藏层的 DL 神经网络(DNN)。因此,DL 或 DLNN 是具有多个隐藏层的 ANN。
本文假设读者熟悉构成监督学习基础的概念,以及 DL 如何利用它来学习数据中的模式(在这种情况下是图像中的模式)以获得准确的结果。为了更好地理解本文的其余部分,需要对以下项目有概念性的理解
- 监督学习
- 深度学习
- 损失函数
- 梯度下降
- 向前和向后传播
为了复习,读者可以在互联网上探索关于这些主题的免费资料,或者浏览我发表的文章,深度学习的基础。深入研究上述每个概念超出了本文的范围。
通过应用完全连接的层,即多层感知器的图像识别
现在让我们深入研究一个实例,说明如何利用人工神经网络来查看图像并将它们分类到适当的类别。我们将从在真实世界数据集上应用 FCN 开始,并衡量所实现的效率。假设读者对什么是 FCN 及其操作方式有更深的理解。在非常高的层次上,FCN 或 MLP 是一个人工神经网络,其中每一层的每个元素都与下一层的每个元素相连接。下面的图 3 展示了 FCN 的样子。更多细节请参考我发表的文章,深度学习的基础。
我们试图解决的问题包括从街道拍摄的门牌号图像中识别数字。数据集名为街景门牌号(SVHN)。SVHN 是一个真实世界的图像数据集,用于开发机器学习和对象识别算法,对数据格式的要求最低,但来自一个明显更难、未解决的真实世界问题(识别自然场景图像中的数字和数字)。SVHN 是从谷歌街景图片中的门牌号获得的。
我们要实现的目标是从 SVHN 数据集获取一个图像,并确定该数字是什么。这是一个多类分类问题,有 10 个可能的类,每个类对应一个数字 0-9。数字“1”的标签为 1,“9”的标签为 9,“0”的标签为 10。虽然,在这个数据集中有接近 6,000,000 幅图像,但我们已经提取了 60,000 幅图像(42000 幅训练图像和 18000 幅测试图像)来做这个项目。这些数据是以一个数字为中心的 32 乘 32 的 RGB 图像的类似 MNIST 的格式(许多图像在边上确实包含一些干扰物)。
我们将使用原始像素值作为网络的输入。这些图像是大小为 32×32 的矩阵。因此,我们将图像矩阵整形为一个大小为 1024 ( 32*32)的数组,并将该数组提供给如下所示的人工神经网络。

图 3: FCN 建筑(图片由作者提供)
链接到数据集的是这里的。
下面是加载数据和可视化一些图像的代码。

图 Jupyter 笔记本的截图,显示了一些图片及其标签
用不同数量的隐藏层和每层中不同数量的神经元构建多个 MLPs。对每个模型的结果进行比较,三个隐藏层的结果最好。模型架构如下所示。它达到了大约 80%的准确率。
下面描述了损失如何逐渐减少和精确度如何迭代增加的可视化

图 Jupyter 笔记本截图
预测了测试数据(未暴露于训练过程)中的一些图像。相同的可视化和与其原始标签的比较如下所示。

图 Jupyter 笔记本截图
我们测试了来自可视化测试集的 3 幅图像,并且看到分类是准确的,这可以给我们成就感和舒适感。如前所述,在整个测试集上达到的准确度为 80%。由于测试集由 18000 幅图像组成,80%的准确率是相当可接受的性能。然而,这是最好的方法吗?这是能达到的最好结果吗?或者有比这更好的方法吗?带着这些问题,让我们进入下一部分。
端到端项目的完整代码可以在我的 github 帐户中找到。代码的链接也可以在这里找到。
卷积神经网络介绍(CNN)
虽然上面解释的全连接网络(FCN)达到了可接受的精度,但它还可以进一步改进。除了 FCN,还有另一类神经网络称为卷积神经网络(CNN),其内部操作更符合视觉图像的处理要求。已经发现 CNN 在涉及图像的用例中给出更好的性能。
CNN 比 FCN 好得多,因为基于它们的共享权重架构和平移不变性特征,它们是平移不变或空间不变的人工神经网络。我们将在下一节中深入探讨这个问题。
在我们详细了解 CNN 如何工作以及为什么它们比 fcn 更好之前,让我们先了解一下 CNN 的基本操作。理解 CNN 最重要的概念是卷积运算。
卷积是一种简单的数学运算,是许多常见图像处理运算符的基础。卷积提供了一种将通常大小不同但维数相同的两个数列“相乘”的方法,以产生维数相同的第三个数列。这可以用在图像处理中,以实现其输出像素值是某些输入像素值的简单线性组合的算子。
在图像处理环境中,输入数组之一通常只是图像的像素表示。第二个数组通常小得多,并且通常是二维的(尽管它可能只有单个像素厚),并且被称为内核。下面是图像和可用于卷积的内核的图形表示。

图 7:图像和内核(作者提供的图像)
卷积是通过在图像上滑动内核来执行的,通常从左上角开始,以便将内核移动通过所有位置,其中内核完全适合图像的边界。(请注意,实现的不同之处在于它们在图像边缘做了什么,如下所述。)每个内核位置对应于单个输出像素,其值通过将内核值和内核中每个单元的底层图像像素值相乘,然后将所有这些数字相加来计算。
因此,在我们的示例中,输出图像中右下角像素的值将由下式给出:
o33 = I33 * W11+I34 * W12+I43 * W21+I44 * W22
由 CNN 完成的图像分类,通过获取输入图像、处理它并将其分类到某些类别下(例如,在我们的 use SVHN 用例中从 0 到 9)来工作。计算机将输入图像视为像素阵列。基于图像分辨率,它会看到 h x w x d( h =高度,w =宽度,d =尺寸)。

计算机视觉:计算机如何看到图像(来源:维基百科
卷积是从输入图像中提取特征的第一层。卷积通过使用输入数据的小方块学习图像特征来保持像素之间的关系。它是一种数学运算,需要两个输入,如图像矩阵和滤波器或内核。
下图说明了卷积(或相关)运算是如何执行的。这里,I 表示图像,W 表示将对输入图像进行卷积的核。

图 8:卷积运算
上面的描述可以通过一个核的实际例子来进一步简化,该核在由像素表示的图像上充当过滤器。上面的公式用于图像输入和内核权重,以得到内核或滤波器输出。

图 9:滤波器输出的计算
因此,当我们有一个更大的图像和一个典型的更小的内核时,内核将在图像上滑动,如下图所示,并通过卷积运算计算每次迭代的输出。滑过绿色方块的黄色方块就是内核。绿色方块是通过像素值表示的图像。卷积输出由右边的粉色方块表示,这实际上是学习到的特征。
图 10(来源:GIPHY 链接此处为。)
为什么 CNN 比 MLPs 更能解决图像相关问题
既然解决了卷积运算,让我们试着回答上面的问题。主要原因是
- 一个简单的 FCN 需要大量的参数来训练模型
- 在图像中,空间相关性是局部的
- 通常可用的图像数量有限
- 平移不变性是处理图像的关键要素
一个简单的 FCN 需要大量的参数来训练模型
FCNs(或 MLPs)对每个输入(例如,图像中的像素)使用一个感知器,并且对于大图像,权重的数量很快变得难以管理。它包含太多参数,因为它是完全连接的。每个节点都与上下一层中的所有其他节点相连,形成一个非常密集的网络。为了便于讨论,如果我们有一个如图 3 所示的人工神经网络,只有两个隐藏层的参数数量超过 2 亿个。这是一个尺寸只有 3232 的图像。对于尺寸为 200200 像素的中等大小的图像,需要训练的参数的数量将会达到顶点。因此,从(计算机的)计算能力的角度来看,使用 FCN(或 MLPs)来解决复杂的图像相关问题不是最佳方法。此外,如果可用图像的数量较少,用如此大量的参数进行训练将导致过度拟合。
在图像中,空间相关性是局部的。CNN 帮助实现平移不变性
另一个常见的问题是 MLP 对输入(图像)及其平移版本的反应不同,它们不是平移不变的。例如,如果一个航天飞机的图片出现在一个图片的图像的左上角,而在另一个图片的右下角,MLP 将试图纠正自己,并假设航天飞机将总是出现在图像的这一部分。请参考下面的图 11 进行说明。因此,MLP 不是图像处理的最佳选择。一个主要问题是,当图像被展平(矩阵到矢量)成 MLP 时,空间信息丢失。在 FCN 中,像素之间的相关性不是局部的,即航天飞机的组件与背景或图像中存在的任何其他对象相关(例如图 11 第一部分中的汽车)。因此,如果画面移动,模型中的学习就会受到影响。这是在 CNN 中解决的,因为它们依赖于卷积运算,并且即使图像移动,激活也将保持不变。当内核在图像上滑动以在每次迭代中提供激活时,激活被定位。重量是共享的。

图 11:CNN 中空间不变性的图示(图片由作者提供)
CNN 的工作
下图是一个 3232 大小的输入图像的 CNN 表示。我们有 20 个维数为 55 的核,它们执行前面解释的卷积运算。对图像进行第一次卷积运算后得到的特征图像的尺寸为 2828。下图中的 CONV 图层表示相同的图像,即 20 幅尺寸为 2828 的特征图像。计算任何卷积运算的结果维数的数学公式将在本节后面给出。

图 11:用于分类的 CNN 的端到端表示
这里引入的新概念是上图中的池层所表示的池操作,这个概念之前没有讨论过。
汇集操作包括在特征图的每个通道上滑动二维过滤器,并总结位于过滤器覆盖的区域内的特征。池层的数学计算与卷积运算非常相似。卷积层有助于学习图像的特征,并产生与使用的核的数量一样多的特征图。汇集层执行下采样操作,并汇总由卷积层生成的多个要素地图所产生的要素。这有助于巩固网络中的感受领域。典型的 CNN 架构通常具有一个接一个堆叠的多个卷积层和池层。
CNN 中的卷积层系统地将学习的滤波器应用于输入图像,以便创建概括输入中那些特征的存在的特征图。
卷积层被证明是非常有效的,并且在深度模型中堆叠卷积层允许靠近输入的层学习低级特征(例如,线),而在模型中更深的层学习高阶或更抽象的特征,如形状或特定对象。
卷积图层的要素地图输出的局限性在于,它们记录了输入中要素的精确位置。这意味着输入图像中特征位置的微小移动将导致不同的特征地图。这可以通过引入池层来解决。汇集图层汇总了由卷积图层生成的要素地图区域中的要素。因此,将对汇总的要素而不是由卷积层生成的精确定位的要素执行进一步的操作。这使得模型对于输入图像中特征位置的变化更加鲁棒。下面给出了两种主要的汇集方法
- 平均池:计算特征图上每个面片的平均值。

图 13:平均池操作的图示(来源
- 最大池(或最大池):计算特征图每个面片的最大值。

图 14:最大池操作的图示(来源)
在开始运行案例研究之前,我想解释的最后一件事是,当卷积图层应用于输入图像时,列出用于计算特征图维度的公式。相同的公式仍然适用于卷积和池的后续层。
输出图像尺寸= (W - F +2P)/S +1
其中 W =图像尺寸
F =过滤器的尺寸
P =填充
S =步幅
步幅表示我们在卷积中的每一步移动了多少步。默认情况下是一个。与步距 1 卷积。我们可以观察到输出的大小小于输入。为了保持输入中输出的维度,我们使用填充。填充是对输入矩阵对称加零的过程。对这两者更详细的解释可以在互联网上更多的免费内容中找到。
MLP 和 CNN 的表现对比
让我们继续我们试图在 SVHN 数据集上解决的数字识别问题。我们之前使用 FCNs(或 MLPs)达到了 80%的准确率。我们现在将在用于训练的数据集上试验 CNN 的应用。
下面提供了加载数据和预处理数据(标准化输入和一个热编码标签)的代码
第一个 CNN 尝试的模型架构如下
然后编译该模型,并通过它解析数据以进行训练。该模型在验证数据上给出了 90%的准确度,这已经比 MLP 实现的性能提高了 10%。下面给出了编译、训练和评估验证数据性能的代码
上面打印语句的输出是 90%。
模型如何迭代地学习特征并逐渐提高性能的可视化表示如下。我们看到了一种趋势,即每一个时期的损耗都在减少,而精度却在提高。

Jupyter Notebook 中模型精度随时期增加的图形表示
在用不同的模型架构进行了几次实验之后,从构建的 7 个 CNN 的集合中获得了最好的结果。准确率达到了 95%。模型中的混淆矩阵如下所示。整个代码的链接是 github 中的这里的。

Jupyter 笔记本的模型输出
结论
在这篇文章中,我们介绍了两种不同的方法来解决图像分类问题,即 FCNs 和 CNN。我们目睹了两种方法的性能差异,这是由于 CNN 优越的操作方式造成的。到目前为止,我们在 CNN 中看到的图像分类的优势进一步建立在解决更复杂的问题上,如对象检测和语义分割。它们都是复杂解决方案的关键组件,如自动驾驶汽车,机器必须识别汽车前方的不同物体及其结构,以便能够以适当的方向和角度驾驶汽车。本文开头的图片是我书房的桌子,后面是桌子上所有物品的标识。所有这些成就都建立在 CNN 潜在的和确定的概念之上。在接下来的几篇文章中,我们将探索一个称为迁移学习的新概念,它可以帮助我们处理训练图像数量不足的情况。敬请期待!!!
计算机视觉和终极乒乓人工智能
应用计算机视觉
使用 Python 和 OpenCV 在线玩 pong

人工智能在行动(右桨)
我最喜欢的一个 YouTuber,CodeBullet,曾经试图创造一个 pong AI 来统治他们。可悲的是,他遇到了麻烦,不是因为他没有能力,而是我不认为他当时的经验在计算机视觉方面有什么作用。他非常搞笑,我强烈推荐你观看他(建议家长咨询),如果你正在考虑阅读这篇文章的其余部分。此外,他是他所做的天才。爱你伙计。看他的视频这里。
这似乎是一个非常有趣和简单的任务,所以我必须尝试一下。在这篇文章中,我将概述我的一些考虑,如果你希望从事任何类似的项目,这可能会有所帮助,我想我会尝试更多的这些,所以如果你喜欢这种类型的事情,请考虑跟随我。
使用计算机视觉的好处是我可以使用一个已经构建好的游戏来处理图像。话虽如此,我们将使用与 CodeBullet 使用的来自 ponggame.org的游戏版本相同的游戏版本。它也有一个 2 人模式,所以我可以对我自己的人工智能玩;我做到了,这很难…
捕捉屏幕
最重要的事情是,得到屏幕。我想确保我的帧速率尽可能快,为此我发现 MSS 是一个很棒的 python 包。有了这个,我很容易达到 60 fps,相比之下,PIL 只有 20 fps。它以 numpy 数组的形式返回,所以我的生命是完整的。
挡板检测
为了简单起见,我们需要定义桨的位置。这可以用几种不同的方法来完成,但我认为最明显的方法是为每个桨屏蔽区域,并运行连接的组件来找到桨对象。下面是这段代码的一部分:
def get_objects_in_masked_region(img, vertices,  connectivity = 8):
    *'''****:return*** *connected components with stats in masked region
    [0] retval number of total labels 0 is background
    [1] labels image
    [2] stats[0] leftmostx, [1] topmosty, [2] horizontal size, [3] vertical size, [4] area
    [3] centroids
    '''* mask = np.zeros_like(img)
    # fill the mask
    cv2.fillPoly(mask, [vertices], 255)
    # now only show the area that is the mask
    mask = cv2.bitwise_and(img, mask)
    conn = cv2.connectedComponentsWithStats(mask, connectivity, cv2.CV_16U)
    return conn
在上面,“顶点”只是一个定义遮罩区域的坐标列表。一旦我有了每个区域内的对象,我就可以得到它们的质心位置或边界框。需要注意的一点是,OpenCV 将背景作为第 0 个对象包含在任何连接的组件列表中,所以在这种情况下,我总是抓取第二大的对象。结果如下——右边绿色质心的桨是玩家/即将成为 AI 控制的桨。

挡板检测结果
移动船桨
现在我们有了输出,我们需要一个输入。为此,我求助于一个有用的软件包和其他人的代码——谢谢 StackOverflow。它使用 ctypes 来模拟键盘按键,在这种情况下,游戏是使用“k”和“m”键来玩的。我在这里得到了扫描码。在通过随机上下移动进行测试后,我们可以开始跟踪了。
乒乓检测
N ext up 用于识别和跟踪 pong。同样,这可以通过多种方式处理,其中一种方式是使用模板进行对象检测,但我还是使用了连接的组件和对象属性,即 pong 的面积,因为它是唯一具有其尺寸的对象。我知道每当乒乓球穿过或接触任何其他白色物体时,我都会遇到问题,但我也认为这没什么,只要我能在大多数时间里跟踪它。毕竟它是直线运动的。如果你看下面的视频,你会看到标记乒乓的红圈是如何闪烁的。这是因为它每 2 帧才发现 1 次。在 60 fps 时,这真的无关紧要。

乒乓检测显示为红色
用于反弹预测的光线投射
在这一点上,我们已经有了一个可以工作的人工智能。如果我们只是移动球员的球拍,使它和乒乓球在同一个 y 轴位置,它做得相当好。然而,当乒乓球反弹时,它确实会遇到问题。球拍太慢了,跟不上,需要预测乒乓球的位置,而不是移动到它现在的位置。这已经在上面的剪辑中实现了,但下面是两种方法的比较。

并排的两个 AI 选项。左为简单跟随,右为光线投射反弹预测
差别不是很大,但在正确的人工智能下,这绝对是一场更稳定的胜利。为此,我首先为 pong 创建了一个位置列表。为了平均起见,我将这个列表的长度保持在 5,但或多或少都可以。可能不想要更多,否则它需要更长的时间来发现它已经改变了方向。得到位置列表后,我使用简单的矢量平均来平滑并获得方向矢量——如绿色箭头所示。这也被标准化为一个单位向量,然后乘以一个长度,以便可视化。
投射光线只是这种方法的一种延伸——使正向投影变得更长。然后我检查未来的头寸是否在顶部和底部区域的边界之外。如果是这样,它只是将位置投射回游戏区域。对于左侧和右侧,它会计算与踏板 x 位置的交点,并将 x 和 y 位置固定到该点。这可以确保桨对准正确的位置。没有这一点,它往往会走得太远。下面是定义预测乒乓未来位置的光线的代码:
def pong_ray(pong_pos, dir_vec, l_paddle, r_paddle, boundaries, steps = 250):
    future_pts_list = []
    for i in range(steps):
        x_tmp = int(i * dir_vect[0] + pong_pos[0])
        y_tmp = int(i * dir_vect[1] + pong_pos[1])
        if y_tmp > boundaries[3]: #bottom
            y_end = int(2*boundaries[3] - y_tmp)
            x_end = x_tmp
        elif y_tmp < boundaries[2]: #top
            y_end = int(-1*y_tmp)
            x_end = x_tmp
        else:
            y_end = y_tmp
        ##stop where paddle can reach
        if x_tmp > r_paddle[0]: #right
            x_end = int(boundaries[1])
            y_end = int(pong_pos[1] + ((boundaries[1] - pong_pos[0])/dir_vec[0])*dir_vec[1])
        elif x_tmp < boundaries[0]: #left
            x_end = int(boundaries[0])
            y_end = int(pong_pos[1] + ((boundaries[0] - pong_pos[0]) / dir_vec[0]) * dir_vec[1])
        else:
            x_end = x_tmp
        end_pos = (x_end, y_end)
        future_pts_list.append(end_pos)
    return future_pts_list
在上面的计算中,可能不太明显的是确定桨片到达目标的左右位置的截距。我们基本上是通过下图所示的相似三角形和等式来实现的。我们知道在边界中给出的桨的 x 位置的截距。然后,我们可以计算 pong 将移动多远,并将其添加到当前的 y 位置。

桨式瞄准拦截位置的计算示意图
这些桨虽然看起来是直的,但实际上有一个弯曲的反弹面。也就是说,如果你用球拍向两端击球,球会反弹,就好像球拍是倾斜的一样。因此,我允许球拍打在边缘,这增加了人工智能的攻击性,导致乒乓球飞来飞去。
结论
答虽然是为 pong 的特定实现而设计的,但相同的概念和代码可以用于任何版本——它只是改变了一些预处理步骤。当然,另一种方法是通过强化学习或只是简单的 conv 网来使用机器学习,但我喜欢这种经典的方法;至少在这种情况下,我不需要强大的通用性或困难的图像处理步骤。正如我提到的,这个版本的 pong 是双人的,老实说,我不能打败我自己的 AI…

如果你在这篇文章的任何部分提供了一些有用的信息或一点灵感,请关注我。
你可以在我的 github 上找到源代码。
链接到我的其他帖子:
- 《我的世界》测绘仪 —计算机视觉和光学字符识别从截图和绘图中抓取位置
Dask 和 PyTorch 的计算机视觉
免责声明:我是 Saturn Cloud 的高级数据科学家,Saturn Cloud 是一个使用 Dask 实现 Python 易于使用的并行化和扩展的平台。
将深度学习策略应用于计算机视觉问题,为数据科学家打开了一个可能性的世界。然而,要大规模使用这些技术来创造商业价值,需要大量的计算资源可用——而这正是土星云要解决的挑战!
在本教程中,您将看到使用流行的 Resnet50 深度学习模型在土星云上使用 GPU 集群进行大规模图像分类推理的步骤。使用 Saturn Cloud 提供的资源,我们运行任务的速度比非并行方法快 40 倍!

在今天的例子中,我们将对狗的照片进行分类!
你将在这里学到:
- 如何在 Saturn Cloud 上设置和管理用于深度学习推理任务的 GPU 集群
- 如何在 GPU 集群上使用 Pytorch 运行推理任务
- 如何在 GPU 集群上使用 Pytorch 使用批处理来加速您的推理任务
设置
首先,我们需要确保我们的图像数据集可用,并且我们的 GPU 集群正在运行。
在我们的例子中,我们已经将数据存储在 S3 上,并使用[s3fs](https://s3fs.readthedocs.io/en/latest/)库来处理它,如下所示。如果你想使用同样的数据集,这是斯坦福狗的数据集,可以在这里找到:http://vision.stanford.edu/aditya86/ImageNetDogs/
要设置我们的 Saturn GPU 集群,过程非常简单。
【2020–10–15 18:52:56】信息— dask-saturn |星团准备就绪
我们没有明确说明,但是我们在集群节点上使用了 32 个线程,总共有 128 个线程。
提示:单个用户可能会发现您想要调整线程的数量,如果您的文件非常大,则减少线程数量——同时运行大型任务的太多线程可能需要比您的工作人员一次可用的内存更多的内存。
这一步可能需要一段时间才能完成,因为我们请求的所有 AWS 实例都需要启动。在最后调用client会监控旋转过程,并让你知道什么时候一切准备就绪!
GPU 能力
此时,我们可以确认我们的集群具有 GPU 功能,并确保我们已经正确设置了一切。
首先,检查 Jupyter 实例是否具有 GPU 功能。
torch.cuda.is_available()
真实
太棒了——现在让我们也检查一下我们的四个工人。
client.run(lambda: torch.cuda.is_available())
在这里,我们将“设备”设置为 cuda,这样我们就可以使用这些 GPU。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
注意:如果你需要一些帮助来建立如何运行单个图像分类,我在 github 上有一个 扩展代码笔记本,可以给你那些说明以及其余的内容。
推理
现在,我们准备开始做一些分类!我们将使用一些定制的函数来高效地完成这项工作,并确保我们的工作能够充分利用 GPU 集群的并行性。
预处理
单一图像处理
这个函数允许我们处理一个图像,但是当然我们有很多图像要处理!我们将使用一些列表理解策略来创建我们的批次,并为我们的推断做好准备。
首先,我们将 S3 文件路径中的图像列表分成定义批处理的块。
s3fpath = 's3://dask-datasets/dogs/Images/*/*.jpg'batch_breaks = [list(batch) for batch in toolz.partition_all(60, s3.glob(s3fpath))]
然后我们将每个文件处理成嵌套列表。然后我们将稍微重新格式化这个列表设置,我们就可以开始了!
image_batches = [[preprocess(x, fs=s3) for x in y] for y in batch_breaks]
注意,我们已经在所有这些上使用了 Dask delayed装饰器——我们还不想让它实际运行,而是等到我们在 GPU 集群上并行工作的时候!
格式化批次
这一小步只是确保图像批次按照模型期望的方式组织。
运行模型
现在我们准备做推理任务了!这将有几个步骤,所有这些步骤都包含在下面描述的函数中,但是我们将通过它们进行讨论,以便一切都很清楚。
我们在这一点上的工作单位是一次 60 个图像的批次,这是我们在上一节中创建的。它们都整齐地排列在列表中,以便我们可以有效地使用它们。
我们需要对列表做的一件事是“堆叠”张量。我们可以在过程的早期这样做,但是因为我们在预处理中使用了 Dask delayed decorator,我们的函数实际上直到过程的后期才知道它们正在接收张量。因此,我们也通过把它放在预处理之后的函数中来延迟“堆栈”。
现在我们把张量堆叠起来,这样就可以把批次传递给模型了。我们将使用非常简单的语法来检索我们的模型:
方便的是,我们加载库torchvision,它包含几个有用的预训练模型和数据集。我们就是从那里拿到 Resnet50 的。调用方法.to(device)允许我们将模型对象分配给集群上的 GPU 资源。
现在我们准备运行推理!它在同一个函数中,风格如下:
我们将图像堆栈(只是我们正在处理的批处理)分配给 GPU 资源,然后运行推理,返回该批处理的预测。
结果评估
然而,到目前为止,我们得到的预测和事实并不是人类真正可读或可比的,所以我们将使用下面的函数来修正它们并得到可解释的结果。
这将从模型中获取我们的结果,以及一些其他元素,以返回好的可读预测和模型分配的概率。
preds, labslist = evaluate_pred_batch(pred_batch, truelabels, classes)
从这里开始,我们就快完成了!我们希望以一种整洁的、人类可读的方式将我们的结果传递回 S3,所以函数的其余部分会处理这一点。它将迭代每个图像,因为这些功能不是批处理。is_match是我们的自定义功能之一,您可以在下面查看。
把它们放在一起
现在,我们不打算手工将所有这些函数拼凑在一起,而是将它们组装在一个单独的延迟函数中,它将为我们完成这项工作。重要的是,我们可以将它映射到集群中的所有映像批次!
在群集上
我们真的已经完成了所有的艰苦工作,现在可以让我们的功能接手了。我们将使用.map方法来有效地分配我们的任务。
通过map,我们确保我们所有的批次都能应用该功能。使用gather,我们可以同时收集所有结果,而不是一个接一个地收集。用compute(sync=False)我们返回所有的期货,准备在我们需要的时候被计算。这可能看起来很艰巨,但是这些步骤是允许我们迭代未来所必需的。
现在我们实际上运行任务,我们也有一个简单的错误处理系统,以防我们的任何文件被搞乱或出现任何问题。
评价
当然,我们希望确保从这个模型中获得高质量的结果!首先,我们可以看到一个结果。
{'name': 'n02086240_1082 ',
' ground _ truth ':' Shih-zi ',
'prediction': [(b"203:'西高地白梗',",3.0289587812148966e-05)],
'evaluation': False}
虽然我们在这里有一个错误的预测,但我们有我们期望的那种结果!为了做更彻底的检查,我们将下载所有的结果文件,然后检查有多少有evaluation:True。
检查的狗狗照片数量:20580
分类正确的狗狗数量:13806
分类正确的狗狗比例:67.085%
不完美,但整体效果不错!
比较性能
因此,我们在大约 5 分钟内成功地对超过 20,000 张图片进行了分类。这听起来不错,但有什么选择呢?

技术/运行时间
- 无集群批处理/ 3 小时 21 分 13 秒
- 带批处理的 GPU 集群/ 5 分 15 秒
添加一个 GPU 集群会带来巨大的不同!
结论
正如这表明的那样,你当然能够在单节点计算中进行深度学习推理,比如在笔记本电脑上,但当你这样做时,你会等待很长时间。GPU 集群为您提供了一种大幅加速工作流的方式,使您能够更快地迭代并改进业务或学术实践。试想:如果您想每小时对您的图像进行推理,该怎么办?在这种情况下,单个节点根本无法工作,因为作业甚至无法在一小时内完成!
GPU 通常被视为机器学习任务的一个极端选择,但在现实中,在 GPU 可能的加速下,您通常可以降低人力时间和美元的总体成本。AWS 的一个 CPU 实例不是免费的,在那个实例上三个小时的计算成本会增加。在许多情况下,为了获得相同的结果,这可能比您在 GPU 集群上花五分钟的时间还要多。
在土星云,我们希望为数据科学社区提供最优质的计算资源,我们希望帮助每个人成为更有效的机器学习实践者。您可以了解更多关于我们和上述工具的信息(并免费试用我们的平台!)在我们的网站。
计算机视觉——使用卷积、池化和张量流创建分类器
计算机如何识别图像,以及如何在分类中使用它

本文旨在解释扁平化、卷积和汇集思想背后的直觉,以及在创建图像分类器时如何在 TensorFlow 中使用这些概念。
使用的技术有 python,TensorFlow 和 Keras API。
使用的数据集:
时尚 MNIST——【https://www.kaggle.com/zalando-research/fashionmnist
关于将用于说明关键概念的数据集的一些信息:
名称:时尚 MNIST
观察次数: 70000
类别数量: 10
类别的标签和名称:
0 - T 恤/上衣
一条裤子
两件套衫
3 -连衣裙
四层涂层
5 -凉鞋
6 件衬衫
7 -运动鞋
8 袋
9 -踝靴
随机样本:

集合中图像的随机样本
让我们从检查来自时尚 MNIST 数据集的图像开始:

28 x 28 t 恤图片
这是一个灰度(意味着它只有一个像素通道)图像,宽 28 像素,高 28 像素。我们只能看到图片的渲染形式,但图像本身是以数字矩阵的形式存储在计算机内存中的:

图片的矩阵表示
典型的像素值从 0 到 255,其中 0 为白色,255 为黑色。
计算机“看到”图像的方式仅仅是通过上面表示的像素值。即使是最先进的图像识别算法也只是将像素转换成数字数组。
基本上,当我们谈论计算机视觉时,我们谈论的是计算机学习数字矩阵和标签之间的关系。
当试图构造图像分类器时,最基本的方法是使用图像中的每个像素作为单独的特征。这个过程叫做展平。

拉平像素
在上面的图片中,我画了一个假想的笑脸图片,总共有 36 个像素。白色像素等于 0,黑色像素等于 1。
扁平化的过程只不过是将二维图片(或者更高维,如果图片有颜色的话)转换成一维向量。我假设数组从 0 开始,所以第 7 个坐标等于 1(代表[2][2]像素),第 10 个坐标等于 1(代表[2][5]像素),依此类推。展平的最终输出是一个 36 维向量。
一般规则是,如果输入是 n×m 像素化的图像,那么输出将是 n×m 长度的向量。
如果每张图片都有一个相关联的标签,比如“快乐”(=1)和“不快乐”(=0),那么模型的矩阵 X 和 Y 的构造就很简单:

从图片中创建矩阵
这个过程是将每张图片展平,并将每个矢量一个叠一个地堆叠起来。所以现在,我们可以开始建立模型,因为我们已经创建了代表图片的数字矩阵,各种优化算法可以开始工作了。
在上面画的例子中,如果我们有 100 张图片,那么 X 矩阵将有 100 行和 36 列(特征),Y 向量的长度将是 100。对于这样的小图片,像逻辑回归这样的技术在预测未来的图像标签时可以做得很好。然而,在本文中,我将只使用深度学习模型。
让我们使用时尚 MNIST 数据集和一个非常简单的深度学习模型,并为此使用 flatten()层:

模型拟合和测试
该模型在训练集和测试集中的准确率分别达到了 91.5% 和 88.3% 。
虽然仅使用展平层作为像素的主要预处理器工作良好,但它有其缺点。
首先,当图片从 28 x 28 像素增长到更真实的尺寸时,如 256 x 256、512 x 512 等等,每张图片的特征矩阵迅速扩展:

X 矩阵的指数增长
因此,即使对于现代机器来说,训练一个具有数百万特征的前馈网络也是非常昂贵的。
第二,当我们使用单个像素作为特征时,图片的轻微变化,如人脸不在中心,各种相机旋转,背景的变化都可能以戏剧性的方式扭曲特征矩阵。这将导致创建在测试集上推广良好的分类器非常困难。
第三,在大多数图片中,有大量的背景噪声,这不会以任何方式提高分类器的决策。例如,在时尚 MNIST 数据集中,背景中有许多白色像素,这只是扩展了特征矩阵,而没有向神经模型添加太多信息。
所有这三个问题都可以通过在神经网络中增加两层来解决——卷积和池化。
TensorFlow 官方文档中的卷积层可以在这里找到:https://www . tensor flow . org/API _ docs/python/TF/keras/layers/Conv2D/。
卷积是一种简单的数学运算,是许多常见图像处理运算符的基础。卷积提供了一种将两个数字数组“相乘”的方法,这两个数组通常大小不同,但维数相同,从而产生第三个维数相同的数字数组。这可以用在图像处理中,以实现其输出像素值是某些输入像素值的简单线性组合的算子。
下面的动画很好地理解了卷积运算:

回旋在行动
左边的块代表具有特定像素值的图像,中间的小 3x3 块通常被称为滤镜。
最终值是通过一个简单的公式计算出来的——滤镜在图像上滑动,滤镜中的值乘以适当的像素值并求和。例如,结果块中的第一个值由以下公式计算:
**7 * 1+0 * 2+-1 * 3+1 * 4+5 * 0+3 -1+3 * 1+3 * 0+2 -1 = 6
不同的滤镜会突出显示图像中的不同属性。一些过滤器强调水平线,另一些强调垂直线和其间的任何东西。
比如一个滤镜:
【0 1 0】
【1-4 1】
【0 1 0】
会突出照片的边缘。python 中的一个例子:

原图

应用滤镜后的图片
张量流卷积层创建固定大小的随机滤波器,然后在训练阶段,算法决定如何改变滤波器值以尽可能优化损失函数。
在获得上一节描述的卷积特征后,我们决定区域的大小,比如说𝑚𝑥𝑛来汇集卷积特征。然后,我们将我们的卷积特征分成不相交的𝑚𝑥𝑛区域,并在这些区域上取平均(或最大)特征激活以获得汇集的卷积特征。然后,这些汇集的特征可用于分类。

联营业务
这种技术很有用,因为至少在某种程度上,它消除了图像中小像素变化的影响。例如,一只鸟的头可以在图像的中间,或者稍微偏左或偏右。
TensorFlow 官方文档:https://www.tensorflow.org/api_docs/python/tf/nn/pool
让我们将两个新层添加到我们的神经网络中:
model.summary() 输出:

模型概述
您可能会注意到,第一个卷积层的输出形状是 26 x 26 x 16,而图片的原始形状是 28 x 28。像素的损失是由于过滤的性质。回想一下第一部分的动画。图片边缘的像素仅用于计算滤波器值和丢弃值。
max-pooling 层输出的形状为 13 x 13 x 16,这是因为我们使用了(2,2)正方形,将原始图片大小缩小到原始图片的四分之一(回想一下动画)。展平层输出大小可以通过公式 13 * 13 * 16 来计算。
数字 16 等于 tf.keras.layers.Conv2D 层中定义的滤镜数。这意味着创建了 16 个独特的过滤器。因此,在网络的中心,从每一张输入图片中,有 16 张新图片被创造出来。
该模型的结果具有与更简单模型中相同的历元数:

模型的训练
池化和卷积层的加入提高了模型的训练和测试精度:训练集中的 96.08% ,测试集中的 91.4% 。
为了更好地理解计算机如何看待图像以及图像中的哪些特征最重要,我们可以直观地检查模型中第一层(即卷积层)的输出。
为此,我将使用以下代码:
对于 t 恤衫:


t 恤的卷积层
对于鞋子:


鞋子的回旋层
对于 t 恤,我们可以看到 t 恤边缘的直线被照亮,这意味着这些像素有助于确定类别。此外,前面的标志也是一个明显的特征。
对于鞋子,完全不同的像素被点亮。在这种情况下,鞋子顶部轮廓的水平线被照亮。
现在卷积的图像与最大池层一起滑动,变平并馈送到简单的前馈网络。
总的来说,最先进的图像分类器是使用数百万张图片训练的,在这些网络的核心,有各种池和卷积层的组合。
在获奖车型中,如
【https://keras.io/api/applications/resnet/#resnet50-function
或者
实例化异常架构。参考可选择加载 ImageNet 上预先训练的重量。请注意,数据…
keras.io](https://keras.io/api/applications/xception/)
有数百万个参数和数百个层,但是基本的构建块是卷积和池。
我希望对于基本的图像分类任务,读者可以使用我提供的代码。
编码快乐!
用于自动道路损伤检测的计算机视觉

城市道路鸟瞰图(来源)
注:本项目由 尼古拉斯·斯特恩 , 克莱尔·斯托尔兹 , 内森·爱因斯坦 , 安卡·德勒古列斯库 合著。
介绍
日益恶化的道路困扰着气候多变、预算紧张的地区。市政府在对热点地区进行分类和定位以解决问题时,如何保持领先是一个持续的挑战。在美国,大多数州仅采用半自动方法来跟踪道路损坏情况,而在世界其他地区,该过程完全是手动的,或者完全被放弃。收集这些数据的过程既昂贵又耗时,而且必须以相对较高的频率进行以确保数据是最新的。这就引出了一个问题:计算机视觉有帮助吗?
对于我们哈佛大学数据科学硕士项目的顶点项目,我们旨在与我们的行业合作伙伴 Lab1886 一起深入研究这个问题。特别是,我们寻求以下问题的答案:
我们能否通过利用汽车仪表盘上智能手机拍摄的原始视频片段,自动检测道路损坏的严重程度并进行分类?这样做需要克服哪些技术挑战?
这篇文章介绍了我们解决这个任务的方法,强调了我们在这个过程中遇到的意外问题。我们希望与更广泛的数据科学社区分享这些信息,以便为那些致力于类似目标的人提供信息并做好准备。
目前的技术水平

图 1:现有道路损坏检测论文的示例图像。
在深入我们自己的实现之前,我们调查了当前的技术水平,以了解其他人已经完成了什么。根据我们的文献综述,我们发现大多数方法可以分为以下几类:
- 3D 分析:使用立体图像分析或激光雷达点云检测路面异常情况。
- 基于振动的分析:利用车载加速度计或陀螺仪。
- 基于视觉的模型:从边缘检测等传统技术&光谱 segmentation⁴到通过卷积神经网络(CNN)进行表示学习和分割。⁵
由于我们的主要任务是固有的视觉,并且无法访问激光雷达或振动数据,我们选择专注于基于视觉的算法,特别是监督学习方法。
我们早期注意到的一个问题是,相关研究主要依赖于特写图像或垂直于路面拍摄的图像。这是有问题的,因为这些图像看起来与从安装在仪表板上的摄像机流出的图像明显不同,所以不能用于训练或校准旨在摄取后者的模型。
我们的数据
我们可以访问从安装在汽车上的照相手机收集的数据集。整个数据集包括大约 27,000 张德国的道路图像,拍摄于 40 多次不同的旅行中,通常是在阳光充足和干燥的条件下。我们图像中的道路类型变化很大:一些是多车道的城市道路,周围有建筑,另一些是没有路标或建筑的乡村道路。路面也各不相同,从混凝土到沥青再到鹅卵石。以大约每秒 1 幅图像的速度连续拍摄图像。下面的图 2 中包含了一些例子。

图 2:来自 Lab1886 提供的数据集的样本图像。
数据注释困境
由于我们的数据集缺少标签,我们需要一种方法来解析每幅图像,并分割出每种道路损坏类型的相关像素,以及某种严重程度标签。手动,这是一个可怕的任务,所以我们的目标是以几种方式简化我们的生活。
首先,由于有许多不同形式的道路损坏(即鳄鱼裂缝、纵向裂缝、坑洼、补丁、油漆),我们选择缩小工作范围,仅查看油漆损坏。我们希望这不仅能使注释数据更容易处理,还能为我们以后识别其他类型的道路损坏提供信息。
其次,我们尝试使用预先训练的分类模型筛选出没有油漆损坏的图像。特别是,我们从前田等人的 al.⁶公司提取了两个预训练模型——在 10,000 多张图像上训练的分类器,以识别 8 种不同类型的道路损坏(包括磨损的油漆线)和边界框。这些模型很难推广到我们的数据集。看看图 3 中的小提琴图,我们可以看到,无论是否存在油漆损坏,模型预测的分布几乎是相同的。

图 3:来自 Maeda 等人、 MobileNet-SSD 和 Inception-SSD 的模型的小提琴图。这些图表明,再多的参数调整也无法帮助模型区分油漆损坏的存在与否。
第三,我们尝试通过 Mechanical Turk (MTurk)众包注释,这是亚马逊提供的一项服务,参与者可以执行简单的任务,以换取金钱补偿。我们的任务是:通过突出显示该区域并从下拉菜单中选择相应的严重性标签来注释图像中的油漆损坏。我们选择了以下简单的严重性等级:
- 1 —轻度损坏
- 2 —中度/中度损坏
- 3 —严重损坏
图 4 提供了一个 MTurk 注释接口的例子。我们用 200 幅图像的样本进行了几次试点实验,每次都修改指令,以纠正我们在之前的实验中观察到的不必要的行为。我们至少有三名工人为每张图片贴标签,以衡量贴标机的一致性。

图 MTurk 注释接口的例子。
即使在我们第三次迭代说明之后,工人们在注释什么和如何注释上仍有分歧。我们使用联合交集(IoU)“一致性”分数来量化同一图像的不同贴标机之间的一致性,如图 5 所示。根据协议分布,大多数标注者在他们的注释中根本没有重叠。这是一个重要的迹象,表明对非专业人员来说,始终如一地标注油漆损坏是一项意想不到的困难任务。
因此,我们最终选择自己标记数据。总的来说,我们注释了 1,357 幅图像,包括每个严重性级别的至少 300 个实例。

图 5:通过 MTurk 标记的图像的贴标机一致性分数的分布(通过对每个单独图像的不同注释取并集的交集来计算)。这显示了任务的高度主观性,以及众包它很难的原因。
建模
我们的核心任务是回答两个问题:
- 损伤在哪里?
- 有多糟糕?
不确定从建模的角度来看哪个问题更难回答,我们采用了两种方法。我们最初的方法是一个多阶段的方法,用两个不同的模型分别解决每个问题。
我们首先需要一种分割算法来识别输入图像中存在油漆损坏的区域。为了激励深度学习的使用,我们尝试了一些传统的计算机视觉技术,看看它们是否能够充分掩盖绘画。我们探索了阈值处理、分水岭分割和简单线性交互聚类(SLIC ),所有这些都需要大量的手动超参数调整,并且无法在多个图像之间进行推广。图 6 显示了这些方法在一个示例图像上的结果。

图 6:三种图像分割算法在我们的数据集中的单个图像上运行的结果。
因此,我们转向流行的卷积编码器-解码器网络,U-Net⁷,来执行单通道语义分割。模型的输出是每个像素的预测概率,即它是否代表油漆损坏。
理论上,我们将对预测进行阈值处理,以生成从输入图像中提取受损区域的掩模,然后将其输入分类器,以预测严重性标签。在实践中,我们使用基础事实注释来准备严重性分类模型的输入,认识到分段模型可能表现不佳的可能性。这样,我们能够分别评估分段和严重性分类的难度。我们使用的分类器是基于 ResNet18 架构的 CNN。

图 7:左图:原始图像。中/右:传递到我们的分类器模型中的相应屏蔽输入。
我们在总图像的 15%的测试集上评估了我们的每个模型。单类语义分割模型的输出示例如图 7 所示。与传统的计算机视觉方法相比,一个巨大的改进是该模型学会了分割油漆线(见图 8)。然而,该模型倾向于过度预测油漆损坏的存在,正如图 9 中显示的像素级精度和召回曲线所揭示的那样。

图 8:左图:原图。中间:地面真相面具。右图:单通道分段模型的阈值输出。
严重性分类器的结果显示,网络能够在一定程度上区分低和高严重性油漆损坏,但难以区分低和中等严重性损坏(见图 10)。这是一个危险信号,可能标记的低度和中度损害的实例彼此过于相似,因为模型对两者做出了相似的预测。考虑到我们自己在区分轻度和中度损伤时的困难程度,这个结果并不令人惊讶。

图 9:作为概率阈值函数的单通道分割模型的像素级精度和召回率。该模型预测,随着阈值的提高,损害会减少。

图 10:严重性分类网络的混淆矩阵。每一类的预测准确率如下:1–74.5%,2–5.9%,3–54.2%,总体:45%。
作为建模的第二种方法,我们调整了我们的 U-Net 以执行多类分割,现在除了包含所有无损坏像素的“背景”遮罩之外,还为每个严重性级别生成一个遮罩。一些预测的例子如图 11 所示。

图 11:多类分割模型的示例输出。从左至右:严重性等级 1、2 和 3 的输入、目标和像素级预测。
多级分割模型的性能与多级方法的分类器非常相似,因为它能够部分地将低严重性油漆损坏与高严重性油漆损坏区分开来,但对低严重性和中等严重性级别做出了相似的预测。这在图 12 中表现得最为明显。

图 12:作为概率阈值函数的多类分割模型的交集/并集。
多类模型的性能对预测阈值非常敏感,即在我们将该像素指定为“受损”之前,该模型在其逐像素预测中必须具有的确定性鉴于该模型在低和中等损害等级之间的不确定性,它倾向于给这两个等级分配非常低的概率。高于 20%的确定性阈值时,我们的多类分割模型仅预测高严重性损害。然而,较低的阈值导致了对受损区域的过度预测。这样,区分严重程度的困难就与识别损伤存在的困难混为一谈了。这支持了我们的假设,即从建模的角度来看,多阶段方法可能更有利于阐明任务的哪些方面最具挑战性。
关键要点
1.普遍性
理想情况下,我们的模型对于在新地点、从不同角度、在不同光照条件或天气下获取的新数据保持准确。我们发现,对于道路损坏检测,概化是一个真正的挑战。特别是,Maeda 等人的模型根本不能推广到我们的数据集。虽然日本的道路和德国的道路确实有细微的系统差异(德国的道路通常更宽,颜色更浅),但我们的工作表明,任何现有的模型都需要大量的再培训和调整,才能处理新的数据。此外,我们已经证明了用复杂的神经网络进行表征学习是必要的,因为简单的计算机视觉方法无法做到这一点。
在你说“咄”之前,事实上,当你考虑到已经注释道路损坏的公开可用数据的缺乏时,这是一个大问题。上面带有严重性标签的数据是不存在的。由于数据收集和注释的艰巨性,对于没有资源获取自己的数据或雇佣受过培训并具有专业知识的人员来构建复杂模型的地方政府来说,拥有预先训练的可概括模型将是一笔巨大的资产。
2.嘈杂的注释
我们的注释目标看似简单明了:识别受损的油漆,并给它分配 1、2 或 3 的严重性分数。然而,当我们开始回顾 MTurk 的结果时,我们发现事情并不那么简单。
即使贴标签机提供了极其详细的说明和大量的例子,工人之间也很少有一致意见。出现的一些意想不到的问题是:
- 应该突出显示整条油漆线,还是只突出显示虚线部分?
- 这里应该有油漆吗?
- 我们应该标记多远的距离?
- 损伤周围的“缓冲”区域有多少应该被注释以提供建模的背景?
即使在讨论了这些要点并自己标注了数据之后,我们还是目睹了几个矛盾的例子,说明什么构成了低与中等严重程度损害的实例。因此,我们建议研究人员将他们的严重程度分成满足他们要求的尽可能少的类别。我们怀疑这是我们的模型学会区分极端情况,但无法区分低度和中度严重程度的关键原因。为了减少这种错误,我们需要更一致的标签(也许由专家),更多的数据,或者更少的严重性等级。
3.模型评估
分割模型的定量评估是微妙的。首先,任何与地面真实遮罩的比较都会受到两个噪声源的影响:
- 来自不一致注释的非故意噪音(对我们来说是一个真正的问题)。
- 在注释过程中,围绕绘画高亮显示的场景上下文的数量。
为了说明第二点,考虑 IoU 指标。假设我们有一个完美的模型,只分割绘画线条,在突出场景上下文中的注释越自由,IoU 分数就越低。
另一种评估选择涉及以像素或图像为单位计算精度和召回率(即,可以对每个像素进行预测或对每个图像进行预测)。为了将像素级预测映射到图像,我们认为图像中任何正像素预测的存在都是该图像的正预测。哪个信息量更大?此外,任何精度和召回率的计算必须由最终用户希望模型有多保守来限定。
请注意,我们用于评估模型的指标并不构成一个详尽的列表。我们的建议是使用一套以像素和图像为单位的指标来了解模型在不同特异性水平上的表现。
结束语
深度学习模型在精选数据集上表现非常好,但在非结构化数据上还有改进的空间。当应用计算机视觉模型来执行自动道路损坏检测时,必须考虑的一些重要因素包括:
- 如何正确地对不同类型的损害进行分层。
- 如何保证标注一致?
- 几百万参数的深度学习模型需要多少标注才能有效学习:(1)哪里有损伤,和 (2)有多坏。
- 如何有效地评估一个细分模型,考虑注释是如何制作的以及最终用户是谁。
我们的贡献是概述这些挑战,并证明即使只有有限的数据和嘈杂的标签,我们的模型也能够学习分割油漆线,并开始分离严重程度的极端例子。建模能力是存在的;瓶颈是数据。
确认
我们要感谢我们的行业合作伙伴 Lab1886 给我们这个机会。此外,我们要感谢我们的导师,帕夫洛斯·普罗托帕帕斯博士和克里斯·坦纳博士,以及顶点课程的学生和教师在注释我们的图像方面给予的帮助。有关哈佛数据科学峰会的更多信息,请访问:capstone.iacs.seas.harvard.edu。
参考
[1] R. Fan,M. Liu,(2019)【IEEE 智能交通系统汇刊】
[2] S. Chen 等, 3D 激光雷达扫描桥梁损伤评估 (2012),法医工程 2012:通向更安全的明天
[3] S. Sattar 等人,使用智能手机传感器进行路面监控:综述 (2018),传感器(瑞士巴塞尔)
[4] E. Buza 等,基于图像处理和谱聚类的坑洞检测 (2013),第二届信息技术与计算机网络国际会议论文集
[5] J. Singh,S. Shekhar,使用 Mask 的智能手机捕获图像中的道路损坏检测和分类 R-CNN (2018), arXiv 预印本 arXiv:1811.04535
[6] H. Maeda,et al., 利用智能手机捕获的图像使用深度神经网络进行道路损伤检测 (2018), Comput。辅助民用基础设施。英语。
[7] O. Ronneberger 等, U-net:卷积网络用于生物医学图像分割 (2015),医学图像计算和计算机辅助介入国际会议
计算机视觉:全景拼接背后的直觉

田纳西州了望山顶的景色(1864 年)来源:https://en.wikipedia.org/wiki/Panoramic_photography
全景拼接的工作原理有 4 个主要部分。在这篇文章中,我将给出一个非常简短的概述,能够充分(希望)建立图像拼接工作背后的直觉。这也意味着我很可能会跳过任何数学概念和计算。
- 探测兴趣点
- 描述这些兴趣点
- 匹配我们兴趣点的这些描述符
- 执行单应完成拼接
1.检测兴趣点
当我们在图像中寻找兴趣点时,有几个特征。它们是:
a)可重复的兴趣点
我们希望能够找到图像中的特征,这些特征最终可以告诉我们同一场景的不同图像之间的匹配位置(从附近的视点)。
b)兴趣点的独特性
我们希望能够可靠地确定一幅图像中的哪个兴趣点与另一幅图像中的相应兴趣点相匹配。
c)缩放和旋转不变性
我们希望能够找到相同的兴趣点,即使图像被旋转、缩放或平移。
d)地点
局部特征将使我们对感兴趣点的检测对杂乱和遮挡更加鲁棒。


图一。我的 mac 默认背景截图。
想象上面的两张图片(图 1),很容易看出,如果我们选择海洋作为兴趣点,很难将左侧图像中的海洋与右侧图像中的海洋特别匹配(因为海洋在大范围的空间中看起来是相同的)。我们还注意到,选择诸如小石峰(橙色圆圈)的区域能够为我们以后的匹配提供更有价值的信息。
事实证明,角实际上是一个非常好的特征,可以作为兴趣点!有一种叫做 Harris 角点检测器的算法,可以帮助我们在图像中找到这样的角点作为兴趣点。

哈里斯角探测器
注意,哈里斯角点检测器只是帮助我们找到这些兴趣点的众多算法之一。还有其他方法,例如 SIFT,它使用高斯差分(DoG)来检测不同尺度的兴趣点。我个人认为这篇文章很值得一读。在后面的部分中理解 SIFT 很重要,因为我们将使用 SIFT 描述符来描述我们发现的兴趣点。
本质上,在取局部最大值(非最大值抑制)的点之前,Harris 角点算法从图像的梯度计算角点分数(使用二阶矩 H 矩阵)并将高于设定阈值的值标记为角点。哈里斯角对于旋转(因为 H 矩阵的特征值即使在旋转之后也保持不变)、平移和强度的附加变化是不变的。然而,它对于强度的缩放和比例不是不变的。为了使 Harris 角点在尺度上保持不变,我们需要一个额外的自动尺度选择步骤来找到一个给出我们的角点分数的局部最大值的尺度。
总结一下从图像中检测兴趣点的第一部分,角是兴趣点的良好表示,可以使用 Harris 角检测器找到。
2.描述我们的兴趣点
描述符基本上是以数学方式描述图像中某个区域的矢量表示。描述符也应该对旋转、缩放和平移不变。我将直接进入 SIFT 描述符,我们可以在我们的 Harris 角点检测器发现的兴趣点上使用它。
尺度不变特征变换 (SIFT) 由 David Lowe 于 2004 年发表。如果他的论文太难阅读,你可以参考这里的快速阅读 SIFT。SIFT 实际上是一种检测兴趣点并描述它们算法。然而,在这种情况下,我将只关注描述符本身。

图 2(显示了缩小的版本)。鸣谢:理查德·塞利斯基
SIFT 描述符的基本功能是在检测到的兴趣点周围提取一个 16x16 的窗口(图 2),然后将其划分为一个 4x4 的单元网格。在由高斯函数加权的窗口中的每个像素处计算梯度方向和幅度。然后,在每个单元中计算具有 8 个面元的加权梯度方向直方图(方向由其幅度加权)。最后,我们将这些直方图折叠成一个 128(16×8)维的向量(每个向量有 16 个 4×4 的单元,每个单元有 8 个面元)。
单元的划分给描述符一种空间知识的感觉,而主方向对直方图宁滨的移动使得描述符旋转不变。我们可以将最终向量归一化为单位长度,基于阈值箝位值,并再次重新归一化,以使其对光照变化相对更鲁棒。
总而言之,SIFT 对于一个描述符来说是非常健壮的。它对比例和旋转是不变的,可以处理视点的变化(高达 60 度的平面外旋转)并且可以处理照明的显著变化。
现在,我们已经通过 Harris 角点找到了我们的兴趣点,并使用 SIFT 描述符将这些兴趣点描述为一个区域。现在剩下的是形成不同点之间的匹配,并执行单应性以将不同的图像缝合在一起以形成全景图!
3.匹配我们兴趣点的描述符
为了在两幅图像的描述符之间形成匹配,我们使用最佳匹配的距离/第二最佳匹配的距离的比率距离方法。


图 3。图片来自 Unsplash。作者:伊山·瓦扎尔瓦
在图 3 中,如果我们只使用目标和最佳匹配的绝对距离(这里的距离指的是描述符的相似性——如果它们非常相似,那么距离就很小,反之亦然),很容易看到有多个模糊匹配可用(这里有许多类似的栅栏作为图像中的兴趣点,因此,我们不能确定最佳匹配是要匹配的正确特征)。
为了拒绝这些不明确的匹配,我们使用比率方法—接近 1 的高值表明匹配是不明确的,因为到最佳匹配的距离非常接近到第二最佳匹配的距离。然后,我们设置一个阈值(通常在 0.5-0.7 左右),并接受低于该阈值的匹配。换句话说,我们拒绝包含高度不确定性的匹配——在图 3 的情况下,不确定性很高,因为许多木质尖端靠近目标。
4.执行单应
一旦我们能够想象当我们试图把不同的图像拼接在一起时,我们试图做什么,就很容易理解单应性。想象两个场景。
(1)-每次拍摄照片时向左移动一步,同时保持相机不动。
(2)-站在固定的位置,手持相机旋转身体,并在旋转的同时拍摄不同的照片。
如果我们想将(1)中的图像拼接在一起,我们可以简单地将一张照片叠加在序列中的另一张照片上,并获得良好的效果。然而,在(2)中,如果我们想要通过简单地将图像依次叠加在另一个的顶部来将图像拼接在一起,我们将意识到拼接的结果是不好的(由于所捕获的图像的不同平面,区域将被遗漏)。因此,我们需要单应映射将一幅图像投影到另一幅图像的同一平面上,然后将它们拼接在一起。
接下来,您可以将单应性基本上视为一个矩阵——一个将点从一个图像转换到同一平面的另一个图像的矩阵。那么下一个问题是我们如何求解单应矩阵 H?
我们使用直接线性变换(DLT) 并通过计算奇异值分解(SVD) 来求解 H,该计算最少需要 4 次对应。我将跳过数学,但如果你感兴趣的话,可以随意查阅。
然而,DLT 可以在噪声环境中产生坏的结果,因为 DLT 是线性最小二乘估计,其考虑了我们的匹配中的坏的离群值。(注意,即使我们在前面的步骤中在匹配描述符时设置了阈值,仍然有可能出现不正确的匹配)。然后,我们可以使用随机样本一致性(RANSAC)产生更稳健的结果,其中我们在 H 矩阵的计算中仅包括内联体(近似正确的匹配)。然后,我们利用 DLT 和 RANSAC 给我们更好的结果。
一旦我们解决了单应矩阵,我们就可以使用矩阵计算从图像 A 到图像 B 的点,并很好地扭曲它以完成我们的全景拼接!
结论
在这里,我简要介绍了全景拼接的工作原理。具体来说,我谈到了使用 Harris 角点来检测作为兴趣点的角点,使用 SIFT 描述符来描述我们的兴趣点周围的区域,我们如何匹配这些描述符,以及我们如何计算单应性来形成不同图像的拼接。请注意,由于这是一个简短的概述,我有意省略了数学细节,并试图不要深入每个特定的算法/概念,因为我的目标是建立整个概念背后的直觉!
还有,这其实是我第一篇关于 medium 的文章!我希望你喜欢这篇文章,并从这篇文章中学到了一些东西。谢谢!
参考
- https://aishack.in/tutorials/harris-corner-detector/
- https://ai shack . in/tutorials/sift-scale-invariant-feature-transform-introduction/
- https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf
- 【https://en.wikipedia.org/wiki/Direct_linear_transformation
- https://en.wikipedia.org/wiki/Singular_value_decomposition
感谢阅读!我希望你喜欢它,这篇文章对你有帮助!
计算机视觉——通过人工智能自我学习进行物体定位
我希望我有一个机器人可以扫描我的房子,找到我丢失的钥匙
随着像脸书这样的公司在增强现实上花费数百万美元,在不久的将来,简单的眼镜将取代世界上几乎每一个屏幕,包括电视、手机,并将我们所有的环境转换成现实和屏幕的混合物。
我相信我的房子里有一个喜欢每天早上吃掉所有钥匙的洞,这是每次我上班迟到时它们从地球上消失的唯一原因。
但我仍然会等待有一天,我可以让我的眼镜或机器人扫描我的房间,找到我每天早上都在努力寻找的钥匙或我在房子某个地方丢失的耳机。
尽管每天都在不停地挣扎,但我决定为未来可能的机器人创建一个小小的后端项目,机器人/人工智能可以扫描房子的周围,为我找到东西。尽管:
- 问题的第一部分是告诉计算机一个物体看起来像什么,所以计算机知道实际上要找什么,所以我选择了最明显的方法,我们都做的检查一个物体看起来像什么,即通过在谷歌上搜索,所以当我告诉机器人在房间里找到我的钥匙或 AirPods 时,首先 Ai 会进行谷歌搜索,看看钥匙或 AirPods 实际上看起来像什么。
- 问题的第二部分是让人工智能意识到物体在图像中的位置,人工智能可以自动学习并决定物体的实际形状和大小,并可以在图像中定位物体。为了解决这个问题,我实现了最小生成树聚类的研究工作——你可以在这里找到研究论文——>http://cs.brown.edu/people/pfelzens/segment/
- 问题的第三部分是教 Ai,如何定制训练一个物体检测 YOLO 模型,——所以不管我要求什么,不管是钥匙,AirPods,还是我丢失的 u 盘 AI 将自动—
 1 谷歌搜索对象的图像
 2 从这些图像中生成训练数据
 3 标记/标注图像中的对象
 4 写下 YOLO 模型所需的所有适当的注释和文本文件
 5 编辑具有适当配置的 YOLO·YAML 文件
 6 训练图像,生成推理图,然后机器人将最终知道“钥匙”是什么,因此它可以打开摄像机并开始寻找它

自学模式的结果
这是完整自动化过程的算法-
1 Google 通过 AI 搜索物体的图片 -
在 Colab 环境中安装 Google chrome 和 Selenium
制作一个数据框,从谷歌获取前 200 个图像结果。
这里计算机/机器人会问你想看什么,我用了键,然后得到 200 个图像
最小生成树聚类 通过这种聚类分割,Ai 将理解对象在图像中的位置,并通过在它周围制作一个方框来标记它,以创建一个训练数据,实现自研究论文http://cs.brown.edu/people/pfelzens/segment/


人工智能标记的对象
现在物体被标记了,是时候找到 Ai 在物体周围创建的黄色轮廓的“框”值了。我们需要找到 xmin,xmax,ymin,ymax,这样就可以为 YOLO 对象模型编写注释了
Ai 自/自动 Yolo 模型训练和注释编写
为了得到物体周围盒子的 xmin、xmax、ymin 和 ymax,我们需要找到物体周围所有黄色的像素点,然后从所有这些点,我们可以很容易地得到我们的坐标。
按照下面我创建的代码,Ai 将首先在我们的训练数据中寻找每个图像中对象周围的所有黄色点,然后用坐标创建一个熊猫数据框

要编写 YOLO 注释,人工智能必须遵循一定的格式,人工智能需要在包含图像的同一文件夹中创建每个图像的文本文件,然后人工智能必须创建一个包含所有图像路径的 train.txt 文件。
image text file formula and fromat
<class_number> (<absolute_x> / <image_width>) (<absolute_y> / <image_height>) (<absolute_width> / <image_width>) (<absolute_height> / <image_height>) 
上面的代码将会给 Ai 训练一个定制的 YOLO 物体检测模型所需的所有文本文件
Ai 将在 YOLO 上训练数据集
现在,人工智能将从图像和标记的注释中学习,一把钥匙或任何其他物体看起来是什么样子,
使用下面的代码,Ai 正在做三件事
1 安装 Yolo API
2 将 train.txt 拆分成训练和测试数据
3 打开 Yolo 模型的 YAML 文件并编辑所有必要的东西,如类的数量、训练的链接、测试数据和类名——在这种情况下,类名是键
训练数据和保存重量
打开网络摄像头并检测物体
现在,人工智能已经学会了什么是钥匙,现在我们可以将这个人工智能放入机器人或我们的增强眼镜中,然后它可以为我们扫描房间并找到钥匙。虽然眼镜或机器人还需要一些时间才能进入市场。所以现在我只是用我的笔记本电脑摄像头测试。

结论
看到 Ai 学习自己并检测物体是非常令人兴奋的,但整个笔记本需要 3 分钟来完成学习过程,这对于实际使用来说太长了。但随着量子计算的发展和并行处理的更好编码,实时自我训练和学习过程可能在未来几年内完成。虽然这个项目确保了让一个机器人去寻找你家丢失的东西不再是科幻幻想
对于,完整的 jupyter 笔记本和代码,可以通过github.com——https://github . com/Alexa Mann/Computer-Vision-Object-Location-through-Ai-Self-Learning查看我的知识库
边缘的计算机视觉
克服将 CV 应用引入生产的挑战

比尔·牛津在 Unsplash 上的照片
开发计算机视觉(CV)应用程序并将其投入生产需要集成几个硬件和软件。我们如何确保各部分无缝协作?使用正确的方法,我们可以加快 CV 应用程序的开发和部署。有必要找到一个平台,其目标是帮助开发人员使用一套集成的免费工具快速、轻松地从头开始创建计算机视觉应用程序。本文描述了开发和部署 CV 应用程序的一些挑战,以及如何减轻这些挑战。如果您想了解更多信息,可以观看关于计算机视觉部署挑战的免费网络研讨会。
世界上的计算机视觉
首先,一些背景。术语“计算机视觉”是指计算机能够像人类一样分析视觉数据,并对数据包含的内容做出推断的过程。当集成到应用程序中时,这些推理可以转化为可操作的响应。例如,以自动驾驶汽车为例:计算机视觉可以用来分析实时视频数据,以检测道路上的物体,然后运行计算机视觉模型的软件可以对这种检测做出反应,以停止汽车或改变其路径来避开物体。
CV 越来越多地被用来解决各种现实世界的问题,从安全和医疗保健到制造业、智能城市和机器人。CV 可用于检测放射报告中的癌细胞,帮助分析身体运动,如正确的步态和姿势,或跟踪生产线上的生产。
在本文的其余部分,我们将探讨构建和部署安全摄像机 CV 应用程序的挑战。您可以找到已经构建在 GitHub 上的这种应用程序的实现。当一个新人进入一个画面时,这个应用程序会注意到并记录下这个人的图像。在这篇博客中,我们将讨论构建这样一个应用程序的挑战,以及那些涉及部署它的人,可能使用多个摄像头,但是如果你想了解更多关于应用程序本身的代码逻辑,你可以阅读这个博客。
原型设计和开发 CV 应用程序
CV 应用程序包含许多组件,开发起来非常复杂。除了将执行处理的硬件与应用软件本身集成之外,开发生产就绪的计算机视觉应用程序的过程还包括许多步骤:数据收集和注释、训练计算机视觉模型、将使用不同框架制作的现有模型集成到您的应用程序中、将现有计算机视觉库集成到应用程序中,等等。考虑到开发计算机视觉解决方案的潜在成本和工作量,理想情况下,您还希望确保您的应用程序是灵活的,以便您的应用程序可以随着您的问题空间的发展而发展。这意味着确保您可以使用额外的模型类型,转移到不同的模型框架,或者在不中断应用程序的情况下更改硬件。
考虑到安全应用,假设您只想检测人。如果你想要一个能保证在一天的不同时间、不同天气等条件下工作的模型。对于生产质量的应用程序,您很可能需要收集和注释从您将使用的相机的角度以及在不同环境中拍摄的图像,并训练您自己的模型,反复几次此工作流以实现所需的性能。然而,假设你只是想要一个原型应用程序,在这种情况下,你可能会使用一个现有的检测人的模型,如 mobilenet_ssd 或 yolo_v2_tiny 。理想情况下,您可以尝试不同的模型,看看哪一个(哪些)效果最好,也许您的应用程序将受益于同时使用两个模型,以检测不同距离或不同条件下的人。一旦有了对象检测模型,就可以构建应用程序了。您将需要使用软件包,使您能够访问来自摄像机的视频流,并且您需要知道如何访问从您训练的模型返回的预测,以便您可以查看该人是否是新加入该帧的,并保存图像中与检测到的人的边界框相对应的部分。
部署计算机视觉应用
开发计算机视觉应用的另一个挑战是部署。一旦你有了一个计算机视觉应用的工作原型,你如何让它在世界上产生影响?虽然云通常被视为机器学习应用程序的一种非常灵活的解决方案,但它可能非常昂贵,具有更高的延迟,并且数据传输会带来安全风险。另一个越来越受欢迎的选择是将你的简历应用到边缘。
边缘上的简历
什么是边缘?一般而言,边缘设备是小型、轻量级的设备,计算机视觉应用可以在其上部署和运行。今天,许多边缘设备甚至具有图形处理单元(GPU)或视觉处理单元(VPU),这使得能够使用更大范围的模型和应用程序复杂性。在本文的上下文中,边缘设备是指诸如 Raspberry Pi、NVIDIA Jetson 设备(如 Jetson Nano 或 TX2)或各种物联网(Iot)设备之类的设备,这些边缘设备能够感知或评估其使用环境,并可能与其进行交互。
虽然可以使用互联网连接来部署应用程序,但是一旦应用程序位于边缘设备上,就不需要使用云连接来运行。这意味着应用程序所做的任何推理都是在边缘设备本身上进行的,而不是在云中,从而大大减少了应用程序将这些推理转化为行动的时间。对于某些用例,如自动驾驶车辆或安全摄像头,这是必不可少的。除了数据可能在发送到云或从云接收时丢失的风险之外,使用云方法所需的额外时间可能意味着无法及时响应任务,这对自动驾驶等任务来说可能是灾难性的。
除了边缘设备,还有专门的外围设备,特别是摄像机,它们本身没有互联网连接,边缘设备使用它们来提高性能或扩展应用功能。有了这样的设备,这种边缘处理的概念就更进一步了。虽然边缘设备通常连接到 USB 或带状摄像机,将图像数据传递到设备进行处理,但这些设备将处理器集成到摄像机本身中,进一步减少了推断时间。
此外,由于数据不需要通过边缘部署传输到云,所有数据都可以留在设备本身的封闭电路中,这更加安全,并且不需要云处理,成本也更低。

在边缘和云中部署 CV 应用程序的比较。创作于卢西德哈特(www.lucidchart.com)。
边缘部署面临的挑战
在边缘上部署生产质量的计算机视觉应用程序会带来一系列挑战。使用云计算,当计算需求增加时,您可以扩展实例,但是使用边缘设备,您会受到单个设备能力的限制。此外,通过边缘部署,如果不集成其他包,您可能无法访问设备状态。
让一切运转起来
我们已经讨论了开发和部署计算机视觉应用程序的复杂性;我们如何缓解这些挑战,尽快将 CV 应用投入生产?如果你看看 alwaysAI 这样的平台,有几个关键的方法可以用来降低工作流的复杂性:容器化和应用程序编程接口(API)抽象。
容器化是指将软件和依赖项捆绑在一个单元中的过程,可以在不同的环境中执行。AlwaysAI 利用使用 Docker 映像的容器化来实现在不同设备上的部署,包括 Raspberry Pi 和 Jetson Nano。它还整合了开源计算机视觉工具,如计算机视觉注释工具 (CVAT),这些工具依赖于直接打包到我们的包中,以简化这些工具的安装和使用。
alwaysAI 还开发了一个 API 来进一步加速开发。通过这个 API,用户可以与计算机视觉相关的对象进行交互并对其进行操作,例如关键点、边界框、标签等。从模型推断返回的,以及硬件特征如照相机,和外部实体如文件(图像、视频等)。)而不必知道这些请求背后的所有细节。这是如何工作的?API 充当复杂后端和最终用户之间的某种协商者。它定义了用户可以发出什么样的请求以及如何返回数据。通过隐藏这些调用中涉及的一些复杂性,这简化了用户的使用,并有助于确保只能发出适当的请求。
alwaysAI API 包装了其他 API,有助于进一步促进应用程序开发。OpenCV-Python API 是一个流行的计算机视觉库;alwaysAI 已经将一些最常见的 OpenCV-Python API 调用合并到其库中,以便简化这些函数的使用,并确保它们与其库的其他方面一起工作。此外,任何 alwaysAI 应用程序都可以直接导入 cv2,即 OpenCV 库,而无需安装库或添加任何依赖项。这样,依赖于 cv2 的遗留代码仍然可以工作,如果需要,用户可以默认使用 OpenCV-Python API,但是为了简单起见,可以使用一个库进行开发。
API 还可以帮助开发人员更好地访问硬件。一些专业相机,如 RealSense 和 OpenNCC Knight 相机,有自己的软件开发工具包(SDK)来促进应用程序的创建。这些 SDK 还包括 API,可能足以将这些相机与现有应用程序集成。虽然可以通过使用这些 API 直接访问硬件功能,但也可以将特定于相机的 API 合并到另一个已经用于应用程序开发的库中,这就是我们对 API 所做的。这意味着最终用户可以使用他们的应用程序已经使用的相同库来访问这些相机功能,并且可以保证正常工作。
API 和容器化也有助于克服上述与部署相关的挑战。使用基于容器的部署服务,您可以部署多个边缘设备并监控它们的状态。Kubernetes 和 T2 Docker Swarm 都支持监控和管理多个 Docker 容器,T4 balena 和 T5 也是如此,balena 是一项免费的商业服务。这两种方法都利用 API,第三方应用程序如开源的 Prometheus 或商业的 Datadog 可以使用 API 来获得对原始指标的更多洞察。虽然这种方法确实依赖于一些云连接,但实际的计算机视觉数据并不通过网络传输,因此您仍然可以保持在边缘部署的速度和低成本特性,并且安全性得到了提高,因为连接到边缘设备需要身份验证,并且应用程序数据仍保留在边缘上。
让我们最后一次回到我们的安全摄像头应用。您已经构建了自己的应用程序,可以轻松地更换不同的摄像机,并且可以在检测到物体时存储图像。现在,您可以使用我们上面提到的容器化技术来协调跨多个边缘设备的应用程序部署,如下图所示。使用正确的方法,平台、库和硬件可以无缝集成,使 CV 应用程序更容易开发和部署,并可扩展用于生产。

已部署的 CV 应用程序概述。创建于卢西德哈特(www.lucidchart.com)。
如果您想了解更多关于克服计算机视觉部署挑战的信息,您可以观看 YouTube 上的网络研讨会。
Eric VanBuhler 和 Stephanie Casola 对本文的贡献
计算机视觉深度学习导论
图像数据处理和特征提取背后的直觉是什么?
“计算机视觉”是机器学习的一个领域,处理图像识别和分类。可以开发计算机视觉模型来完成诸如面部识别、识别狗属于哪个品种,甚至从 CT 扫描中识别肿瘤等任务:可能性是无限的。
在关于这个主题的一系列文章中,我将探讨一些围绕计算机视觉的关键概念。在这篇文章中,我将提供一些关于计算机如何处理图像以及如何识别物体的直觉。
后续文章将处理深度学习模型的实际实现,该模型将学习将图像分类到几个类别中的一个——所有这些都在不到一百行代码中。
计算机是如何“看见”图像的?
计算机看图像的方式与人类不同——它们只能理解数字。因此,任何计算机视觉问题的第一步都是将图像包含的信息转换成机器可读的形式。幸运的是,一旦我们把一幅图像分解成它的组成部分,这是非常容易做到的。
图像由像素网格组成,每个像素就像一个小盒子,覆盖了图像的很小一部分,如下所示:

来自像素的原始图像
每个像素可以被看作是单一颜色的“点”。像素越多,就越能精细地表现图像的各个部分(分辨率越高的图像像素越多)。
现在我们知道了每个图像的构建模块是什么,但我们仍然需要弄清楚如何将它转换成一系列准确描述图像的数字。让我们来看一张 1920 * 1080 像素的高清图像。我们已经确定这个图像可以被分解成小盒子。在这种情况下,我们将有 2,073,600 个这样的小盒子,每个盒子可以用它的颜色来表示。因此,我们实际上有大约 200 万条信息——每条信息都描述了图像特定部分的颜色。
幸运的是,有一种用数字形式描述颜色的方法。每种可能的颜色都由一个独特的 3 位数字代码描述——这是 RGB 坐标(你可以在这里看到任何颜色的 RGB 代码)。所以我们现在可以用数字来表示任何图像,把它分解成像素,用一组 3 个数字来表示每个像素。我们现在已经把高清图像分解成大约 600 万个我们的计算机可以理解的数字。唷!
我们如何识别物体?
想想人类是如何识别物体的:即使我们只看到它们的轮廓,我们也能区分猫和狗,也就是说,我们不必进行彻底的视觉检查,也不必处理它们形状的每个细节,就能区分它们。这是因为我们的大脑会将一些关键的“特征”(比如大小和形状)与每个物体联系起来。然后,我们可以只关注这些特征,我们的大脑仍然能够识别它是什么。
显而易见,计算机视觉模型应该做同样的事情——从图像中提取特征,并将每个特征与特定类别的对象相关联。然后,当给它一张新的图像时,它会尝试通过将其特征与正确的类别进行匹配来识别它。但是我们如何训练计算机从一系列数字中提取这些特征呢?我们通过一个叫做“卷积”的过程来做到这一点。
卷积和特征提取
让我们通过一个简单例子来理解卷积是如何工作的:

卷积:作者图片
要将原始网格转换为“卷积”网格,我们需要一个卷积矩阵。该矩阵的目的是指定要提取的“特征”。然后,我们将该矩阵应用于原始数字网格,方法是将其值乘以原始网格的相应值,然后将结果相加。
在上面的例子中,我们有一个 99 的网格,我们希望与我们的 33 卷积矩阵卷积。我们将原始网格分成 9 个更小的 3*3 网格(以匹配卷积矩阵的大小)。然后,我们将前 9 个单元格(灰色)乘以卷积矩阵。在这个简化的例子中,除了 14 之外,所有的单元格都将变成 0,加上所有这些数字,我们得到:14。这就成为我们“转换”网格的第一个单元。我们对原始网格中的所有其他单元都遵循这一过程来填充我们的卷积网格。实际上,这个过程还需要考虑其他参数,例如“步长”,即每次运算后卷积矩阵向右“移动”多少位置,以及“填充”边缘,但核心原则保持不变。
那么我们在这里做了什么?我们将 99 的网格缩减为 33 的网格。如果我们认为这个操作是在图像中每个像素的 RGB 坐标上执行的,那么我们已经有效地从原始图像中“提取”了一些特征。这个非常简单的过程可以产生强大的效果,并且是每个图像过滤器的底层机制!
不服气?我们可以在下面的例子中看到这一点:

卷积特征提取:“轮廓”:作者图片
(你可以通过下载GIMP GUI 并遵循这里的说明来尝试应用你自己的卷积。)
我们现在可以从以下几个方面来理解卷积是多么强大。减少数据的维度(我们的 99 网格变成了 33 网格)
2。从我们的图像中提取特征,然后我们的模型可以学习将这些特征与不同的物体联系起来,从而学习识别它们。
在实践中,我们也可以“汇集”卷积的结果,进一步降低图像的维度。事实上,这是我们在开发深度学习模型以学习识别本系列后面的对象时将遵循的方法。
把所有的放在一起
在一个非常高的层面上,为了开发一个计算机视觉模型,我们将遵循的步骤是:
1。将图像转换成机器可读的数据(数字)
2。设置一些卷积,可以从图像中提取“特征”3。将这些信息输入神经网络,然后神经网络学习哪些特征与哪类物体相关(比如“狗”和“猫”)
4。拟合和评估这个模型
利用深度学习识别狗的品种
继续我们关于计算机视觉的讨论(关于该领域的详细介绍,请参见这篇文章)我们将建立一个深度学习模型,根据狗的图像将狗分类到 120 个品种中的一个。为此,我们将使用谷歌的tensor flowPython 平台。
卷积神经网络:蓝图
在我们建立实际的模型之前,有必要讨论一下卷积神经网络的构建模块。每个模型都是由几个堆叠在一起的“层”组成的,每一层都有特定的功能。我将在下面简要讨论最重要的层的直觉,但是有许多资源提供了关于这些层如何工作以及如何针对特定任务对它们进行微调的更详细的文档(我将从来自 Keras 的官方文档开始)。
- 卷积层 卷积层用于从图像中提取特征,正如本系列第 1 部分所讨论的。我们通过指定要从图像中提取多少特征以及要使用的卷积矩阵的大小来设置卷积层。我们不需要告诉模型提取哪些特征:例如,我们不需要告诉它检测边缘和轮廓——模型“学习”这一点,因为它被给予数据进行训练。
- 汇集层
 汇集层用于通过“汇总”图像特定片段中包含的信息来减少数据的维度。这方面的一个例子是,通过用最大值表示每个线段(我们也可以选择平均值而不是最大值),将 44 的网格缩减为 22 的网格。池化有两个目的,一是减少维度,二是使模型对要素的确切位置不太敏感,这是我们所希望的,因为我们希望模型能够识别出一个要素,即使它稍微偏于其参考位置的左侧或右侧。

最大池:作者图片
- 密集层 密集层由固定数量的“神经元”或细胞组成,它们从卷积层(在 CNN 的情况下)获取一维输入,并对其进行处理以供进一步使用——这些层的输出要么被前馈到其他密集层,要么用于预测最终输出。例如,如果我们有一个从图像中提取 64 个特征的卷积层,我们希望使用这些特征来达到我们的预测。我们可以将此信息传递给密集层,例如 16 个节点。密集层中的每个节点都完全连接到卷积层,即它从所有 64 个特征中收集信息。然后,每个节点将一组不同的权重应用于来自每个特征的输入,并得出一个“分数”,该分数随后被提供给其他密集层或用于预测结果。
- 展平和删除层 卷积层返回 2D 输出(因为图像被处理为 2D 网格),但我们的密集层只接受 1D 输入。为了允许这些层进行通信,我们需要将这些信息从 2D“扁平化”到 1D。我们可以通过使用“全局池”来做到这一点,即通过用单个概要图来表示整个图像,或者通过使用“展平”层。
 删除层用于防止模型过度拟合数据。例如,丢失率为 30%的层将告诉模型每次随机忽略前一层的 30%的节点。这意味着,当 30%的节点将被随机忽略时,模型必须很好地“概括”以给出准确的输出。
过度拟合与欠拟合的快速说明 理解过度拟合和欠拟合的最佳方式是使用类比。让我们想象一下,我们有三个学生正在准备考试。a 的准备包括记忆课程材料,B 花了时间去理解概念,而 C 根本懒得准备。“过拟合”模型就像一个——它“记忆”它所训练的数据集的特征。我们不希望模型过度拟合的原因是,当我们实际上想要对它没有见过的数据使用模型时,它将表现不佳(就像在要求应用他应该已经学习过的概念的考试中一样)。“欠适应”模型就像 C——它没有学习训练数据的特征,我们显然不希望这样。理想的模型就像 B——它从我们训练它的数据中学习,但能够概括这个数据集的特征,并仍然对新数据做出准确的预测。
通常,我们会在同一个模型中使用几个卷积层和密集层。一个简单的结构可能如下所示:
简单的模型结构
数据
我们将使用 10,000 多张属于 120 个品种的狗的图像数据集。数据集在这里可用。我们将把这个数据集的大部分交给模型来训练它,然后看看它能够在剩余的图像上预测品种的准确程度,这是它以前没有见过的。随机猜测正确品种的概率约为 1/120——让我们看看我们的模型表现如何。
数据扩充
对于大多数计算机视觉问题,增加数据集通常是个好主意。我们获取现有的图像,然后在一些设定的参数内随机变换它们。例如,我们可以将图像旋转多达 30 度,将图像的亮度增加和减少多达 20%,增加图像的缩放等。这做了两件事——它增加了我们必须处理的数据量,它有助于确保模型仍然可以识别(在这种情况下)狗的品种,即使图像略有移动。这有明显的优势,因为我们遇到的每一张新图像都不会有完全相同的缩放、亮度等。
为了说明这一点,让我们见见黑兹尔。她是一只可卡犬,不属于用于训练或测试模型的数据集。

原图:作者
现在,让我们“放大”这张图片,创建 15 张图片,每张图片都略有不同。输出如下所示:

增强图像:作者
模型
现在考虑到问题的复杂性(我们必须区分 120 个品种,其中许多看起来很像),像前面描述的简单模型不太可能胜任这项任务。我们可能想要一个更深的模型,有更多的层次。选择正确的模型结构的过程必然涉及大量的试验和错误。幸运的是,有一些“预训练”模型可供使用,这些模型已经被训练(当然是在不同的数据集上)来分类多达 1000 个类别。我们可以使用这些模型,重新训练数据集上的所有或部分图层,并观察其表现如何。这就是所谓的“迁移学习”。
在这里,我使用了 DenseNet 121 模型(它有 121 层和超过 800 万个参数!).我用一个有 120 个节点的密集层替换了最后一个有 1000 个节点的密集层(因为原始模型是在一个有 1000 个类的数据集上训练的,而我们只有 120 个品种),并且只重新训练了模型的最后 11 层。
模型架构在他们的论文中有详细描述,摘录如下:

DenseNet 架构:摘自上述 DenseNet 论文
作为 Keras 库的一部分,所有可用的预训练模型的详细信息可在此处找到。
寻找“引擎盖下”
由于其复杂性,神经网络通常是一个“黑箱”。因此,在展示代码和模型性能之前,尝试并可视化模型在最终预测的不同步骤中“看到”的内容是一个好主意。
最终的模型有 120 个卷积层(每个卷积层从图像中提取几个特征)和一个密集层。虽然看到每个图像经历的所有卷积的结果是不实际的,但我使用 Hazel 的图像作为例子,并在下面展示了最终(训练)模型的不同层生成的 3 个“卷积”图像。这将允许我们看到模型所看到的内容(即使它只是模型用来进行预测的全部信息的一小部分)。

看 CNN 的“引擎盖下”:作者图片
正如我们所看到的,在前几层提取的特征对我们来说可能仍然是可识别的,但当模型到达第 27 层(120 层)卷积层时,最终的图像基本上是人眼无法识别的。(重要的是要明白,我们没有告诉 CNN 要寻找什么样的特征,这是它自己学会的。)
最后一行显示了最终的一组图像(注意,这是一个 77 的像素网格,每个像素用不同的颜色来表征),然后将这些图像“展平”,并传递到密集层上进行预测。这意味着该模型达到了它的预测,即图像显示的 120 种狗中的哪一种是基于一堆 77 的图像,我们甚至不能再将这些图像与原始图像联系起来。虽然这对我们来说看起来像是胡言乱语,但 CNN 经过仔细校准,可以识别这些“点”中复杂的模式,帮助它做出预测。
模型性能
该模型在测试数据集中的准确率约为 75%(请记住,这些图像不是该模型训练的图像)。虽然可以通过调整一些参数和重新训练原始 DenseNet 模型的更多层来提高性能,但考虑到随机猜测正确品种的概率小于 1%,并且许多狗品种看起来彼此非常相似,这已经是很好的了。
至于预测黑兹尔属于哪个品种,这个模型是这么说的:

密码
计算 Ansys 动力学模型的质量特性
使用 MAPDL 命令计算任意坐标系质量特性的方法

群众要紧!(鸣谢: xibarodays 上 Pixaby)
Ansys 是一个商用有限元分析(FEA)软件包。虽然 Ansys 已经获得并集成了许多不同的分析工具,但是它的隐式结构包是健壮的并且得到了很好的支持。Ansys 的隐式结构求解器的一个独特功能是能够使用简单的逗号分隔的 MAPDL 命令编写命令。无论是使用 Ansys 经典接口还是通过 Workbench 接口使用现代 Ansys Mechanical,MAPDL 对于预处理模型和/或后处理结果都非常有用。本文介绍了一些 MAPDL 命令,用于提取关于用户定义坐标系的模型整体或部分的质量属性。
执行动态有限元分析时,验证质量是一个重要步骤。毕竟这是等式的一半!对于三维实体模型,质量是非常微不足道的。但是,在动态模型中,元件通常表示为壳、梁和/或弹簧。此外,动态模型通常包含非结构质量,如SURF154表面元素和点质量元素。验证质量可能变得不简单。
在切换到 Ansys Mechanical 之前的黑暗时代,我会严重依赖 MAPDL 文本文件来定义所有的材料属性和元素类型数据。我会尝试对这些属性进行分组或者控制它们的编号(通常在电子表格中),这样我就可以根据我的意图检查 solve.out 文件中的元素类型批量输出列表。如果动态表示与组件质量(通常从 CAD 获得)不匹配,我会将“质量系数”应用于密度,然后冲洗并重复,直到单个元素类型质量和总质量与预期值匹配。虽然这种方法有效,但有点麻烦,而且没有给我我有时想要的良好控制。
我现在几乎只使用 Ansys 机械应用程序进行预处理。在 Mechanical 中,你可以点击单个物体,从属性窗口中获得质量(无需求解!)或使用选择工具获取多个选定实体的体量,非结构体量的存在会带来一些挑战。使用 Ansys Mechanical,我们放弃了属性编号控制,以换取一系列易于使用的功能。您可以使用选择工具获得一些有限的信息,但同样,我们错过了非结构质量和惯性质量属性。
这些年来,我从谢尔登·今冈的/合作解决方案、nsys.net 以及 PADT 主办的 xansys 论坛(看起来并不活跃)中得到一些提示。因此,站在巨人的肩膀上,我创建了一个锅炉板 MAPDL 脚本,它使用组件对模型的各个部分进行分组,并写出一个 csv 摘要文件,然后我可以将它复制/粘贴到电子表格中进行额外的验证。它提供的主要优势是:
- 使用组件对模型的任意部分进行分组
- 获取任意坐标系的部分模型质量属性(包括转动惯量)
- 专门用于质量属性摘要的格式化输出文件
这里有一个 github 存储库,它包含了几个版本的 MAPDL 脚本文件和一个包含它们的示例工作台项目档案。让我们来看看重要的部分!
关于用户定义坐标系的模型的质量属性
虽然我们可以将模型转换到感兴趣的坐标系,运行分析,并从solve.out文件中读取属性,但这并不总是理想的。注意:我们不能使用CSYS或RSYS命令来强制 Ansys 分析不同坐标系的质量。
这里有一个脚本,它计算关于用户定义的坐标系的整个模型的质量属性。我们走一遍吧!
在上面的部分中,我们确保用ALLSEL选择了一切,然后将我们的坐标系 id 分配给cs_mass参数。my_cs如果使用手动编号控制(或 Ansys Classic ),可以是一个整数,也可以是坐标系属性卡上定义的“APDL 名称”。我们*GET坐标系的位置和欧拉角。注意变量替换语法(%var_name%)的使用将在执行时用变量值填充字符串或命令的该部分。这在这里并不重要,但以后会有帮助。
在下一节中,我们进入/sol模块并运行魔法命令(IRLF、& PSOLVE),这些命令形成元素矩阵,因此我们可以获得质量属性,但不会运行模型的完整解。同样,这对于整个模型的质量属性来说并不完全必要,但是我们稍后会用到它。
现在我们开始一些计算!在上面的部分中,我们将惯性属性从重心(CG)转换到坐标系的位置。然后,我们创建一个 APDL 2D 数组,并将其填充为惯性张量。
在这里,我们建立转换矩阵,并将其分配给一个 APDL 2D 数组。
现在来看一些有趣的东西。为了将一些线性代数命令用于矩阵乘法,我们需要将 APDL 数组移到“APDL 数学”空间。我们用*DMAT命令来做这件事。在“APDL 数学”中,我们用适当的转置执行矩阵乘法,最后用*EXPORT命令将结果从 APDL 数学空间复制回 APDL。接下来,我们将质量属性分配给以my_开头的变量,这是默认前缀,Mechanical 会查找该前缀以将参数值打印到命令片段属性卡。

关于从全局 CS 旋转的 CS 计算质量属性
关于唯一坐标系的多个分量的质量性质
虽然获得整个模型在任意坐标系下的质量属性很好,但这并不是特别有用。这里,我们将扩展上面的命令,向一些数组添加组件,定义一个*DO循环,并使用*VWRITE命令将格式化的输出写入一个文本文件。这是基于组件的质量属性的完整文件,我将在下面一一介绍。
首先,我们开始将模型分成我们想要计算质量属性的部分。如果使用 Ansys Mechanical 应用程序,命名选择将作为具有相同名称的组件发送到解算器。一个警告是,Mechanical 允许命名选择的名称中有空格,但解算器不允许。最好在 N 个命名选择的名称中避免空格。组件可以包含节点或元素,但不能同时包含两者。对于质量属性,我们希望使用基于元素的组件。通常,当选择模型的任何部分作为质量属性时,我们还希望这些实体上有非结构质量。Ansys 使用与底层结构元素共享相同节点的SURF154元素添加分布式非结构质量,但不添加任何刚度。我认为这比将非结构性体量与一处房产捆绑在一起要灵活得多。下面的代码片段确保 C 组件中的元素节点上的任何SURF154元素都被添加到 C 组件中。对于每个命名的选择,它可以重复。
cmsel,s,my_named_selection
nsle,s 
esln,1 
cm,my_named_selection,elem
以类似的方式,我们可以通过使用远程点上的 P ilot 节点 APDL 名称属性,创建一个附加到远程点的点质量元素的组件。下面我们创建一个组件,它包括单节点质量元素,通过选择连接到远程点的引导节点的所有元素,这些元素完全由该节点定义。
nsel,s,node,,my_rp_name
esln,1
cm,my_pm_component,elem
现在,我们已经根据需要创建并扩充了所有构件,以包括非结构体量元素,我们可以继续下一步。下面的命令设置我们将用于文件名的 f_name 参数,并初始化两个数组。一个数组用于组件名称字符串,另一个数组用于分配给坐标系 id 的变量字符串。这样,我可以保持大部分脚本的通用性,并在每次使用时修改这个数组。
接下来,我初始化输出文件。%_wb_userfiles_dir(1)%被替换为工作台项目目录中项目 user_files 文件夹的绝对路径,而%f_name% 被替换为上面我们分配给f_name参数的字符串。然后,我使用*VWRITE将标题写入文件。我初始化了一个临时输出数组outVars,并告诉 Ansys 使用一个新的文件名来将这个输出与结构分析输出分开。
接下来,我们进入组件循环,与总模型情况一样,我们从获取该组件坐标系的属性(位置和角度)开始。唯一的区别是使用了%css(1,j)%来获取参数值,该参数值是使用变量的名称和坐标系 ID 作为值来分配的。然后,我们只选择感兴趣的组件中的元素和相关的节点,并发出IRLF 和PSOLVE命令。这些命令的美妙之处在于,Ansys 不必执行完整的求解来获得每个组件的质量属性。
下一组命令与通过*EXPORT 命令将惯性张量从重心转换到坐标系的总模型情况完全相同,此时我们将旋转张量带回 APDL 2D 阵列。唯一的区别是我们在圈内。这里就不赘述了。
现在,我们不再将质量属性赋给以my_前缀开头的变量,而是将它们赋给outVars数组。然后我们退出循环并*vwrite我们的组件名称、坐标系变量名和每个组件的质量属性。然后用*cfclos关闭文件,返回默认文件名和后处理器。
以下是输出文件中的一个示例:
我通常把包含这些命令的命令片段放在机械模型树的解对象中,作为后处理步骤。一旦求解完成,您应该在项目目录的 user_files 文件夹中找到一个my_mass.txt文件。
就是这样!查看 github 资源库中的命令片段、一个示例输出文件和一个非常简单的工作台归档文件(2020 版 R1 ),其中包含了使用这些命令片段的两个系统。我希望这对你在 Ansys 中的动态建模/大规模验证冒险有所帮助!
查看其他与 Ansys MAPDL 相关的文章:
用 MAPDL 定义和后处理关节
medium.com](https://medium.com/@steve.kiefer/ansys-mechanical-all-about-joints-173f1fa40e15) [## 激活您的 Ansys 动态和机械分析
利用 MAPDL 命令在 Ansys Mechanical 中对模态有效质量和势能进行后处理
medium.com](https://medium.com/@steve.kiefer/energize-your-ansys-dynamic-mechanical-analyses-b94c58da9a30)
用图形数据库嵌入计算节点:Neo4j 及其图形数据科学库
借助 pygds,使用新版 Neo4j 图形数据科学插件计算节点嵌入并将其提取到熊猫数据帧中。

图片作者格尔德·奥特曼
现在的机器学习都是关于向量的。执行分类任务要求将数据排列成行(观测值),每行包含相同数量的要素(列)。虽然这很容易从最初存储在 Excel 表或 SQL 或 noSQL 数据库中的数据中获得,但当问题涉及复杂对象(如文本、图像或图形)时,转换就远不明显。
为了将这些对象表示为向量,使用了嵌入技术。嵌入算法将一个给定“小”尺寸的向量分配给这些复杂对象中的每一个,否则这些复杂对象将需要数千个(至少)特征。嵌入的挑战是保留您试图建模的对象的一些特征,同时减少特征的数量。例如,单词嵌入将尝试并捕获单词的含义,使得语义上彼此接近的单词具有相似的向量表示。
图形嵌入
根据要表示的对象,图嵌入涵盖了几种技术。最常见的是节点嵌入,其中要表示为向量的实体是节点,但我们也可以找到边嵌入或整图嵌入。本文将重点讨论前一个问题,节点嵌入。
图形数据库和 Neo4j
图形数据库的主要目的是使关系更容易管理,无论我们谈论的是具有复杂的多对多关系的 web 应用程序,还是图形数据科学。对这种商店的兴趣几乎一直在增加,特别是自从 Cypher 查询语言被引入 Neo4j 之后。

来源:https://db-engines.com/de/blog_post/53
Cypher 让编写直观的查询变得如此容易。例如,我确信您将立即了解这个查询正在做什么:
MATCH (:User {name: "Emil"})-[:FOLLOWS]->(u:User)
WHERE NOT u.name STARTS WITH "neo"
RETURN u.name, u.dateJoined
上面的查询需要注意一些事情:
- 由括号()分隔的节点有一个标签,通过前导:可以识别
- 由方括号[]分隔的关系必须有一个类型。按照惯例,关系类型是大写的。
正在尝试 Neo4j
说服给 Neo4j 一试?你有两种方法可以做到:
- 使用沙箱在有限的时间内(3 天,可以延长 7 天)运行 Neo4j 实例:https://sandbox.neo4j.com/
- 下载 Neo4j 桌面并在本地运行,享受所有功能:https://neo4j.com/download/
在这两种情况下,这是完全免费的。在这篇博客的后面,我将使用 Neo4j 4.1 创建一个新的图表。
导入一些数据
让我们将一些数据导入图表。为此,我们将使用got浏览器指南来使用“权力的游戏”数据集。类型:
:play got
在浏览器中,并按照说明进行操作。
导入数据后,图表模式如下所示:

“权力的游戏”图表模式。(调用 db.schema.visualization)
它包含一个节点标签,Character,五种关系类型(取决于哪本书中的人物彼此交互)和一个全局INTERACTS关系。在这篇文章的剩余部分,我们将只使用最后一个。如果您想要可视化某些数据,您可以使用:
MATCH (n)
RETURN n
LIMIT 200
让我们继续进行图形分析和节点嵌入。
图形数据科学插件(GDS)
GDS 是图形算法插件的继任者,其首次发布可追溯到 2018 年。它的目标是支持使用图形算法,从路径查找算法到图形神经网络,而不必从 Neo4j 中提取数据。
按照https://neo4j . com/docs/graph-data-science/current/installation/# _ neo4j _ desktop中的步骤进行安装。
投影图
Neo4j 图通常包含大量数据:具有不同标签的节点、具有不同类型的关系以及附加到它们的属性。大多数时候,数据科学算法只需要这些实体中的一小部分:只有一些节点标签或一些关系类型,以及只有一个属性(例如,最短路径算法的关系权重)。这就是为什么 GDS 没有在完整的 Neo4j 图上运行,而是在一个投影(更轻)版本上运行。所以,让我们开始建立我们的投影图。在 Neo4j 浏览器中,执行:
CALL gds.graph.create(
    "MyGraph", 
    "Character", 
    "INTERACTS"
)
这里我们创建一个名为MyGraph的投影图,包含所有标签为Character的节点。此外,我们将类型INTERACTS添加到这个投影图关系中。
我们在这里,我们的投影图创建,我们可以继续下去,并在它上面执行算法。
执行节点 2vec
在MyGraph投影图上运行 node2vec 算法的最简单方法是使用这个简单的查询:
CALL gds.alpha.node2vec.stream("MyGraph")
浏览器中的结果如下图所示,其中一个编号列表被分配给每个节点(由其内部 Neo4j ID,nodeId标识):

带有默认参数的 node2vec 过程的输出。
如果您对 node2vec 的工作原理有所了解,您就会知道有许多配置参数可以用来配置:
- 建立训练数据(随机行走参数):步数、每个节点生成的行走数、输入输出和返回因子。
- 训练 skip-gram 神经网络:嵌入大小、初始学习速率等。
参数的完整列表如下所示,来自 GDS 文档页面。

来自 GDS 文档的 Node2vec 参数https://neo4j . com/docs/graph-data-science/current/algorithms/node-embeddings/node 2 vec/
例如,让我们尝试减小嵌入大小:
CALL gds.alpha.node2vec.stream(“MyGraph”, {walksPerNode: 2, embeddingSize: 10})
不出所料,现在的输出如下所示:

在处理这些结果的用法之前,让我们看看如何使用另一个嵌入算法 GraphSAGE。
正在执行 GraphSAGE
虽然 Node2vec 只考虑图结构,但是 GraphSAGE 能够考虑节点属性(如果有的话)。
在我们的 GoT 图中,节点只有一个name属性,这个属性对于嵌入没有多大意义。然后,我们将只使用节点度,或附加到它的关系数,作为属性:
CALL gds.alpha.graphSage.stream("MyGraph", {degreeAsProperty: true})
完整的参数列表包括属性配置(nodePropertyNames)、聚合器功能(默认为mean)、批量……完整列表见下图。

来自 GDS 文档的 GraphSAGE 参数:https://neo4j . com/docs/graph-data-science/current/algorithms/alpha/graph-sage/
通过 Python 利用结果
Neo4j 提供了一个 python 驱动,可以通过 pip 轻松安装。然而,在这篇文章中,我将谈论我开发的一个小工具,它允许毫不费力地从 Python 中调用 GDS 过程: pygds 。它仍然需要大量的测试,所以如果您发现任何问题,请随时报告。
让我们从在您最喜欢的 python 环境中安装软件包开始:
pip install "pygds>=0.2.0"
然后,您可以导入库并定义连接到 Neo4j 图形的凭证:
from pygds import GDS
URI = "bolt://localhost:7687"
AUTH = ("neo4j", "<YOUR_PASSWORD>")
pygds的用法如下:
with GDS(URI, auth=AUTH) as gds:
    # create the projected graph
    # NB: make sure a graph with the same does not already exists
    # otherwise run CALL gds.graph.drop("MyGraph") 
    gds.graph.create(
        "MyGraph", 
        "Character", 
        "INTERACTS",
    ) # run any algorithm on it 
例如,要运行 node2vec 算法,我们将编写:
result = gds.alpha.node2vec.stream(
    "MyGraph", 
    {
        "walksPerNode": 2, 
        "embeddingSize": 10
    }
)
然后可以将结果解析成一个DataFrame:
import pandas as pd_tmp = pd.DataFrame.from_records(result)
df = pd.DataFrame(_tmp["embedding"].tolist())
print(df.head())
从那里开始,您可以执行任何机器学习算法,从可视化的 PCA 到分类,如果节点也有一些目标类…
最后一步,无论你使用的是 Cypher 还是 pygds,你都必须删除存储在你的计算机内存中的投影图:
gds.graph.drop("MyGraph")
想了解更多?
太好了!GDS 包含更多奇妙的算法实现(路径查找、节点重要性、社区检测、节点相似性、链接预测)和功能(例如,将算法结果作为节点属性写入,以将结果持久存储在图中,而不是以流的形式存储)。
如果您有兴趣了解更多关于图算法和 Neo4j 的信息,可以查看以下几个在线资源:
- 免费书籍“图算法:Apache Spark 和 Neo4j 中的实际例子”,作者:马克·李约瑟:https://neo4j.com/graph-algorithms-book/(注意:这本书是使用图算法库(GDS 的前身)编写的,但仍然是理解图算法用例的必读书籍(此处有迁移指南以及使用 GDS 更新的示例);警告 2:它可能不会无限期免费,Neo4j 会定期宣布其“免费期”的结束,到目前为止,免费期一直在延长,但谁知道呢)
- 查看 Neo4j 创建的 GraphAcademy 及其数据科学课程:
 https://Neo4j . com/graph academy/online-training/data-science/part-0/
- 你可以关注 medium 上的托马兹·布拉坦尼克和他的博客,看看 GDS 所有算法的许多有趣的用例:https://tbgraph.wordpress.com/
- 当然,阅读文件:https://neo4j.com/docs/graph-data-science/current/
- 关于这里讨论的算法的更多信息,例如参见 Kung-Hsiang,Huang (Steeve) 的【图神经网络(Basis、DeepWalk 和 GraphSage)的温和介绍】(T3):
概念漂移如何破坏你的模型性能
概念漂移特征的理论回顾和实用技巧,确保您的模型随着时间的推移而适应

在 Unsplash 上由 Manja Vitolic 拍摄的照片
T 今年是 2019 年,你部署了一个预测卫生纸(或其他任何东西,真的)需求的机器学习模型。2020 年,新冠肺炎出现,消费者去商店抢购空前数量的卫生纸。实际的销售数字并不是异常值,因为由于人们更经常呆在家里,消费者的行为已经发生了变化。因此,你的卫生纸需求模型不再符合新的、COVID 时代的消费者需求。
机器学习系统如何解释这种损害模型性能的变化?
世界本质上是动态和不稳定的——也就是说,不断变化。
随着时间的推移,许多机器学习模型的性能下降是不可避免的。这与人类行为相关的模型尤其相关。如果没有明确的训练示例,当代机器学习模型不能很好地推广到新环境。随着数据和“基本事实”的变化,模型性能可能会开始下降。这些是机器学习系统中必须考虑的关键弱点。但是,这种导致模型低质量响应的变化的特征是什么?您如何监控和维护您的模型,以便下游用户总是看到高质量的输出?
预测来自动态现实环境的数据变化将如何影响您的模型以及如何处理这些变化是至关重要的。
本文将首先为理解概念漂移如何出现和表现提供理论基础,然后讨论解决漂移的方法。了解数据和模型如何漂移对于设计模型监视器和响应计划至关重要。
让你的机器学习系统能够适应世界的变化
towardsdatascience.com](/the-intuition-behind-model-monitoring-bd8b5d8e540b)
什么是概念漂移?
下图提供了一个“概念”的简单说明。根据某个未知过程 G. 二维数据点被映射为红色或绿色,“概念”是数据点到颜色的真实映射G→。浅灰色线显示了已学习的映射 F ,它区分了接近 G. 的红色和绿色数据点

一个概念——红色和绿色数据点的区别。灰线代表学习到的区分红色和绿色的概念。
简而言之,概念漂移 发生在 G 发生变化的时候,这是生成你观察到的数据的基础函数。当然,我们无法直接观察到 G 。相反,我们通过从由 G. 生成的数据中采样来间接观察 G 。该定义假设我们随着时间的推移接收到描述我们想要建模的某些现象的数据点,例如卫生纸消耗。
在卫生纸需求的情况下,基础数据生成函数是消费(和囤积)的卫生纸数量。当然,无法直接观察到实际的冲水( G) 和卫生纸的囤积。相反,我们只能通过观察通过各种供应渠道购买的数量,从实际消费中“取样”。然后,我们使用观察到的购买数据来训练卫生纸消费的模型, F,。新冠肺炎引发了卫生纸消费和购买模式的变化——也就是 GG的漂移。
概念漂移是什么样子的?
漂移可以表现为虚拟漂移(对模型性能没有直接影响)或真实漂移(对模型性能有影响)。请注意,区别是相对于模型。
真实概念漂移
真实概念漂移是生成数据的机制发生变化,从而导致模型性能下降。如下图所示,概念(红色和绿色数据点之间的区别)已经旋转并改变了形状。在方案 A 期间学习该概念的模型现在在方案 B 下已经过时,并且将具有较差的性能。在真实概念漂移之后缺少对模型的更新,模型将不再正确地描述完整的目标概念空间。

真实概念从状态 A 漂移到状态 b。在状态 A 期间学习的函数对在状态 b 中观察到的一些数据点进行了错误分类。
卫生纸方面,COVID 前消费模式为“A 区制”,COVID 后消费模式为“B 区制”。一个在疫情之前熟练预测 2019 年 TP 需求的模型将在 2020 年疫情期间表现不佳。反之亦然,在 COVID 导致的经济封锁(人们很少离开家)期间,一个已经更新的预测 TP 需求的模型可能会在人们经常离开家的历史数据上表现不佳。
虚拟概念漂移
G 中的概念漂移不一定影响你的模型 F. 在虚拟概念漂移中,观测数据的分布发生了变化,但之前学习的映射 F 仍然正确地应用于新的数据生成过程G’。下图显示了从状态 A 到状态 b 的虚拟漂移。尽管每个状态中的数据分布不同,但模型 F 仍然根据浅灰色线正确分配数据点颜色。因此,你的机器学习模型只有在真正的概念漂移下才会变得过时。

虚拟概念从状态 A 漂移到状态 b。根据状态 A 的数据训练的函数 F(划分红色和绿色点的灰线)在状态 b 中仍然有效。
在现实世界中,您的模型性能不会表明发生了虚拟漂移,因为您的模型仍然表现良好!事实上,即使您正在监视数据本身的分布,您也可能需要“大量”的观察来确定虚拟漂移的存在。(如果数据有多个维度,直接监控数据的分布通常是不切实际的)。
虚拟漂移是个问题吗?看情况。如果您的目标是确保您的模型继续满足给定的性能指标,那么您的模型在存在虚拟漂移的情况下是足够的。虚拟漂移带来了一个隐藏的风险,即模型可能会错误地对待新分布中的某些异常值。

潜在的虚拟漂移风险:B 区灰色数据点的正确分类是什么?模型(灰线)会继续对这些点做出准确的预测吗?
漂移严重程度
漂移的严重程度变化很大。大多数概念漂移以交叉漂移的形式出现,其中部分输入空间在新旧概念中都有相同的目标类。在极端情况下,当所有的例子在新的目标概念下被错误分类时,就会发生严重的漂移。错误分类可能是由于目标概念中的新类和类定义中的变化造成的。

状态 A 和状态 B 之间出现交叉漂移,因为只有一些数据点分类发生了变化。在状态 A 和状态 C 之间发生了严重的漂移,其中所有数据点的所有分类都发生了变化。
如果您可以检测和测量漂移,您可以指定一个阈值来区分“主要”和“次要”漂移。二元分类任务的漂移量级可以用类别已改变的观测值的百分比来度量。如果漂移幅度小于阈值,模型只需用新数据更新。如果漂移幅度大于阈值,则漂移是“严重的”,应该放弃该模型,而采用新训练的模型。严重漂移通常很少见,如果不使用复杂的漂移检测,严重漂移的存在往往很明显。
漂移速度和稳定性
突然漂移
漂移持续时间短的概念漂移称为突然漂移。当数据生成函数突然停止生成概念为 G 的数据,并突然根据概念G’生成数据时,会发生突然漂移。突变漂移范式假设概念漂移发生在离散的时间周期内,以没有漂移的稳定周期为界。
作为一系列突然漂移的例子,医学杂志 Lancet 报道称,在 2020 年 1 月 15 日至 3 月 3 日期间,中国七次改变了新冠肺炎的病例定义。定义的每一次变化都会导致病例统计方法的改变;因此,每一次变化都会导致中国每日 COVID 病例计数概念的突然转变。
增量漂移和稳定性
增量漂移 寓意漂移持续时间长,也称连续漂移。在这种情况下,变化是从概念 G 到概念G’的稳定进展。概念漂移的速度或持续时间是一个新概念完全取代一个旧概念的时间步数。
在分类的情况下,在每个随后的时间步,根据旧概念 G 分类较少的数据点,根据新概念G’分类较多的数据点。你可以把 G 和 G '之间的每一个时间步长看作不同的中间概念。
增量漂移的一个例子是,随着新冠肺炎经济封锁在一个地区解除,消费者行为如何逐渐改变——人们可能会犹豫是否要恢复“正常”,只是慢慢恢复。行为中的每一个小变化都是 G(锁定期间的行为)和 G’(解除限制后的“正常”行为)之间的一个中间概念。值得注意的是,从 G(锁定)到 G’(“正常”)的行为进展是非线性的,因为人们会对像病例数变化这样的外部因素做出反应,从而特异性地改变他们的行为。
一个概念是不稳定的在概念 G 和 G’之间,概念之间的周期因为它会再次改变。这种不稳定性可以通过数据和模型度量中更大的噪声观察到。此外,一些概念本质上是不稳定和混乱的,永远不会达到一个稳定的概念,如市场价格运动。
下图显示了 100 个时间步长内的增量漂移。函数 v1(t)和 v2(t)分别模拟新旧概念中的一个例子在时间 t 出现的概率。漂移的速度是 v2(t)的斜率。

增量漂移的图示。(资料来源:民酷、怀特、姚 2010 )
如何检测和解决漂移问题?
当然,如果没有检测和校正模型和数据漂移的解决方案,这个讨论将是不完整的。许多人将漂移检测的主题称为模型监控。
解决概念漂移的基本方法是监控您的模型以检测漂移,重新训练模型,并部署新的模型版本。
这种基本方法适用于你期望概念漂移从一个稳定的概念突然转移到一个新的稳定的概念的情况。如果对模型性能的变化有一定的容差,在小漂移和/或增量漂移的情况下,这也是可以接受的。对于不太稳定的状态或持续漂移的情况,用新的观测值对模型进行增量更新可能更合适。在在线模型更新的情况下,监控模型漂移仍然很重要。
监测概念漂移有三种基本方法。
监控模型性能
原则上,监控模型性能是直接的:如果模型性能下降到某个预期水平之下,那么重新评估模型。要实现这一点,您需要做出几个决定,这些决定将影响漂移检测的灵敏度和频率。
1.在计算模型性能时,您使用了多少新的预测?
2.您评估哪些绩效指标,应用哪些阈值?
3.您多久监测一次漂移?
4.你的模型的用户有多容错?这将有助于你对(1)-(3)的回答。
5.你如何应对漂移?对你的模型进行人工评估和再培训?自动更新?
监控模型预测置信度的统计测量值
另一种方法是监控模型预测或残差值的分布或这些值的置信度。与潜在的高维输入数据相比,监控模型产生的值的分布变化要容易得多。统计监视器的具体形式取决于要监视的预测的速度和数量。除了上一节中的问题之外,要做出的一些相关决策包括:
- Kolmogorov–Smirnov(KS)测试是否表明您的预测或残差值的分布发生了变化?为了用 KS 检验准确地比较两个分布是否相同,需要一些最小数量的例子。
- 给定的预测/残差是否落在训练期间观察到的预测/残差分布的给定置信区间内?
有关学术文献中漂移检测的其他自适应测试统计,请参考参考文献部分的 Dries 和 Ruckert (2009 年)。
在线更新
一种选择是在线训练你的模型——也就是说,定期用新的观察值自动更新你的模型权重。更新的周期可以是每天、每周或每次收到新数据时。如果您预期增量概念漂移或不稳定的概念,这个解决方案是理想的。
这种选择并不是万无一失的,因为尽管在线更新,仍然存在模型偏离真实目标的风险。发生这种情况的原因有很多。
- 一个离群值可能对在线训练中的模型产生过大的影响,使学习到的模型进一步远离目标概念。这种风险可以通过使用成批的观察数据而不是单个数据点进行在线更新来降低。
- 学习率可能太小,从而在存在大漂移的情况下阻止模型足够快地更新。
- 学习率也可能太大,导致模型超出目标概念并继续表现不佳。
由于这些原因,监视在线更新的模型仍然很重要。
其他方法
在学术文献中已经提出了各种算法来检测概念漂移。漂移检测中的工作通常旨在高效、准确地识别概念漂移的真正点,同时最小化漂移检测时间。对这些提议的回顾超出了本文的范围。
结论
理解和检测漂移是一项艰巨的任务。最终,选择检测和应对漂移的最佳方法通常需要对数据、模型和应用有深入的了解。漂移检测和模型再训练的理想频率是主观的,取决于您的数据和应用。最后,在任何解决方案中,重要的是要考虑模型性能指标的变化是否是由于样本偏差,或者感知的漂移是否是由于随机性或异常值,而不是数据分布或目标概念的变化。
参考
巴赫斯蒂芬和马克.马洛夫。2010."概念漂移的贝叶斯方法."神经信息处理系统进展。127–135.
陈,凯莉,许允声,和帕特丽夏里德尔。2015."跟踪数据流中的漂移严重程度."《人工智能 2015:人工智能的进步:第 28 届澳大拉西亚联合会议》,澳大利亚堪培拉,ACT,2015 年 11 月 30 日-12 月 4 日,会议录,Bernhard Pfahringer 和 Jochen Renz 编辑,96–108。查姆:斯普林格国际出版公司。doi:10.1007/978–3–319–26350–2 _ 9
德里斯,安东和乌尔里希·拉克特。2009.“自适应概念漂移检测。”统计分析和数据挖掘(威利在线图书馆)2:311–327。
Gama、Joao、Indre Zliobaite、Albert Bifet、Mykola Pechenizkiy 和 Abdelhamid Bouchachia。2014."概念漂移适应综述."ACM 计算机。Surv。(美国计算机学会)46:44:1–44:37。doi:10.1145/2523813。
明库,莱安德罗 l,艾伦 p .怀特和姚欣。2010."存在概念漂移时多样性对在线集成学习的影响."IEEE Trans。据了解。和数据工程。(IEEE 教育活动部)22:730–742。doi:10.1109/TKDE
韦伯、杰弗里·I、罗伊·海德、曹洪、阮海龙和弗朗索瓦·珀蒂让。2016.“表征概念漂移。”数据挖掘和知识发现 30:964–994。doi:10.1007/s 10618–015–0448–4。
机器学习中的概念漂移
“悲观者抱怨风;乐观者期待它改变;现实主义者调整风帆。”威廉·亚瑟·沃德

任何事物都会随着时间而变化,数据也不例外。数据的变化导致机器学习模型的测试性能随着时间而降低。最终,模型得出的错误预测会影响其商业价值。
输入和输出标签属性之间的关系不是静态的,而是随着时间而变化,这会影响模型性能,因为它无法理解新数据中存在的新的底层模式。这种效应在机器学习中被称为概念漂移。
在这篇文章中,我将提供这个概念的一个简要概述,这个概念在机器学习中使用得非常频繁,并且对每个从业者来说都是非常重要的。
这里简单提一下我将在这篇文章中讨论的要点。
- 什么是概念漂移?
- 这一概念与数据科学生命周期有何关联?
- 为什么我们需要监测这种效应?
- 如何解决这个问题?
- 结论。
什么是概念漂移?
概念漂移是一种导致机器学习模型的性能随着时间退化的效应。这种退化是由于测试模型的新数据集和训练模型的数据集之间的底层模式的变化而发生的。这种变化是由于客户的产品购买模式发生了变化,或者由于一些天气参数随着时间的推移发生了变化。
我们都非常熟悉这个基本的函数概念:
Y= f(X)
这里我们有一个函数 f,它理解自变量 X 和因变量 y 之间的模式或关系。
但是当这种模式消失时,模型就会给出错误的输出,变得等同于垃圾。
这就是概念漂移发挥作用的地方。

这一概念与数据科学生命周期有何关联?
我们都知道数据科学项目是在不同的阶段执行的,对吗?从以下内容开始:
- 问题识别及其业务环境。
- 数据集集合。
- 数据探索和特征工程。
- 数据可视化。
- 模型培训和开发。
- 模型测试和部署。
- 模型再训练和更新过程。
我认为我们都非常熟悉 6 大概念。概念漂移出现在最后一个阶段,即模型再训练和更新。在这里,模型被部署到客户端,并且每天都进行频繁的模型测试。为了避免模型的偏差,它的预测被监控和检查,就好像它给出了正确的预测或者没有保持业务生产力。
为什么我们需要监测这种效应?
我们需要监控这种影响,因为它会给它所竞选的企业实体带来巨大的问题。错误的预测可能会导致商业公司失去声誉和忠诚的客户,因为模型可能会提供错误的建议,与用户的新购买模式不匹配。
让我们以科罗纳疫情时间为例,那里的人们经历了购买模式的重大转变。他们只是迎合非常必要的东西的需求,这是模型所不知道的。因此,它不断向他们推荐不符合客户选择的产品。因此,企业可能会损失大量收入。
如何解决这个问题?
有许多方法可以处理这个问题。
1。什么都不做(维护单个静态模型)。
不,我不是在开玩笑...!。我们可以假设数据中的潜在模式不会随着时间的推移而改变,而在许多情况下,这种情况确实会发生。
因此,我们可以专注于构建一个最佳模型来进行未来预测,并专注于其他一些项目。
2。定期重新拟合模型。
这个可能比第一个有效一点。我们在新的数据集上重新训练我们过时的模型,从而解释数据集中新的潜在模式。
这避免了模型成为垃圾,并持续带来商业价值。
3。定期更新模型。
当我们的测试显示先前的模型给出错误的预测时,我们可以一次又一次地训练和部署新的模型,而不是在新的数据集上更新过时的模型。
这种方法更有效一点,因为模型的改变可以导致更准确的预测。但是模型训练及其部署需要大量时间。
4。将新型号与旧型号组合在一起。
在这种方法中,我们将在新数据集上训练的一些新模型与过时的模型集成。在这里,新模型与旧模型一起工作,同时纠正旧模型的错误预测。
这个方法看起来有点复杂,但是比上面提到的方法更有效。

[编辑]如果你想深入主题,请在 neptune.ai 浏览这篇文章
结论...
好了,伙计们,今天到此为止。我想我们一定学到了一些新概念。这种影响在大多数时候被初级数据科学家忽略了,他们在完成一个项目后认为他们的工作已经结束了,但事实并非如此。他们的责任不止于此。为了维护我们的商业价值,我们必须监控和跟踪我们如何为客户体验增加价值,增加什么价值,以及推荐提供商是否为他们提供了好的推荐。
更多此类访问在这里。
如果这篇文章对你有所帮助。请在这里支持我https://www.buymeacoffee.com/shobhitsri
如果你有任何不清楚的地方,请随时在下面评论。我会尽快回复。你可以在 LinkedIn 上联系我。谢谢你的合作。祝你愉快。
15 分钟的概念和无痛介绍蒙特卡罗方法和应用贝叶斯推理
贝叶斯方法和推理
这篇文章应该作为我的教程使用(Py)Stan 的应用贝叶斯推理的无痛介绍和使用 r-INLA 的(近似)贝叶斯回归的无痛介绍的背景。
在这篇文章中,我将对贝叶斯推理和蒙特卡罗方法提供一个非常简短、自成一体的介绍,希望能启发你更深入地研究我所包含的一些参考资料。

你可能听说过也可能没有听说过贝叶斯推理/回归/建模/分析...您可能只是偶然遇到它,从未真正想过它的用途,或者没有任何好的资源来开始。
事实上,贝叶斯推理[1]比它的频率主义对应物要复杂一些,大多数人都曾在学术界或非常专业的工业应用中真正使用过它。然而,它提供了一个非常丰富的框架来理解并最终在推理过程中利用更多的信息。此外,它以一种令人愉快的方式提升了概率和应用统计学之间的联系——一个真正的贝叶斯模型是一个生成模型。
对于这一系列文章,我将采用(传统的)基于可能性的建模方法。
贝叶斯推理 5 分钟
贝叶斯推理允许我们将一些先入为主的,即先于的信念纳入到我们的建模中。我们可以从后验概率分布中进行采样,从而在完成实验后很好地了解我们感兴趣的参数的行为,而不是找到使我们的可能性最大化的参数值(最大可能性估计)。

模型拟合和推理中的原始贝叶斯恒等式。
简单来说,我为我的模型参数( 𝜃 )声明一个先验分布,并将其乘以我的观察数据的可能性(d)。为了让这个量作为概率密度函数,我必须用一个归一化常数来除它。然后我得到我的后验分布函数。
请注意,上面的归一化常数 P(D )(通常表示为 C)是难以处理的,除了在您有共轭先验的情况下,我们通常依靠蒙特卡罗方法从这种推断的后验分布中取样。在用各种各样的基本模型这样做的时候,我们可以排除这个量 C 而不会失去我们的睡眠。
你可能会问:为什么要这样做?
更好的科学,更古板的推理,正则化,数值稳定性,昂贵的数据采集不允许极大似然估计,还有很多很多更多的原因。
你可能会问:什么是好的前科?
- 易处理性?共轭!例如,正态先验 x 正态似然产生一个正态后验,可以解析求解!
- 科学相关性
- 正规化。在标准线性回归的情况下,回归器/beta 服务器上的 N(0,1)先验可以用作 L2 正则化。
理想情况下,先验知识不需要查看数据。
你可能会问:有什么样的前科?
- 无信息的(扁平的)前科。在大多数情况下,原始的无信息先验将是均匀分布的,更具体地说,在下面掷硬币问题的上下文中。
- 信息不足的前科。我在下面的抛硬币问题中声明的β先验提供的信息很少,因为我没有丢弃θ参数空间中的区间或点,而我倾向于“公平”。
- 信息丰富的前科。这些通常基于专家意见或扎实的科学知识。
举个例子,假设我在地板上发现了一枚硬币,我想知道它是否公平。我宣布 p=𝜃 是在一次给定的投掷中,硬币将产生“正面”的概率(y=1)。那么 q=1- 𝜃 就是硬币产生“反面”(y=0)的概率。注意,硬币的这种行为可以被建模为具有以下函数形式的伯努利随机变量:

假设我掷硬币 4 次,观察到它总是给我“反面”如果我采用最大似然框架,那么我有以下 ML 估计(样本比例):

在我们的例子中,上述结果为 0。但这有点太极端了……两面表面积相等意味着硬币应该有非零的“正面”落地概率(尽管非常小)。如果一边比另一边小得不成比例,我们会立即注意到。
现在,作为 Bayesians 人,说我们遇到了同样的硬币。在我们自己扔硬币之前,我们会形成一个关于硬币的先验信念。根据我的经验,硬币通常是公平的,所以我认为这枚硬币很可能是公平的,也有可能是不公平的。然后,我会进行我的实验,并获得一种感觉,这个硬币是否坚持我的信念。哦,我得到相同的结果{0,0,0,0}。
对于有效的(弱信息)先验,我将使用 beta(a,b)分布,其中 a,b 为非负整数。这个先验是有意义的,因为贝塔分布的支持度是[0,1],这里的 𝜃 对应一个概率不能小于 0 或者大于 1。设置 a=b=u,其中 u 是正整数,产生在 0.5 附近对称的分布。此外,贝塔分布与伯努利分布共轭(也与二项式分布共轭,因为二项式随机变量由伯努利随机变量的和组成)。

贝塔(5,5)分布的模拟
然后我考虑以下模型:

这导致了:

此处 C ≥0 表示归一化常数,对于本应用而言,不需要估计该常数。
如果我们在上面的先验中设置 a=b=5,stan[2]——如何操作参见教程——得出 𝜃 的近似后验均值0.36<5。因此,我们的估计表明一个有偏见的硬币,没有立即去 0。
蒙特卡罗方法 5 分钟
出于本教程的目的,蒙特卡罗方法[3]将允许我们从模型参数的后验分布中取样。
估计一个实值可积函数 f(x)的积分(I)的最简单的蒙特卡罗方法(假设在 0 ≤x≤1 上)表明,我们在[0,1]上对 x 的 K>0 个独立样本进行采样,对这些 x_{i}中的每一个估计 f(x}),然后对它们求和并求平均。我们来要求 I

As K goes to infinity, this vanilla Monte Carlo estimate should converge to the value of the integral.
The above is nice and easy, but we can actually use even more knowledge of statistics by observing the below:

We can think of the integral I as the expected value of f(x) and the below quantity as its unbiased estimator
Our estimator is just an approximation, so it would behoove us to quantify its accuracy:

Here we simply change the notation a bit and show the formula for the standard error
So, what can we do now that we have an unbiased estimate for the mean and an estimate of the standard error? Let’s invoke the 中心极限定理,对于 K 足够大是合理的。

使用上述方法,我们可以为积分的估计量建立 95%的置信区间:

上面的方法非常幼稚和不准确,特别是当目标(概率)在维度、多模态等方面增长时。然而,这给了你 MC 方法的要点:
从一个分布中抽取样本,以逼近另一个分布或感兴趣的数量。
请注意,蒙特卡罗方法是一个非常活跃的研究领域,具有非常丰富的理论和应用,我鼓励你阅读更多关于它们的内容。蒙特卡洛计算引擎上也有几个包装器(PyMC3、Stan、BUGS 等),可以很容易地集成到 Python、R 和其他现代统计/机器学习软件中。
下面是一些非常基本的方法让你开始:
- 重要性抽样
- 拒绝抽样
- Metropolis-Hastings 算法(马尔可夫链蒙特卡罗)
- 奖励:模拟退火(优化)
乳胶和数学交流
最后一件事。如果你真的想发展简洁而精确地交流数学/统计思想的能力,那么我鼓励你开始尝试使用 LaTeX [5]。LaTeX 是一个强大的排版系统,可以让你写出漂亮的公式,并无缝地呈现在你的文档中。我在本文档和我的所有作品中的所有数学表达式中都使用 LaTeX。
结论
随着更好的计算硬件和软件的出现,贝叶斯推理和计算最近经历了一次辉煌的复兴。此外,贝叶斯方法跨越了大量活跃的研究领域,包括统计学、数学、计算机科学、人口学、经济学和许多其他领域。
以下是现代 ML 和统计研究中的一些最热门的主题,它们依赖或大量使用贝叶斯框架进行推理:
- 因果推理
- 可解释性(DL 和 RL)
- 贝叶斯超参数优化(AlphaGo)
- 多任务学习
- RL 中的探索与开发
- 高效、计算稳定的 MCMC (HMC)
参考文献
[1] R. Neal, 贝叶斯推理教程,供 ML (2004),NeurIPS,2004 .
[2] B. Carpenter 等, Stan:一种概率编程语言 (2017),统计软件杂志。
[3] C .罗伯特和 g .卡塞拉, 蒙特卡洛统计方法 (2005),施普林格文本《统计学》。
[4] D .瓦克利等人, 数理统计与应用 (2014),森盖奇学习
[5]LaTeX 3 项目, LaTeX — A 文件编制系统 (1985),LaTeX 项目公共许可证(https://www.latex-project.org/lppl/)
聚类无监督学习简明指南!
借助聚类算法详细理解无监督学习的概念。

威廉·艾文在 Unsplash 上的照片
机器学习任务通常有一些数据集,其中我们有一些参数,对于那些结果参数,我们有它们各自的输出。从这些数据集中,我们建立的机器学习模型可以预测类似数据的结果。这个过程就是在监督学习中发生的。
监督学习的一个例子是用于确定患者是否出现肿瘤。我们有一个大型数据集,其中有一组与他们各自的结果相匹配的患者参数。我们可以假设这是一个简单的分类任务,1 代表肿瘤,0 代表无肿瘤。

照片由 Tran Mau Tri Tam 在 Unsplash 上拍摄
然而,假设我们有一个关于狗和猫的数据集。没有预先训练好的结果让我们来判断他们中的哪一个是猫还是狗。这种具有未标记数据集的问题可以在无监督学习的帮助下解决。用技术术语来说,我们可以将无监督学习定义为一种机器学习,在没有预先存在的标签和最少人工监督的情况下,在数据集中寻找以前未检测到的模式。聚类和关联是两种最重要的无监督学习算法。今天,我们将只关注集群。
聚类:

来源:维基百科
使用某些数据模式,机器学习算法能够找到相似之处,并将这些数据分组。换句话说,聚类分析或聚类是对一组对象进行分组的任务,使得同一组中的对象(称为聚类)彼此比其他组(聚类)中的对象更相似(在某种意义上)。
在聚类中,我们没有任何预测或标记数据。我们得到了一组输入数据点,使用这些数据点,我们需要找到最相似的匹配,并将它们分组到聚类中。聚类算法有广泛的应用,我们将在以后的章节中讨论。
让我们分析各种可用的聚类算法。我们将讨论三个最流行和最受欢迎的算法技术。我们还将了解用于无监督学习的性能指标,并最终讨论它们在现实世界中的应用。
聚类算法:
有许多聚类算法,但今天我们将主要关注三种最流行和最重要的聚类算法。这些聚类算法是—
- 基于质心的聚类(K 均值聚类)
- 基于连通性的聚类(层次聚类)
- 基于密度的聚类(DBSCAN)
我们将详细分析每一种算法,并理解它们到底是如何工作的。我们也将看看这些算法的优点和局限性。所以,事不宜迟,让我们开始吧!
1.k 均值聚类:

来源:维基百科
K-means 聚类算法是执行聚类分析的最流行的方法之一。在 K 均值聚类中,用于评估的超参数是“K”
‘K’=聚类数。
聚类的数量将决定将要执行的聚类分类的类型。在上图中,我们可以假设选择的 K 值为 3。K 值将决定分离和分组过程中考虑的中心数量。
超参数 K 的“正确”值可以用网格搜索或随机搜索等方法来确定。因此,我们可以说,K-means 的主要目标是找到最佳质心,并相应地以适合特定数据集的方式对聚类进行分组。
K 均值聚类要遵循的步骤—
1\. Initialization: Randomly picking the n points from the dataset and initialize them. Choose the K value as well.
2\. Assignment: For each selected point find the nearest centroid values. 
3\. Update: Re-compute the centroid values and update them accordingly. 
4\. Repetition: Repeat the step 2 and step 3 until you reach convergence. 
5\. Termination: Upon reaching convergence terminate the program. 

上图展示了 K-means 的收敛过程。
优势:
- 实现和执行相对简单。
- 有效且高效地处理大型数据集。
- 保证在某一点上收敛。
局限性:
- 选择最佳超参数“k”可能很困难。
- 计算高维数据的问题。
- 在存在异常值或噪声的情况下,更容易出错。
2.分层聚类:

来源:维基百科
基于连通性的聚类,也称为层次聚类,其核心思想是对象与附近的对象比与更远的对象更相关。
层次聚类的概念基本上是将相似的事物从较大的块分组到较小的块,反之亦然。当我们看下面两种类型的层次聚类方法时,可以更好地理解这一点:
- 凝聚聚类—这是一种“自下而上”的方法:每个观察从自己的聚类开始,随着一个观察在层次结构中向上移动,聚类对被合并。
- 分裂聚类——这是一种“自上而下”的方法:所有的观察都从一个聚类开始,随着一个聚类向下移动,分裂被递归地执行。
凝聚聚类通常优于分裂聚类。因此,我们将进一步研究凝聚性集群而不是分裂性集群的分析。

来源:维基百科
上面的树状图展示了层次聚类的工作原理,特别是凝聚聚类的工作原理。
让我们来理解聚集成簇过程中所涉及的步骤
1\. Compute the [proximity matrix](https://hlab.stanford.edu/brian/proximity_matrix.html) which is basically a matrix containing the closest distance of each of the similarities i.e., the inter/intra cluster distances. 
2\. Consider each point to be a cluster.
3\. Repeat the following step for every point. 
4\. Merge the two closest points. 
5\. Update the proximity matrix. 
6\. Continue until only a single cluster remains. 
优点:
- 查看树状图更容易判断集群的数量。
- 总体上易于实现。
限制:
- 对异常值非常敏感。
- 不适合较大的数据集。
- 它具有很高的时间复杂度,这对于某些应用来说是不理想的。
3. DBSCAN:

来源:维基百科
DBSCAN 代表基于密度的带噪声应用程序空间聚类,并且越来越受欢迎。在 DBSCAN 中,我们为密度高于数据集其余部分的区域创建聚类。需要分离聚类的稀疏区域中的对象通常被认为是噪声和边界点。
DBSCAN 在最小点(或 MinPts)和ε中使用了两个重要的超参数。在我们查看如何解决这些问题的步骤之前,让我们分析一下这些超参数到底是如何工作的。
- MinPts: 数据集越大,MinPts 的值应该选得越大。minPts 必须至少选择 3 个。
- ε'ϵ':然后可以通过使用 k-距离图来选择ϵ的值,绘制到 k = minPts 最近邻居的距离。ϵ的好值是图中显示一个像肘形的强弯曲的地方。

来源:维基百科
对于 DBSCAN 算法的实现,我们需要遵循的逐步过程如下所述:
1\. Find the points in the ε (eps) neighborhood of every point, and identify the core points with more than minPts neighbors.
2\. Label each of the selected points as a core point or border point or noise point. This initial labeling is an important step for the overall functionality.
3\. Remove all the noise points from your data because sparse regions do not belong to any clusters. 
4\. for each core point 'p' not assigned to a cluster create a loop as follows - 
   a. Create a new cluster with the point 'p'. 
   b. Add all points that are density connected to into this newly created cluster. 
5\. Assign each non-core point to a nearby cluster if the cluster is an ε (eps) neighbor, otherwise assign it to noise. Repeat the procedure until convergence is reached. 
优点:
- 基于密度的聚类方法对噪声和异常值有很强的抵抗力。
- 它通常适用于任何形状,不像前面提到的两种算法在处理非球形(非凸形)形状时有困难。
- 与 K-means 不同,它们对要设置的聚类数没有特定的要求。
限制:
- DBSCAN 不能很好地对密度差异较大的数据集进行聚类。
- 它不是完全确定的,并且在高维数据集上容易出错。
- 由于它有两个可变的超参数,因此容易受到它们的变化的影响。
绩效指标:
监督学习中使用的性能指标,如 AUC(ROC 曲线下面积)或 ROC(接收器操作特性)曲线,不适用于无监督学习。因此,为了评估无监督学习的性能度量,我们需要计算一些参数,如类内和类间参数。看看下面的参考图。

作者图片
类内:属于同一类的两个相似数据点之间的距离。
类间:属于不同类的两个相异数据点之间的距离。
任何好的聚类算法的主要目标是减少类内距离和最大化类间距离。用于聚类的主要性能指标之一是邓恩指数参数。
邓恩指数:邓恩指数旨在识别密集且分离良好的集群。它被定义为最小类间距离与最大类内距离之比。对于每个聚类分区,邓恩指数可以通过以下公式计算:

比率中的第一行试图最小化类间距离,第二部分试图增加类内距离。由于内部标准寻找具有高的组内相似性和低的组间相似性的组,所以产生具有高 Dunn 指数的组的算法是更理想的。
如果有人对这种说法感到困惑,请将邓恩指数“D”视为衡量两个参数最坏情况的参数,因此当“D”较高时,它被视为理想的聚类。也有其他性能指标可供选择,如戴维斯–波尔丁指数,但在大多数情况下,邓恩指数通常更受青睐。
聚类的应用:
- 数据挖掘:从现有数据集中提取有用的数据元素。聚类算法可用于选择对任务有用的数据组,其余的数据可以忽略。
- 模式识别:我们还可以利用聚类算法找出对象之间的明显模式。模式识别是自动识别数据中的模式和规律。
- 图像分析:可以将相似性和相似类型的图像分组到一起,得到想要的结果。这方面的一个例子是猫和狗的隔离,这在前面的章节中已经提到过。
- 信息检索:信息检索是从资源集合中获取与信息需求相关的信息系统资源的活动。搜索可以基于全文或其他基于内容的索引。聚类可用于类似的自然语言处理任务,以获得选定的重复模式。
- 生物医学应用和生物信息学:聚类在分析医学数据和扫描以确定模式和匹配的领域中极其有益。这方面的一个例子是 IMRT 分割,聚类可用于将注量图分成不同的区域,以便在基于 MLC 的放射治疗中转换成可实施的射野。
- 异常检测:聚类是推断模式和检测异常值存在的最佳方式之一,通过将相似的组分组为聚类,同时忽略异常值,即数据集中存在的不必要的噪声信息。
- 机器人学:机器人学领域以跨学科的方式利用上述所有学科的集群。机器人需要在没有标记数据的情况下自己寻找模式,聚类会有很大帮助。它们还用于机器人情境感知,以跟踪物体和检测传感器数据中的异常值。
- 电子商务:我们要讨论的最后一个但肯定不是最不重要的应用是电子商务。聚类广泛用于市场营销和电子商务,以确定客户在其业务中的规格。这些独特的模式允许这些公司决定向他们特定的客户销售什么产品。
集群还有很多应用,我强烈推荐大家去看看在集群领域可以利用的各种应用。
结论:

作者图片
聚类是机器学习的一个极其重要的概念,可以用于各种任务。它在数据挖掘过程和探索性数据分析的初始阶段也非常有用。上图是对数据应用聚类后使用 TSNE 构建的图表图像。
在上述三种算法的帮助下,您应该能够非常容易地解决大多数集群任务。提到的技术和应用让我们简要了解为什么聚类算法有助于对未标记数据集进行分类。我希望这篇文章有助于解释这些概念。
看看这些你可能感兴趣的最近的文章吧!
[## 4 个基本正则表达式操作符使自然语言处理变得更简单!
了解四种基本的常规操作,以清理几乎任何类型的可用数据。
towardsdatascience.com](/natural-language-processing-made-simpler-with-4-basic-regular-expression-operators-5002342cbac1) [## 带有完整代码片段和有用链接的 5 个最佳 Python 项目创意!
为 Python 和机器学习创建一份令人敬畏的简历的 5 个最佳项目想法的代码片段和示例!
towardsdatascience.com](/5-best-python-project-ideas-with-full-code-snippets-and-useful-links-d9dc2846a0c5) [## 人工智能是破解宇宙奥秘的关键,下面是原因!
人工智能、数据科学和深度学习的工具是否先进到足以破解人类大脑的秘密
towardsdatascience.com](/artificial-intelligence-is-the-key-to-crack-the-mysteries-of-the-universe-heres-why-56c208d35b62) [## OpenCV:用代码掌握计算机视觉基础的完全初学者指南!
包含代码的教程,用于掌握计算机视觉的所有重要概念,以及如何使用 OpenCV 实现它们
towardsdatascience.com](/opencv-complete-beginners-guide-to-master-the-basics-of-computer-vision-with-code-4a1cd0c687f9) [## 迷失在密林中:用简单的代码对机器学习中稀疏性的直觉!
为什么 ML 需要稀疏性?理解稀疏性的核心概念。
towardsdatascience.com](/lost-in-a-dense-forest-intuition-on-sparsity-in-machine-learning-with-simple-code-2b44ea7b07b0)
谢谢你们坚持到最后。我希望你喜欢读这篇文章。祝大家有美好的一天!
基于机器学习的混凝土抗压强度预测

里卡多·戈麦斯·安吉尔在 Unsplash 上的照片
机器学习在土木工程中的应用
混凝土抗压强度
混凝土的抗压强度决定了混凝土的质量。这通常由混凝土圆柱体上的标准压碎试验来确定。这需要工程师用不同的原材料组合建造小型混凝土圆柱体,并测试这些圆柱体的强度随每种原材料的变化而变化。测试钢瓶的建议等待时间是 28 天,以确保正确的结果。这消耗了大量的时间,并且需要大量的劳动力来准备不同的原型并测试它们。此外,这种方法容易出现人为错误,一个小错误就可能导致等待时间急剧增加。
减少等待时间和减少尝试组合数量的一种方法是利用数字模拟,我们可以向计算机提供我们所知道的信息,计算机尝试不同的组合来预测抗压强度。这样,我们可以减少物理上可以尝试的组合数量,并减少实验时间。但是,要设计这样的软件,我们必须知道所有原材料之间的关系,以及一种材料如何影响强度。可以推导出数学方程,并基于这些方程进行模拟,但我们不能期望现实世界中的关系是相同的。此外,这些测试已经进行了很多次,我们有足够的现实世界的数据可以用于预测建模。
在本文中,我们将分析混凝土抗压强度数据集,并建立机器学习模型来预测抗压强度。这个笔记本包含了所有的代码,可以并行使用。
数据集描述
数据集由 1030 个实例组成,具有 9 个属性,没有缺失值。有 8 个输入变量和 1 个输出变量。七个输入变量代表原材料的数量(以 kg/m 计量),一个代表龄期(以天计)。目标变量是混凝土抗压强度,单位为兆帕(MPa —兆帕)。我们将研究这些数据,看看输入特征是如何影响抗压强度的。
探索性数据分析
数据科学项目的第一步是在建模之前理解数据并从数据中获得洞察力。这包括检查任何缺失值、绘制与目标变量相关的特征、观察所有特征的分布等等。让我们导入数据并开始分析。
让我们检查输入特征之间的相关性,这将给出每个变量如何影响所有其他变量的想法。这可以通过计算特征之间的皮尔逊相关来实现,如下面的代码所示。
corr = data.corr() sns.heatmap(corr, annot=True, cmap='Blues')

我们可以观察到抗压强度 (CC_Strength)和水泥之间的高度正相关。这是真的,因为混凝土的强度确实随着制备过程中水泥用量的增加而增加。另外,龄期和超塑化剂是影响抗压强度的另外两个因素。
这些特征之间还有其他强相关性,
- 超塑化剂与水呈强负相关。
- 超塑化剂与粉煤灰、细骨料呈正相关关系。
这些相关性有助于详细理解数据,因为它们给出了一个变量如何影响另一个变量的想法。我们可以进一步使用 seaborn 中的 pairplot 来绘制所有特性之间的成对关系以及特性沿对角线的分布。
sns.pairplot(data)

配对图直观地展示了所有特征之间的相关性。
我们可以在 CC_Strength 和其他特征之间绘制散点图,以查看更复杂的关系。
CC _ 强度 vs(水泥、龄期、水)
sns.scatterplot(y="CC_Strength", x="Cement", hue="Water",size="Age", data=data, ax=ax, sizes=(50, 300))

从这幅图中我们可以观察到,
- 抗压强度随着水泥量的增加而增加,当我们在 x 轴上向右移动时,圆点向上移动。
- 抗压强度随着年龄的增长而增加(因为圆点的大小代表年龄),情况并非总是如此,但在一定程度上是可以的。
- 时间越短的水泥需要越多的水泥来获得更高的强度,因为当我们在 x 轴上向右移动时,较小的点会向上移动。
- 水泥越老,需要的水就越多,可以通过观察圆点的颜色来确认。深色的大点表示年龄大,水多。
- 制备混凝土时使用较少的水混凝土强度增加,因为较低侧(y 轴)的点较暗,较高端(y 轴)的点较亮。
CC 强度 vs(细骨料、超塑化剂、粉煤灰)
sns.scatterplot(y="CC_Strength", x="FineAggregate", hue="FlyAsh",
   size="Superplasticizer", data=data, ax=ax, sizes=(50, 300))

观察,
- 抗压强度降低飞灰增加,因为黑点集中在代表低抗压强度的区域。
- 抗压强度随着超塑化剂的增加而增加,因为点越大,曲线中的点越高。
我们可以直观地理解 2D、3D 和 max 直到 4D 图(由颜色和大小表示的特征)如上所示,我们可以进一步使用 seaborn 的行和列绘图特征来做进一步的分析,但我们仍然缺乏自己跟踪所有这些相关性的能力。出于这个原因,我们可以转向机器学习来捕捉这些关系,并对问题给出更好的见解。
数据预处理
在我们对数据拟合机器学习模型之前,我们需要将数据分割成训练和测试分割。可以重新调整特征的比例,使其均值为零,标准差为 1,即所有特征都落在相同的范围内。
X = data.iloc[:,:-1] # Features 
y = data.iloc[:,-1] # Target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=2) sc = StandardScaler() X_train = sc.fit_transform(X_train) 
X_test = sc.transform(X_test)
模型结构
在准备好数据之后,我们可以在训练数据上拟合不同的模型,并比较它们的性能,以选择性能好的算法。由于这是一个回归问题,我们可以使用 RMSE(均方根误差)和$R $分数作为评估指标。
1.线性回归
我们将从线性回归开始,因为这是任何回归问题的首选算法。该算法试图在输入特征和目标变量之间形成线性关系,即它拟合由下式给出的直线,

线性回归
其中 w_i 对应于特征 x_i 的系数。
通过对成本函数使用正则化项,可以进一步控制这些系数的大小。将系数的大小相加将导致系数接近于零,这种线性回归的变化称为拉索回归。将系数的平方和添加到成本函数将使系数在相同的范围内,这种变化被称为岭回归。这两种变化都有助于降低模型的复杂性,从而减少数据过度拟合的机会。
# Importing models 
from sklearn.linear_model import LinearRegression, Lasso, Ridge # Linear Regression 
lr = LinearRegression() # Lasso Regression 
lasso = Lasso() # Ridge Regression 
ridge = Ridge() # Fitting models on Training data 
lr.fit(X_train, y_train) 
lasso.fit(X_train, y_train) 
ridge.fit(X_train, y_train) # Making predictions on Test data y_pred_lr = lr.predict(X_test) 
y_pred_lasso = lasso.predict(X_test) 
y_pred_ridge = ridge.predict(X_test) from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score print("Model\t\t\t RMSE \t\t R2") 
print("""LinearRegression \t {:.2f} \t\t{:.2f}""".format(  np.sqrt(mean_squared_error(y_test, y_pred_lr)), r2_score(y_test, y_pred_lr))) print("""LassoRegression \t {:.2f} \t\t{:.2f}""".format( np.sqrt(mean_squared_error(y_test, y_pred_lasso)), r2_score(y_test, y_pred_lasso))) print("""RidgeRegression \t {:.2f} \t\t{:.2f}""".format( np.sqrt(mean_squared_error(y_test, y_pred_ridge)), r2_score(y_test, y_pred_ridge)))
输出

这三种算法的性能没有太大差别,我们可以用下面的代码画出这三种算法为特性分配的系数。
coeff_lr = lr.coef_ 
coeff_lasso = lasso.coef_ 
coeff_ridge = ridge.coef_ labels = req_col_names[:-1] 
x = np.arange(len(labels)) 
width = 0.3 fig, ax = plt.subplots(figsize=(10,6)) 
rects1 = ax.bar(x - 2*(width/2), coeff_lr, width, label='LR') 
rects2 = ax.bar(x, coeff_lasso, width, label='Lasso') 
rects3 = ax.bar(x + 2*(width/2), coeff_ridge, width, label='Ridge') ax.set_ylabel('Coefficient') 
ax.set_xlabel('Features') 
ax.set_title('Feature Coefficients') 
ax.set_xticks(x) 
ax.set_xticklabels(labels, rotation=45) 
ax.legend() def autolabel(rects): 
   """Attach a text label above each bar in *rects*, displaying its height.""" 
   for rect in rects: 
      height = rect.get_height() 
      ax.annotate('{:.2f}'.format(height), xy=(rect.get_x() + rect.get_width() / 2, height), xytext=(0, 3), textcoords="offset points", ha='center', va='bottom') autolabel(rects1) 
autolabel(rects2) 
autolabel(rects3) 
fig.tight_layout() 
plt.show()

如图所示,Lasso 回归将系数推向零,而正常线性回归和岭回归的系数几乎相同。
我们可以通过绘制真实值和预测值来进一步了解预测情况,
fig, (ax1, ax2, ax3) = plt.subplots(1,3, figsize=(12,4)) ax1.scatter(y_pred_lr, y_test, s=20) 
ax1.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2) 
ax1.set_ylabel("True") 
ax1.set_xlabel("Predicted") 
ax1.set_title("Linear Regression") 
ax2.scatter(y_pred_lasso, y_test, s=20) ax2.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2) ax2.set_ylabel("True") 
ax2.set_xlabel("Predicted") 
ax2.set_title("Lasso Regression") 
ax3.scatter(y_pred_ridge, y_test, s=20) ax3.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2) 
ax3.set_ylabel("True") 
ax3.set_xlabel("Predicted") 
ax3.set_title("Ridge Regression") 
fig.suptitle("True vs Predicted") 
fig.tight_layout(rect=[0, 0.03, 1, 0.95])

如果预测值和目标值相等,那么散点图上的点将位于直线上。正如我们在这里看到的,没有一个模型能正确预测抗压强度。
2.决策树
决策树算法用树状结构表示数据,其中每个节点表示对某个功能做出的决策。在这种情况下,这种算法将提供更好的性能,因为我们在一些输入特征中有许多零,如上面的对图中的分布所示。这将有助于决策树基于某些特征条件构建树,从而进一步提高性能。
from sklearn.tree import DecisionTreeRegressor 
dtr = DecisionTreeRegressor() 
dtr.fit(X_train, y_train) 
y_pred_dtr = dtr.predict(X_test) print("Model\t\t\t\t RMSE \t\t R2") 
print("""Decision Tree Regressor \t {:.2f} \t\t{:.2f}""".format( np.sqrt(mean_squared_error(y_test, y_pred_dtr)), r2_score(y_test, y_pred_dtr))) plt.scatter(y_test, y_pred_dtr) 
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2) 
plt.xlabel("Predicted") 
plt.ylabel("True") 
plt.title("Decision Tree Regressor") plt.show()


均方根误差(RMSE)从 10.29 下降到 7.31,因此决策树回归器显著提高了性能。这可以在图中观察到,并且更多的点更靠近线。
3.随机森林
使用决策树回归器提高了我们的性能,我们可以通过集成更多的树来进一步提高性能。随机森林回归器使用从训练数据中采样的随机数据子集来训练随机初始化的树,这将使我们的模型更加健壮。
from sklearn.ensemble import RandomForestRegressor rfr = RandomForestRegressor(n_estimators=100) 
rfr.fit(X_train, y_train) y_pred_rfr = rfr.predict(X_test) print("Model\t\t\t\t RMSE \t\t R2") print("""Random Forest Regressor \t {:.2f} \t\t{:.2f}""".format( np.sqrt(mean_squared_error(y_test, y_pred_rfr)), r2_score(y_test, y_pred_rfr))) plt.scatter(y_test, y_pred_rfr) 
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2) 
plt.xlabel("Predicted") 
plt.ylabel("True") 
plt.title("Random Forest Regressor") 
plt.show()


通过集合多棵树,RMSE 进一步减少。我们可以为基于树的模型绘制特征重要性。特征重要性显示了在进行预测时特征对模型的重要性。
feature_dtr = dtr.feature_importances_ 
feature_rfr = rfr.feature_importances_ labels = req_col_names[:-1] 
x = np.arange(len(labels)) 
width = 0.3 
fig, ax = plt.subplots(figsize=(10,6)) 
rects1 = ax.bar(x-(width/2), feature_dtr, width, label='Decision Tree') 
rects2 = ax.bar(x+(width/2), feature_rfr, width, label='Random Forest') 
ax.set_ylabel('Importance') 
ax.set_xlabel('Features') 
ax.set_title('Feature Importance') 
ax.set_xticks(x) 
ax.set_xticklabels(labels, rotation=45) 
ax.legend(loc="upper left", bbox_to_anchor=(1,1)) 
autolabel(rects1) 
autolabel(rects2) 
fig.tight_layout() 
plt.show()

基于树的模型将水泥和年龄视为最重要的特征。在预测混凝土强度时,粉煤灰、粗骨料和细骨料是最不重要的因素。
比较
最后,让我们比较所有算法的结果。
models = [lr, lasso, ridge, dtr, rfr] 
names = ["Linear Regression", "Lasso Regression", "Ridge Regression", "Decision Tree Regressor", "Random Forest Regressor"] rmses = [] for model in models: 
   rmses.append(np.sqrt(mean_squared_error(y_test, model.predict(X_test)))) x = np.arange(len(names)) 
width = 0.3 
fig, ax = plt.subplots(figsize=(10,7)) 
rects = ax.bar(x, rmses, width) 
ax.set_ylabel('RMSE') 
ax.set_xlabel('Models') 
ax.set_title('RMSE with Different Algorithms') 
ax.set_xticks(x) 
ax.set_xticklabels(names, rotation=45) 
autolabel(rects) 
fig.tight_layout() 
plt.show()

结论
我们分析了抗压强度数据,并使用机器学习来预测混凝土的抗压强度。我们使用了线性回归及其变体、决策树和随机森林来进行预测,并比较了它们的性能。随机森林回归器具有最低的 RMSE,是解决该问题的好选择。此外,我们可以通过执行网格搜索或随机搜索来调整超参数,从而进一步提高算法的性能。
参考
- 叶一成,“使用人工神经网络对高性能混凝土的强度进行建模”,水泥与混凝土研究,第 28 卷,第 12 期,第 1797–1808 页(1998 年)。
- Ahsanul Kabir,医学博士 Monjurul Hasan,Khasro Miah,“混凝土的强度预测模型”,ACEE Int .土木与环境工程学报,第 2 卷第 1 期,2013 年 8 月。
- https://archive . ics . UCI . edu/ml/datasets/Concrete+抗压+强度
原载于 2020 年 3 月 5 日https://pranaymudukuru . github . io。
Golang 中的并发数据管道
利用 Golang 的并发模型为数据密集型应用创建并发数据管道

米卡·鲍梅斯特在 Unsplash 上的照片
数据是所有应用程序的重要组成部分。应用程序主要接收数据,处理数据,然后从中生成某种输出。如今可用的数据量是巨大的,这在试图理解数据并将其转化为有用信息时带来了许多挑战。除了通常的数据系统流程之外,我们还必须考虑由这些系统的执行所产生的数据,比如日志和指标。一切都是数据。
因此,对数据驱动的应用程序的需求越来越大。系统只处理移动和转换数据的情况并不少见。从一个地方到另一个地方或者从一个建筑到另一个建筑。这在分布式系统中更为常见,因为每个系统都为自己的目的生成数据,并以更易于处理的方式进行组织。然而,这些系统仍然必须进行通信,因此数据必须进行转换和移动。此外,有时数据必须转移到不同的环境或不同的平台。更麻烦!
有非常复杂的工具来完成这项工作,但通常配置和维护它们都是一件非常痛苦的事情,更不用说所涉及的成本了。因此,它们并不总是最佳选择。有时,根据用例,您可以创建自己的管道,用更简单的解决方案来移动数据。在本文中,我们将了解如何使用 golang 构建数据管道。
数据管道
管道是表达我们想要如何处理数据的一种非常好的方式。根据定义,管道是一系列被执行的步骤或动作。通常上一步的输出被用作下一步的输入。当设计管道时,每一步都不必担心其他步骤的工作,只依赖于输入和输出契约。这使得设计非常健壮,因为它是基于契约和抽象的。此外,这是一个非常灵活的设计,因为你可以添加更多的步骤,只要你不违反合同。好玩!
做这件事有无数种方法。在这篇文章中,我们将关注如何利用 go 的并发模型来创建数据管道,我在这里写的是关于。
使用 goroutines 和通道创建数据管道
正如我们前面提到的,数据管道是一系列的步骤,使用前一个步骤的输出作为下一个步骤的输入来执行。这听起来像是 goroutines 和 channels 的一个很好的用法。我们可以将每个步骤创建为一个 goroutine,并将它们之间的通信创建为通道。这意味着我们的数据管道可以并发执行,如果明智地使用,这是一个很大的好处。让我们跳到一个例子来简化一下。
在本例中,我们将创建一个简单的管道,用于从文本文件中读取一些 GUID,将它们转换为我们要用于处理的输入数据结构,从外部服务中获取相关数据,最后使用批处理操作将数据保存在数据存储中。以下是高级工作流的外观:

高级工作流
正如我们在这张图上看到的,每个操作都将使用一个 goroutine(由 gophers 表示)创建,并使用通道连接在一起。这样做的整体思路是,每个函数(除了数据生成器之外)将接收其输入通道作为参数,并返回输出通道作为函数的返回值。在每个函数中,我们将旋转一个 goroutine 来实际监听输入通道,并对收到的每一项进行操作,同时在输出通道上发送结果。
为简单起见,所有通道都是无缓冲的,这意味着存储在guids.txt文件中的每个 GUID 都将单独通过管道。唯一的批处理操作是在最后,我们将它们保存到数据存储的批处理中。此外,为了简单起见,错误处理已经被简化,请记住,对于生产管道,您可能希望使其更加健壮。好了,解释够了,让我们开始这个例子。你可以在我的 github 库中找到完整的源代码。
下面是guids.txt文件的样子:
ea464197-d864-4f8a-8a60-2d3a6f3d87bf
74c1a7bd-b536-4f9e-be36-5e16c491d833
c524c1e9-e473-42eb-b74d-97d82575b720
d5003029-7e10-4128-8a18-392e384ef0bd
b1fbf405-7b3c-4b82-8b51-4c5c2c0cca56
b4f6a28c-86b2-4741-89e5-612451346eb7
353629f7-c9ba-4ed1-8e1b-b7694381a36a
d3b305cb-eb1b-4f8a-8a23-4741960c26a4
66c03c1a-45f7-4255-b568-669d9dc553d9
9272d9f4-da5a-4449-8826-f8529ce4f428
我们管道的第一部分是读取该文件的每一行,将其解析为一个uuid.UUID类型,并通过一个通道发送出去。这个函数可能是这样的:
这里重要的部分是我们正在创建输出通道(第 13 行)并返回它(第 37 行),以及在实际执行读取文件和发送解析后的uuid.UUID值(第 15 行)工作的 goroutine 上使用它。这非常强大,因为它允许我们直接使用通道作为另一个函数输入,而不用担心内部工作是如何完成的。同样重要的是,一旦工作完成,我们就关闭通道,这样我们就可以在通道上安全地使用for range语句,而不用担心无限循环。让我们进入第二步,准备数据:
这里,函数签名略有变化。我们正在接收我们想要用作输入的频道。然而,创建通道输出通道(第 16 行)并返回它(第 26 行)的内部技术仍然是相同的。对于这个特定的步骤,我们只使用在输入通道上接收到的uuid.UUID,并将它们转换成一个虚拟结构inputData,同时记录正在处理的内容。这是一个非常基本的例子,但是它说明了你可以为数据转换做什么(数据管道中非常常见的一步),你只需要为你想要的任何结构改变应用相同的逻辑。让我们来看一个更深入的例子,关于我们的第三步,数据获取:
这个函数使用了和以前几乎一样的技术,我们接收一个输入通道并返回一个输出通道。在这种情况下,为了简单起见,我决定模拟外部调用,因此我们实际上是在内存中为给定的 GUID 生成随机相关的值。有趣的是,我们实际上使用并发来调用这个(假的)外部服务。这允许管道的其余部分在我们等待从外部值返回特定值时仍然运行。我们仍然希望在关闭我们的输出通道之前,等待对外部服务的所有调用完成(也就是说,我们对这部分管道的工作已经完成)。
为了做到这一点,我引入了一个非常常见的模式,使用sync.WaitGroup来处理需要在某个时间点同步的异步调用。我们在调用第 29 行上的close(oc)之前等待(同步)。这样,我们可以确保在关闭通道之前,所有对外部服务的调用都已完成。发生这种情况是因为这个特定的步骤可能比数据管道上的前一个步骤运行得慢,因为它依赖于外部服务,所以在我们调用外部服务处理所有值之前,可能会从输入通道接收所有值。
这是一个非常有趣的步骤,让我们来看看管道的最后一步,数据存储:
在这一步中,我们将介绍一种不同的技术,批处理。这对于为每个项目调用每个操作在性能或成本方面效率不高的服务来说可能很有用。因此,为了解决这个问题,我们可以将一些项目成批放在一起。我创建了一个简单的批处理机制,它基于我们想要添加到批处理中的数据量。你可以使用任何你想要的机制,我相信逻辑会非常相似。在本例中,我们一次批处理 7 个项目,所以每次我们有 7 个项目要持久存储到数据库中时,我们调用 persisting 函数(false)并打开下一批。在结束之前,我们检查是否有一个开放的批处理,并保存在这种情况下。此外,我们将返回一个通道,其中包含保存了哪些项目的信息以及时间戳。现在我们只需要把这条管道组装起来:
如你所见,我们以相反的顺序调用管道。如果您愿意,可以更改参数以使其更加清晰。然而,在这种情况下,它几乎像一个装饰模式。如果我们从generateData()函数开始,它不接收通道,但返回一个。因此从该函数返回的通道被用作prepareData的输入参数。该功能的输出通道用作fetchData的参数。输出通道最终被saveData用作输入。最后一个函数也返回一个通道,我们将在这个通道上打印哪些项目已经保存。如果我们执行这个示例,我们将会看到类似这样的内容:
./data-pipeline-golang
2020/06/10 09:49:50 Data ready for processing: {id:ea464197-d864-4f8a-8a60-2d3a6f3d87bf timestamp:1591778990056111000}2020/06/10 09:49:50 Data ready for processing: {id:74c1a7bd-b536-4f9e-be36-5e16c491d833 timestamp:1591778990056467000}2020/06/10 09:49:50 Data ready for processing: {id:c524c1e9-e473-42eb-b74d-97d82575b720 timestamp:1591778990056587000}2020/06/10 09:49:50 Data ready for processing: {id:d5003029-7e10-4128-8a18-392e384ef0bd timestamp:1591778990056744000}2020/06/10 09:49:50 Data ready for processing: {id:b1fbf405-7b3c-4b82-8b51-4c5c2c0cca56 timestamp:1591778990056773000}2020/06/10 09:49:50 Data ready for processing: {id:b4f6a28c-86b2-4741-89e5-612451346eb7 timestamp:1591778990056803000}2020/06/10 09:49:50 Data ready for processing: {id:353629f7-c9ba-4ed1-8e1b-b7694381a36a timestamp:1591778990056814000}2020/06/10 09:49:50 Data ready for processing: {id:d3b305cb-eb1b-4f8a-8a23-4741960c26a4 timestamp:1591778990056984000}2020/06/10 09:49:50 Data ready for processing: {id:66c03c1a-45f7-4255-b568-669d9dc553d9 timestamp:1591778990057220000}2020/06/10 09:49:50 Items saved: {idsSaved:[66c03c1a-45f7-4255-b568-669d9dc553d9 c524c1e9-e473-42eb-b74d-97d82575b720 353629f7-c9ba-4ed1-8e1b-b7694381a36a b4f6a28c-86b2-4741-89e5-612451346eb7 74c1a7bd-b536-4f9e-be36-5e16c491d833 b1fbf405-7b3c-4b82-8b51-4c5c2c0cca56 ea464197-d864-4f8a-8a60-2d3a6f3d87bf] timestamp:1591778990097935000}2020/06/10 09:49:50 Items saved: {idsSaved:[d3b305cb-eb1b-4f8a-8a23-4741960c26a4 d5003029-7e10-4128-8a18-392e384ef0bd] timestamp:1591778990105146000}
我们可以看到,prepareData函数记录了 9 个准备处理的项目,而main函数记录了 2 个实际保存的批次,第一个包含 7 个项目,第二个包含 2 个项目。
最后的想法
对于处理数据驱动或数据密集型应用程序的工程师来说,创建数据管道是一项非常常见的任务。数据管道对于将数据从一个系统转移到另一个系统、从一个平台转移到另一个平台,甚至从一个环境转移到另一个环境是必不可少的。如果我们理解了 golang 的并发模型,我们就可以利用它以一种非常简单明了的方式创建并发数据管道。这里使用的主要模式是为每个步骤创建并返回一个通道,这样我们可以很容易地将所有步骤连接在一起。使用这种范式,我们可以用各种酷的东西来提升我们的管道,比如步骤的并行执行,特定管道步骤的多个工人,等等。
这种技术并不总是正确的选择,有许多工具可以根据具体情况成为更简单的解决方案。
Conda + Google Colab
使用 Google Colab 时安装 Conda 的指南

让 Conda 在 Google Colab 上工作有点乏味,但如果你不能与 pip 相处,这是必要的。来源
Conda 是许多流行的数据科学工具的推荐环境和包管理解决方案,包括 Pandas 、 Scikit-Learn 、 PyTorch 、 NVIDIA Rapids 和许多其他工具。Conda 还大大简化了安装流行的深度学习工具的过程,如 TensorFlow 。
Google Colab 是一项免费服务,通过与 Jupyter 笔记本非常相似的用户界面提供交互式计算资源,运行在谷歌云平台(GCP)上,并提供对 GPU 和 TPU 的免费访问。Google Colab 是一个很棒的教学平台,也可能是唯一一个与你的同行共享 GPU 或 TPU 加速代码的免费解决方案。不幸的是,Conda 在 Google Colab 上默认是不可用的,在 Google Colab 的默认 Python 环境中安装并正常工作 Conda 有点麻烦。
在这篇文章中,我将向您介绍我在 Google Colab 中工作时需要使用 Conda 安装包时所经历的过程。
预赛
首先,您需要确认 Google Colab 中默认使用的是哪种 Python。运行以下命令将返回默认 Python 可执行文件的绝对路径。
!which python # should return /usr/local/bin/python
现在检查这个默认 Python 的版本号。
!python --version
在编写时,上述命令返回Python 3.6.9。这意味着,为了使用所有预装的 Google Colab 包,您需要安装一个默认情况下与 Python 3.6 兼容的 Miniconda 版本。默认情况下,Miniconda 的最新版本(即 4.5.12+)面向 Python 3.7 或 Python 3.8。针对 Python 3.6 的 Miniconda 的最新版本是 Miniconda 4.5.4,因此这是您应该安装的版本。
最后,检查是否已经设置了PYTHONPATH变量。
!echo $PYTHONPATH
在编写这个命令时,它只返回/env/python(奇怪的是,这个目录似乎并不存在于 Google Colab 文件系统中)。
通常情况下,在安装 Miniconda 之前取消设置PYTHONPATH变量是一个好主意,因为如果通过PYTHONPATH中包含的目录安装和访问的软件包与 Miniconda 中包含的 Python 版本不兼容,这可能会导致问题。
您可以使用以下命令取消设置PYTHONPATH变量。这一步是可选的,但是如果你不取消设置这个变量,那么你会在安装 Miniconda 后看到一个警告消息。
%env PYTHONPATH=
安装 Miniconda
当在 Google Colab 单元中执行时,下面的代码将下载 Miniconda 适当版本的安装程序脚本,并将其安装到/usr/local中。直接安装到/usr/local,而不是默认位置~/miniconda3,确保 Conda 及其所有必需的依赖项在 Google Colab 中自动可用。
%%bashMINICONDA_INSTALLER_SCRIPT=[Miniconda3-4.5.4-Linux-x86_64.sh](https://repo.continuum.io/miniconda/Miniconda3-$MINICONDA_VERSION-Linux-x86_64.sh)
MINICONDA_PREFIX=/usr/local
wget [https://repo.continuum.io/miniconda/](https://repo.continuum.io/miniconda/Miniconda3-$MINICONDA_VERSION-Linux-x86_64.sh)$MINICONDA_INSTALLER_SCRIPT
chmod +x $MINICONDA_INSTALLER_SCRIPT
./$MINICONDA_INSTALLER_SCRIPT -b -f -p $MINICONDA_PREFIX
一旦你安装了 Miniconda,你应该能够看到 conda 可执行文件是可用的…
!which conda # should return /usr/local/bin/conda
…并且版本号是正确的。
!conda --version # should return 4.5.4
请注意,虽然安装 Miniconda 似乎不会影响 Python 可执行文件…
!which python # still returns /usr/local/bin/python
…然而,Miniconda 实际上安装了一个略有不同的 Python 版本。
!python --version # now returns Python 3.6.5 :: Anaconda, Inc.
更新 Conda
既然您已经安装了 Conda,那么您需要将 Conda 及其所有依赖项更新到最新版本,而不需要将 Python 更新到 3.7(或 3.8)。下面的conda install命令实际上是将 Conda 更新到最新版本,同时保持 Python 版本固定在 3.6。然后,conda update命令将 Conda 的所有依赖项更新到最新版本。
%%bashconda install --channel defaults conda python=3.6 --yes
conda update --channel defaults --all --yes
现在,您可以通过检查 Conda 的版本号来确认更新。
!conda --version # now returns 4.8.3
另外,请注意 Python 版本再次发生了变化。
!python --version # now returns Python 3.6.10 :: Anaconda, Inc.
追加到sys.path
现在您已经安装了 Miniconda,您需要将 conda 将安装包的目录添加到 Python 在查找要导入的模块时将搜索的目录列表中。通过检查[sys.path](https://docs.python.org/3/library/sys.html),您可以看到 Python 在查找要导入的模块时将搜索的当前目录列表。
import syssys.path
在撰写本文时,Google Colab 上的sys.path如下所示。
['',  
 '/env/python',
 '/usr/lib/python36.zip',
 '/usr/lib/python3.6',
 '/usr/lib/python3.6/lib-dynload',
 '/usr/local/lib/python3.6/dist-packages', # pre-installed packages
 '/usr/lib/python3/dist-packages',
 '/usr/local/lib/python3.6/dist-packages/IPython/extensions',
 '/root/.ipython']
请注意,Google Colab 附带的预安装软件包安装在/usr/local/lib/python3.6/dist-packages目录中。您可以通过简单地列出这个目录的内容来了解哪些包是可用的。
!ls /usr/local/lib/python3.6/dist-packages
你用 Conda 安装的任何包都将被安装到目录/usr/local/lib/python3.6/site-packages中,所以你需要把这个目录添加到sys.path中,以便这些包可以被导入。
import sys_ = (sys.path
        .append("/usr/local/lib/python3.6/site-packages"))
请注意,因为包含预装 Google Colab 软件包的/usr/local/lib/python3.6/dist-packages目录出现在 Conda 安装包的/usr/local/lib/python3.6/site-packages目录之前,所以通过 Google Colab 获得的软件包版本将优先于通过 Conda 安装的同一软件包的任何版本。
安装软件包
现在你需要做的就是安装你喜欢的软件包。只要记住在安装软件包时包含--yes标志,以避免提示确认软件包计划。
!conda install --channel conda-forge featuretools --yes
摘要
在本文中,当我需要使用 conda 来管理 Google Colab 上的包时,我介绍了安装和配置 Miniconda 的过程。希望这能在你下次需要在 Google Colab 上分享 Conda 管理的数据科学项目时有所帮助。
康达(+ pip)和 Docker FTW!
困扰数据科学项目的环境和包管理问题的解决方案。

结合 Conda envs 和 Docker 容器可以提高数据科学工作流的可重复性。来源
进入鲸鱼!
因为这篇文章的重点是将 Docker 整合到现有的 Conda (+ pip)工作流中,所以我假设您已经在使用 Conda (+ pip)来管理数据科学项目的环境。如果您不熟悉 Conda 的基础知识,或者还没有使用 Conda 管理特定项目的环境,那么我建议您查看我最近的文章Conda入门和 使用 Conda 管理特定项目的环境。
我还假设您对 Docker 背后的基本思想有所了解。如果没有,那么我推荐你看一看杰夫·黑尔的这个优秀的系列文章
为什么不用康达(+ pip)?
虽然 Conda (+ pip)解决了我的大多数日常数据科学环境和包管理问题,但将 Docker 整合到我的 Conda (+ pip)开发工作流中,使我的数据科学工作流从我的笔记本电脑/工作站移植到远程云计算资源变得更加容易。将 Docker 整合到我的开发工作流程中,通过消除不明显的操作系统级依赖性(特别是当从运行 Mac OSX 和 Windows 的本地笔记本电脑/工作站迁移到运行 Linux 的远程服务器时),也使我的工作更具可重复性。
然而,让 Conda (+ pip)像预期的那样在 Docker 容器中工作比我预想的要困难得多。我遇到的大多数困难涉及到如何在图像内部正确激活环境,以便在容器内部使用 Conda (+ pip)时的 UX 与在容器外部使用 Conda (+ pip)时的 UX 相同。
所有这些困难都可以通过使 docker 文件“恰到好处”来解决。在下一节中,我将一步一步地向您介绍我开发的 Dockerfile 文件。
写作Dockerfile
让 Conda (+ pip)和 Docker 顺利合作的诀窍是编写一个好的Dockerfile。在这一节中,我将带你一步一步地了解我开发的Dockerfile的各个部分。希望你可以在下一个数据科学项目中不加修改地使用这个Dockerfile。
从这里开始,我假设您已经组织了类似于我的 Python 数据科学项目模板的项目目录。特别是,我将假设您存储了所有与 Docker 相关的文件,特别是项目根目录下的docker子目录中的Dockerfile。
使用标准基础图像
每个Dockefile都有一个基础或父映像。对于父映像,我使用的是 Ubuntu 16.04 ,这是数据科学社区中最常用的 Linux 版本之一(恰好也是我的工作站上安装的同一操作系统)。
FROM ubuntu:16.04
将bash设为默认外壳
构建 Docker 映像时用于运行Dockerfile命令的默认 shell 是/bin/sh。不幸的是/bin/sh目前不是conda init命令支持的 shells 之一。幸运的是,可以使用[SHELL](https://docs.docker.com/engine/reference/builder/#shell)指令改变用于运行Dockerfile命令的默认 shell。
SHELL [ "/bin/bash", "--login", "-c" ]
注意--login标志的使用,它确保~/.profile和~/.bashrc都有正确的来源。为了使用各种conda命令在 Docker 映像中构建 Conda 环境,正确地提供~/.profile和~/.bashrc是必要的。
创建非超级用户
在您的 Docker 映像中创建一个非 root 用户是一个 Docker 安全“最佳实践”。我创建非根用户的首选方法是使用构建参数来定制非根用户的username、uid和gid。我对uid和gid使用标准默认值;默认用户名设置为[al-khawarizmi](https://en.wikipedia.org/wiki/Muhammad_ibn_Musa_al-Khwarizmi)(为了纪念这位著名的波斯学者,他的名字就在我工作的大楼上)
# Create a non-root user
ARG username=al-khawarizmi
ARG uid=1000
ARG gid=100
ENV USER $username
ENV UID $uid
ENV GID $gid
ENV HOME /home/$USERRUN adduser --disabled-password \
    --gecos "Non-root user" \
    --uid $UID \
    --gid $GID \
    --home $HOME \
    $USER
复制配置文件
创建非 root 用户后,我复制了创建 Conda 环境所需的所有配置文件(即environment.yml、requirements.txt、postBuild)。我还复制了一个 Bash 脚本,我将把它用作 Docker ENTRYPOINT(下面将详细介绍)。
COPY environment.yml requirements.txt /tmp/
RUN chown $UID:$GID /tmp/environment.yml /tmp/requirements.txtCOPY postBuild /usr/local/bin/postBuild.sh
RUN chown $UID:$GID /usr/local/bin/postBuild.sh && \
    chmod u+x /usr/local/bin/postBuild.shCOPY docker/entrypoint.sh /usr/local/bin/
RUN chown $UID:$GID /usr/local/bin/entrypoint.sh && \
    chmod u+x /usr/local/bin/entrypoint.sh
Docker 的新版本支持以非根用户的身份复制文件,但是 DockerHub 上的 Docker 版本还不支持以非根用户的身份复制文件,所以如果你想为你的 Git 存储库设置自动构建,你需要以根用户的身份复制所有文件。
以非根用户的身份安装 Miniconda。
作为 root 用户复制配置文件后,我切换到非 root 用户并安装 Miniconda 。
USER $USER# install miniconda
ENV MINICONDA_VERSION 4.8.2
ENV CONDA_DIR $HOME/miniconda3
RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-$MINICONDA_VERSION-Linux-x86_64.sh -O ~/miniconda.sh && \
    chmod +x ~/miniconda.sh && \
    ~/miniconda.sh -b -p $CONDA_DIR && \
    rm ~/miniconda.sh# make non-activate conda commands available
ENV PATH=$CONDA_DIR/bin:$PATH# make conda activate command available from /bin/bash --login shells
RUN echo ". $CONDA_DIR/etc/profile.d/conda.sh" >> ~/.profile# make conda activate command available from /bin/bash --interative shells
RUN conda init bash
创建项目目录
接下来,我在非根用户主目录中创建一个项目目录。Conda 环境将在项目目录中的env子目录中创建,然后所有其他项目文件和目录可以挂载到这个目录中。
# create a project directory inside user home
ENV PROJECT_DIR $HOME/app
RUN mkdir $PROJECT_DIR
WORKDIR $PROJECT_DIR
构建康达环境
现在我已经准备好构建 Conda 环境了。请注意,我可以使用与我在笔记本电脑或工作站上为项目构建 Conda 环境几乎相同的conda命令序列。
# build the conda environment
ENV ENV_PREFIX $PWD/env
RUN conda update --name base --channel defaults conda && \
    conda env create --prefix $ENV_PREFIX --file /tmp/environment.yml --force && \
    conda clean --all --yes# run the postBuild script to install any JupyterLab extensions
RUN conda activate $ENV_PREFIX && \
    /usr/local/bin/postBuild.sh && \
    conda deactivate
确保运行时激活 Conda 环境
快完成了!倒数第二步是使用一个[ENTRYPOINT](https://docs.docker.com/engine/reference/builder/#entrypoint)脚本来确保 Conda 环境在运行时被正确激活。
ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ]
下面是/usr/local/bin/entrypoint.sh脚本,供参考。
#!/bin/bash --login
set -econda activate $ENV_PREFIX
exec "$@"
为 Docker 容器指定默认命令
最后,我使用[CMD](https://docs.docker.com/engine/reference/builder/#cmd)指令来指定 Docker 容器启动时运行的默认命令。因为我在所有的 Conda 环境中都安装了 JupyerLab,所以在执行容器时,我倾向于默认启动一个 JupyterLab 服务器。
# default command will launch JupyterLab server for development
CMD [ "jupyter", "lab", "--no-browser", "--ip", "0.0.0.0" ]
建立码头工人形象
下面的命令(应该从包含Dockefile的docker子目录中运行)用定制的$USER(以及相关的$UID和$GID)以及特定的$IMAGE_NAME和$IMAGE_TAG为您的项目构建一个新的映像。这个命令应该在项目的docker子目录中运行,因为 Docker 构建上下文被设置为../,它应该是项目根目录。
docker image build \
  --build-arg username=$USER \
  --build-arg uid=$UID \
  --build-arg gid=$GID \
  --file Dockerfile \
  --tag $IMAGE_NAME:$IMAGE_TAG \
  ../
运行 Docker 容器
一旦构建了映像,下面的命令将运行基于映像的容器$IMAGE_NAME:$IMAGE_TAG。该命令应该从项目的根目录中运行。
docker container run \
  --rm \
  --tty \
  --volume ${pwd}/bin:/home/$USER/app/bin \
  --volume ${pwd}/data:/home/$USER/app/data \ 
  --volume ${pwd}/doc:/home/$USER/app/doc \
  --volume ${pwd}/notebooks:/home/$USER/app/notebooks \
  --volume ${pwd}/results:/home/$USER/app/results \
  --volume ${pwd}/src:/home/$USER/app/src \
  --publish 8888:8888 \
  $IMAGE_NAME:$IMAGE_TAG
使用 Docker 撰写
手写上述 docker 命令时很容易出现打字错误。一种不太容易出错的方法是使用 Docker Compose 。上述 docker 命令可以封装到docker-compose.yml配置文件中,如下所示。
version: "3.7"services:
  jupyterlab-server:
    build:
      args:
        - username=${USER}
        - uid=${UID}
        - gid=${GID}
      context: ../
      dockerfile: docker/Dockerfile
    ports:
      - "8888:8888"
    volumes:
      - ../bin:/home/${USER}/app/bin
      - ../data:/home/${USER}/app/data
      - ../doc:/home/${USER}/app/doc
      - ../notebooks:/home/${USER}/app/notebooks
      - ../results:/home/${USER}/app/results
      - ../src:/home/${USER}/app/src
    init: true
    stdin_open: true
    tty: true
以上docker-compose.yml文件依靠变量替换。获取$USER、$UID和$GID的值。这些值可以存储在一个名为.env的文件中,如下所示。
USER=$USER
UID=$UID
GID=$GID
您可以通过在项目的docker子目录中运行以下命令来测试您的docker-compose.yml文件。
docker-compose config
该命令获取docker-compose.yml文件并替换.env文件中提供的值,然后返回结果。
一旦您确信.env文件中的值被正确地替换到了docker-compose.yml文件中,就可以使用下面的命令来打开一个基于项目 Docker 映像的容器,并启动 JupyterLab 服务器。这个命令也应该从项目的docker子目录中运行。
docker-compose up --build
当您完成开发并关闭 JupyterLab 服务器后,下面的命令将拆除正在运行的容器的网络基础设施。
docker-compose down
摘要
在这篇文章中,我经历了一个将 Conda (+ pip)环境注入 Docker 映像的Dockerfile。我还详细介绍了如何使用 Docker Compose 构建结果图像和启动容器。
如果你正在寻找一个生产质量的解决方案,概括上述方法,那么我会鼓励你看看[jupyter-repo2docker](https://repo2docker.readthedocs.io/en/latest/)。
jupyter-repo2docker是一个从源代码库中构建、运行和推送 Docker 映像的工具。repo2docker获取一个存储库(从 GitHub、GitLab、Zenodo、Figshare、Dataverse installations、Git 存储库或本地目录)并构建一个可以执行代码的容器映像。映像构建过程基于存储库中的配置文件。
Conda (+ pip)和 Docker 的结合显著提高了我的数据科学开发速度,同时提高了我的数据科学工作流的可移植性和可重复性。
希望这篇文章可以帮助你在下一个数据科学项目中将这三个伟大的工具结合起来!
条件可控的生成对抗网络
理解条件可控 GAN 并在 TensorFlow 2.x 中实现 CGAN

在本文中,我们将了解条件和可控 GAN,它们的需求是什么,以及如何使用 TensorFlow 2.x 实现简单的条件 GAN。在您进一步阅读之前,我希望您熟悉 DCGANs,您可以在这里找到。
为什么有条件甘
到目前为止,生成器是随机生成图像的,我们无法控制要生成的图像的类别,即在训练 GAN 时,生成器每次都会生成一个随机数字,即它可能会生成一个、六个或三个,我们不知道它会生成什么。但是条件句我们可以告诉生成器生成一个或六个图像。这就是有条件的 GAN 派上用场的地方。通过有条件的 GAN,您可以生成您选择的类别的图像。
它是如何工作的?
到目前为止,我们将图像作为唯一的输入提供给我们的生成器和鉴别器。但是现在我们将向两个网络提供班级信息。
- 生成器将随机噪声和一个独热编码类标签作为输入。并输出特定类别的假图像。
- 鉴别器获取一个带有作为深度添加到图像(通道)的独热标签的图像,即,如果您有一个大小为 28 * 28 *1 的图像和大小为 n 的独热向量,则图像大小将为 28 * 28 * (n+1)。
- 鉴别器输出图像是否属于该类别,即真实或虚假。
密码
本文的代码与 DCGAN 的代码几乎相同,只是做了一些修改。让我们看看这些差异。
注意:跟随实现是一种幼稚的方式,并且非常慢。你可以参考 这里的 找到一个更好的编码条件 gan 的方法。
组合图像和标签
- 首先,我们加载 MNIST 数据集并归一化图像。
- 然后,我们定义一个 add_channels 函数,它将一个图像和相应的一个 hot 标签作为输入。并且输出具有表示独热标签的附加深度通道的图像。在添加的所有深度通道中,只有一个通道包含值 1,所有其他通道都包含零值。
- 首先,我们迭代所有的图像和相应的标签。对于独热标签中的每个数字,我们创建一个图像形状向量,其中包含等于该数字的每个值。之后,我们把这些向量和图像叠加起来。
- 这里,我们有 10 个类,这就是为什么我们在一个热标签中循环了 10 个数字。
结合噪音和标签
- 以下代码将噪声向量与一键标签和其他函数结合起来,输出生成器和鉴别器的输入尺寸。
- 这是必要的,因为我们的模型是使用顺序 API 编码的,我们需要显式地传递输入维度。
训练循环
- 训练循环也与 DCGAN 相同,但这一次我们为生成器合并了噪声和标签。并且在将伪图像送入鉴别器之前对其进行图像处理。
上面的 condition GANs 实现代码非常慢,但它运行良好,有助于理解这个概念。
可控 GANs
条件甘帮助生成我们选择的类的图像。但是我们无法控制输出图像的内容,也就是说,如果我们想要一只戴着红帽子或眼镜的狗怎么办。这就是可控 GANs 出现的原因。但是它是如何工作的呢?所以让我们来找出答案。
概念
可控的 GANs 对于在生成的图像中获得选择的特征是有用的。例如,如果我们想生成一个黑头发绿眼睛的人的图像,那么你需要调整输入噪声。
- 当您向发生器提供随机噪声向量时,该噪声向量中的元素对应于所生成图像中的某些特征。
- 当您更改噪波向量中的元素时,它会更改图像中的一些特征,例如向量中的一些更改可能会更改人的头发或眼睛的颜色。
- 这可以通过在一些向量空间中映射具有相关特征的向量来实现。
- 这可以通过使用预先训练的分类器来执行,该分类器告知特定特征是否出现在生成的图像中,例如人的眼睛是否是绿色的。这可以用于找到不同特征的噪声矢量。
可控氮化镓面临的挑战
可控 GANs 面临以下两个主要挑战。
- 相关性黑白特征:当我们为了改变一个特征而改变噪声向量的一个元素时(就像给女性的图像添加面部毛发),会导致其他特征的改变(就像改变图像中人的性别)。这可能是因为模特们见过面部毛发带有阳刚之气的脸。
- 矢量空间纠缠:噪声矢量中的元素在对应特征时发生纠缠。
结论
条件 gan 用于生成属于我们选择的类的图像,而可控 gan 用于控制图像中的特征。
你可以在这里找到这篇文章的完整代码。请继续关注即将发表的文章,我们将在那里实现更多的算法。
所以,本文到此结束。谢谢你的阅读,希望你喜欢并且能够理解我想要解释的内容。希望你阅读我即将发表的文章。哈里奥姆…🙏
参考
由 DeepLearning.AI 提供关于 GANs 生成对抗网络(GANs)是强大的机器学习模型…
www.coursera.org](https://www.coursera.org/specializations/generative-adversarial-networks-gans)
Power BI 中的条件格式
了解如何在 Power BI 视觉效果中应用条件格式
在本文中,我将解释什么是 Power BI 中的条件格式。顾名思义,条件格式是一种可视化数据的方式,它基于一些预定义的条件对所选指标的值应用特殊的格式规则。它主要用于数据以表格形式呈现的情况;但是,它也可以用于任何图表,如条形图、折线图等。
Power BI 中的条件格式是什么?
当有大量数据以表格形式表示时,乍一看很难理解哪个值更高或更低。通常,您需要深入观察单元格值,以了解或比较显示的各种指标。
例如,如果您考虑下面左边的图,数据太多了,您需要单独比较每个单元格的值,以便了解更多信息。然而,如果你看到右边的图,你只要看一眼就能明白,什么是较高的值,什么是中间值,什么是较低的值。这就是 Power BI 中的条件格式。

图 1 —条件格式比较
设置数据
让我们首先尝试将一些数据导入 Power BI 数据模型。一旦数据被导入,我们将研究如何实现条件格式。为了这篇文章,我将从微软提供的著名的 WideWorldImportersDW 数据库中导入数据。你可以在本地轻松下载并安装这个数据库。因为我已经安装了数据库,所以我不会在这里介绍这些步骤。为了将数据导入到 Power 中,您可以按照下面的步骤与我一起进行:
打开 Power BI 桌面,点击获取数据。从出现的下拉菜单中选择 SQL Server 。

图 2 —获取功率 BI 中的数据
在出现的连接服务器对话框中,提供服务器名称和数据库名称并点击确定。

图 3 —服务器详情
单击 Connect 后,您会看到对话框中列出了数据库中的所有表。对于本文,我们将只选择三个表— 维度。,维度城市。日期和事实。销售。完成后点击加载。

图 5 —选择表格
在 Power BI 中构建报告
现在,让我们使用矩阵可视化快速构建一个电源 BI 报告。它将使用我们刚刚导入的表中的数据。点击可视化面板上的矩阵,将各个字段拖放到行、列和值中,如下图所示。

图 7 —构建矩阵

图 8 —构建矩阵
一旦字段就绪,您将看到 Power BI 报告中创建了一个类似的矩阵。这是一个简单的矩阵,显示每个销售区域和年度的含税总额。如你所见,理解矩阵中的最高值和最低值并不容易;我们需要深入视觉,理解这些价值观。

图 9 —新矩阵
在 Power BI 中实现条件格式
为了在 Power BI 中实现条件格式,让我们继续定义我们想要可视化的规则。让我们考虑一下,我们将根据每个单元格保存的值来更改单元格的背景颜色。例如,具有最低值的单元格将具有红色的背景色,而最高值将以蓝色突出显示。您可以按照下面的步骤来实现相同的功能。
右键单击值部分的含税总额字段。或者,您也可以点击格式按钮,打开条件格式下的背景色。

图 11 —条件格式
将打开背景颜色-格式对话框

图 12 —背景颜色
现在让我们通过选项选择色标作为格式,并如前所述选择颜色。对于最低值,我们将指定为红色,而最高值的颜色将为蓝色。完成后点击确定。

- 图 13 —定义色标
现在我们已经了解了 Power BI 中的条件格式是怎么回事,让我们来探索一些实现相同内容的不同方法。在上述步骤中,格式规则基于单元格值的范围。这意味着,根据单元格的值,背景颜色的饱和度会发生变化。但是,在某些情况下,您可能希望基于固定数值来可视化格式,而不希望更改颜色的饱和度。或者,您可能还希望将度量值分为、和值等类别。
在下面的步骤中,我们将尝试将条件格式的规则从范围修改为固定值。
- 右键单击值部分的含税总额字段
- 选择条件格式然后点击背景颜色
- 或者,您也可以点击格式按钮,打开条件格式下的背景色
- 将打开背景颜色-格式对话框
- 现在,我们将创建一些规则,基于这些规则来应用格式。您可以通过点击新规则按钮创建尽可能多的规则
因此,在上面的练习中,我们看到了如何基于两种不同的方式在 Power BI 中实现条件格式。第一个是通过使用色标,第二个是通过为格式定义特定的规则。
Power BI 中带有图标的条件格式
现在让我们更进一步,在单元格中实现一些图标作为视觉指示器。这些特定的图标在定义任何 KPI 或根据标准比较指标时非常有用。您可以按照下面提到的步骤来实现单元格中的图标。
- 右键单击值部分的含税总额字段
- 图标——格式化对话框打开
- 让选项的格式作为规则****
- 选择图标布局为数据右侧的,选择图标对齐为中间的****
- 从样式下拉菜单中选择任意选项,点击确定
- 图 20 —图标定义规则
- 现在你可以看到矩阵根据上一步定义的条件显示了额外的图标

- 图 21 —带有图标的条件格式
- 这是一个重要的特性,有助于用户直观地了解指标是否表现良好
- 同样,您可以根据需求添加任意多的规则
结论
在本文中,我解释了 Power BI 中的条件格式是什么以及如何实现它。我还解释了如何应用条件格式以及在什么情况下应用条件格式的不同方式。最后,我们看到了如何在视觉效果中包含一些信息图视觉效果(图标),这有助于在很大程度上提高报告的可读性。
原载于 2020 年 3 月 18 日 https://www.sqlshack.com。
条件概率、西雅图雨和狡猾的朋友

本·达顿在 Unsplash 上的照片
通过解决这个数据科学面试问题(使用 Python)更好地理解概率
我最不喜欢的一种数据科学面试题是概率。只是这不是我每天想的事情,所以每当我被迫锻炼概率肌时,我总是感觉它们超级生锈。但是,如果你正在寻找一份数据工作,不可避免的是,你会在某个时候遇到一份——所以让我们通过一些实践来保持我们的概率技能。像往常一样,我们将使用模拟(和 Python 代码)来更好地可视化正在发生的事情。
这个问题
你要去西雅图。你想知道你是否应该带一把伞,所以你打电话给住在那里的三个随机的朋友,分别问他们是否在下雨。每个朋友都有 2/3 的机会对你说真话,1/3 的机会撒谎(好刻薄!).三个朋友都告诉你“是的,下雨了”。西雅图真的下雨的概率有多大?
我第一次看到这个问题时,我想“只有当我的三个朋友都对我撒谎时,这才意味着西雅图没有下雨”。因为只要我的一个朋友没有撒谎,那么肯定会有一个是真的(暗示下雨)。
probability rain = 1 - probability all lying= 1 - (1/3)^3 = 0.963
但后来我觉得这似乎太简单了。如果这个问题如此简单,它怎么会如此臭名昭著。遗憾的是,我的直觉是对的,这个问题比看起来更复杂。
这是一个条件概率
我以前的方法忽略了给定的条件。你问什么条件?面试官告诉我们,我们的三个朋友都回答是。这是我们的解决方案需要考虑的相关信息。

奥斯曼·拉纳在 Unsplash 上拍摄的照片
要知道为什么,想象一下西雅图正在下雨。当我们问朋友是否在下雨时,他们回答[是,是,是]。让我们仔细想想在这种世界状态下可能出现的结果。如果他们都说了实话,那么这与我们所处的世界状态是一致的(因为西雅图正在下雨)。
如果他们都撒谎了呢?那是不可能的。在世界正在下雨的情况下,我们的朋友不可能回答“是”并撒谎(要撒谎,他们必须回答“不是”)。有没有可能他们中只有一个撒谎了?这也是不可能的——所有人都说“是”,而躺在下雨的世界里需要说“不”。所以如果西雅图下雨,唯一可能的结果就是我们的朋友都在说实话。

塞尔吉奥·索萨在 Unsplash 上的照片
现在想象西雅图阳光明媚。我们可以做同样的练习来找出在这个世界状态下可能发生的事情。如果是晴天,那就意味着我们的三个朋友都对我们撒了谎。那是唯一的可能!因为在阳光明媚的世界里,我们的朋友必须回答“不,没有下雨”才能说实话。他们都没这么说过。
所以只有两种可能:
**World state = it is raining:**
[yes,yes,yes] = **[truth,truth,truth] = raining**
                [lie,lie,lie] = impossible
                [lie,lie,truth] = impossible
                [lie,truth,truth] = impossible
                [lie,truth,lie] = impossible
                [truth,truth,lie] = impossible
                [truth,lie,lie] = impossible
                [truth,lie,truth] = impossible**World state = it is not raining:**
[yes,yes,yes] = [truth,truth,truth] = impossible
                **[lie,lie,lie] = not raining**
                [lie,lie,truth] = impossible
                [lie,truth,truth] = impossible
                [lie,truth,lie] = impossible
                [truth,truth,lie] = impossible
                [truth,lie,lie] = impossible
                [truth,lie,truth] = impossible
基本上,如果下雨,他们一定都在说实话。如果不是,他们一定都在撒谎。如果我们的三个朋友都同意,这是唯一的可能性。
因此,我们正在估计一个条件概率:
Prob(rain | [yes,yes,yes])Which is read as probability of rain given that the responses were yes, yes, and yes].
我们可以在这里突破贝叶斯定理,但我更喜欢一种更直观和深入的方法,这样我们才能真正掌握正在发生的事情。
用 Python 模拟
让我们写一些代码来模拟这个脑筋急转弯。我在代码中添加了一些注释来描述它是如何工作的以及我的思考过程。但是让我们也快速浏览一下。
我们将运行 10 万次模拟。我们还需要具体说明下雨的概率。很奇怪,对吧?我们不是在估算下雨的概率吗?不,实际上我们是在估计下雨的概率,因为我们的朋友都说“是的,下雨了”。为了做到这一点,我们需要模拟世界的两种可能状态(下雨或不下雨)。我们可以通过类似抛硬币的随机变量(0 和 1 之间的均匀分布)来实现这一点,如果随机变量 rain 小于 prob_rain,则在我们的模拟中正在下雨。
接下来,我们以同样的方式模拟我们 3 个朋友的反应——如果反应中的 3 个随机变量中的任何一个大于 2/3,那么这个特定的朋友对我们撒谎了(因为他们有 2/3 的机会说真话,1/3 的机会撒谎)。
我们有 3 个计数器变量来跟踪我们关心的结果:
- times_rained_3yes 记录下雨的次数,我们的朋友都实话实说,说“是的,下雨了”。
- times_dry_3yes 记录了没有下雨的次数,我们的朋友都撒谎说“是的,下雨了”。
- times_rained 跟踪我们的模拟中下雨的次数(大约是我们设置 prob_rain = 0.5 以来的 50%)。
# Inputs
sims = 100000
prob_rain = 0.50# Counter variables to tally the simulation outcomes
times_rained_3yes = 0
times_dry_3yes = 0
times_rained = 0# Loop that runs the simulations
for i in range(sims):   
    # Random variable to simulte whether it rains or not
    rain = np.random.random()
    # Raining Case
    if rain < prob_rain:
        # If it is raining, increment counter
        times_rained += 1
        # Random variables to simulate whether friends lie
        responses = [np.random.random(),
                     np.random.random(),
                     np.random.random()]
        # Tally the number of lies
        lies = sum([1 for i in responses if i > 2/3])
        # If sum(lies) == 0, then all truth
        if lies == 0:
            # This is outcome we are looking for, where the
            # world state = RAINING and all friends tell truth
            times_rained_3yes += 1
    # Not Raining Case
    else:    
        # Random variables to simulate whether friends lie
        responses = [np.random.random(),
                     np.random.random(),
                     np.random.random()]
        # Tally the number of lies
        lies = sum([1 for i in responses if i > 2/3])
        # If sum(lies) == 3, then all lies
        if lies == 3:
            # This is outcome we are looking for, where the
            # world state = DRY and all friends lie
            times_dry_3yes += 1
运行模拟后,我们可以使用计数器变量来计算我们感兴趣的概率。
sims = 100,000times_rained = 49,990times_rained_3yes = 14,852times_dry_3yes = 1,889
所以大约 50%的时间都在下雨(因为我们是这样设置的)。而当真的下雨的时候,我们的朋友大约有 29% (14,852/49,990)的时候会说实话——(2/3)= 0.295。而不下雨的时候,我们 3 个朋友对我们撒谎的时间都是 3.77% (1,889/50,010)-(1/3)= 0.037,由于模拟噪音的原因略有差异。
我们现在已经有了解决问题所需的所有数字!你能看出是怎么回事吗?让我们回想一下我们的问题——当我们的三个朋友都说“是的,下雨了”时,我们想知道下雨的概率。所以我们概率计算的分子应该是下雨的次数和所有人都说是的次数。而分母应该是我们所有的朋友在世界所有的状态下回答是的次数(只有 2 种状态——下雨和不下雨)。
**Numerator** = times_rained_3yes = 14,852**Denominator** = number of times our friends said 3 yeses
            = times_rained_3yes + times_dry_3yes
            = 14,852 + 1,889 = 16,741**The Answer = 14,852 / 16,741 = 0.888**
所以根据我们朋友的回答,西雅图有 89%的可能在下雨(一定要带伞)。
解析解
我们也可以如下解析求解:
- 分子是 3 赞成和下雨的联合概率。因为它们是独立的事件,我们可以用下雨的概率乘以 3 个真相的概率(因为下雨时回答是等于说实话)。
- 分母是 3 通过的概率。我们可以通过认识到 3 个“是”只能产生于 2 种情况来计算:要么是下雨时的 3 个真相,要么是不下雨时的 3 个谎言。我们已经有了第一个概率(它是我们的分子)。我们可以用同样的方法计算第二个(不下雨时的 3 个谎言)——用 3 个谎言的概率乘以不下雨的概率。最后,我们将两个案例的概率相加,得到 3 个通过的概率。
**Numerator** = Prob. 3 Truths * Prob. Rain 
 = (2/3)^3 * (1/2)**Denominator** = Prob. 3 Yeses
 = (Prob. 3 Truths * Prob. Rain) + (Prob. 3 Lies * (1 - Prob. Rain))
 = (2/3)^3 * (1/2) + (1/3)^3 * (1/2)**Plugging into Python:**
(2/3)**3*(1/2) /((2/3)**3*(1/2) + (1/3)**3*(1/2))**We get:**
**0.888**
最后,需要注意的是,根据我们对下雨概率的估计(我们之前设定为 0.5),答案会有所不同。这是有道理的——不管我们朋友的反应如何,我们先前对下雨概率的假设仍然很重要。我们朋友的反应会改变我们开始的先验假设,但不会推翻它。例如,如果我们要去拉斯维加斯而不是西雅图,即使有三个“是的,下雨了”的回答,我们也可以不带伞。下图显示了我们关于下雨概率的假设(我代码中的 prob_rain 变量)如何改变我们的答案:

我们的开始下雨概率假设对答案的影响
今天的节目到此结束。祝你面试和数据科学一切顺利。干杯!
Python 示例中的条件概率
假设一个学生错过了 10 节或更多的课,使用 python 计算该学生数学得 A 的条件概率。

本文有 2 个部分:
1。条件概率背后的理论
2。python 的例子
第一部分:条件概率背后的理论和公式
这一次,维基百科有了一个平易近人的定义,
翻译:给定 B 为真,A 也为真的概率是多少。
有具体例子的东西更容易理解。下面是一些我们可以计算的条件概率的随机例子。
例子:
- 如果一个人是大学生,睡眠少于 8 小时的概率是多少?
- 如果一只狗是边境牧羊犬,它活超过 15 年的概率有多大?
- 如果你为政府工作,用掉所有假期的概率是多少?
公式:

条件概率的公式是P(A|B) = P(A ∩ B) / P(B)。
零件:
P(A | B)= A 发生的概率,给定 B 发生
P(A∩B)= A 和 B 都发生的概率
P(B)= B 发生的概率
|意为“给定”。意思是“在其他事情发生的情况下”。
∩的意思是相交,你可以把它想象成and,或者是维恩图中的重叠。

但是为什么我们在公式中用P(B)除P(A ∩ B)?
因为我们要排除非 B 情况的概率。我们正在确定落入B的概率。

除以P(B)去除了非B的概率。C — B以上。
第 2 部分:python 示例
我们将计算一个学生数学得 A (80%+)的概率,假设他们错过了 10 节或更多的课。
从 kaggle 下载数据集并检查数据。
import pandas as pd
df = pd.read_csv('student-alcohol-consumption/student-mat.csv')
df.head(3)

并检查记录的数量。
len(df)
#=> 395
我们只关心列,absences(缺席次数),和G3(从 0 到 20 的最终分数)。
让我们基于这些列创建几个新的布尔列,使我们的生活更容易。
添加一个名为grade_A的布尔列,记录学生最终分数是否达到 80%或更高。原始值在 0-20 范围内,所以我们乘以 5。
df['grade_A'] = np.where(df['G3']*5 >= 80, 1, 0)
如果学生缺了 10 节或更多的课,创建另一个名为high_absenses的布尔列,值为 1。
df['high_absenses'] = np.where(df['absences'] >= 10, 1, 0)
再添加一列,使构建数据透视表更容易。
df['count'] = 1
删除所有我们不关心的栏目。
df = df[['grade_A','high_absenses','count']]
df.head()

很好。现在我们将由此创建一个数据透视表。
pd.pivot_table(
    df, 
    values='count', 
    index=['grade_A'], 
    columns=['high_absenses'], 
    aggfunc=np.size, 
    fill_value=0
)

我们现在有了进行计算所需的所有数据。让我们从计算公式中的各个部分开始。
在我们的例子中:
P(A)是一个等级 80%或更大的概率。
P(B)是缺课 10 节及以上的概率。
P(A|B)是 80%+成绩的概率,给定缺课 10 节或以上。
零件的计算:
P(A)=(35+5)/(35+5+277+78)= 0.10126582278481013
P(B)=(78+5)/(35+5+277+78)= 0.21012658227848102
P(A∩B)= 5/(35+1
并且按照公式,P(A|B) = P(A ∩ B) / P(B),把它拼在一起。
p(A | B)= 0.012658227848101266/0.21012658227848102 = 0.06
我们找到了。如果缺课 10 节或更多,得到至少 80%最终成绩的概率是 6%。
结论
虽然从我们的具体例子中学到的很清楚——如果你想要好成绩就去上课,条件概率可以应用于更严重的情况。
例如,在给定测试结果的情况下,一个人患有特定疾病的概率。
在使用贝叶斯定理进行更复杂的概率估计之前,理解也是必不可少的。
置信区间、计算和特征
什么是置信区间,如何计算它,以及它的重要特征

来源:维基百科
置信区间在统计学和数据科学中非常重要。在这篇文章中,我将解释置信区间,如何计算它,以及它的重要特征。
置信区间(CI)是一个数值范围。它以百分比的形式表示,预计包含统计参数的最佳估计值。95%的置信区间意味着,我们的总体参数有 95%的把握位于该置信区间之间。
置信区间的解释
这里有一个声明:
在对 659 名带着蹒跚学步的孩子的父母进行的抽样调查中,有 540 人(约 85%)表示,他们在带着孩子的所有旅行中都使用汽车安全座椅。根据这些结果,提供了 95%的置信区间,从大约 82.3%到 87.7%。”
这句话的意思是,95%确定的是,带着他们的孩子在所有旅行中使用汽车安全座椅的人口比例是 82.3 和 87.7。如果我们从这个人群中抽取几个子样本,在 95%的情况下,带着蹒跚学步的孩子出行时使用汽车安全座椅的人群比例将在 82.3%到 87.7%之间。
我们能说置信区间(82.3,87.7)包含了真实的人口比例吗?答案不得而知。人口比例是一个固定值,但未知。记住 95%的信心并不意味着 95%的可能性,这一点很重要。
为什么置信区间很重要?
这一点很重要,因为大多数时候不可能从一个群体中的每一个人身上获取数据。在上面的例子中,样本量是 659。我们从 659 名父母样本中估计了所有旅行中都使用汽车座椅的幼儿父母的人口比例。我们无法从所有带着蹒跚学步的孩子的父母那里获得数据。因此,我们从现有样本中计算人口比例,并考虑误差幅度。有了这个误差范围,我们就得到了一个范围。这个范围称为置信区间。置信区间是一种表示样本数据代表总体情况的方式。可以计算任意数的置信区间(小于 100%)。但是 95%的置信区间是最常见的。
如何计算置信区间
置信区间的公式为:

我们通常想要一个高置信度,比如 75%、95%或 99%。置信水平(CL)越高,精度越低。在上面的例子中,最好的估计是 85%。我们可以通过以下公式计算估计 SE:

在等式中,p1 以上是最佳估计,n 是样本大小。下面是一些常用置信水平的 z 分数表。

输入所有的值,

置信区间分别为 82.3%和 87.7% 。
CL 越高,CI 的范围越大
同样的,我们可以计算出 99%的置信度。你只需要改变 z 值。从上表中可以看出,99%置信水平的 z 值为 2.57。将该值代入置信区间公式,99%置信水平的置信区间为 81.43%至 88.57%。置信区间的范围越大,置信水平越高。

来源:维基百科
在上图中,中间的‘mu’是最佳估计值,sigma 是标准差。我们在示例中使用了标准误差,因为总体标准偏差是未知的。图中显示,68%的人群保持在距离最佳估计值 sigma 的范围内。这是 68%的置信区间。同样,95%置信区间和 99.7%置信区间在距离最佳估计值‘mu’2 sigma 和 3 sigmas 的范围内。另一种表达方式是,95%和 99.7%的人口位于距离最佳估计值‘mu’2σ和 3σ的范围内。如果这张图片让您感到困惑,请不要担心,使用 z 得分表从上面的公式计算不同的置信水平。那会给你一个趋势的概念。
样本越大,置信区间越窄
样本量越大,置信区间越精确。让我们用父母带着蹒跚学步的孩子的例子来证明一下。让我们假设最佳估计值保持不变,0.85。但是样本量是 1500 而不是 659。现在,在公式中插入这个新的样本量,并计算出 95%的置信区间。

95%置信水平的置信区间变为 83.2%和 86.8%,比 82.3%和 87.7%窄。
向业务用户解释置信区间
如何简洁地回答著名的数据科学面试问题

图片来自 Coursera-stats
当我们作为数据科学家开始我们的旅程时,少数几个任何人都可能被卡住的话题之一是“置信区间”。是我的实习经历让我对这个概念有了清晰的认识。最近,当我在一次采访中被问到“你如何向商业用户解释置信区间?”,我仍在努力恰当地表达我的答案。
这篇文章是写给那些正在努力理解置信区间概念的人,也是写给那些理解了这个概念但不能和非技术人员交流的人。我在这里的目的只是给出一个关于这个话题的直觉。请将此视为构建概念的起点。
这篇文章有两个部分。在第一部分中,我将尝试用简单的英语解释置信区间的概念。在第二部分,我将展示这个概念背后的数学原理。
用英语解释置信区间
假设你是亚马逊的一名数据科学家。现在,亚马逊拥有 1 亿客户。想象一下,其中 5000 万是男性顾客,5000 万是女性顾客。
你想分析两性的消费习惯是否不同。你需要回答的具体问题是:平均而言,女性每笔交易的花费是否比男性多?
一种确定方法是跟踪所有这 5000 万女性顾客的每笔交易的花费金额,跟踪所有这 5000 万男性顾客的每笔交易的花费金额,计算平均值并得出结果。您刚才所做的是收集总体数据并确定总体参数。然而,你几乎没有时间和资源(有时是金钱)来收集整个人口数据。因此,你依靠经典统计学来解决问题。
首先,让我们试着计算一下女性顾客每笔交易的平均支出。
设每笔交易金额用 Si 表示。所有 5000 万女性顾客的平均交易量,即人口平均数是:

所有 5000 万女性顾客的平均消费
如前所述,由于你没有时间和资源去等待和收集所有 5000 万女性顾客的消费,你可以从这 5000 万女性顾客中抽取一个(随机独立的)女性顾客样本,比如说 30000 个。您将记录这 30,000 名女性顾客的每笔交易支出:
所以现在你有 30,000 笔交易。

现在您将计算这 30,000 笔交易的平均值。

样本中 30,000 名女性顾客的平均消费
使用经典统计学有效解决业务案例
在经典统计学中,您使用这个样本平均值来找出总体平均值。在这种情况下,您使用 30,000 名女性客户样本的平均支出来找出 5,000 万名女性客户的整个人口的平均支出。
有两种方法可以做到这一点
- 你得出结论,3 万女性顾客的平均消费,(样本平均)等于所有 5000 万女性顾客的平均消费(人口平均)。这被称为点估计,在这里你使用样本数据得出未知总体参数的最佳猜测。
- 一个更好更有说服力的方法是用这个样本平均值来找出总体平均值所在的区间。使用这个 30,000 名女性顾客的样本,你将计算出 5,000 万女性顾客的平均消费可能处于的区间。
您使用样本数据计算出的总体参数所在的可信区间称为置信区间。间隔的宽度主要由业务决定:90%、95%或 99%是最常见的。
用简单的英语来说,95%的置信区间告诉你 95%的人口参数值,即 5000 万女性顾客在这里的平均消费,所在的范围。因此,我们可以 95%地确信总体均值将位于区间内。(您必须记住,这 95%的总体数据区间是仅利用样本数据中的信息计算出来的)
这里的一个重要方面是如何对数据进行采样。要获得总体的无偏代表样本:
- 样本量不应该太小
- 样本应该是随机和独立的。
即,
收集一个数据点不应以任何方式决定您的下一个数据点。例如,仅仅因为你收集了一个 50 岁已婚女性的样本作为你的第一个数据点,你不应该认为“现在我将收集一个 20 岁的单身女性”。如果这样做,就会引入偏差,第二个数据点就会依赖于第一个数据点。这样可以确保样本中收集的每个数据点都是随机的并且相互独立。(点击此处了解更多关于选择偏差的信息)
所以,下次当你得到这个问题用非技术的方式解释置信区间时,你可以用这个想法来表述答案。
假设您想要计算出您感兴趣的关于人口的一些参数(根据业务进行定制)。收集全部人口的数据既费时又费钱。所以你收集一个随机的数据样本,从这个样本中,使用统计方法,你可以计算出一个总体参数可能存在的区间。这个区间称为置信区间。
现在回到业务问题
为了更好地理解置信区间的用处,让我回到我们最初的问题。平均而言,女性在每笔交易上花的钱比男性多吗?这是手头的问题
就像你拿了 3 万个女客户的样本,算出平均值,你就拿了 3 万个男客户的样本,算出平均值。让我们假设从这个样本中计算出的女性顾客的每笔交易的平均花费(Female_avg)是 2350 美元,而从样本中计算出的男性顾客的每笔交易的平均花费(male_avg)是 1350 美元。这里女性平均值大于男性平均值。但是在得出女性在每笔交易中花费更多的结论之前,记住你只有样本信息。即使我们正确地获得了样本,样本平均值也可能与总体平均值相差很大。这就是你找到男顾客和女顾客平均消费的置信区间。
假设你得到的男性用户平均消费对应的 95%置信区间为[1150,1250],女性用户为[2340,2360]。这意味着 95%的(全部 5000 万)男性顾客的平均消费介于 1150 至 1250 英镑之间。只有 5%的情况下(非常罕见),你会发现男性的平均支出超过 1250 美元。同样,对于女性来说,只有 5%的时间平均支出低于 2340 美元。因此,你可以得出结论,平均而言,女性在每笔交易中花费的钱比男性多。
具体来说,您正在检查置信区间是否与重叠。如果置信区间没有重叠,那么我们可以说有差异。
假设你得到男性用户平均消费的 95% CI 为[2330,2350]。这里你可以看到置信区间重叠。因此,我们不能得出女性在每笔交易中比男性花费更多的结论。
我希望我能让你对这个概念有个直观的理解。现在是数学时间了。
置信区间背后的数学
在深入研究之前,你必须具备的一个先决条件是很好地理解中心极限定理。
以及实际应用中的例子
towardsdatascience.com](/central-limit-theorem-in-action-1d4832599b7f)
我们在这里使用的数据集是 Kaggle 的黑色星期五销售数据。这里的数据集是黑色星期五在一家零售店进行的交易的样本。为了解释的连续性,考虑这个数据集是亚马逊的。
Kaggle 是世界上最大的数据科学社区,拥有强大的工具和资源来帮助您实现您的数据…
www.kaggle.com](https://www.kaggle.com/sdolezel/black-friday?select=train.csv) 
黑色星期五销售数据集
记住,我们只有 500 万英镑的交易样本。
你想知道的第一件事是,两性之间的花费是否存在差异。从这个数据集中找出女性顾客在黑色星期五期间的平均消费,然后与男性顾客的平均消费进行比较。
我们先从计算女性顾客的平均消费开始。

女性的平均支出
如前所述,这只是女性用户交易的样本平均值。我们必须用这 8734 美元来得出一个区间,在这个区间内,人口平均数可能会下降。为了计算这个区间值,我们利用中心极限定理。
简而言之,中心极限定理表明,无论总体分布如何,如果总体分布具有有限均值和有限方差σ,样本均值的分布将遵循均值和标准差σ /√n 的高斯分布,其中 n 是样本大小,假设样本大小足够大。
对该数据集应用中心极限定理
- 从数据集中随机抽取女性交易的替换 100 个数据点(样本大小)

2.记录该样本的平均值(8124 美元)
3.重复步骤 1 和 2 10,000 次。你得到 10,000 个平均值。

计算 100 个样本中每个样本的平均值。
4.绘制上面收集的所有 10,000 个样本平均值的分布图。那么根据中心极限定理,这个分布将是一个高斯分布。
5.计算这些样本均值的平均值 x。

x 大约计算为 8700 美元。
在步骤 4 中获得的分布将具有 X 的平均值,因此将以 X 为中心(因为分布是高斯分布),标准偏差σ /√100。(100 为样本量)。然后用这个 X,我们可以给出一个总体均值的估计范围。
但是怎么做呢?
标准差为σ的高斯分布的一个重要特性是,95%的值将位于[ -2σ,+2 σ]范围内。

利用这个性质,我们可以计算出以 X 为中心的 95%范围。记住,X 只不过是样本平均值的平均值。所以这个 95%的范围可以理解为:“95%的样本均值将位于[ -2 σ,+2 σ]之间”。
计算 95%的置信区间
假设我们知道总体标准差σ,设σ为 500。(大假设!!)
样本量=100
样本统计平均值= X = 8700 美元
根据高斯分布的性质,95%的值位于[8600,8800]之间。
区间[8600,8800]是所有女性交易平均花费的 95%置信区间。
说白了,我们可以说【8600,8800】涵盖了所有女性客户平均消费的 95%的数值。就是这样!
或者我们可以说,95%的时间实际人口意味着将位于这个区间。或者我们可以说,我们有 95%的把握认为实际人口平均数在这个范围之内。
任何超出此区间[8600,8800]的值只在 5%的时间内出现。比如所有女性客户平均消费 8200 的概率小于 0.05。
因此,使用一个样本统计量,我们能够给出总体中一个未知参数的一系列似是而非的值。
需要注意的一点是,所有这些都是因为中心极限定理才成立的,该定理认为采样分布本身是高斯分布。如果我们不知道抽样分布是高斯分布,我们就不能利用这个性质来计算区间的上限和下限。
现在你可以猜测 99%的置信区间告诉我们什么。它只是给出了一个覆盖女性顾客平均消费 99%的区间。遵循同样的性质,计算为[ -3σ,+3 σ]。因此,99%的置信区间比 95%的置信区间更宽。
为了简单起见,我做了一个很大的假设,我们知道总体标准差。我们大多不会有这方面的信息。当总体标准差未知时,我们用 t 分布来计算置信区间。
注意事项:
- 在机器学习中,因为数据的数字化,所以非常容易得到人口数据。如果我们有人口数据,就没有必要计算 C.I。如果获取数据的成本很高(医疗应用)或获取此类数据很困难(第三方营销公司),在这种情况下,像这样的经典统计方法就派上用场了。
- 计算置信区间的一个用例是 AB 测试。在 AB 测试中,你将你的人群(用户)随机分成两个(或更多)组:控制组和挑战者组。对照和挑战者是总体的两个样本。使用这些样本,你需要确定这个群体的行为。
- 我们使用中心极限定理,只在需要计算均值的置信区间时才计算置信区间。估计标准差、中位数或第 90 百分位等的置信区间。我们使用自举。这是因为 CLT 不适用于标准差或中位数,而是适用于任何加法或加法后加运算的函数。
我很想知道这篇文章是否对你有所帮助,或者你是否有任何反馈。通过你的评论让我知道。
此外,查看另一个令人困惑的主题的简单解释。
理解什么是 P 值,它与零假设有什么关系
towardsdatascience.com](/learn-to-read-p-value-in-english-11725c09f30e)
如果这篇文章对你有帮助,请鼓掌!
[## Aparna Gopakumar -会员- Beta Gamma 适马| LinkedIn
一名有抱负的数据科学家和一名刚刚毕业的商业分析和项目管理理学硕士…
www.linkedin.com](https://www.linkedin.com/in/aparna-gopakumar/)
XGBoost 的置信区间
构建正则化分位数回归目标
更新:发现我关于渐变提升的新书,实用渐变提升。这是用 python 中的许多例子对渐变增强的深入探究。
[## 实用的渐变增强:深入探究 Python 中的渐变增强
这本书的梯度推进方法是为学生,学者,工程师和数据科学家谁希望…](https://amzn.to/3GqteUN)
梯度增强方法是一种非常强大的工具,可对大型数据集、非线性依赖于大量要素的复杂变量快速执行精确预测。潜在的数学原理在我的另一篇文章中有解释:
[## 用不到 200 行 python 代码 DIY XGBoost 库
XGBoost 解释了梯度推进方法和惠普调整,通过建立自己的梯度推进库…
towardsdatascience.com](/diy-xgboost-library-in-less-than-200-lines-of-python-69b6bf25e7d9)
而且实现方式多种多样: XGBoost 、 CatBoost 、GradientBoostingRegressor,各有千秋,这里讨论这里讨论或者这里讨论。这些实现都有一个共同之处,那就是能够选择一个给定的目标进行最小化训练。更有趣的是,XGBoost 和 CatBoost 提供了对自定义目标函数的简单支持。
为什么我需要定制物镜?
大多数实现都提供了标准的目标函数,如最小二乘法、最小偏差法、休伯法、RMSE 法……但有时,您正在处理的问题需要更具体的解决方案来达到预期的精度水平。使用自定义物镜通常是我最喜欢的调整模型的方法。
请注意,您可以使用超参数调节来帮助找到最佳物镜。参见我关于这个主题的两篇论文:
[## 用 XGBoost 调优 XGBoost:编写自己的 Hyper Parameters 优化引擎
towardsdatascience.com](/tuning-xgboost-with-xgboost-writing-your-own-hyper-parameters-optimization-engine-a593498b5fba) [## 用 SMAC 进行快速超参数调整的 AutoML
使用 AutoML 在高维空间中寻找你的路径
towardsdatascience.com](/automl-for-fast-hyperparameters-tuning-with-smac-4d70b1399ce6)
你能给我们举个例子吗?
当然可以!最近,我一直在寻找一种方法,将我们的一个模型的预测与置信区间联系起来。简单提醒一下,置信区间有两个特征:
- 区间[x_l,x_u]
- 置信水平 C 确保 C%的时间,我们想要预测的值将位于该区间。
例如,我们可以说地球平均温度的 99%置信区间是[-80,60]。
将置信区间与预测相关联可以让我们量化预测的可信度。
你如何计算置信区间?
你需要训练两个模特:
- 一个代表区间的上限
- 一个代表区间的下限
你猜怎么着?您需要特定的指标来实现:分位数回归目标。scikit-learnGradientBoostingRegressor和 CatBoost 实现都提供了一种使用分位数回归目标函数计算这些值的方法,但两者都使用了这种回归的非平滑标准定义:

其中 t_i 为第 I 个真值, a_i 为第 I 个预测值。 w_i 是用于衡量误差的可选权重。并且α定义了分位数。
例如,使用这个目标函数,如果您将 alpha 设置为 0.95,95%的观察值低于预测值。相反,如果将 alpha 设置为 0.05,只有 5%的观测值低于预测值。90%的真实值介于这两个预测值之间。
让我们使用下面的代码来绘制它,对于范围[-10,10]和各种 alphas:
正如你在下面的结果图中看到的,这个目标函数是连续的,但它的导数不是连续的。(0,0)中有一个奇点,即。就误差而言,这是一个 C_0 函数,但不是 C_1 函数。这是一个问题,因为梯度增强方法需要 C_2 类的目标函数,即可以微分两次来计算梯度和 hessian 矩阵。

如果您熟悉 MAE 目标,您应该已经认识到这些分位数回归函数只是 MAE、缩放和旋转函数。如果你不是,下面的截图应该可以说服你:

对数目标
提醒一下,MAE 目标的公式很简单

MAE 目标公式
上图还显示了 MAE 的正则化版本,logcosh 目标。如您所见,该目标非常接近 MAE,但却是平滑的,即其导数是连续且可微的。因此,它可以用作任何梯度增强方法中的目标,并且与默认的不可微方法相比,提供了合理的收敛速度。
因为它是 MAE 的非常接近的近似值,如果我们设法缩放和旋转它,我们将得到分位数回归目标函数的两次可微近似值。
你可能已经注意到,平均风速曲线和对数余弦曲线之间有轻微的偏移。我们将在下面详细解释这一点。
对数曲线的公式很简单:

对数目标的公式
对数曲线的旋转和缩放
我们现在需要做的就是找到一种方法来旋转和缩放这个目标,使它成为分位数回归目标的一个很好的近似。这里没什么复杂的。由于 logcosh 与 MAE 相似,我们应用了与分位数回归相同的变化,即我们使用 alpha 对其进行缩放:

使用对数余弦的平滑分位数回归
这可以用这 12 行代码来完成:
这是可行的,如下所示:

但是等一下!
您可能会好奇为什么将 log 和 cosh 这两个非线性函数结合起来会得到如此简单的近似线性曲线。
答案就在 cosh 的公式里:

科斯公式
当 x 为正且足够大时, cosh 可以近似为

当 x >> 0 时 cosh 的近似值
相反,当 x 足够负时, cosh 可以近似为

x << 0
We begin to understand how combining these two formulae leads to such linear results. Indeed, as we apply the log to these approximations of cosh, we get :

logcosh simplification for x > > 0 时 cosh 的近似值
对于 x >>0。同样代表 x << 0 :

It is now clear why these two functions closely approximate the MAE. We also get as a side benefit the explanation for the slight gap between the MAE and the logcosh. It’s log(2)!
让我们在一个真实的例子上试试
现在是时候确保我们上面进行的所有理论数学在现实生活中有效了。我们不会在一个简单的窦上评估我们的方法,如 scikit 这里所提议的;)相反,我们将使用从 TLC 行程记录数据集中提取的真实世界数据,该数据包含超过 10 亿次出租车行程。
下面的代码片段实现了上面提出的想法。它定义了对数分位数回归目标 log_cosh_quantile ,计算其梯度和 hessian。这些是最小化目标所必需的。
如本文开头所述,我们需要训练两个模型,一个用于上限,另一个用于下限。
代码的剩余部分只是加载数据并执行最少的数据清理,主要是删除异常值。
在这段代码中,我们选择计算 90%的置信区间。因此,我们使用α= 0.95作为上限,使用α= 0.05作为下限。
超参数调整已经手动完成,使用相当标准的值。当然还可以改进,但是结果已经足够好来说明这篇论文了。
脚本的最后几行专用于绘制随机构建的测试集的前 150 个预测及其置信区间:

请注意,我们还在脚本末尾包含了一个计数器,用于计算置信区间正确的实数值的数量。在我们的测试集上,24 889 个真实值中有 22 238 个(89.3%)在计算的置信区间内。
该模型已经在 TLC 行程记录数据集的 2020 年 1 月数据集的前 100 000 行上进行训练。
结论
通过简单的数学,我们已经能够定义一个平滑的分位数回归目标函数,该函数可以插入任何基于目标优化的机器学习算法中。
使用这些正则化函数,我们已经能够为我们的预测预测可靠的置信区间。
这种方法比这里的所示的方法具有无参数的优势。超参数调优已经是优化 ML 模型的一个要求很高的步骤,我们不需要用另一个参数来增加配置空间的大小;)
机密机器学习
在培训之前、期间和之后保密
机密机器学习- ConfML - 是数据所有者在与 ML 服务共享训练数据时遵循的协议。该协议在训练过程中保持训练数据的机密性。
静态数据和传输中数据的机密性可以通过加密来确保。数据在训练开始之前就被解密,并且在训练过程结束之前一直是易受攻击的。ConfML 解决了这个漏洞:它确保了训练过程中训练数据的机密性。
ConfML 协议由两个步骤组成,这两个步骤结束了培训过程:
- 数据所有者在将训练数据文件发送到 ML 服务之前,使用秘密密钥对它们进行加扰。秘密密钥不与 ML 服务共享。
- 在从 ML 服务接收到网络训练加扰数据后,数据所有者使用步骤 1 的秘密密钥将该网络转换成一个行为相同的网络,就好像它是在原始的未加扰数据上训练的一样。
这两个步骤确保 ML 服务永远看不到原始数据,而数据所有者获得他们想要的网络。

机密机器学习(图片由作者提供)
scramble_files.py
数据所有者可以使用类似于以下程序的东西来扰乱将用于训练完全连接的前馈深度神经网络的特征和标签文件。
该程序使用密钥对特征和标签 CSV 文件中列的顺序进行加密。这种列顺序的混乱使得入侵者很难理解数据,但对训练质量几乎没有影响。
#scramble_files.pyimport random
import pandasdef bld_scram_idx(lst_len, key_secret):  #random list based on a key
   my_seed = int(''.join(list(map(str, map(ord, key_secret)))))
   random.seed(my_seed * lst_len)
   scram_idx = list(range(lst_len))
   random.shuffle(scram_idx)
   *return* scram_idxdef scram_list(lst, scram_idx):  #scramble a list of integers
    scram_lst = [0] * len(lst)
    *for* i, item *in* enumerate(lst):
        scram_lst[i] = scram_idx.index(item)
    *return* scram_lstdef scram_df(df, scram_idx):  #scramble a dataframe
    cols_idx = list(range(len(df.columns)))
    cols_idx_scram = scram_list(cols_idx, scram_idx)
    *return* df.reindex(labels = cols_idx_scram, axis='columns')def read_csv_file_write_scram_version(csv_fname, key_secret):
    df_csv = pandas.read_csv(csv_fname, header=None)
    scram_idx = bld_scram_idx(len(df_csv.columns), key_secret)
    df_csv = scram_df(df_csv, scram_idx)
    csv_scram_fname = csv_fname.split('.csv')[0] + '_scrambled.csv'
    df_csv.to_csv(csv_scram_fname, header=None, index=None)
    print(csv_scram_fname + ' file written to disk')KEY_SECRET, FT_CSV_FNAME, LB_CSV_FNAME = "", "", ""  #insert values
read_csv_file_write_scram_version(FT_CSV_FNAME, KEY_SECRET)
read_csv_file_write_scram_version(LB_CSV_FNAME, KEY_SECRET)
解读 _net.py
数据所有者使用该程序,结合用于对训练数据文件进行加扰的密钥,将网络训练加扰数据转换成一个网络,该网络的行为就好像是对原始数据进行训练一样。
这种转换的结果将是网络在原始数据上具有与网络训练加扰数据的在加扰数据上相同的精度。
这个程序的细节根据网络如何用代码实现而有所不同。然而,主要思想是根据scramble_files.py程序中建立的加扰指数恢复网络输入和输出的顺序。
为 AWS 上的批处理 ML 作业配置云环境

在这篇文章中,我将带您完成准备步骤,使您能够在 AWS 上将容器化的机器学习模型作为批处理作业进行调度。这是本系列文章的第二部分,假设您已经完成了第 1 部分中的前 2 步,创建了一个Dockerfile并构建了一个容器映像。
为了了解您在准备什么,最终,我们将使用 AWS Batch 来安排 ML 作业。
AWS Batch 使用弹性容器服务(ECS)将作业作为 Docker 容器执行。它根据提交的批处理作业的数量和特定资源要求,动态调配计算资源的最佳数量和类型(例如,CPU 或内存优化实例)。
如果您的模型需要很长时间运行或者有复杂的依赖关系,AWS Batch 是一个很好的解决方案。它支持任何可以作为 Docker 容器执行的作业。
但是为了能够使用 AWS Batch,需要为您的云环境做一些准备。在本文中,我们将首先在注册表中注册我们构建的容器,并确保我们有适当的配置。
步骤 3—注册您的容器👩🏻✈️
我们构建的 Docker 映像当前驻留在我们的本地机器上。要在其他地方运行它,比如云,我们需要把它推到 Docker 注册中心。这非常像将您的本地代码推送到 GitHub,以便您的 EC2 可以克隆它。
类似于我们有不同的版本控制工具,如 GitHub、GitLab、BitBucket 等,也有不同的容器注册中心。Docker Hub 是最受欢迎的一个,但是 AWS 、 Azure 、 GCP 也都提供了它们自己的容器注册表。你可以根据自己的喜好选择其中的任何一个。对于本教程,我们将使用 AWS 原生— Amazon 弹性容器注册中心(或 ECR)。
创建存储库
打开 AWS 控制台并转到 ECR,选择 Create repository 并给它命名。然后点击创建存储库。

创建存储库
现在,您已经准备好将容器从您的终端推送到这个存储库了(需要 AWS CLI )。单击存储库名称查看推送命令。

通过单击存储库名称查看推送命令

查看推送命令

容器注册库的推送命令
推送命令是什么意思
1)向您的注册中心认证您的 Docker 客户端
打开您的终端,复制步骤 1 中括号中的内容,以检索您的登录命令,该命令向您的注册表验证您的 Docker 客户端。然后运行返回的登录命令。
如果您有不同的配置文件设置(比如一个用于个人,一个用于商业),记得在最后添加--profile username选项来检索正确的凭证。
2)树立你的形象
3)标记图像,这样我们可以将图像推送到这个注册表
所以在这里,我们用你的亚马逊 ECR 注册 URI 来标记你创建的图像。
$ docker tag <IMAGE_NAME>:<TAG> 012345678901.dkr.ecr.us-east-1.amazonaws.com/<IMAGE_NAME>:<TAG>
4)将图像推送到该注册表
$ # push your image to the AWS ECR repository
$ docker push 012345678901.dkr.ecr.us-east-2.amazonaws.com/<IMAGE_NAME>:<TAG>
您可以通过从注册表中提取来测试推送是否成功。
$ docker pull 012345678901.dkr.ecr.us-east-2.amazonaws.com/<IMAGE_NAME>:<TAG>
现在,您的云帐户可以访问您的容器了,您已经准备好进入第 4 步来配置您的云环境。
步骤 4 —配置权限和环境👩🏻🎤
EC2
你们大多数人都已经熟悉 EC2 的设置,但我只想在这里提几件事。我们不会手动创建 EC2,但是无论您选择在 AWS 上使用什么服务,它最终都会创建一些 EC2 来进行计算,因此拥有正确的设置非常重要。
1)密钥对
我们需要为 AWS 批处理计算环境容器实例(ec2)设置 pem 密钥。您可以在 EC2 控制台中创建密钥对。创建密钥对后,您可以在创建计算环境时指定密钥对的名称,然后在使用 SSH 登录时提供私钥。
2)安全组
我们还需要为 AWS 批处理容器实例指定防火墙规则。例如,允许从您自己的 IP 使用 SSH,从任何地方使用 HTTP 和 HTTPS,以及任何其他规则来打开您的任务所需的端口。您也可以在 EC2 控制台中进行设置。
VPC
虚拟私有云(VPC)使您能够将 AWS 资源启动到您定义的虚拟网络中。您可以完全控制虚拟网络环境,包括选择自己的 IP 地址范围、创建子网以及配置路由表和网络网关。
为了在您安全实践中更加警惕,我们应该设置一个 VPC,并让我们的计算节点驻留在 VPC 的私有子网中(如果需要访问互联网,则使用 NAT 网关)。
但是出于测试目的,您可以创建一个带有单个公共子网的 VPC,我们应该没问题。
IAM 角色和权限
在您开始尝试 AWS Batch 之前,请通过参考该清单确保您拥有所需的权限(如果您是管理员,请跳过此步骤)。
简而言之,AWS Batch 将创建一个 EC2 节点来使用 Amazon ECS 运行您的代码,因此您需要创建一个 IAM 角色,该角色具有访问计算环境和容器实例的权限,然后将该角色与您的计算环境相关联。
AWS 批处理计算环境和容器实例角色是在首次运行控制台时自动创建的,因此如果您打算使用 AWS 批处理控制台,可以继续下一节。如果您计划使用 AWS CLI,在创建您的第一个计算环境之前,完成 AWS 批处理服务 IAM 角色和 Amazon ECS 实例角色中的过程。
AWS CLI
如果你还没有安装 AWS CLI,现在是安装的好时机。我们将使用它将我们的容器推送到 AWS 注册中心,并与 AWS 批处理交互。
现在我们已经将代码容器化,构建了容器映像,设置了基本的云环境,并确保我们拥有所需的权限,我们已经准备好开始在 AWS 上调度我们的第一批作业。本系列的第 3 部分再见!
参考
- aCloudGuruAWS ECS——由 Nick Janetakis 教授的 Scaling Docker 课程
- 从 AWS 文档开始使用 AWS 批次
- 使用 AWS 文件中的 AWS 批次进行设置
- 创建简单的“获取&运行”AWS 批处理作业Bryan Liston
- Sunit Mehta 的 AWS 批次入门
- 快速介绍 Docker 标签
关于作者💁🏻
我是一名机器学习工程师,除了技术教程之外,我还撰写关于生产力和自我发展的文章。我最近的兴趣是学习广东话和中国书法。
用 YAML 配置 XGBoost 型号
如何使用 YAML 文件使您的模型快速共享,非常灵活,并为生产做好准备
介绍
没有什么比开始一个新的 ML 项目更令人愉快的了。首先,你用一些数据清理来弄脏你的手,然后你开始将那些干净的数据加载到熊猫 df 中,开始一些探索性的数据分析(在那里你不可避免地发现你需要回去做更多的数据清理……)接下来,你进入特征工程,然后最后,你处于准备构建一些模型和实验的阶段!
在整个过程中,您可能已经将所有代码放在了您可信赖的 jupyter 笔记本中。现在,您想要实际应用您的模型,但是您该如何开始呢?你当然不能把笔记本投入生产。
人们可以写一本关于机器学习模型生产化的书。我并不想教你生产所需的所有工程步骤。相反,把这篇文章看作是生产化 改善 :你可以对你的代码做一个小小的改变来为生产做准备。
在本文中,我将向您展示如何使用 YAML 配置文件来使您的代码更整洁、更具可伸缩性、更易于维护,并且在运行实验时更高效。
YAML 不是标记语言
YAML 是一种递归命名的语言,用于以人类可读的格式存储信息[1]。
对 yaml 结构的详细讨论将使我们离题太远,所以为了满足我们的目的,让我们考虑下面的 yaml 块来初始化 XGBoost 分类器。该块定义了一个字典“模型设置”,将另一个字典作为其值参数字典。**参数 字典保存我们想要设置的 xgboost 模型的每个参数的值。
# basic yaml config
model_settings:
  random_state: 90210
  objective: 'binary:logistic'
  eval_metric: ['error', 'map', 'auc', 'aucpr']
  n_jobs: -1
  learning_rate: 0.01
  colsample_bytree: 0.8
  subsample: 0.75
  reg_alpha: 2.5
  reg_lambda: 1.25
  max_depth: 7
  gamma: 0.2
  n_estimators: 1251
  max_delta_step: 8.5
  scale_pos_weight: 2.0
通过在 yaml 配置中编写模型的参数,您可以获得 4 个主要好处:
- 在模型的初始化中,参数不再是硬编码的,这使得代码更加清晰
- 合作者可以通过传递单个文件(最好是在 git repo 中)轻松实现您的模型
- 对模型进行更改不再需要代码重构
- 遵循(3),您可以轻松地在您的模型上进行“假设”实验。例如 如果 我把 max-depth 改成更深一层呢?
您的模型的配置对象
我们希望能够将 yaml 文件中包含的信息传递给项目中的其他脚本。为此,我们为您的项目定义了一个包含所有子类(将它们视为模块)的超类,这些子类将包含 yaml 中的值。通过这样做,我们可以强制类型,这样用户就不能在需要 int 的地方传递字符串。让我们定义一个这样的对象。
class YamlDemo:
    class ModelSettings:
        def __init__(self, random_state: int, objective: str, eval_metric: list, n_jobs: int, 
                     learning_rate: float, colsample_bytree: float, subsample: float, 
                     reg_alpha: float, reg_lambda: float, max_depth: int, gamma: float, 
                     n_estimators: int, max_delta_step: float, scale_pos_weight: float):
            self.random_state = random_state
            self.objective = objective
            self.eval_metric = eval_metric
            self.n_jobs = n_jobs
            self.learning_rate = learning_rate
            self.colsample_bytree = colsample_bytree
            self.subsample = subsample
            self.reg_alpha = reg_alpha
            self.reg_lambda = reg_lambda
            self.max_depth = max_depth
            self.gamma = gamma
            self.n_estimators = n_estimators
            self.max_delta_step = max_delta_step
            self.scale_pos_weight = scale_pos_weight
    def __init__(self, model_settings: ModelSettings, config_path: str):
        self.model_settings = model_settings
        self.config_path = config_path
配置分析器
有了 yaml 配置和配置对象,最后一个组件是编写解析器来读取 yaml 并用值填充配置对象。一个简单的配置解析器函数可以帮你做到这一点。
import yamldef parse_config(config_path: str) -> YamlDemo:
    """
    parses yaml config; returns populated YamlDemo object
    """
    with open(config_path, 'r') as stream:
            try:
                config_settings = yaml.safe_load(stream)
            except yaml.YAMLError as e:
                print(e)model_settings = YamlDemo.ModelSettings(
            random_state = config_settings["model_settings"]["random_state"],
            objective = config_settings["model_settings"]["objective"],
            eval_metric = config_settings["model_settings"]["eval_metric"],
            n_jobs = config_settings["model_settings"]["n_jobs"],
            learning_rate = config_settings["model_settings"]["learning_rate"],
            colsample_bytree = config_settings["model_settings"]["colsample_bytree"],
            subsample = config_settings["model_settings"]["subsample"],
            reg_alpha = config_settings["model_settings"]["reg_alpha"],
            reg_lambda = config_settings["model_settings"]["reg_lambda"],
            max_depth = config_settings["model_settings"]["max_depth"],
            gamma = config_settings["model_settings"]["gamma"],
            n_estimators = config_settings["model_settings"]["n_estimators"],
            max_delta_step = config_settings["model_settings"]["max_delta_step"],
            scale_pos_weight = config_settings["model_settings"]["scale_pos_weight"]
        )
    config = YamlDemo(model_settings=model_settings, config_path=config_path)
    assert isinstance(config, YamlDemo)
    return config
将配置对象传递给模型
现在,这个过程的最后一步是将 config 对象中的信息传递给模型。
首先,我们必须解析我们的配置以获得一个填充的对象:
config_settings = parse_config('/Users/cole.brendel/Desktop/demo.yaml')
接下来,让我们获取一个示例数据集,以查看“运行中”的模型:
from sklearn.datasets import load_iris
X,y = load_iris(return_X_y=True)# notice how I set the value for `random_state` from the config!from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.33, random_state=config_settings.model_settings.random_state)
最后,我们将把这个配置传递给一个训练模型的函数。
# fits a model on the training set/evals on test set
import pandas as pd
from pandas import DataFrame
import xgboost
from xgboost import XGBClassifier
from sklearn.metrics import classification_reportdef fit_xgb_model(config_settings: YamlDemo, X_train: DataFrame, y_train: DataFrame, X_test: DataFrame, y_test: DataFrame) -> xgboost.XGBClassifier:
    """
    fits an XGB model and returns model obejct 
    """
    eval_set = [(X_test, y_test)]  # used for early stopping# populate the model with settings from config
    xgb = XGBClassifier(
        random_state=config_settings.model_settings.random_state,
        objective=config_settings.model_settings.objective,
        eval_metric=config_settings.model_settings.eval_metric,
        eval_set=eval_set,
        n_jobs=config_settings.model_settings.n_jobs,
        learning_rate=config_settings.model_settings.learning_rate,
        colsample_bytree=config_settings.model_settings.colsample_bytree,
        subsample=config_settings.model_settings.subsample,
        reg_alpha=config_settings.model_settings.reg_alpha,
        reg_lambda=config_settings.model_settings.reg_lambda,
        max_depth=config_settings.model_settings.max_depth,
        gamma=config_settings.model_settings.gamma,
        n_estimators=config_settings.model_settings.n_estimators,
        max_delta_step=config_settings.model_settings.max_delta_step,
        scale_pos_weight=config_settings.model_settings.scale_pos_weight
    )
    # fit the model
    xgb_fit = xgb.fit(X_train, y_train)
    # predict on test set
    y_pred = xgb.predict(X_test)
    # show results
    print(classification_report(y_test, y_pred, digits=4, target_names=['setosa', 'versicolor', 'virginica']))
    return xgb_fitxgb = fit_xgb_model(config_settings, X_train, y_train, X_test, y_test)
还有维奥莱!模型已经训练评估过了!

已配置 XGBoost 模型的分类报告
这个过程的优点的简短题外话
你可能会问自己:
为什么我要写这些代码只是为了给我的模型传递一些参数呢?这看起来像一吨的工作和方式比必要的更多的代码!
确实,您可以简单地用所有的参数填充一个模型,但是您现在将自己锁定在那个配置中了!要进行更改,您需要进入并修改代码。如果你想向你的队友展示你的最新配置,它产生了最好的结果?你会复制/粘贴你的模型代码吗?为什么不把配置文件松掉呢?
这个配置只是冰山一角。通过使用我在这里概述的过程,您可以很容易地指定更多与您的模型管道相关的附加信息。例如,培训文件放在哪里?将该路径放入配置中!每个 ETL 应用后要运行哪些 ETL,临时文件存放在哪里?配置那个!RandomSearch 应该运行多少次迭代?再次配置!为了让您对这些配置的范围有所了解,请考虑以下高度简化的示例:
ec2_settings:
  ip_address: xxx.xxx.xxx
  pem_file: '/Users/{}/Desktop/demo/ssh/my.pem'
data_settings:
  deploy_date: 'today' # or 'YYYY-MM-DD'
  target_names: ['no', 'yes']
  raw_bucket: '/Users/{}/Desktop/demo/datasets/raw'
  etl_bucket_train: '/Users/{}/Desktop/demo/datasets/etl_train'
  etl_bucket_score: '/Users/{}/Desktop/demo/datasets/etl_score'
  tmp_bucket: '/Users/{}/Desktop/demo/datasets/tmp'
  df_name: 'df.csv'
logger_settings:
  log_name: 'demo.log'
  logs_path: '/Users/{}/Desktop/demo/logs'
etl_settings:
  pickles: '/Users/{}/Desktop/demo/src/etls/pickles'
  target_variable: 'xxx_yyy'
  idx_column: 'idx_col'
model_settings:
  pickles: '/Users/{}/Desktop/demo/src/modeling/pickles'
  predictions: '/Users/{}/Desktop/demo/predictions'
  eval_metric: ['error', 'map', 'aucpr']
  n_iters: 500
  sampling_points: 1000
  optimization_metric: 'f1_weighted'
  cv_folds: 5
有了这样一个系统,分享结果和实验就变得简单了,就像把你的同事的文件从一个 yaml 文件中删除一样。
结论和下一步措施
现在,您已经拥有了开始为您的机器学习项目编写配置所需的所有工具!这个过程需要一些时间来建立,但从长远来看是值得的。
您可以进行快速实验,并且不必与代码交互就可以对模型进行更改。你使其他科学家能够简单地改变一些设置并进行测试。通过将 YAML 读入 python 类,您可以轻松地强制类型并防止传递错误的值,然后再花钱购买昂贵的计算资源。
参考
python 中统计计算的数据结构,McKinney,第九届科学中的 Python 会议录,第 445 卷,2010 年。
@software{reback2020pandas,
    author       = {The pandas development team},
    title        = {pandas-dev/pandas: Pandas},
    month        = feb,
    year         = 2020,
    publisher    = {Zenodo},
    version      = {latest},
    doi          = {10.5281/zenodo.3509134},
    url          = {https://doi.org/10.5281/zenodo.3509134}
}
Harris,C.R .,Millman,K.J .,van der Walt,S.J .等人用 NumPy 进行阵列编程。自然 585,357–362(2020)。DOI:10.1038/s 41586–020–2649–2。
sci kit-learn:Python 中的机器学习,Pedregosa 等人,JMLR 12,第 2825–2830 页,2011 年。
陈,t .,& Guestrin,C. (2016)。XGBoost:一个可扩展的树提升系统。第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集(第 785–794 页)。美国纽约州纽约市:ACM。https://doi.org/10.1145/2939672.2939785
在 Windows 子系统 Linux (WSL2)中配置 Jupyter 笔记本
我将解释如何配置 Windows 10/11 和 Miniconda 来与使用 WSL2 的笔记本一起工作
我们将了解如何:
- 安装和配置 WSL2
- 安装和配置 Windows 终端
- 安装 Miniconda 和公共库
- 发布 Jupyter 实验室/笔记本电脑
- 将 GUI 和远程桌面安装到 WSL2,以便像 Spyder 或 Anaconda Navigator 一样使用所需的 GUI
- 谢谢
- 一些有用的资源
首先,安装 WSL2
Windows 子系统 Linux (WSL2)将于 2020 年 5 月在 Windows 10 版本 2004 中推出。如果你没有,那么按照https://docs.microsoft.com/en-us/windows/wsl/install-win10中的说明安装一个 Ubuntu 发行版


我推荐从微软商店安装 Windows 终端,而不是使用默认的 Ubuntu 终端,因为这样可以让你在同一个窗口中拥有多个终端。
使用 Sublime Text 或 Notepad++从 Windows 资源管理器编辑 Linux 文件也更方便,使用的路径是: *\wsl$*
您可以在窗口中浏览和编辑 Ubuntu 文件:

并将默认配置文件更改为打开至 Ubuntu 终端打开设置文件,并根据 Ubuntu 指南更改 defaultProfile 的值:



用 Visual Studio 代码打开的 settings.json
更新你的 Linux 系统
在 Ubuntu 上打开一个新的终端,运行以下命令:
sudo apt-get update
sudo apt-get upgrade
sudo apt autoremove

我将使用 Ubuntu 20.04.1 LTS 来检查版本,如下所示:
lsb_release -a
uname -r

安装迷你康达
最好用 Miniconda 而不是 Anaconda。后者包含了许多你通常不会用到的库,这意味着更慢的发行版更新和更多的磁盘空间。
我的朋友 Ted Petrou 在https://medium . com/dunder-data/anaconda-is-budget-set-up-a-lean-robust-data-science-environment-with-miniconda-and-conda-forge-b 48 E1 AC 11646中写了一篇详细的文章
从https://repo.anaconda.com/miniconda/下载最后一个文件,并按照说明操作:
cd ~
wget [https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh](https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh)
chmod +x Miniconda3-latest-Linux-x86_64.sh
sh Miniconda3-latest-Linux-x86_64.sh
rm Miniconda3-latest-Linux-x86_64.sh
如果你更喜欢 Anaconda,过程类似,你需要使用来自https://repo.anaconda.com/archive/的最后一个文件

按回车键并阅读完整的许可证

选择安装位置,回车默认

我推荐“是”来设置你的 Ubuntu 用户的活动环境
下次打开终端时,您会注意到在 user@server 提示符前有一个 (base) 文本,这意味着在 conda:
python
conda info
conda info --envs

接下来,我们将进行一些更新:
conda update conda
conda update --all


安装库&工具
接下来,我们将进行一些基础安装,以获得熊猫、Jupyter 实验室/笔记本和机器学习:
conda install pandas scikit-learn matplotlib jupyter jupyterlab sqlalchemy seaborn pip git
然后安装 Jupyter 扩展并刷新任何更新:
conda install -c conda-forge jupyter_contrib_nbextensions
conda update conda
conda update --all
推出 Jupyter 实验室和笔记本电脑
我更喜欢 Jupyter Lab 而不是 Notebook,因为它可以让你更灵活地在同一个标签浏览器下打开多个窗口,允许你在命令提示符之外打开多个文件。为了避免消息错误,运行每个带有无浏览器参数的命令
朱庇特实验室
使用以下命令打开一个新的 Ubuntu 终端:
jupyter lab --no-browser

复制并粘贴完整的 URL,包括令牌

Jupyter 笔记本
使用以下命令打开一个新的 Ubuntu 终端:
jupyter notebook --no-browser

复制并粘贴完整的 URL,包括令牌。


网络连接问题
如果您失去了网络连接,例如,在 ping Google 时显示超时错误,那么可能与 resolv.conf 文件中的 servername 的更改有关。
ping google.com
要修复它,您需要删除该文件以断开与运行文件夹的链接,并创建一个 wsl.conf 文件以避免再次生成 resolv.conf 文件,并执行以下语句:
sudo rm /etc/resolv.conf
sudo bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
sudo bash -c 'echo "[network]" > /etc/wsl.conf'
sudo bash -c 'echo "generateResolvConf = false" >> /etc/wsl.conf'
sudo chattr +i /etc/resolv.conf
解决本地主机的问题
有些版本无法使用 localhost 连接。Windows 将发布更新来解决此问题,直到您可以使用 IP 地址连接并更改 localhost 。要知道您的 IP 地址,您可以通过命令使用斜杠前面的数字:
ip addr | grep eth0 | grep inet
安装图形界面和远程桌面
WSL2 不包括 GUI,任何需要用户界面的程序都会给出类似的错误:
QStandardPaths: XDG_RUNTIME_DIR not set
qt.qpa.screen: QXcbConnection: Could not connect to display
Could not connect to any X display.
如果您想运行一些图形接口,如 anaconda-navigator 或 spyder IDE,下一步是可选的。使用以下命令安装:
conda install spyder anaconda-navigator
如果你通过终端启动程序会这样失败:

按照以下步骤在端口 3390 中安装 XFCE4 lightdm 和远程桌面( xrdp )。我更改了端口以避免与本地窗口冲突。
运行下面的命令,选择 lightdm 作为 X 显示管理器:
sudo apt update && sudo apt -y upgrade
sudo apt-get purge xrdp
sudo apt-get install -y xfce4 xfce4-goodies
sudo apt-get install xrdp
sudo cp /etc/xrdp/xrdp.ini /etc/xrdp/xrdp.ini.bak
sudo sed -i 's/3389/3390/g' /etc/xrdp/xrdp.ini
sudo sed -i 's/max_bpp=32/#max_bpp=32\nmax_bpp=128/g' /etc/xrdp/xrdp.ini
sudo sed -i 's/xserverbpp=24/#xserverbpp=24\nxserverbpp=128/g' /etc/xrdp/xrdp.ini
echo xfce4-session > ~/.xsession
sudo systemctl enable dbus
sudo /etc/init.d/dbus start
sudo /etc/init.d/xrdp start
sudo /etc/init.d/xrdp status

选择 lightdm
使用您的 



打开终端运行 spyder IDE
谢谢
最后,我要感谢泰德·彼得鲁和 T2·斯科特·波斯顿,他们是善良而知识渊博的人,在我开始学习 Python 熊猫的时候给了我指导和帮助,还有我的兄弟 T4·迈克尔·萨维德拉,是他们启发了我写这篇文章。
有用的资源
[## Anaconda 过于臃肿——用 Miniconda 和 Conda-Forge 建立一个精简、健壮的数据科学环境
在本教程中,我将描述在您的……上建立一个瘦的和完整的 Python 数据科学环境的过程
medium.com](https://medium.com/dunder-data/anaconda-is-bloated-set-up-a-lean-robust-data-science-environment-with-miniconda-and-conda-forge-b48e1ac11646) [## 使用 WSL 在 Windows 上构建 Python 开发环境
2016 年,微软推出了面向 Linux 的 Windows 子系统(WSL),为 Windows 带来了强大的 Unix 功能。在…
pbpython.com](https://pbpython.com/wsl-python.html) [## 用 Visual Studio 代码在 Linux 的 Windows 子系统中开发
Visual Studio Code Remote - WSL 扩展允许您将 Windows Subsystem for Linux (WSL)用作您的全职…
code.visualstudio.com](https://code.visualstudio.com/docs/remote/wsl) [## 使用 Visual Studio 代码在 Linux 的 Windows 子系统中工作
本教程将带您了解如何启用 Windows Subsystem for Linux (WSL ),以及如何使用…
code.visualstudio.com](https://code.visualstudio.com/docs/remote/wsl-tutorial) [## 在 Visual Studio 代码中使用 Jupyter 笔记本
Jupyter(以前的 IPython Notebook)是一个开源项目,可以让您轻松地将 Markdown 文本和可执行文件结合起来…
code.visualstudio.com](https://code.visualstudio.com/docs/python/jupyter-support) [## Linux 的 Windows 子系统概述
Linux 的 Windows 子系统允许开发人员运行 GNU/Linux 环境——包括大多数命令行工具…
docs.microsoft.com](https://docs.microsoft.com/en-us/windows/wsl/) [## Craig Loewen,Windows 命令行的作者
该网站使用 cookies 进行分析、个性化内容和广告。通过继续浏览本网站,您同意…
devblogs.microsoft.com](https://devblogs.microsoft.com/commandline/author/crloewenmicrosoft-com/)
确认偏差是探索性数据分析的敌人
人类有一种倾向,想要证明先前的信念,这可能会对数据分析推动变化的能力产生毁灭性的影响。

在 16 世纪到 19 世纪之间,在西欧,成千上万的妇女在政治迫害中被处死。由于识别女巫的困难性,特殊的测试被用来确定一个女人是否是女巫。
其中一个例子是将这名女子双手反绑扔进水中。如果她漂浮,她是一个女巫,被认为是被撒旦所救,并被判处死刑。如果她是淹死的,她是无辜的。
确认偏差
尽管政治迫害和数据分析之间的联系可能还不清楚,但它们都受制于被称为确认偏差的认知偏差。
当一个人搜索或解释信息以符合他们先前的信念时,就会产生确认偏差。
政治迫害经常被用作确认偏差的一个例子。如果一个女人在这个过程中死去,证明她是无辜的没有任何实际意义。但这不是测试的重点。取而代之的是,它被设计成一种纯粹的方法来确认先前的信念,内疚。
探索性数据分析
探索性数据分析 (EDA)是对数据的初步调查,通常使用统计和图形表示,对其进行总结和理解。
本质上,EDA 是回答关于数据的问题的过程。其中一些可能非常简单:
- 数据有多少行/列?
- 列的类型有哪些?
- 有没有数据缺失?
然而,随着 EDA 的发展,问题开始变得更加复杂。让我们以显示一部新电影票房信息的数据集为例。一个问题可能是
周末售票多吗?
你可能会注意到,我写这个问题是为了确保可以用简单的“是”或“不是”来回答。这可能看起来微不足道,但正是在这里,偏见可能开始出现。我可以用不同的方式写这个问题,
一周中的特定日子会有更多的门票销售吗?
我没有设计一个问题来确定一周的门票销售分布,而是提出了一个问题来验证我现有的信念,即周末会卖出更多的门票。

杰瑞米·雅普在 Unsplash 上的照片
背景可能相去甚远,但这和女巫测试是一样的,设计女巫测试只是为了证明女人是女巫,而不是一个女人是不是女巫。
偏见的问题
这似乎是一个微不足道的观点。在一天结束的时候,你将最终决定周末是否能卖出更多的票。
问题不在于分析本身,而在于之后会发生什么。就其本质而言,EDA 是通向另一个问题的垫脚石…那又怎样?
我们使用 EDA 过程中发现的信息来推动变革;也许修改现有的过程或创建一个新的过程。但是改变本身就包含了对原有信念的颠覆。
如果确认偏见阻止人们寻找违背他们先前信念的证据,那么再多的 EDA 也不能推动有意义的改变。
相反会发生什么?
想象一下,我们发现这一周的门票销售增加了。大多数人会跳进解释的兔子洞,而不是采取行动(也许在周末增加广告以提高周末门票销售)。

利用进一步的(通常是人为的)分析,他们会试图解释为什么他们的信念是正确的,尽管有证据。然后,EDA 不再成为变革的工具,而是成为无效理论的自我提升。
减轻确认偏差
人类对确认偏差有一种天然的倾向,我们经常分不清自己在做什么。因此,我们需要确保我们正在采取积极和有目的的措施来减轻它。
小的改变,比如如上所述的重新构思问题,可以产生很大的不同,因为我们下意识地将自己从这种偏见中移除。
这并不是说我们应该完全无视我们先前的信念。我们通常有一个理由,如果有相反的证据,我们应该质疑它。相反,我们需要确保当我们质疑它时,我们是从公平和平衡的角度这样做的。
这通常是很难做到的,所以另一个避免确认偏差的技巧是获得第二意见。请其他人看看证据,看看他们的想法。
EDA 是建立在数据基础上的强大工具。但是数据不仅包含固有的偏见,而且还会被分析它的人所偏见。我们有责任了解何时何地我们可能会有偏见,并减少偏见。
如果你喜欢这篇文章,你可能会喜欢阅读 为什么所有数据科学家都应该了解行为经济学 以了解更多关于确认偏差以及它如何与其他类型的认知偏差一起影响数据分析和数据科学的信息。
以人性化的个人化面对认知的不公正
利用 GDPR 的伦理基础引入数字身份和个性化的新范式

GDPR 基于欧洲对人的特殊看法,旨在保护数字时代人的基本方面。 DDP 在 Unsplash 上拍照
基于机器学习的个性化服务已经成为我们日益数字化的生活中不可或缺的一部分。个性化依赖于大量的行为大数据(BBD) :人类与应用程序、设备和社交网络交互时产生的个人数据。BBD 是我们数字表现的基本原材料。
随着网络生活的稳步发展,人的数字表现在法律和道德上变得越来越重要。有影响力的欧洲法律理论家和哲学家甚至写了一份关于生活的宣言,围绕着数字时代对人类意味着什么展开讨论。与此同时,IEEE 已经阐明了一个伦理一致设计 (EAD)的愿景,让“个人能够管理他们的身份和他们数据的伦理含义。”
但是这样的设计对于推荐系统来说意味着什么呢?而给人这种权力又有什么意义?驱动这种想法的是人类的哪些潜在观念?这就是我们要解开的。

威廉·狄尔泰和马克斯·韦伯认为人文科学需要与自然科学完全不同的方法论。我们建议将叙事理解作为一种范式,赋予我们的数字体验以意义,并尊重不同的观点。Andriyko Podilnyk 在 Unsplash 上拍摄的照片
我们的愿景:人性化个性化
我们引入我们的概念 人性化个性化 作为思考数字身份和个性化的一种方式,这借鉴了欧盟通用数据保护条例 (GDPR)中体现的基本道德价值观。人性化的个性化首先着眼于哪些能力使人与众不同,然后试图想象如果推荐系统和个性化支持这些能力,它们会是什么样子。
我们的人性化个性化概念设想通过数据控制者和数据主体之间的对话叙事结构的概念,从由我们的“有机体”兴趣主导的隐含的、基于行为的表示范式,转变为以意识为中心的、明确的和“反思的】反馈。人文主义的个性化受到康德、黑格尔、哈贝马斯、利科、德里达等人的哲学思想的启发。
除了个性化,对叙事的关注可能会对人工智能/人工智能的未来产生广泛的影响。如果我们要打破人工智能中意义的障碍,我们也需要打破叙事的障碍。此外,叙事的内在可理解性在“以用户为中心”的可解释人工智能的新兴领域可能是有用的,特别是在 GDPR 等法规赋予数据主体对算法决策进行清晰、可理解的解释的权利的情况下。最后,由于其直观的“解释力”,叙事解释可以作为因果建模新方法的有趣镜头。
叙述的准确性和认知的不公正
我们提出 叙述准确性 的概念,作为个性化的定向设计目标。通过最大化用作推荐系统输入的个人数据和最终推荐本身的叙述准确性,我们可以减少通过个性化对个人造成的 认知不公正 。
认知不公正是多方面的。它指的是我们以一种不公平的方式“分配”可信度给一个人的真理主张,即贬低他们作为一个认知者的能力。这也意味着我们缺乏理解他人经历的概念资源。为什么关注叙述的准确性及其补充,认知的不公正?
因为我们相信叙事的概念是值得保护的人类经验的重要特征。与此同时,我们拒绝启蒙理想中解决真理问题的单一、普遍的“方法”。
换句话说,实现完全客观的“无中生有的观点”是不可能的。相反,当我们融合来自不同视角的不同输入时,知识会变得更加强大。如果你想阅读更多的细节,特别是关于 GDPR 的哲学人类学,请参阅我们的工作论文 《超越我们的行为:GDPR 和人文个性化 》。
那么,什么是个性化呢?
个性化是一项巨大的业务。例如,网飞声称由于其个性化努力每年节省 1B 美元。下面是脸书描述它如何使用你的个人数据来“个性化”你在这个平台上的体验的新服务条款:
您在脸书的体验与众不同:从您在 News Feed 或我们的视频平台上看到的帖子、故事、事件、广告和其他内容,到您关注的页面和您可能使用的其他功能,如趋势、市场和搜索。我们使用我们拥有的数据来个性化您的体验,例如,关于您建立的联系、您选择的选项和设置,以及您在我们的产品上和产品外分享和做的事情。
为什么我们建议在 GDPR 建立人工智能伦理
简而言之,我们认为 GDPR 作为人工智能/人工授精的法律规范和伦理基础具有双重意义。主要原因是我们不认为增加当前道德 AI/ML 的竞争原则、指导方针和框架是有成效的。仅一篇论文就列出了至少 84 个“AI 伦理”指南的例子,其中大部分还不到 4 年。没有人能理解这一切。
此外,通过 GDPR 将道德原则与法律规范联系起来是有价值的,因为原则上,GDPR 适用于世界任何地方的任何数据控制者(T0)———处理居住在欧盟的数据主体(T5)的个人数据。法律有制度支持,通过国家对合法使用武力的垄断来实现遵守。因此,在我们看来,(潜在地)由武力支持的道德更有可能促进全球工业和研究人员的遵守。
信息自决和人格权
GDPR 赋予数据主体的权利反映了欧洲对人的某种理解。这些原则很有价值,因为它们经受住了几个世纪以来激烈的哲学审视。从欧洲的角度来看,数据保护和隐私是旨在维护人类尊严的工具。如果你真的对细节感兴趣,我们会在与上面链接的论文中探索使人类生命有价值背后的哲学思想。
无论如何,两个关键概念构成了 GDPR 的伦理基础:信息自主及其前身个人个性自由发展的权利。根据法律学者 Antoinette Rouvroy 和 Yves Poullet 的说法,信息自决被定义为“个人对有关他的数据和信息的控制”,并且是任何人类自决的先决条件。自决也有一个政治层面,因为一个合作、民主的社会取决于其公民是否有自决的能力。在我们的论文中,我们将这些观点与尤尔根·哈贝马斯的“交流理性”联系起来。
我们的数字行为从根本上被曲解了
我们断言,并不是个性化如此难以置信地准确,而是我们作为语言、社会和身体动物,在我们自由活动和思想的潜力方面欺骗了自己。
数字环境进一步限制和约束了人类的潜能。也许更糟糕的是,随着数字环境中自由度的减少,人类行为的诠释学问题出现了,并提出了伦理问题。首先,收集隐性数据背后的行为主义假设没有意识到一个重要的警告:数字行为的意义从根本上说是不确定的。因为人类是有意识、有意图的存在,我们发起的每一个行动都可以从外部/物理主义或内部/现象学的角度来看。
当 BBD 平台和数据科学家将复杂行为(例如,完成一项交易)分解为过于狭窄的“子符号”类别(例如,点击、鼠标轨迹和其他“微行为”)时,意图变得与结果分离。更重要的是,从意图到行动的明确的一对一映射变得不可能。一个人不能意愿去做他不能首先识别的事情。
正如心理学家 Vallacher 和 Wegner 所说,
正如哲学家早就注意到的,行为的任何部分都可以有意识地用许多不同的方式来识别。像“与某人见面”这样简单的事情……任何一个有轻微活跃精神生活的人都可能认为是“社交”、“寒暄”、“了解一个新的人”、“展示一个人的个性”,甚至是“说话”。
因此,误解(或缺乏完整的解释)被融入到数字生活中,当自动化系统能够实时动态地改变数字环境时,这种情况会变得更糟,比如脸书使用的基于强化学习的推荐系统。因此,我们面临着解释的危机。怎么办?
如果我们遵循 GDPR,我们让数据主体自己决定。

根据哲学家保罗·里科尔的说法,我们是“角色”,其行为通过融入我们讲述的关于自己的故事而获得意义。照片由雅各布·欧文在 Unsplash 上拍摄
通过叙事身份连接道德和社会身份
著名的心理学家和语言学家迈克尔·托马塞罗认为,我们在语言群体中的成员关系维系着我们的社会和道德身份。我们为自己的行为给出的理由与我们在这个社区中的角色和地位有关。从很小的时候起,孩子们就必须决定做什么,形成什么样的道德和社会身份。孩子们做出这些决定的方式对他们社区中的其他人和他们自己都是合理的。
社会身份和道德身份是通过与他人交流、讲道理的过程联系在一起的。托马塞罗声称,这一过程已经内化为一种规范的自我管理形式,构成了我们的道德身份。从内在视角(自我、私人)和外在视角(他人、公众)来看,我们的心理统一性要求我们做某些事情,以便继续做我们自己。对同一事件的内在和外在观点之间的认知差距是驱动认知不公正的原因,我们将在下面讨论。
道德和社会身份是共时(横截面)结构。它们是我们在特定的时间点向自己展示自我的方式。但是我们还没有解释这些身份是如何随时间演变的。为此,我们需要一个历时(纵向)的身份账户。
叙事身份:随着时间的推移,它赋予你的生活意义
根据心理学家杰罗姆·布鲁纳的说法,叙事是我们的大脑构建现实的工具。值得指出的是,它们的一些独特特征捕捉到了人类经历中所有混乱和不完美的荣耀。
- 历时性:叙事说明了人类时间内有序事件的顺序,而不是绝对的“时钟时间”
- 特殊性:记叙文是由叙述者的特定化身讲述的按时间顺序排列的事件。
- 意向状态蕴涵:在一个叙述中,原因是意向状态(信念、欲望、价值观等)。)作为原因和/或解释。
- 解释学可组合性:文本与文本意义之间存在差距。意义产生于对部分与整体关系的理解。
- 可指性:叙事中的现实主义来自共识,而不是来自与某种“真实”现实的对应。
- 语境敏感性和协商性:一篇文章的读者“根据自己的条件吸收它”,从而在这个过程中改变自己。我们通过对话协商意义。
通过叙事的历时性,我们随着时间的推移将我们的道德和社会身份结合起来,从而产生了人的独特性。

当个人的真理主张没有得到适当的证据支持时,认知不公正就会出现。由粘土银行在 Unsplash 上拍摄
叙述的准确性和认知的不公正
认识论是对知识及其基础的研究。我们改编了米兰达·弗里克的认知不公正概念,并用它来揭示个性化叙事的准确性问题。
弗里克对不公正感兴趣,因为它涉及到不尊重某人的“认知能力”认知上的不公正本质上降低了一个人对自己判断的信任和理解自己生活经验的能力。认知不公正有康德和黑格尔的两个方面。值得注意的是,认知公正要求相互承认他人的观点和经验,特别是那些处于不对称认知权力位置的人(即,相对于数据收集者的数据主体)。
证明不公
有两个维度的认知不公正适用于数据主体接收个性化推荐的情况。首先,当偏见或偏向导致数据收集者对数据主体对记录的行为或事件(包括建议)的解释给出“降低的可信度”时,可能会出现证据不公正。
例如,如果一个数据收集者只使用无意识产生的 BBD,而不对明确的反馈进行加权,那么就出现了一种证明不公正。另一个例子可能是 BBD 平台允许用户“降级”不好的推荐,但是这些实际上并没有被考虑到改变推荐。
从贝叶斯模型平均的角度来看,我们也可以认为,当模型选择中的不确定性(忽略数据主体的主观“模型”)被忽略而有利于数据收集器或处理器的预定义模型时,证明是不公平的。
诠释的不公正
当数据收集者或数据收集平台缺乏“解释资源”来理解数据主体的生活经历,从而使其处于不利地位时,可能会出现解释不公正。根本问题是, 算什么 ?
在对一个事件的一种解释下,我们可以产生统计规律,而在另一种解释下,我们可以得到不同的统计规律,这些规律被编码在 ML 模型的参数中。因此,BBD 没有一个“最佳”的表示或编码。
对于什么算什么,在不同的解释下有不同的表达。
例如,目前,BBD 平台记录的事件类别通常由系统设计者预先定义,而没有来自平台用户的任何输入。如果推荐系统的设计者在使用该系统时不考虑数据主体的预期行为、价值和目标的多样性和丰富性,解释的不公正将是不可避免的。
人性化个性化的未来
制作和维持连贯的数字自我叙事是人类独有的能力,我们不能把它留给他人或外包给自动化代理。这种观点得到了 GDPR 和 IEEE EAD 原则的认同。我们是讲述自己的故事中的人物。我们知道哪些事件定义了我们,我们知道哪些价值观驱动着我们,我们知道我们行为背后的原因。如果我们做不到,我们有能力去尝试找出答案。
BBD 收集平台的公司所有者和数据科学家可能会根据他们对我们观察到的行为的统计分析提出相反的主张,但我们认为信息自决权胜过这些主张。
正如后现代主义者所指出的,伦理和解释的问题是不可分的。我们所相信的真理会影响我们对正确事物的判断。但如果意义是社会建构的,单靠数据主体是无法解决这些问题的。这需要社区和真诚的交流来制定我们共同语言游戏的“规则”。数据科学家将需要在数字领域的意义协商和身份形成的辩证中发挥更大的作用。毕竟,如果类别的原意是“公开指责”,那么数据主体作为公众的一员,应该在这个过程中发挥作用。
怀疑论者可能会反驳说,优化叙事准确性需要在推荐系统准确推荐项目和预测特定行为的能力之间进行权衡。商业利润也可能受到影响。然而,GDPR 迫使我们问这样一个问题:
我们最终是否希望根据企业或人类的需求和利益来代表自己?
[1] 解释学最初是关于圣经文本解释方法的研究,但在 20 世纪被哲学家们重新发明为一种认识论方法。
[2]最初在希腊语中,“category”一词的意思是“公开指责”注意社会共识所扮演的角色。
困惑于如何在 Coursera 上挑选最好的在线课程?
Psst,建一个航向分析器。

帕特里克·托马索在 Unsplash 上的照片
问题是
如今网上课程并不缺乏。在过去几年中,MOOC 平台的受欢迎程度飙升,促使许多其他此类平台涌现出来。
其中最受欢迎的 Coursera 长期以来一直是许多学习者的宠儿。
然而,并不是很多用户对目前的所有课程都满意。
有时难度太高,或者动手实验已经过时,或者教师隐藏在技术术语后面,或者视频缺乏上下文清晰度等等。
面对如此多的课程,学习者只选择最好的课程来节省时间和金钱是很重要的。
一门课程的真正勇气是由负面评价的数量来检验的,因为只有真正失望的用户才会费心去写一个糟糕的评价并给出一个低评级;这就是为什么即使很高的负面评价率也能告诉我们很多关于课程质量的信息。在这个时间就是金钱的时代,重要的是我们不要在不那么好的课程上投入时间。
但是怎么做呢?每门课程都有成千上万的评论,如果不是几百的话,而且大多数显示在首页的评论都是顶级评论,嗯…总是好的。
此外,人们不能只看收视率,因为它们几乎不可靠。甚至我还见过高达 3/4 星的收视率还伴随着差评。
需要是发明之母。
我需要一个工具来判断选择哪门课程,而不用列出我所有潜在课程的所有评论和意见。
而 CourseraAnalyzer 就是这样诞生的。
在这篇文章中,我将带您快速浏览一下我是如何开发这个为我提供了极好用途的本地 web 应用程序的。如果您想更深入地研究这背后的代码,请随意查看这个项目的库。
解决方案
构建一个应用程序,查找用户指定的课程,对评论进行情感分析,然后以可视化的形式显示结果。
听起来很简单,对吧?
不完全是。
这包含了网络抓取+自然语言处理+机器学习+网络开发。难?是的。不可能?号码
我的第一步是摘录课程的评论。
我用的是 BeautifulSoup(想用硒也可以)刮痧。将 URL 作为输入后,我修改了 URL 以访问课程的评论页面,然后从所有页面中抓取所有评论。
( 注意 :如果您不能完全理解这一点,请不要担心,因为代码片段只是整个文件的一小部分。我的目的只是给你一个小小的机会,让你知道它是如何工作的。)
然后,我使用一个文本分类器(我已经事先训练和腌制过)将评论分为正面和负面评论。
统计完评论后,我将数字传递给 Chart.js,它在网页上显示漂亮的交互式图表。
但是有一个讨厌的小问题。
花了太多时间。
搜集成千上万的评论并对它们使用文本分类器是非常耗时的。但我想让它走得更快。
所以我创建了一个. json 文件来存储搜索到的课程列表及其结果。如果用户输入的课程已经存在于。json 文件,我只是加载结果,而不是每次有请求时都重复相同的过程。
最后,我创建了一个小 Flask 应用程序,定义了不同的 API 端点和函数,以便将所有这些程序转换成一个单元。我用 Heroku 把它放到了网上。
这是网站https://courseraanalyzer.herokuapp.com/主页的截图。

我显然不是最擅长设计的人,但是,只要它能用,对吗?
在左下方,显示了已经分析过的课程列表。如果您正在寻找的课程不存在,请在搜索框中输入课程的 URL,然后单击“分析”。
它将给出所有评论的简要报告,并显示一些可视化效果来帮助您得出结论。
就是这样!
这个应用程序在许多方面仍然落后,但是对于第一个版本来说,这已经很好了。如果您对此有任何疑问,请随时联系我。谢谢大家!
Github 库:
https://github.com/sthitaprajna-mishra/coursera-analyzer
用于目标检测的混淆矩阵
一种评估实例分割模型性能的度量

在训练机器学习分类器之后,下一步是使用相关度量来评估其性能。混淆矩阵是评估指标之一。
混淆矩阵是显示给定一些真值/实例的分类器性能的表格(监督学习类型)。
但是对于对象检测和实例分割任务,混淆矩阵的计算不太直观。首先,有必要理解另一个支持度量:并集上的交集(IoU)。交集/并集(IoU)在对象检测和实例分割任务的计算指标中起着关键作用。
交集超过并集(借据)
IoU ,也称为 Jaccard index ,是一种评估地面真实掩码( gt )和预测掩码( pd )之间重叠的度量。在对象检测中,我们可以使用 IoU 来确定给定的检测是否有效。
IoU 的计算方法为 gt 和 pd 之间的重叠/相交面积除以两者之间的并集面积,即:

IoU 的定义如下:

图 1(来源:作者)
注: IoU 度量范围从 0 到 1,0 表示没有重叠,1 表示 gt 和 pd 之间完全重叠。
混淆矩阵由 4 个分量组成,即真阳性(TP)、真阴性(TN)、假阳性(FP)和假阴性(FN) 。为了定义所有组件,我们需要根据 IoU 定义一些阈值(比如α)。

图 2:2 类的混淆矩阵:1 和 2(来源:作者)
- 真阳性(TP) —这是当事实确实为阳性时分类器预测为阳性的实例,即 IoU ≥ α的检测。
- 假阳性(FP) —这是一种误阳性检测,即 IoU < α的检测。
- 假阴性(FN)-这是分类器未检测到的实际实例。
- 真阴性(TN) —如果实际实例也是阴性的,此度量意味着阴性检测。在对象检测中,该度量不适用,因为存在许多不应该在图像中检测到的可能预测。因此,TN 包括未被检测到的所有可能的错误检测。
这些概念可以通过一些图形示例直观地理解(让我们考虑 IOU 阈值,α = 0.5)


图 3:左:假阴性(FN),右:真阳性(TP),演职员表:丹尼尔·延森在 Unsplash 上的照片


图 4:都是假阳性(FP),演职员表:照片由丹尼尔·延森在 Unsplash 上拍摄
备注:根据 IoU 阈值的定义,如果我们选择阈值大于 0.86,则图 3 右侧变为 FP,如果我们选择 IoU 阈值小于 0.14,则图 4 右侧变为 TP
可以从混淆矩阵中得出的其他指标包括:
- 精度是分类器仅识别相关对象的能力。它是正确的正面预测的比例,由下式给出

- Recall 是一个衡量分类器找到所有相关案例(即所有基本事实)的能力的指标。它是在所有基本事实中检测到的真阳性的比例,定义为

₁分数是精确度和召回率的调和平均值。

示例
考虑下面的图像,带有基本事实(深蓝色)和分类器检测(红色)。通过观察,你能说出 TP,FP 和 FN 的数目吗?

图 5(来源:富士-SfM 数据集(引用于参考资料部分))
Python 实现
在 Python 中,可以使用 Shapely 库计算混淆矩阵。以下函数 ( **evaluation(ground,pred,iou_value)** →用于 TP、FP、FN、Precision、Recall、F₁的 6 值元组)可用于确定上述图像的混淆矩阵(图 5)
参数:
- 地面 —是n×m×2数组,其中 n 是给定图像的地面真实实例的数量, m 是在掩膜圆周上采样的 (x,y) 对的数量。
- pred 为 p × q × 2 数组,其中 p 为检测数, q 为预测掩码采样的 (x,y) 点数
- iou_value 为 iou 阈值
对于图 5 和 IoU 阈值,α = 0.5,**evaluation(ground,pred,iou_value)**→
TP: 9   FP: 5   FN: 0   GT: 10
Precall: 0.643   Recall: 1.0   F1 score: 0.783
感谢您的阅读:-)
在 https://medium.com/@kiprono_65591/membership加入媒体以获取媒体上的所有报道。
你也可以在我发文章的时候通过这个链接把文章发到你的邮箱里:https://medium.com/subscribe/@kiprono_65591
参考文献
若尔迪·吉恩-莫拉、里卡多·桑斯-科尔蒂埃拉、琼·罗塞尔-波罗、何塞普-拉蒙·莫罗斯、哈维尔·鲁伊斯-伊达尔戈、维罗尼卡·维拉普拉纳和爱德华·格雷戈里奥。(2020).富士 SfM 数据集。芝诺多。http://doi.org/10.528/zenodo.3712808
Everingham,m ,, es lami,s . A ,, Van Gool,l ,, Williams,c . k ,, Winn,j ,,和 Zisserman,A,《Pascal 视觉对象类挑战:回顾》。国际计算机视觉杂志,111(1):98–136,2015。
对 ROC 分析的介绍。模式识别快报,27(8):861–874,2006。
混淆矩阵—已解释
如何利用混淆矩阵

分类是一种监督学习方法,其中目标变量是离散的(或分类的)。评估机器学习模型和构建它一样重要。我们正在创建模型来处理新的、以前看不到的数据。因此,需要进行全面和多方面的评估,以创建稳健的模型。当涉及到分类模型时,评估过程变得有些棘手。
困惑矩阵背后的动机
准确度显示正确预测与所有预测的比率:

在某些情况下,准确性不足以评估模型。假设我们为二元分类任务建立一个模型,目标变量的分布是不平衡的(93%的数据点在 A 类中,7%在 B 类中)。

我们有一个只预测 A 类的模型,甚至很难称之为“模型”,因为它预测 A 类,没有任何计算。然而,由于 93%的样本属于 A 类,因此我们的模型的准确率为 93%。
如果正确检测 B 类样本至关重要,而我们又不能对任何 B 类样本进行错误分类(例如癌症预测),该怎么办?这就是混淆矩阵发挥作用的地方。
混乱矩阵
混淆矩阵不是评估模型的指标,但它提供了对预测的洞察。学习混淆矩阵对于理解其他分类指标很重要,例如精度和召回。
混淆矩阵通过显示每个类别的正确和错误(即真或假)预测,比分类准确性更深入。在二进制分类任务的情况下,混淆矩阵是 2×2 矩阵。如果有三个不同的类,那就是一个 3x3 的矩阵,以此类推。

假设 A 类是正类,B 类是负类。混淆矩阵的关键术语如下:
- 真阳性(TP) :预测阳性类别为阳性(ok)
- 假阳性(FP) :将阴性类别预测为阳性(不正常)
- 假阴性(FN) :将阳性类别预测为阴性(不正常)
- 真阴性(TN) :预测阴性类别为阴性(ok)
期望的结果是预测类和实际类是相同的。这可能看起来令人困惑,但你可以想出一个窍门来记住。我的如下:
第二个词是模型预测的。
第一个字表示预测是否正确。

注意:假阳性也称为 I 型错误。假阴性也称为 II 型错误。
混淆矩阵用于计算精度和召回率。
精度和召回
精确度和召回率度量将分类准确性向前推进了一步,并允许我们获得对模型评估的更具体的理解。选择哪一个取决于任务和我们的目标。
Precision 衡量当预测为正时我们的模型有多好。它是正确的正面预测与所有正面预测的比率:

回忆测量我们的模型在正确预测正类方面有多好。它是正确的正面预测与所有正面类别的比率。

精度的焦点是正预测所以它表示有多少正预测是真的。召回的焦点是实际肯定类别,因此它指示模型能够正确预测多少肯定类别。
精准还是召回?
我们不能试图同时最大化精确度和召回率,因为它们之间有一个平衡。提高精度会降低召回率,反之亦然。我们可以根据任务来最大化精确度或回忆。
考虑一个垃圾邮件检测模型,我们试图最大化精确度,因为我们希望当一封邮件被检测为垃圾邮件时是正确的。我们不想将一封普通的电子邮件标记为垃圾邮件(即误报)。如果模型不能捕捉一些垃圾邮件,这是可以接受的。但是,如果一封非常重要的电子邮件被标记为垃圾邮件,后果可能会很严重。
另一方面,对于取消细胞检测任务,我们需要最大化召回,因为我们想要检测每个阳性类别(恶性细胞)。如果模型预测恶性细胞为良性(即假阴性),这将是一个严重的错误。
还有一个将精确度和召回率结合成一个数字的方法,那就是 F1 分数。
F1 分数
F1 分数是精确度和召回率的加权平均值。

对于类别分布不均匀的问题,F1 分数是比准确度更有用的度量,因为它考虑了假阳性和假阴性。
f1 得分的最佳值是 1,最差值是 0。
敏感性和特异性
敏感度,也称为真阳性率(TPR),与回忆相同。因此,它测量被正确预测为阳性的阳性类别的比例。
特异性类似于敏感性,但侧重于负类。它测量被正确预测为负面的负面类别的比例。

结论
免费的午餐定理在这里也不适用。对于所有的任务,没有一个最优的、容易找到的选择。我们需要清楚地定义需求,并根据这些需求选择度量标准。
感谢您的阅读。如果您有任何反馈,请告诉我。
多类机器学习模型的混淆矩阵
关于如何计算多类分类问题的精确度、召回率和 F1 分数的初学者指南。

混淆矩阵是一种可视化预测模型性能的表格方式。混淆矩阵中的每个条目表示模型做出的正确或错误分类的预测数量。
任何已经熟悉混淆矩阵的人都知道,大多数情况下,它是针对二元分类问题来解释的。这个解释不在其中。今天我们将看到混淆矩阵如何在多类机器学习模型上工作。然而,我们将从使用二元分类的一点背景开始,只是为了正确地看待事情。
二元分类的混淆矩阵

二元分类的混淆矩阵
如你所见,二进制分类问题只有两个类要分类,最好是一个正类和一个负类。现在让我们看看混淆矩阵的度量。
真阳性(TP): 指分类器正确预测阳性类为正的预测次数。
真否定(TN): 指分类器正确预测否定类为否定的预测次数。
假阳性(FP): 指分类器错误地将阴性类预测为阳性的预测次数。
假阴性(FN): 指分类器错误地将阳性类预测为阴性的预测次数。
用混淆矩阵作为你的机器学习模型的评估标准总是比较好的。它为您的模型提供了一个非常简单而有效的性能测量方法。以下是困惑矩阵中一些你可以使用的最常见的绩效衡量标准。
准确性:它给出模型的总体准确性,即被分类器正确分类的样本占总样本的比例。计算精度,用下面的公式:(TP+TN)/(TP+TN+FP+FN)。
错误分类率:它告诉你有多少预测是错误的。它也被称为分类错误。可以用(FP+FN)/(TP+TN+FP+FN)或者(1-精度) 来计算。
Precision: 它告诉你一个正类的预测中有多少部分实际上是正的。要计算精度,用下面的公式: TP/(TP+FP) 。
回忆:它告诉您所有阳性样本中有多少被分类器正确预测为阳性。它也被称为真阳性率(TPR)、灵敏度、检测概率。计算召回率,使用以下公式:【TP/(TP+FN)。
特异性:它告诉您所有阴性样本中有多少部分被分类器正确预测为阴性。它也被称为真实负利率(TNR)。要计算特异性,用下面的公式:【TN/(TN+FP)。
F1-score: 它将精确度和召回率结合成一个单一的度量。从数学上来说,这是精确度和召回率的调和平均值。它可以计算如下:

现在,在一个完美的世界里,我们想要一个精度为 1,召回率为 1 的模型。这意味着 F1 分数为 1,即 100%的准确率,这通常不是机器学习模型的情况。因此,我们应该尝试的是获得更高的查准率和更高的查全率。好了,现在我们知道了混淆矩阵的性能度量,让我们看看如何在多类机器学习模型中使用它。
多类分类的混淆矩阵
为了简单起见,让我们考虑我们的多类分类问题是一个 3 类分类问题。比方说,我们有一个数据集,它有三个类标签,即 苹果 、 桔子 和 芒果 。下面是这些类的一个可能的混淆矩阵。

多类分类的混淆矩阵
与二进制分类不同,这里没有正类或负类。起初,找到 TP、TN、FP 和 FN 可能有点困难,因为没有正类或负类,但实际上很容易。这里我们要做的是找到每个类的 TP,TN,FP 和 FN。例如,如果我们取苹果类,那么让我们看看混淆矩阵中的度量值是多少。
- TP = 7
- TN =(2+3+2+1)= 8
- FP = (8+9) = 17
- FN = (1+3) = 4
由于我们从混淆矩阵中获得了苹果类的所有必要度量,现在我们可以计算苹果类的性能度量。例如,苹果公司有
- 精度= 7/(7+17) = 0.29
- 回忆= 7/(7+4) = 0.64
- F1-得分= 0.40
类似地,我们可以计算其他类的度量。下表显示了每个类的每个测量值。

每门课的精确度、召回率和 F1 分数
现在我们可以通过这些措施做更多的事情。我们可以将每个类的 F1 分数结合起来,为整个模型提供一个单一的度量。有几种方法可以做到这一点,现在让我们来看看。
微型 F1
这被称为微观平均 F1 分数。它是通过考虑模型的总 TP、总 FP 和总 FN 来计算的。它不单独考虑每个类,而是全局计算指标。所以在我们的例子中,
- 总 TP = (7+2+1) = 10
- 总 FP =(8+9)+(1+3)+(3+2)= 26
- 总 FN =(1+3)+(8+2)+(9+3)= 26
因此,
- 精度= 10/(10+26) = 0.28
- 回忆= 10/(10+26) = 0.28
现在,我们可以使用 F1 分数的常规公式,并使用上述精度和召回率获得微观 F1 分数。
微 F1 = 0.28
正如您所看到的,当我们计算全局指标时,所有度量都变得相等。如果你计算准确度,你会发现,
精度=召回=微 F1 =准确度
宏 F1
这是 F1 的宏观平均分数。它分别计算每个类别的指标,然后取这些指标的未加权平均值。从图 【精确度、召回率和 F1-每类得分】 中我们看到,
- 类苹果 F1-得分= 0.40
- 级橙 F1-得分= 0.22
- 类芒果 F1-得分= 0.11
因此,
宏 F1 = (0.40+0.22+0.11)/3 = 0.24
加权 F1
最后一个是加权平均 F1 分。与宏 F1 不同,它采用测量值的加权平均值。每个类别的权重是该类别的样本总数。因为我们有 11 个苹果,12 个橘子和 13 个芒果,
加权 F1 =((0.40 * 11)+(0.22 * 12)+(0.11 * 13))/(11+12+13)= 0.24
最后,让我们看一个使用 Python 的 Scikit-learn 计算这些度量的脚本。
下面是脚本的输出。

信用:谷歌 Colab
注意:Scikit-Learn使用行作为“真实类”,使用列作为“预测类”这与我们对苹果、桔子和芒果例子的考虑相反,但在逻辑上是相似的。无论如何,您都可以考虑真实类和预测类。但是如果你使用 Scikit-Learn,那么你必须遵守他们的规则。
希望你找到了你想要的。感谢阅读。
混乱矩阵-不再那么混乱了!
机器学习基础
了解混淆矩阵的所有方面
在本帖中,我们将重点了解混淆矩阵和使用混淆矩阵计算的指标——I 型误差、II 型误差和准确度。这在分类算法的情况下使用,以确定/评估模型的性能。

在构建任何统计或 ML 模型时,我们从开发数据集开始。将数据集分为两部分:训练和测试。将测试数据集放在一边,并使用训练数据集训练模型。一旦模型准备好进行预测,我们就尝试对测试数据集进行预测。一旦我们将结果分割成类似于上图所示的矩阵,我们就可以看到我们的模型能够正确预测多少,以及它的预测有多少是错误的。

我们用测试数据集中的数字填充上图所示的 4 个单元格(例如,有 1000 个观察值)。
- TP(真阳性):在测试数据集中,该列的实际标签为“是”,而我们的逻辑回归模型也预测为“是”。(500 次观察)
- TN(真阴性):在测试数据集中,该列的实际标签为“否”,而我们的逻辑回归模型也预测为“否”。(200 项观察)
- FP(假阳性):在测试数据集中,该列的实际标签为“否”,但我们的逻辑回归模型预测为“是”。(100 项观察)
- FN(假阴性):在测试数据集中,该列的实际标签为“是”,但我们的逻辑回归模型预测为“否”。(200 项观察)
这 4 个单元格构成了“混淆矩阵”,因为在该矩阵中,通过清楚地描绘出我们模型的预测能力,可以减轻对我们模型的良好性的所有混淆。
混淆矩阵是一个表格,通常用于描述分类模型(或“分类器”)对一组真实值已知的测试数据的性能。
可以理解的与混淆矩阵相关的其他指标
第一类错误

第 1 类错误也称为假阳性,当分类模型错误地预测了最初错误观察的真实结果时就会发生。
例如:假设我们的逻辑模型正在处理垃圾邮件,而不是垃圾邮件。如果我们的模型将一封重要的电子邮件标记为垃圾邮件,那么这就是我们的模型的第一类错误的例子。在这个特定的问题陈述中,我们对尽可能减少 I 类错误非常敏感,因为重要的电子邮件进入垃圾邮件会产生严重的后果。
3.第二类错误

第二类错误也称为假阴性,发生在分类模型错误地预测了原本真实的观察结果的错误结果时。
例如:假设我们的逻辑模型正在处理一个用例,它必须预测一个人是否患有癌症。如果我们的模型将一个患有癌症的人标记为健康人,并将其错误分类,那么这是我们模型的第二类错误的一个例子。在这个特定的问题陈述中,我们对尽可能减少 II 型错误非常敏感,因为在这种情况下,如果疾病在受影响的人中继续未被诊断,假阴性可能导致死亡。
4.准确(性)
现在,上面讨论的三个指标是通用指标,与您拥有的训练和测试数据的种类以及您为问题陈述部署的分类算法的种类无关。
我们现在将讨论非常适合特定类型数据的指标。
让我们从这里开始讨论准确性,这是一个最适合用于平衡数据集的指标。参考下图,该图来源于本媒体文章。

来源:链接
如您所见,一个平衡的数据集是这样的:1 和 0、是和否、正和负由训练数据均等地表示。另一方面,如果两个类别标签的比率是倾斜的,那么我们的模型将偏向一个类别。
假设我们有一个平衡的数据集,让我们学习什么是准确性。

准确度是测量结果与真实值的接近程度。它告诉我们,我们的分类模型能够多准确地预测问题陈述中给出的类别标签。
例如:假设我们的分类模型试图预测客户流失情况。在上图中,在总共 700 个实际流失客户(TP+FN)中,模型能够正确地对 500 个流失客户进行分类(TP)。同样,在总共 300 个保留客户(FP+TN)中,该模型能够正确地对 200 个保留客户(TN)进行分类。
准确率=(TP+TN)/客户总数
在上面的场景中,我们看到模型在 1000 个客户的测试数据集上的准确率是 70%。
现在,我们知道了准确性是一个应该只用于平衡数据集的指标。为什么会这样呢?让我们看一个例子来理解这一点。

在这个例子中,这个模型是在不平衡的数据集上训练的,甚至测试数据集也是不平衡的。准确性指标的得分为 72%,这可能给我们的印象是我们的模型在分类方面做得很好。但是,仔细看,这个模型在预测负面的阶级标签方面做得很糟糕。它只预测了 100 个总阴性标签观察中的 20 个正确结果。这就是为什么如果数据集不平衡,就不应该使用精度指标。
这篇文章的重点是全面理解困惑矩阵。
如果您想了解更多有关可用于评估分类模型的其他指标,如召回率、精确度、AUC-ROC 等。,您可以参考我下面关于这个主题的详尽文章。
解释非常规,这将作为评估分类机器学习模型的详尽列表。
towardsdatascience.com](/top-10-model-evaluation-metrics-for-classification-ml-models-a0a0f1d51b9)
请继续关注这个空间,了解更多关于数据科学、机器学习和统计的信息!
快乐学习:)
混淆矩阵“未混淆”
打破混乱矩阵
工业中应用机器学习的目标是驱动商业价值。因此,能够评估您的机器学习算法的性能对于深入了解您的模型极其重要。
在这篇文章中,我的目标是以一种对那些积极使用它们的人和那些只是好奇的人来说容易理解的方式深入到困惑矩阵中。也就是说,本文的结构如下:
- 什么是混淆矩阵
- 如何解读混淆矩阵
- 案例研究(准确度、召回率、精确度和 F 值)
混淆矩阵是什么?
混淆矩阵是用于机器学习中分类任务的有用工具,其主要目的是可视化机器学习模型的性能。
在负类为 0、正类为 1 的二元分类设置中,混淆矩阵由一个 2x2 网格表构成,其中行是数据的实际输出,列是模型的预测值。

图 1:混淆矩阵的可视化表示(没有注释)
我将解释为什么某些网格是一种颜色,而其他的却是另一种颜色,现在,这只是混淆矩阵是什么以及它对于二进制分类任务看起来会是什么样子的视觉表示。
注意:许多来源以不同的方式构建混淆矩阵,例如,行可能是预测值,列可能是实际值。如果您正在使用框架(即 Sci-kit learn ),阅读混淆矩阵的文档以了解混淆矩阵的格式是很重要的。
无论您在图 1 中停留在哪个网格块上,其相邻的网格都会以另一种颜色高亮显示。这考虑到了混乱矩阵的不同变化,如果您简单地旋转网格,您可能会看到这些变化。

图 2:混淆矩阵的变化
很好!既然你已经熟悉了二元分类任务的混淆矩阵的格式,现在知道如何填写混淆矩阵是很重要的。
让我们用经典的猫和狗的例子。如果我们的分类任务已经被训练来区分猫和狗,混淆矩阵将为预测模型创建结果摘要以供进一步分析。

图 3:取自 Badgerati 的混淆矩阵结果
在图 3 中的 50 只猫中,模型预测实际上有 15 只猫,35 只狗。关于狗,模型预测 40 只为猫,10 只为狗。所有正确的预测都被分配在表格的对角线上(从上到下)——是的,你猜对了!这就是为什么这些盒子被涂上了与图 1 中一样的颜色。
你现在已经学会了如何绘制你自己的困惑矩阵。作为数据科学家,我们想知道为什么模型做得不好,以便我们更好地了解我们的数据,我们可以使用这些数据为下一个建模阶段设计新功能。
如何解读困惑矩阵
我将首先描述一些术语,如果你能猜出它们的走向,那么你就成功了一半:
真阳性:模型预测阳性,标签实际阳性。
真阴性:模型预测为阴性,标签实际为阴性。
假阳性:模型预测为阳性,而标签实际上是阴性——我喜欢把这想象成 被错误地归类为阳性 。
假阴性:模型预测为阴性,而标签实际上是阳性——我喜欢把这想象成 被错误地归类为阴性 。

图 4:带注释的混淆矩阵
如果你能够将这些标签归类到这些列中,那就太棒了,你现在已经全面掌握了混淆矩阵。让我们进一步挖掘!
我们的混淆矩阵在我们的网格上有两个红色补丁,这向我们推断出我们的模型正在犯的错误的类型。第一类错误是当我们的模型错误地将实际上是阴性的东西分类为阳性(假阳性)——因此它可以被认为是一个错误的警报——例如预测某人患有哮喘,而他们实际上没有。第二类错误是当我们错误地将实际上是阳性的东西归类为阴性(假阴性)——在我们的哮喘病例中,这将是说某人没有哮喘,而实际上他们有。
我知道这些文字会让你很难理解,所以如果你觉得它还没有被很好地接受,回到图 4 来获得一个视觉概念。
您现在已经了解了混淆矩阵是一种用于评估预测模型在分类任务中的性能的工具。此外,您还知道与我们的预测模型的每个输出相关的术语。
案例研究:
我们的任务是建立一个分类器,预测不同交易的欺诈或非欺诈。这些数据是由英国一家安全措施非常严格的大银行交给我们的——出于隐私考虑,所有客户的个人信息都被加密了——因此欺诈交易不会经常发生。事实上,他们给我们的数据有 10,000 个负面案例(非欺诈)和 1000 个(欺诈)案例。图 5 显示了我们分类器的结果。

图 5:用例模型的结果
准确度
准确度是指测量值与标准值或已知值的接近程度。在我们建立了一个分类器并使用它进行预测之后,我们很快地调出我们的混淆矩阵来确定我们的模型有多准确,我们惊讶地发现我们的模型有 92%的准确性。当我们开始梦想我们将从中获得的奖金时,有什么东西拉了拉我们的数据科学骨骼并告诉我们更多地查看我们的模型预测,因为我们不知道为什么我们的模型如此准确,因为当我们的目标变量(在这种情况下,欺诈或非欺诈)不平衡时,准确性可能是一个误导性的指标。我们再次回顾了混淆矩阵(参见图 5) ,以获得更多的指标来告诉我们为什么我们的模型如此准确。

图 6:精确度公式
精度和召回
精度(也称为阳性预测值)告诉我们,从我们的分类器标记为阳性的标签,实际上是阳性的量。

图 7:精度公式
另一方面,回忆(也称为敏感度或真实阳性率)告诉我们我们的模型正确识别的实际阳性的数量。

图 8:召回公式
注:精确度和召回率基于对阳性标签(相关性)的理解和测量。
我们计算刚刚学过的精确和回忆公式:
精度 = 83%
是啊!!这太棒了,这意味着 83%被我们标记为阳性的标签实际上是阳性的。这是惊人的…是吗?让我们检查一下召回情况。
回忆 = 24%
这告诉我们,我们的分类器正确识别的阳性标签的数量。换句话说,欺诈在 76%的时间里都没有被发现!不需要博士学位就知道这一点也不好。
对于这个问题,我们希望优化召回,这意味着我们希望通过在更多情况下预测阳性来减少假阴性的数量(当实际标签是阳性时错误地分类为阴性)。然而,有些时候你可能想优化精确度,例如在垃圾邮件分类器中,我们不希望重要的邮件被归类为垃圾邮件并发送到我们的垃圾邮件中。就我们的欺诈分类器而言,我们希望优化召回率,为了做到这一点,我们必须降低分类器的阈值,以将其分类为肯定类别。请注意,由于我们的分类器现在预测阳性更多,它降低了(我们曾经很高的)83%的精度,因为现在我们的模型预测假阳性发出更多假警报的可能性更高,但在这种情况下我们不介意。
精确和回忆爱恨关系
精确度和召回率很少被孤立地讨论,正如我们在上面发现的,它们之间经常有一种反比关系——增加一个度量就会减少另一个。当我们需要在精确度和召回率之间取得平衡时,一个更好的度量标准是 F1 值(也称为 F 值)。F1-score 是一种考虑精确度和召回率的精确度衡量标准(下面的公式将向您展示它是如何做到这一点的)。

图 9:F1 分数的公式
总之,从我们的混淆矩阵中可以得出更多的度量。对混淆矩阵中的术语有一个牢固的理解,这是继续理解可以推导出的其他度量的坚实基础。如果你想继续, Sarang Narkhede 在他的个人资料上有一个关于困惑矩阵的惊人帖子(点击这里导航到那里)。 Koo Ping Shung 更详细地介绍了何时使用准确度、精确度、回忆和 F1 分数,并且以友好的方式撰写,因此任何人都可以理解(单击此处获取链接)。我们还有著名的维基百科,你可以点击这里进入。我在下面附上了一个帖子,它扩展了混淆矩阵,我们用它来绘制 ROC 曲线,如果你不知道那是什么,或者你知道但不太理解它,那么点击下面的链接可以很好地掌握它。
深入了解 AUC-ROC…
towardsdatascience.com](/comprehension-of-the-auc-roc-curve-e876191280f9)
国会如何投票?

Unsplash 的 Darren Halstead
使用机器学习来预测国会投票
使用国会投票记录,我们将尝试预测国会议员对某些国会问题的投票,并可视化投票。
数据
数据集可以在 Kaggle 这里找到。

这些栏目是:"班级名称"、"残疾人-婴儿"、"水项目-费用分摊"、"通过预算-决议"、"医生-费用-冻结"、"萨尔瓦多-援助"、"宗教团体-学校"、"反卫星-禁试"、"援助-尼加拉瓜-contras "、" MX-导弹"、"移民"、"合成燃料-公司-削减"、"教育-支出"、"超级基金-起诉权"、"犯罪"、"免税-出口"、"出口-管理-法案-南非"。
第一栏“班级名称”不是“共和党”就是“民主党”。其他几栏代表国会的决定,或者是‘n’代表‘不’,或者是‘y’代表‘是’,或者是“?”缺少值。
清理数据
为了将数据传递到机器学习模型中,所有数据都必须是数字。用 Python 的。map()函数,我们可以将数据转换成数值形式。


当前数据为 435 行 17 列。理想情况下,我们会删除所有 NaN 值以保持最高的数据质量,而不必猜测丢失的值是什么。但是,在删除所有具有 NaN 值的行之后,得到的数据是 232 行乘 17 列,大约减少了一半。
相反,我们将选择估算,或计算每个 NaN 值的最可能值。一些估算方法包括众数、中位数和平均数,但在这种情况下,没有一种方法有效,因为我们无法根据最流行或最平均的观点来确定政治家在某个问题上的立场。
KNN 估算
相反,我们将使用 K-最近邻(KNN)来计算缺失值。
KNN 是如何工作的?

首先,所有的数据点都绘制在多维空间中,这取决于数据有多少个特征(列)。我们的数据有 16 个维度,因为每个国会成员投票表决了 16 个不同的国会决定。
其次,KNN 计算了 k 个最近的数据点(使用欧几里德距离)。需要指定 k 的值。然后,数据点是 k 邻居值的平均值(或模式,如果指定的话)。
应用 KNN 估算后,数据如下所示:

在 65–35 训练测试分割上训练逻辑回归模型后,该模型的平均绝对误差(MAE)值约为 0.006。
在尝试了所有的k-值之后,MAE 似乎没有什么变化:

因此,我们现在将坚持使用值为 5 的 k- 。
系数分析
逻辑回归模型的系数可用于分析:

具有正系数的要素增加了选民是共和党人的概率,具有负系数的要素减去了选民是共和党人的概率(增加了选民是民主党人的概率)。这些系数如下所示:

下面是一个 SHAP (SHapley 附加解释)图,它分析了特征对于确定分类的重要性。这形象化了党派间最大的分歧——也就是说,立法机关冻结了医生医疗保险费用,削减了合成燃料公司。底部的功能,如向萨尔瓦多提供援助或反卫星测试禁令,是有争议的,这意味着许多共和党人和民主党人都投了赞成票和反对票,无法根据支持度合理预测政党。

排列重要性是评估特征重要性的另一种方法。通过打乱每一列并观察随之而来的准确性下降,排列重要性可以看出哪些列对准确性最重要。绿色的列是那些更多地被党派界线划分的列,红色的列是更多的混合。

SHAP 价值观也可以用其他方式来形象化。
这张 SHAP 力量图显示了哪些因素增加或减少了选民是共和党人的可能性。不支持冻结医生费用或批准采用预算决议等因素增加了可能性,而缺乏对水项目成本的支持则降低了可能性。这个力图是根据一个单独的案例绘制的。

结论
- 医生医疗费用冻结几乎完全是由政党路线投票决定的。
- 不支持免费医疗保险是共和党的一个非常重要的特征。
- 对尼加拉瓜反政府组织和萨尔瓦多的援助是最有争议的,这意味着共和党人和民主党人都投票赞成和反对一项提议。
共轭先验解释
有例子和证据
1.什么是先验?
先验概率是在我们看到数据之前某个事件的概率。
在贝叶斯推断中,先验是我们在新数据可用之前,基于我们现在所知的对概率的猜测。
2.什么是共轭先验?
不知道贝叶斯推理就无法理解共轭先验。
贝叶斯推理的艺术在于你如何实现它...](https://medium.com/@aerinykim/bayesian-inference-intuition-and-example-148fd8fb95d6)
对于博客的其余部分,我假设你知道先验、抽样和后验的概念。**
本质上共轭先验
****对于一些似然函数,如果选择某个先验,后验最终与先验处于相同的分布。这样的先验称为共轭先验。
通过例子来理解总是最好的。下面是计算二项式概率的后验概率的代码。θ** 是成功的概率,我们的目标是选择最大化后验概率的 θ。**
问你一个问题:在上面的代码块中有什么让你担心的吗?
有两件事使得后验计算很昂贵。
首先,我们计算每个θ 的后验概率。
为什么我们要计算成千上万个 thetas 的后验概率?因为你正在使后部正常化(第 21 行)。即使选择不归一化后验概率,最终目标也是找到后验概率的最大值**(最大后验概率)。为了以简单的方式找到最大值,我们需要考虑每个候选者——对于每个 θ的可能性 P(X|θ) 。**
第二,如果没有后验分布的闭合形式公式,我们就得通过数值优化来求最大值,比如 梯度下降 或者牛顿法。
3.共轭先验有什么帮助?
当你知道你的先验是共轭先验时,你可以跳过posterior = likelihood * prior的计算。此外,如果你的先验分布有一个封闭形式的表达式,你已经知道最大后验概率是多少。
在上面的例子中,贝塔分布在二项式可能性之前是共轭的。这是什么意思?这意味着在建模阶段,我们已经知道后验也将是贝塔分布。因此,在进行更多的实验后,你可以简单地将接受和拒绝的次数分别加到现有的参数α,β上来计算后验概率,而不是将似然性乘以先验分布。这个很方便!(下一节证明。)
作为一名数据/ML 科学家,你的模型永远不会完整。随着更多数据的到来,你必须更新你的模型(这就是为什么我们使用贝叶斯推理)。如你所见,贝叶斯推理中的计算可能很繁重,有时甚至难以处理。然而,如果我们可以使用共轭先验的封闭形式公式,计算变得非常轻。
4.证明——为什么贝塔分布是二项式可能性之前的共轭分布?
当我们使用贝塔分布作为先验时,二项似然的后验也将遵循贝塔分布。
显示贝塔产生贝塔。
二项式和 Beta 的 pdf 是什么样子的?

让我们把它们代入著名的贝叶斯公式。
θ 是成功的概率。
x 是成功次数。
n 是试验的总次数,因此 n-x 是失败的次数。

为什么最后一个积分变成 B(x+α,n-x+β) ?→https://bit.ly/2t1i2KT
先验分布 P(θ) 为β(α,β)** ,从实验中得到 x 个成功和 n-x 个失败后,后验也变成参数为 (x+α,n-x+β)的β分布。**
这里的好处是,你不用做计算,就可以通过分析知道这个。
5.共轭先验分布
贝塔分布是伯努利分布、二项式分布、负二项式分布和几何分布的共轭先验分布(看起来这些分布涉及成功&失败)。
**<Beta posterior>**
Beta prior * **Bernoulli** likelihood → Beta posterior
Beta prior * **Binomial** likelihood → Beta posterior
Beta prior * **Negative Binomial** likelihood → Beta posterior
Beta prior * **Geometric** likelihood → Beta posterior **<Gamma posterior>**
Gamma prior * **Poisson** likelihood → Gamma posterior
Gamma prior * **Exponential** likelihood → Gamma posterior**<Normal posterior>** 
Normal prior * Normal likelihood (mean) → Normal posterior
这就是为什么这三种分布(β、γ和正态)被大量用作先验分布的原因。
一种有趣的说法是,即使你做了所有这些实验,并将你的可能性乘以先验,你最初选择的先验分布如此之好,以至于最终分布与先验分布相同。
等式中的共轭先验 P(θ) :
**P(θ) such that P(θ|D) = P(θ)**
共轭先验=方便先验
需要注意一些事情:
- 当我们使用共轭先验时,顺序估计(每次观察后更新计数)给出的结果与批量估计相同。
- 为了找到最大后验、,你不必归一化似然(抽样)和先验(分母中每个可能的 θ 的积分)的乘积。

不用归一化还是能找到最大值。然而,如果你想比较不同模型的后验概率,或者计算点估计,你需要归一化。
将 BigQuery 连接到 Google Sheets
在这篇谷歌博客文章之后。

由 Linus Mimietz 在 Unsplash 上拍摄的照片
你为什么会这样做?为什么不是 SQL 数据库?
我的一个朋友是一家非营利机构的总裁,这家机构(谢天谢地)正经历着成长的烦恼。一群 20 岁出头的电气和机械工程师策划了一项为乌干达的学校安装太阳能电池板的计划,辛勤的工作和慷慨的捐赠推动了更大计划的成功。但是,可惜的是,他们的数据以 Google Sheets 的形式分散在不同的驱动帐户中,以当前的数据状态理解他们的影响或可视化他们的未来是不可能的。我很幸运地通过我的朋友加入了这个组织,他抱怨说他们的数据收集很糟糕,他们试图将所有的信息收集到一个数据库中。作为一名正在攻读公共政策硕士学位的学术数据科学家,我无法相信他们有如此宝贵的数据,如乌干达农村学校安装太阳能电池板前后的学生健康和教育的学校级数据,并请求伸出援手。
在一间公寓的会议室里,一边吃着甜甜圈和喝着咖啡,我亲眼目睹了可以想象的最不整洁的数据集。生产和太阳能电池板零件表在一个人的驱动中,学校调查和安装程序在另一个人的驱动中。他们甚至不知道所有的数据在哪里。
第一步是在一个地方收集所有的数据。我们同意在两周内会面,将所有数据放在一个地方,我来处理云集成。他们想要的非常简单:使用 SQL 语法查询多个工作表。我做了一个巧妙的演示,展示了一个 dash 网站的原型,该网站使用 SQL 数据库来生成报告,但是当我问“数据将如何更新?”时,这个演示就消失了他们回答说,“我们将复制并粘贴到谷歌的工作表中。”我立即意识到,我是房间里唯一一个具有 SQL 或云工程知识的人,但这不是一个优势,而是非营利组织的一个负债,因为它不能因为我的缺席或退出而失去对数据库的访问权。
BigQuery 太神奇了
由于数据库将通过 Google Sheets 随机访问和更新,我决定推迟我最初为这个数据库项目准备的 Airflow + SQL + Flask 应用程序。由于预算限制和组织不愿意建立一个复杂的云流程,我需要在谷歌上建立一些便宜又简单的东西。BigQuery 和 Data Studio 立刻跃入我的眼帘。
在下一次会议之前,我想有一个 BigQuery 可以为他们的组织做什么的原型,并决定使用联合国人类发展报告——次国家指数来建立一个模拟数据库。
创建项目
在 Google 云平台中,创建一个新项目。我选择了“HDR-SubnatIndex”作为我的项目名称。

创建新项目。
启用 API
接下来,为您的新项目启用 Sheets API。请注意,我在顶部的项目是 HDR-SubnatIndex。

启用工作表 api。
创建数据集
导航到 BigQuery 控制台并选择创建数据集。

为数据集命名,并配置到期和加密。单击创建数据集。

创建表格
找到您想要连接到 BigQuery 的工作表,并复制它的可共享链接。

在左下角的项目下选择新的数据集,然后选择 CREATE TABLE。

选择 Create table from: Drive,将整个工作表 URI 粘贴到 Select Drive URI 中,选择 CSV(我注意到选择 Google 工作表时出现了一些问题,它无法识别列名),输入表名,在这种情况下,我们可以自动检测模式和参数。

运行测试查询
BigQuery 的美妙之处在于它能够对任何东西执行 SQL 查询。若要确保正确允许与工作表的连接,请运行测试查询。这里我选择了数据集的前 10 行。

现在让我们运行一个稍微高级一点的查询,只提取乌干达的地区、年份、健康指数、收入指数和教育指数的国家以下级别的数据。

但是这些信息都可以很容易地从应用于 Google Sheets 的过滤器中收集到,对吗?这种方法的好处是,您现在有了一个可以随时保存和运行的查询,而不必复制数据集和重新创建筛选步骤。然而,BigQuery 真正突出的地方在于它在多个表之间执行连接的能力。
SQL 连接
我按照上面相同的步骤,从我的 google drive 中的 google sheet 添加了第二个 BigQuery 表,其中包含来自世界银行的 GDP 数据。如果没有 BigQuery 和 SQL JOINS 的帮助,使用 Google Sheets 组合这两个数据集并链接它们会困难得多。在这里,我查询了这两个表,以创建每个国家的年度国家人类发展指数和 GDP 的报告。

在分析多维贫困时,这是一个重要的比较,因为国内生产总值(GDP)并不能说明全部情况。《联合国人类发展报告》建立了人类发展指数,将健康、教育、预期寿命、平均受教育年限、预期受教育年限和人口等变量考虑在内,从而对特定国家的需求或所经历的贫困类型有了更加动态的了解。
结论
BigQuery 数据集本质上是表之上的抽象,它允许您对动态表中的多个数据源运行 SQL 查询。现在您有了一个查询,您可以在需要时保存并运行它,或者通过单击 Explore Data 导出到 Data Studio。此外,从 Data Studio 中,您可以创建一个报告,并在您的 BigQuery 数据集上使用相同的查询作为报告的数据源。
奖励:BI 引擎
现在您已经看到了 BigQuery 从 google sheets 收集数据和标准化报告的强大功能,让我们试着加快查询速度,好吗?Google BI Engine 本质上是为一个项目购买加速计算资源。
在 BigQuery 控制台的左侧,选择 BI 引擎。如果需要,启用它。选择创建预订。选择资源将位于的区域。我们的小查询将使用不到 1 GB 的存储空间。

现在,您将看到您的新 BI 引擎列在 BI 引擎下。运行相同的查询后,我们只看到时间减少了 2.3 秒。但是 BI Engine 真正的亮点是在 Google Data Studio 中,当你玩图表来寻找可视化分析的最佳方式时,它的更新速度明显更快。

感谢您的阅读!
使用环境变量隐藏您的密钥
Python | 编程 | 办公时间
关于如何在使用 Python 连接到数据库时使用环境变量保护密码的教程

我向我的读者强烈推荐的课程:
- Python 中的命令行自动化(data camp)→学习自动化许多常见的文件系统任务,并能够管理和与进程通信。
介绍
假设您正在进行一个数据科学项目,您决定通过使用 Python 建立连接来从公司数据库中提取数据。如果有必要通过 GitHub 分享你的脚本,让更广泛的团队审阅,那该怎么办?那么,在这种情况下,为了避免其他人滥用,你最好隐藏你的秘密细节。
在本文中,我将展示如何通过命令行使用环境变量来保证用户名、密码和数据库名的安全,避免不必要的信息泄露。
[## 使用 Python 将 Google 电子表格写入数据库
在本教程中,学习如何用几行代码构建简单的 ETL 来跨系统移动数据。
towardsdatascience.com](/write-datasets-from-g-sheet-to-your-database-with-python-bd2c2643f958)
环境变量解释
环境变量是 KEY=value 对,类似于 Shell 变量,可以通过命令行、创建,但功能更强大,因为一旦声明,它们就可以被任何编程语言使用,因为它们与您的环境/操作系统相关联。
在命令行环境中:
- 变量键必须用 大写字母 书写,并且可以是字符、数字和下划线的混合。
- 另一方面,任何值数据类型都可以赋给它们,在 Bash 中使用字符串时引号是可选的,除非字符串包含空格。这是因为 Bash 对空格很敏感,所以包含空格的字符串除非用引号括起来,否则无法正常工作。
- 最后,在“=”符号前后不能留有空格,如下所示:
**MY_VARIABLE=”my_value”** --> works(capital, no space, quotes)**MY_VARIABLE=my_value** --> works(capital, no space, no quotes but underscore)**MY_VARIABLE=my value** --> fails(capital, no space, space in value)**MY_VARIABLE = ”my_value”** --> fails(capital, space between equal sign, quotes)
在这篇简短的教程中,我将展示如何在 Python 中计算加权平均值,要么定义自己的函数,要么使用…
towardsdatascience.com](/3-ways-to-compute-a-weighted-average-in-python-4e066de7a719)
配置环境变量
在使用 Mac OS 时永久声明环境变量的一个简单方法是将它添加到中。bash_profile 是在您的主目录中定义的一个隐藏文件,用于在您的系统中配置 Bash Shell。要访问该文件,首先运行您的终端,使用 cd ~ 导航到主目录,然后在打开的情况下打开文件编辑器。bash_profile 命令:
$ cd ~$ open .bash_profile
在文件编辑器中,为您想要隐藏的秘密信息键入 KEY=value 对,前面是 export 命令,表明您希望这些变量在 shell 之外也可用:
export USERNAME=”Insert Your User Name”
export DBPW=”Insert Your PW"
export DBNAME=”Insert Your DB Name"
添加完所有变量后,保存文本文件并用源刷新 bash 概要文件。bash_profile 命令:
$ source .bash_profile
如果您忽略运行上面的命令,您将无法立即看到刚刚创建的环境变量,您需要退出并重新登录命令行。
现在,在尝试数据库连接之前,让我们通过在命令行中打开交互式 Python 控制台并导入 os 模块 来确保变量确实可以在 Python 中使用。其中,os 模块使 Python 能够使用您的操作系统环境变量。您可以使用 os.environ 列出它们,这是一个返回 Python 字典的方法,该字典包含系统中当前可用的所有变量,或者选择每个键,如下所示:
**$ ipython****In [1]: import os****In [2]: os.environ['USERNAME']****Out[2]: 'Insert Your User Name'****In [3]: os.environ['DBPW']****Out[3]: 'Insert Your PW'****In [4]: os.environ['DBNAME']****Out[4]: 'Insert Your DB Name'**
太棒了。似乎您现在可以在数据库连接设置中用环境变量键替换秘密信息字符串。如果你像我一样使用psycopg2.connect()和pd.read_sql()方法和来连接红移,你会得到这样的结果:
结论
在本教程中,我尝试解决了一个我最近几次遇到的实际问题:在处理共享 Python 脚本时隐藏密钥,我希望你会发现环境变量对你的工作流和对我一样有用。请注意,这只是一个介绍性的指南,还有很多关于环境变量的内容需要学习。例如,我发现一门特别有启发性的课程是由 DataCamp 教授的 Python 中的命令行自动化。
免责声明:这个帖子包括附属链接,如果你购买的话,我可以免费给你一点佣金。
你可能也喜欢:
[## 在你开始第一个项目之前,要掌握 15 个 Git 命令
您需要掌握的最后一个 Git 教程是命令行版本控制。
towardsdatascience.com](/15-git-commands-you-should-learn-before-your-very-first-project-f8eebb8dc6e9) [## Python 编码面试前要解决的 10 个算法
在这篇文章中,我介绍并分享了 FAANG 中经常出现的一些基本算法的解决方案
towardsdatascience.com](/10-algorithms-to-solve-before-your-python-coding-interview-feb74fb9bc27) [## 5 套算法解决之前,你的 Python 编码屏幕
你对 Python 中的集合了解多少?用这些“简单”和“中等”的 LeetCode 问题挑战自己。
medium.com](https://medium.com/analytics-vidhya/5-pythons-sets-problems-to-solve-before-your-coding-interview-41bb1d14ac25)
从本地机器连接到 Docker 容器中运行的 MySQL
在 Docker 容器中部署带有持久存储的 MySQL 数据库并连接 Docker 化的 MySQL 数据库的分步指南

来自皮萨贝的帕帕萨恰里亚斯
如果你在软件工程行业工作,或者如果你打算在软件工程领域工作,你可能听说过码头工人。
2013 年, Docker 引入容器概念,永远改变了软件工程版图。
容器是一个标准化的软件单元,它允许开发者将他们的应用从环境中分离出来,解决了“它在我的机器上工作”的头痛问题。码头工人仍然是集装箱化的“事实上的”标准。
Docker 的另一个重要用途是,开发人员可以下载并运行任何容器化的应用程序,而无需直接将其安装在本地机器上。由于几乎所有必需的应用程序都有 Docker 版本,Docker 有助于尝试和运行应用程序,同时保持您的操作系统简洁明了。
MySQL 是最流行的开源数据库之一,也是“四大”关系数据库之一。它被工业界、学术界和社区广泛使用。在一篇博文中,我对业界十大数据库做了详细的分析和排名,MySQL 拔得头筹。如果您想进一步了解 MySQL 为什么是第一大数据库,包括它的主要特性、使用案例、托管 MySQL 服务和替代方案,您可以阅读我的文章:
MySQL,Oracle,PostgreSQL,微软 SQL Server,MongoDB,Redis,Elasticsearch,Cassandra,MariaDB,IBM Db2
towardsdatascience.com](/top-10-databases-to-use-in-2021-d7e6a85402ba)
在这里,我将展示如何运行一个 Dockerized MySQL 数据库,然后从您的本地机器连接它。
安装 Docker
你可以在几乎所有的主要操作系统中安装 Docker,无论是 Linux、Windows 还是 macOS。请按照 docker 官方网站上的说明在你的本地机器上安装 Docker:https://docs.docker.com/engine/install/
安装并启动 Dockerized MySQL
Docker 容器是无状态的。所以,如果你使用一个容器化的 MySQL,那么一旦重启容器,你将会丢失所有保存的数据。避免这个问题的一个方法是创建一个 docker 卷,并将其附加到 MySQL 容器中。以下是在本地机器上创建一个包含附加卷的 MySQL 容器的命令:
以下命令将在您的本地计算机上创建卷,您可以稍后用 MySQL 容器连接该卷:
*λ* **docker volume create mysql-volume**
mysql-volume
以下命令将从 Docker 注册表中提取 MySQL server 8 . 0 . 20 版,然后实例化一个名为“mk-mysql”的 Docker 容器。它还会将之前创建的卷“mysql-volume”与数据库连接起来,并公开端口 3306,以便您可以访问容器外部的 mysql 数据库:
*λ* **docker run --name=mk-mysql -p3306:3306 -v mysql-volume:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql/mysql-server:8.0.20**
Unable to find image ‘mysql/mysql-server:8.0.20’ locally
8.0.20: Pulling from mysql/mysql-server
您可以通过列出正在运行的容器来检查容器是否正在运行:
*λ* **docker ps**
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d54e7992094b mysql/mysql-server:8.0.20 "/entrypoint.sh mysq…" Less than a second ago Up 4 seconds (health: starting) 0.0.0.0:3306->3306/tcp, 33060/tcp mk-mysql
您还可以使用以下命令检查正在运行的 MySQL 容器的日志文件:
*λ* **docker logs mk-mysql**
现在,您可以使用以下命令连接到容器的交互式 bash shell:
*λ* **docker exec -it mk-mysql bash**
bash-4.2#
一旦进入容器,就可以连接到 MySQL 服务器并创建一个新的数据库,如下所示:
bash-4.2# mysql -u root -p
Enter password:
...mysql> CREATE DATABASE MYSQLTEST;
Query OK, 1 row affected (0.00 sec)
请注意,您必须给出与我们定义的相同的密码来运行容器( my-secret-pw )。
默认情况下,出于安全原因,MySQL 限制除本地机器(这里是 Docker 容器)之外的连接。因此,要从本地机器连接,您必须更改连接限制:
mysql> update mysql.user set host = ‘%’ where user=’root’;
Query OK, 1 row affected (0.02 sec)
尽管出于安全原因,最好创建一个新的非管理员用户,并只授予该用户访问权限。
安装 Dockerized phpMyAdmin
您可以使用任何 MySQL 客户端程序来连接 MySQL 服务器。我个人最喜欢的是 phpMyAdmin,这是一个简单而强大的 Web MySQL 客户端。此外,与其在我的机器上安装 phpMyAdmin,我更喜欢使用 Dockerized phpMyAdmin。
您可以从 docker 注册表中提取 phpMyAdmin 映像,并使用以下命令运行容器:
**λ docker volume create phpmyadmin-volume**
phpmyadmin-volume
**λ docker run --name mk-phpmyadmin -v phpmyadmin-volume:/etc/phpmyadmin/config.user.inc.php --link mk-mysql:db -p 82:80 -d phpmyadmin/phpmyadmin**
ef21905790dc42bc2e20d449b853d675d4922cb1249131513fdee885fc1088f8
您可以通过列出所有正在运行的容器或检查日志文件来检查 phpMyAdmin 是否正在运行:
λ **docker ps -f "name=mk-phpmyadmin"**
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ef21905790dc phpmyadmin/phpmyadmin "/docker-entrypoint.…" Less than a second ago   Up 10 minutes 0.0.0.0:82->80/tcp mk-phpmyadmin**λ docker logs mk-phpmyadmin**
Mac/Windows 的注意事项:
基于你的操作系统,你的 DOCKER_HOST 是不同的。在 Linux 上,它将是您的本地主机。对于 Mac/Windows,您可以使用以下命令获取 DOCKER_HOST:
λ **docker-machine ip default**
对于 Windows/Mac,您可以连接 DOCKER_HOST IP 地址。另一个选项是端口转发。在 Windows 中,Docker 机器是在主机中的 VirtualBox 下运行的虚拟机。
要为 MySQL 和 phpMyAdmin 启用端口转发,请执行以下步骤:
- 打开“Oracle 虚拟机虚拟箱”
- 选择您的 Docker 机器 VirtualBox 映像(例如,默认)
- 打开设置->网络->高级->端口转发
- 添加应用程序名称、所需的主机端口和来宾端口,如下所示:

通过 phpMyAdmin 访问 MySQL
打开浏览器,访问http://localhost:82进入 phpMyAdmin UI:

一旦您使用之前配置的密码(my-secret-pw)登录,您应该能够查看 phpMyAdmin 管理视图,如下所示:

在左侧面板中,您可以看到之前创建的数据库(MYSQLTEST)。现在,您应该能够管理您的数据库(创建/删除表,运行 SQL 查询,等等。).
如果您已经如上所述配置了 Docker 卷,那么即使您重新启动 MySQL 容器,您的数据库更改也将保持不变。否则,您在数据库中所做的所有更改都将丢失。
类似文章:
MySQL,Oracle,PostgreSQL,微软 SQL Server,MongoDB,Redis,Elasticsearch,Cassandra,MariaDB,IBM Db2
towardsdatascience.com](/top-10-databases-to-use-in-2021-d7e6a85402ba) [## 2021 年使用的 5 个最佳公共云数据库
亚马逊 DynamoDB,谷歌 BigQuery,Azure SQL Server,Azure Cosmos DB,亚马逊红移
towardsdatascience.com](/5-best-public-cloud-database-to-use-in-2021-5fca5780f4ef) [## 有效的微服务:10 个最佳实践
正确实施微服务架构的 10 个技巧
towardsdatascience.com](/effective-microservices-10-best-practices-c6e4ba0c6ee2)
使用 psycopg2 的 Python 模块连接到 PostgreSQL 数据库服务器

Jan Antonin Kolar 在 Unsplash 上拍摄的照片
动手教程
开发一个整洁的脚本和目录来本地或远程连接到 PostgreSQL
目录(仅适用于 web)
1 [What’s it all about?](#6107)
2 [The psycopg module to connect a PostgreSQL](#64a2)
3 [How to connect with PostgreSQL](#2ac2)
4 [Create, Read, Update, and Delete (CRUD) using psycopg2](#23f6)
  • [Create a table](#e1a3)
  • [Insert data to a table](#be50)
  • [Retrieve a table](#b149)
  • [Update a table](#5d20)
  • [Delete rows or a table](#fd24)
5 [Conclusion](#194d)
6 [References](#0c68)
这是怎么回事?
ostgreSQL 是一个关系数据库管理系统,它是开源的,也有很多功能。在 Python 中,我们有几个模块可以连接到 PostgreSQL,比如**SQLAlchemy**、**pg8000**、**py-postgresql**等。然而,**psycopg2**成为最受欢迎的一个。所以在本教程中,我们将讨论如何使用**psycopg2** 连接到 PostgreSQL。
连接 PostgreSQL 的 psycopg 模块
**psycopg2**是 Python 开发人员通常用来连接 Python 的 PostgreSQL 连接器。它是本教程的核心模块,所以请确保我们已经在机器上安装了它。在终端上使用以下命令安装**psycopg2**模块。
**$**pip3 install psycopg2
如何连接 PostgreSQL
要连接到 PostgreSQL,我们必须在 PostgreSQL 上创建一个代表我们的数据库的文件。我们将把数据库配置文件保存到**<filename>.ini**。 如果我们与公众分享我们的主要 Python 脚本 ,它会保证我们数据库配置的安全。在这种情况下,我们将它命名为**database.ini** 。请输入您的数据库配置,如主机名,数据库名,用户名,密码,端口号等至**database.ini** 如下。
[postgresql]
host = localhost
database = customer
user = postgres
password = admindb
port = 5432
关于数据库配置的信息。
- **host**—是运行 PostgreSQL 的服务器名称或 IP 地址
- **database**—是我们要连接的数据库名称
- **user**—是 PostgreSQL 的用户名称
- **password**—是连接 PostgreSQL 所需的密钥
- **port**—监听 TCP/IP 连接时将使用的端口号。默认端口号是 5432
为了解析来自**database.ini**文件的信息,我们用如下脚本创建了**config.py**文件。它还创建了一个返回项目根文件夹的函数。
注意:
**section = ‘postgresql’**的设置取决于**database.ini**文件的文件头。我们以后可以修改它。
# Import libraries
from configparser import ConfigParser
from pathlib import Path
def get_project_root() -> Path:
  """Returns project root folder."""
  return Path(__file__).parents[1]
def config(config_db):
  section = 'postgresql'
  config_file_path = 'config/' + config_db
  if(len(config_file_path) > 0 and len(section) > 0):
    # Create an instance of ConfigParser class
    config_parser = ConfigParser()
    # Read the configuration file
    config_parser.read(config_file_path)
    # If the configuration file contains the provided section name
    if(config_parser.has_section(section)):
      # Read the options of the section
      config_params = config_parser.items(section)
      # Convert the list object to a python dictionary object
      # Define an empty dictionary
      db_conn_dict = {}
      # Loop in the list
      for config_param in config_params:
        # Get options key and value
        key = config_param[0]
        value = config_param[1]
        # Add the key value pair in the dictionary object
        db_conn_dict[key] = value
      # Get connection object use above dictionary object
      return db_conn_dict
当我们的作品需要定期读取数据库上的表格时,我们可以创建一个文件名为**db_conn.py**的函数,如下。它将加载符合我们输入的表(**config_db**和**query**)。
# Import libraries
import pandas as pd
import psycopg2
from config.config import config
# Take in a PostgreSQL table and outputs a pandas dataframe
def load_db_table(config_db, query):
    params = config(config_db)
    engine = psycopg2.connect(**params)
    data = pd.read_sql(query, con = engine)
    return data
为了编译我们的脚本,我们设置我们的目录来定位**database.ini**、**config.py**和**db_conn.py**。使用以下设置。

运行脚本的推荐目录(图片由作者提供)
等等!有**main.py**文件,是什么?好吧,那么,它必须是一个 Python 脚本来测试我们通过 Python 的 PostgreSQL 连接。所以,这里是**main.py**。
# Import libraries
from src.data.db_conn import load_db_table
from config.config import get_project_root
# Project root
PROJECT_ROOT = get_project_root()
# Read database - PostgreSQL
df = load_db_table(config_db = 'database.ini', query = 'SELECT * FROM tablename LIMIT 5')
print(df)
有用!现在,我们将自定义并创建其他 PostgreSQL 命令,以便通过 Python 创建、读取、更新或删除。
使用 psycopg2 创建、读取、更新和删除(CRUD)
创建表格
通过**psycopg2**创建一个表使用了**CREATE TABLE**语句,但是是在 Python 脚本中。之前,我们保存了数据库配置文件。当我们想连接 PostgreSQL 时,只需导入**config**函数。
# Import libraries
import pandas as pd
import psycopg2
from config.config import config
# Connect to PostgreSQL
params = config(config_db = 'database.ini')
engine = psycopg2.connect(**params)
print('Python connected to PostgreSQL!')
# Create table
cur = con.cursor()
cur.execute("""
CREATE TABLE customer(
customer_id INT PRIMARY KEY NOT NULL,
name CHAR(50) NOT NULL,
address CHAR(100),
email CHAR(50),
phone_number CHAR(20));
""")
print('Table created in PostgreSQL')
# Close the connection
con.commit()
con.close()
将数据插入表格
创建一个新表**customer**后,我们将通过 SQL 语句**INSERT INTO**向表中插入一些值。这里有一个例子。
# Import libraries
import pandas as pd
import psycopg2
from config.config import config
# Connect to PostgreSQL
params = config(config_db = 'database.ini')
engine = psycopg2.connect(**params)
print('Python connected to PostgreSQL!')
# Insert values to the table
cur = con.cursor()
cur.execute("""
INSERT INTO customer (customer_id,name,address,email,phone_number)
VALUES (12345,'Audhi','Indonesia','myemail@gmail.com','+621234567');
""")
cur.execute("""
INSERT INTO customer (customer_id,name,address,email,phone_number)
VALUES (56789,'Aprilliant','Japan','email@gmail.com','+6213579246');
""")
print('Values inserted to PostgreSQL')
# Close the connection
con.commit()
con.close()
取回桌子
我们通过**SELECT** SQL 语句检索我们的**customer**表。在这种情况下,我们将检索**customer**表的前五行。
注意:要从 PostgreSQL 中检索数据,请确保我们选择了正确的表名和列。不要使用
*****语句,因为它会减慢我们的进度
# Import libraries
import pandas as pd
import psycopg2
from config.config import config
# Connect to PostgreSQL
params = config(config_db = 'database.ini')
engine = psycopg2.connect(**params)
print('Python connected to PostgreSQL!')
# Read the table
cur = con.cursor()
cur.execute("""
SELECT customer_id,name,email FROM customer LIMIT 5;
""")
print('Read table in PostgreSQL')
# Close the connection
con.commit()
con.close()
更新表格
当在数据库上工作时,定期更新我们的数据库是正常的,使用**UPDATE**语句很容易执行。假设我们将更新拥有**customer_id = 12345**的客户的地址。查看我们的表格,它将通过**psycopg2**自动更新。
# Import libraries
import pandas as pd
import psycopg2
from config.config import config
# Connect to PostgreSQL
params = config(config_db = 'database.ini')
engine = psycopg2.connect(**params)
print('Python connected to PostgreSQL!')
# Insert values to the table
cur = con.cursor()
cur.execute("""
UPDATE customer SET address = 'Japan' WHERE customer_id = 12345;
""")
print('Values updated in PostgreSQL')
# Close the connection
con.commit()
con.close()
删除行或表格
最后, D 用于使用**DELETE**语句删除(行或表)。在这种情况下,我们希望从一个**customer**表中删除拥有**customer_id = 12345**的客户信息。
# Import libraries
import pandas as pd
import psycopg2
from config.config import config
# Connect to PostgreSQL
params = config(config_db = 'database.ini')
engine = psycopg2.connect(**params)
print('Python connected to PostgreSQL!')
# Delete rows from the table
cur = con.cursor()
cur.execute("""
DELETE FROM customer WHERE customer_id = 12345;
""")
print('Values deleted from PostgreSQL')
# Close the connection
con.commit()
con.close()
结论
当处理大量代码时,我们不仅需要创建一个特定的脚本,还需要创建一个整洁干净的目录。有时,我们的脚本包含秘密信息,如数据库配置等。所以,我们在主脚本之外创建了**database.ini**。对于**psycopg2**模块,它有许多函数可以与 PostgreSQL 本地或远程交互,这使得我们可以更容易地开发自动化,而不是通过 PgAdmin 手动进行 CRUD 操作。
参考
[1] Python-Dev。psycopg 2 2 . 8 . 6【https://pypi.org/project/psycopg2/】【2020】。
将 Apache Hive 连接到 Microsoft Power BI
逐步连接法。

埃德加·恰帕罗在 Unsplash 上拍摄的照片
在这篇博文中,我们提到了执行所需连接的步骤。Hive 数据库基于 cloud era & Power BI 桌面安装在 Windows 上。这种连接也可以通过其他 BI 工具实现,如 Tableau &等。因为 Hive 可以与许多 BI 工具集成,但这个过程可能很有挑战性。让我们期待采取连接所需的步骤。
1.下载和安装 ODBC 驱动程序
最初,我们需要从 Cloudera 社区下载 ODBC 驱动程序。有两个版本一个是 64 位另一个是 32 位版本。这取决于您使用的 PC/笔记本电脑的版本。您可以通过点击窗口按钮和暂停/中断按钮来检查您的 PC 版本。将打开一个面板,显示系统的技术特征。然后,您可以根据您的系统安装 Hive ODBC 驱动程序。另外,下面给出了 Power BI 桌面下载的链接!
2.在 Cloudera 上启动 Hive Thrift 服务器

拉胡尔·帕塔克
- 这里,我们使用命令启动了 Hive Thrift 服务器
hive --service hiveserver2
- 键入 ifconfig 以获取 inet 地址,该地址将成为您的连接的主机。
ifconfig
3.ODBC 驱动程序的配置
- 首先同时按下 Windows + R 并键入 odbcad32。一个窗口会出现。

拉胡尔·帕塔克
- 在用户 DSN 选项卡中点击添加按钮

拉胡尔·帕塔克
- 点击用于 Apache Hive 的 Cloudera ODBC 驱动并点击完成
- 一扇新的窗户将会打开

拉胡尔·帕塔克
- 键入所需的数据源名称(DSN)。在这种情况下它的蜂巢 _ 连接。
- 描述是可选的
- 选择 Hive Server 2 作为您的服务器类型
- 使用 ifconfig 输入您从 Cloudera 终端获得的主机地址
- 端口是 10000
- 数据库部分是可选的
- 选择机制为用户名
- 这个连接的用户名是 Cloudera
- 选择 SASL 在节俭运
- 点击测试
- 检查测试是否成功。成功完成后,点击确定
4.电源 BI 的配置
- 点击获取数据= >更多
- 在搜索栏中键入 ODBC 并点击连接
- 选择 Cloudera Hive DSN 并点击确定
- 在 Power BI 的导航器栏中,可以看到来自 Hive 数据库的表格
5.可视化数据
一旦连接,我们可以将从配置单元表导入的数据可视化到 Power BI。

感谢您从头到尾阅读本文。我希望我们能够帮助您连接这些工具。快乐学习🤗。
连接到 AWS 上私有子网中的 ec2 实例
这篇文章是关于如何连接到 AWS 上私有子网中的实例的“指南”。它包括一个在 Terraform 中所需的基础设施的例子,并对您的 SSH 配置文件进行修改。

弗洛里安·范·杜恩在 Unsplash 上拍摄的照片
如果你想跳过,这篇文章中讨论的所有代码都可以在我的 GitHub 这里找到。
亚马逊虚拟私有云(亚马逊 VPC)使您能够将 AWS 资源启动到您定义的虚拟网络中。子网是 VPC 内的一系列 IP 地址。子网可以是带有 internet 网关的公共子网,也可以是私有子网。在公共子网中启动的实例可以向互联网发送出站流量,而在私有子网中启动的实例只能通过公共子网中的网络地址转换(NAT)网关来这样做。自然,私有子网更安全,因为管理端口不会暴露在互联网上。通常,在模块化 web 应用程序中,前端 web 服务器将位于公共子网内,而后端数据库位于私有子网内。
连接到私有子网中的实例是必要的,原因有很多:
- 应用程序的后端数据库位于专用子网内,工程师需要访问该数据库来执行专门的分析。
- 私有子网被列入另一个第三方服务的白名单,并且需要与该服务进行交互。
连接到专用子网
同一个 VPC 中的实例可以通过它们的私有 IP 地址相互连接,因此可以从公共子网中的实例连接到私有子网中的实例;也称为堡垒主机。
Amazon 实例使用 SSH 密钥进行认证。因此,连接到私有实例将需要 bastion 主机上的私钥;同样,连接到 public 实例需要您的主机上有一个私钥,但是这是非常糟糕的做法。不要把你的私人密钥暴露给堡垒主机!
另一种解决方案是使用 SSH 代理转发,它允许用户从堡垒连接到另一个实例,而无需在堡垒上存储私钥。SSH 代理是一个跟踪用户身份密钥及其密码短语的程序,可以使用以下命令进行配置:
# Generate SSH keys
ssh-keygen -k mykeypair# Add the keys to your keychain
ssh-add -K mykeypair
一旦生成了密钥并将其添加到 keychain 中,就可以使用-A 选项通过 SSH 连接到 bastion 实例。这将启用转发,并让本地代理在从您的堡垒连接到实例时响应公钥质询。
# Connect to the bastion host:
ssh -A <bastion-ip-address>
下一步是使用 Terraform 部署所需的基础设施。
基础设施
以下基础设施已使用 Terraform 部署;作为代码软件的开源基础设施(也是自切片面包以来最好的东西!).

使用https://creately.com创建的图表
VPC: 亚马逊 VPC(虚拟私有云)是 AWS 云的一个独立部分,您可以在其中配置基础设施。您的组织很可能已经有了 VPC,如果是这种情况,您可以跳过这一步。
子网:子网本质上是 VPC 中可用地址的子集,为您环境中的资源增加了一层额外的控制和安全性。如上所述,您的组织可能已经设置了子网,但如果没有,请参见下文。
私有子网和公共子网之间的主要区别是map_public_ip_on_launch标志,如果这是真的,那么在这个子网中启动的实例将有一个公共 IP 地址,并且可以通过互联网网关访问。
辅助 CIDR: 如果您的组织在其 VPC 中的所有 IP 地址都被私有子网占用,一种解决方法是创建一个辅助 CIDR 块,并在其中启动一个公共子网。
互联网网关:要使子网能够访问互联网,需要 AWS 互联网网关。互联网网关允许互联网流量进出您的 VPC。
路由表:路由表指定哪些外部 IP 地址可以从子网或互联网网关联系到。
Nat 网关:Nat 网关使私有子网中的实例能够连接到互联网。Nat 网关必须部署在具有弹性 IP 的公共子网中。创建资源后,与专用子网相关联的路由表需要将互联网流量指向 NAT 网关。
安全组:安全组充当您的实例的虚拟防火墙,控制传入和传出流量。下面的安全组启用端口 22 (SSH)上的所有流量。私有和公共子网中的两个实例都需要该安全组。
Ec2 实例和键:在定义了所有必要的基础设施之后,我们可以设置我们的 Ec2 实例了。这些实例需要一个 AWS 密钥对来验证访问,该密钥对是使用 aws_key_pair 资源和前面创建的现有 ssh 密钥创建的。
现在基础架构已经完成,下一步是部署。这可以通过 Terraform 目录中的以下 terraform 命令来实现:
terraform initterraform apply
如果部署成功,您将能够在 AWS 控制台中看到两个新的 EC-2 实例。

SSH 配置文件
SSH 配置文件是存储您所连接的远程机器的所有配置的一个很好的资源。它位于您的主目录下:.ssh/config。配置文件不是自动创建的,所以如果它不存在,您必须创建它。
Host bastion-instance
   HostName <Bastion Public IP>
   User ubuntuHost private-instance
   HostName <Private IP>
   User ubuntu
   ProxyCommand ssh -q -W %h:%p bastion-instance
private-instance 中的ProxyCommand告诉 SSH 建立到 bastion-instance 的连接,然后建立到 private-instance 的 TCP 转发。
最后,您可以使用下面的命令通过 SSH 连接到私有实例:
ssh private-instance
瞧,你成功了!
我希望你喜欢这篇文章;如果你有任何问题或建议,甚至是对未来帖子的想法,请在评论区告诉我,我会尽最大努力回复你。
请查看我的其他帖子:
- https://towards data science . com/Apache-air flow-automating-the-collection-of-daily-email-attachments-213 BC 7128 d3a
- https://towards data science . com/selenium-on-air flow-automate-a-daily-online-task-60 AFC 05 afaae
通过 SSH 隧道连接到 AWS 远程 MySQL 服务器

图片来自 un flash
这是一个瓶颈,我觉得我碰到了路障。特别是因为我想在不支持 SSH 连接的 Microsoft PowerBI 上访问数据库。我正在想办法解决这个问题,但是找到了其他的解决方案,中途就放弃了。
什么是宋承宪?SSH 协议(也称为安全 Shell)是一种从一台计算机安全远程登录到另一台计算机的方法。
顾名思义,隧道是通过 SSH 的隧道,我们将在其上转发特定的端口。该端口在您的本地机器上是可访问的,但是会被自动转发到远程机器,所以看起来好像您的远程服务(在本例中是 MySQL)实际上是本地的。SSH 端口转发是 SSH 中的一种机制,用于将应用程序端口从客户端机器隧道传输到服务器机器,反之亦然。
你需要什么:
怎么做
- 打开 PuTTY 并输入服务器主机名或 IP 地址。—这里需要注意的一件重要事情是,主机名(或 IP 地址)是 SSH 主机+端口

主机名、端口和连接类型
2.如果您有用户名(如果没有,可以跳过这一步)

2.如果您有用户名(如果没有,可以跳过这一步)
3.接下来,我们需要 Putty 来定位您的私钥文件。

找到您的 PPK 文件
4.接下来,我们需要设置隧道。在类别页面上,选择连接-> SSH ->隧道。输入源端口为 3306。输入目的地作为您的 AWS 主机名,如下所示。单击添加。

建立隧道
单击 Open,您应该会看到这个终端指示连接成功。

连接成功
好处:将 SSH 隧道连接到 PowerBI
- 选择您的数据源,我将使用 MySQL 数据库进行说明

1.选择您的数据源,我将使用 MySQL 数据库进行说明
2.输入服务器和数据库。如果愿意,您还可以选择传递一个查询。

2.输入服务器和数据库。如果愿意,您还可以选择传递一个查询。
3.选择数据库,输入你的用户名和密码。

3.选择数据库,输入你的用户名和密码。
4.您应该可以看到所有的表(如果没有指定查询的话)或者数据库中的表
让我知道它是如何工作的!
连接你的思想
皮质
第 1 部分:大脑-计算机接口之旅的开始

图片来自 FreePikCompany
理论物理学家加来道雄曾经说过,人脑有 1000 亿个神经元,每个神经元连接到 10000 个其他神经元,是已知宇宙中最复杂的物体。虽然大脑已经困扰了我们许多世纪,但我们现在终于开始了解它是如何工作的,但更重要的是,我们如何从根本上提取信号,并找到修复神经退行性异常的模式,并实现高级生理功能。现在,随着机器智能、深度学习和电极硬件的进步,我们可以分离和分析大脑信号,让科学家和研究人员在基础层面上理解思想和感知,这为使用计算机进行神经调节开辟了可能性。
这个系列试图让一个在脑机接口(BCI)领域没有专业知识的人了解他们是什么,以及我们如何使用机器学习以独特的方式分析脑电波。本系列探索的项目是与阿拉巴马大学人类技术交互实验室的克里斯·克劳福德博士合作的。这也证明了业余科学家可以使用基本的脑电图(EEG)设备来提取和分析脑电波,进而可以部署大规模的开源平台,以便公众可以用自己的大脑数据进行实验。
这个系列讲的是什么?
本系列旨在通过下述项目提供脑机接口的综合指南。这个系列将有四个部分:第一部分是对 Muse 和数据存储的介绍,第二部分建立了实验所需的机器学习,第三部分讲述了用于提高准确性的复杂机器学习算法,第四部分是对该领域的总体评估,并对 BCI 的未来进行了评论。
是什么项目?
HTIL 开发的项目是一个脑电图分析平台,使用 Muse 消费级脑电图耳机和基于网络的应用程序来运行简单快速的运动想象和情绪分类实验。它包含实时数据流、提供高精度分类的强大集成机器学习算法,以及用户友好界面中的交互式可视化。
在这里,我们将介绍项目和本系列背后的基础知识,从以下内容开始:
- BCI 简介:我们将过一遍什么是脑机接口,电极放置的重要性,以及电信号的不同频率。
- Muse、蓝牙和架构:MuseEEG 是一种非常有用的设备,用于通过电极收集大脑信号。我们将介绍它是如何工作的,以及它如何连接到 React web 应用程序和 Chrome 的 web 蓝牙系统。
- 数据管理:管理跨多个通道和试验的数据是困难的,因此我们将探索如何使用 React 状态和上下文来做到这一点。
为了实现本文中描述的项目部分,您将需要以下内容:
- 对 JavaScript/React 以及如何使用 React 组件创建组件树的工作理解。这是 React 文档。
- Chrome 浏览器,了解并访问网络蓝牙 API,以及 Muse EEG 版本 2。
什么是脑机接口?
脑机接口(BCI)是直接与大脑活动连接的设备,由一种能够读取大脑活动的设备测量,称为脑电图(EEG)。EEG 设备通过有创或无创的电极放置来记录电活动。侵袭性 BCI 通常需要手术,这带来了感染或脑损伤的风险;然而,收集到的信号要强得多。无创 BCI 侵入性较小,允许更大范围的大脑活动,但收集的信号有噪声,模式不容易找到。虽然侵入式 BCI 设备是许多有趣的研究论文和突破性技术的主题,但本系列的重点将放在非侵入式 BCI 设备上,因为它对业余研究人员和公众来说更实用。
如前所述,电极的放置对于从大脑的特定区域获得不间断、清晰的信号非常重要。国际上进行了大量研究来确定电极放置的通用系统,最终产生了国际 10–20 系统。10–20 系统是国际公认的用于脑电图数据收集的头皮电极放置系统。

图 1:国际 10-20 系统(维基百科)
“10”和“20”指的是相邻电极之间的实际距离,其为相对于鼻根和枕骨突(分别为头部的前部和后部)的纵向和横向头骨距离的 10%或 20%。许多非侵入性 BCI 设备采用这种电极放置系统来收集脑数据流。例如,InteraXon 公司在 2014 年设计的 Muse ,是一款消费级可穿戴脑电图头带,旨在收集脑电波数据,用于冥想。研究人员已经研究了将 Muse 设备重新用于其他用例,因为研究结果表明,其电极的放置提供了运动想象和情绪分类功能。例如, Munawar Riyadi 等人使用支持向量机(SVM)算法和 Muse 设备开发了一个运动想象分类模型,在识别运动状态方面达到了 86.6%的训练准确度和 100%的测试准确度。此外,、、和朱等人利用公共空间模式(CSP)和算法,利用 Muse 设备开发了一个运动想象分类模型,达到了 95.1%的准确率。这些进步允许研究人员在运动想象中使用来自 Muse 的脑波数据,而无需直接或侵入性的运动皮层交互。

图 2: InteraXon Muse 脑电图
虽然 BCI 设备使用电极来收集数据,但收集的究竟是什么类型的数据呢?当人们执行任何认知功能时,无论是身体的还是精神的,大脑都会产生电信号(数据),这些电信号可以被电极检测到。脑电波不是这些大脑状态的来源或原因,而是这些复杂过程的可观察的反映。脑电波是根据它们的振幅和频率来分类的。例如,低活动度脑电波具有低频率和高振幅,而高活动度脑电波具有高频率和低振幅。在这两个范围内,研究人员根据其特定的认知相关性确定了这些电信号的五个不同频率范围: delta、theta、alpha、beta、和 gamma 如下所示。

图 3: 脑波分类
与运动想象和情绪分类相关的认知功能通过α和β波进行检测,并通过一种被称为事件相关去同步化(ERD)和同步化(ERS)的现象进行最佳表达。我们可以使用像 Muse 这样的设备来检测这种感觉运动活动。
缪斯、蓝牙和建筑
当你第一次使用 Muse 设备时,你会注意到该公司已经为 iOS 平台开发了一个内置的连接系统,允许用户将数据从他们的 Muse 传输到他们各自的 Muse 移动应用程序。在 HTIL 的一个项目中,我注意到 Muse 没有提供流数据的 web 界面解决方案,但是我的实验室已经开发出了一种在 Chrome 浏览器中从 Muse 中提取数据的方法。在我们进入如何与 Muse 建立蓝牙连接的细节之前,了解 Chrome 的网络蓝牙 API 是很重要的。

图 4: Chrome 的网络蓝牙 API
上图显示了 Chrome 的网络蓝牙 API 的基本接口,它允许用户在本地应用程序和网络浏览器中连接蓝牙设备。它利用 Chrome 远程接口将 Muse 蓝牙系统与 Chrome 的蓝牙系统连接起来。
HTIL 已经为这个问题开发了一个解决方案,但是代码是专有信息,如果您的应用程序是公开部署的,就必须和它的源代码一起使用。联系我获取有关访问这些文件的更多信息。
当使用该桥运行 web 应用程序时,Chrome 中的蓝牙系统将寻找准备配对的 Muse 设备,主机将显示它识别的设备。这个蓝牙桥系统有很多功能,但最主要的是,你现在可以将 Muse EEG 与网络浏览器配对,并开始使用网络应用程序传输数据。

图 5:应用程序架构图
现在我们的蓝牙系统已经就绪,让我们来看看应用程序的整体架构。在图 5 中,图的第一部分显示了 Muse 和 Chrome 的蓝牙系统之间的连接。在 React web 应用程序中可视化结果之前,会发生多个步骤:数据从 Muse 设备直接传输到应用程序,并存储在 React 上下文中,数据通过数据带通滤波器进行预处理, BCI.js 用于实现基于机器学习的分析和分类,结果通过 web 应用程序实验仪表板中的 web 组件进行可视化。这看起来很多,本文只关注数据存储和蓝牙,但是在以后的文章中,我们将会详细介绍这个过程的每一步。请随意使用这个架构图作为本系列和您自己项目的指南。
电极和反应状态管理
一旦你的 Muse EEG 连接到 Chrome 的蓝牙系统,该设备就会自动开始将数据从四个电极直接传输到 React web 应用程序。然而,如果没有合适的存储系统,代表来自这些电极的信号的数据点就会被丢弃。为了建立电极管理的行话,我将用国际 10–20 系统中的电极名称来指代电极:AF7、AF8、TP9 和 TP10。在 BCIDevice.build.js 中建立的 MuseClient 系统已经有了一个仅用于 AF7 的连接和数据提取系统,但它可以很容易地推断出覆盖所有四个。下面的代码显示了 startMuse 函数,它利用了所有四个电极的 map 数据类型。
图 6: Muse 连接功能
你可以在图 6 的中看到,我们将采样率设置为 256,这是每秒收集的数据点的数量,将缓冲区大小设置为 2000,这是包含大脑数据的数组中任何时候存储的数据点的最大数量。我们对每次存储的数据点数量设置了限制,以便我们的机器学习模型不需要超过几秒钟的时间来训练,从而实现更实时的体验。该函数遍历电极图中的每个电极,并对其运行过程更新函数,该函数包含两个参数,来自电极图的电极和数据有效载荷本身;该功能对于存储极其关键,因为它管理数据流,以允许机器学习模型正确地训练和测试。
在我们进入 processUpdate 函数实际做什么之前,必须先了解一下 React 状态。React 使用两种类型的普通 JavaScript 对象在组件之间和组件内部进行通信:props 和 state。Prop 是 properties 的缩写,它被传递给组件(从父组件传递给子组件),而 state 包含私有信息,只能由初始化它的组件编辑和使用。本质上,状态是组件类在所有后续函数和渲染中使用的全局变量。因为我们不需要跨多个组件的通信,所以 states 非常适合存储在这个组件中。有两种能力我们希望被状态控制:正在运行和计数器。
图 7:数据存储功能
isRunning 是一个布尔值,它告诉我们 Muse 是否正在为训练或测试集传输重要数据,而计数器则让我们知道我们正处于实验的哪个阶段。例如,在图 7 中的 switch 语句中,如果正在运行为真,并且计数器为 1,则正在进行第一次训练。更多的内容将会在下一篇关于机器学习的文章中介绍。 processUpdate 函数检查 Muse 是否正在将数据传输到重要的数据集,如果是,它将决定将其分配给哪个试验,如果不是,数据将被转移到存储阵列并被转储。然后,该函数使用 FIFO(先进先出)存储技术将数组的最大大小保持在 2000 个数据点;机器学习模型不能很好地处理数据量不同的数据集,因此每个电极的同时更新功能可以保持所有电极阵列的数据点数量标准化。总之,这个函数检查正在进行的实验,并将数据点放入适当的数据集中。启动和停止实验的特性调用了 processUpdate 函数,这将在下一篇文章的实验流程部分进一步探讨。在 processUpdate 中,状态系统与电极图和数据管理系统的使用相结合,实现了端到端的存储系统,可以使用 React 上下文在多个实验中复制。
反应上下文存储
前面,我们讨论了道具和状态的区别;如果我们希望组件树能够访问某些变量,每个组件都必须将 props 传递给下一个组件。然而,React 有一个名为 Contexts 的内置 API,它提供了一种通过组件树传递数据的方式,而无需手动通过每个组件传递属性。最后一节建立了用于存储 AF7、AF8、TP9 和 TP10 电极的单独会话数据的系统,但是上下文将用于管理多个会话和数据集,以用作机器学习模型的训练和测试数据。
图 8:多个试验存储的反应上下文
React 上下文文档提供了一种设置上下文的简单方法,但我们需要添加两个主要功能:设置函数和组件返回值(如图图 8 )。对于我们的实验流程,正确分类需要两个训练集和一个测试集;因此,我们必须为所有三个数据集设置带有存储的上下文。上下文本身也包含状态,但是它们允许我们通过上下文提供者在不同的组件之间传递状态。在代码中,您可以看到状态包含用于训练 1、训练 2 和测试数据集的数组。下一步是建立我们的集合函数,这些函数将状态设置为参数中定义的属性。例如,如果我们有一个包含第一个训练集数据的数组,我们可以使用 set 函数将第一个训练集数据存储在特定的上下文位置。在这个项目的环境中,有三组函数用于训练和测试数据集;这些功能现在需要在所有组件中都可用。这就是上下文提供者的用武之地。在 render 部分,有一个称为 Provider 的包装器,它向组件“提供”值和函数;在这里,我们将状态和设置功能传递给子组件。上下文架构看起来很简单,但是对于跨组件存储来说,React 实现它的方式非常强大。
图 9:上下文建立和数据访问
既然已经建立了上下文,我们如何跨组件访问这些数据呢?有两个简单的步骤:在子组件中建立上下文和使用集合函数。为了在组件中使用上下文数据,您必须使用下面显示的命令,该命令允许组件访问使用和更改上下文中的数据。此外,在组件中,有一个 updateData 函数,它使用 set 函数将训练或测试集存储到上下文中的适当位置,允许机器学习组件访问它。例如,对于 updateData 函数中的情况 3,下面显示的命令将数组(该期间来自四个电极的数据)设置为上下文中训练集 2 的特定点(如图图 9 所示)。该数据然后被用于机器学习组件,并且在绘图组件中创建视觉效果。从使用状态存储单个电极阵列到为多次试验创建环境,整个系统允许对运动和情绪分类进行端到端的实验。
结论
在本文中,我们介绍了脑机接口的基础知识,如何将 Muse EEG 连接到 Chrome 的网络蓝牙系统,以及如何管理不同电极和多次试验的数据。现在,您可以实现上述解决方案,开始提取和存储多个参与者的大脑数据。以下是一些有用的提示:
- 记得检查你的 Muse 上的版本,并充满电以获得最佳性能。有时不同版本的 Muse 需要修改源代码中的 MuseClient 配置。
- 尝试创建一个计时器,以确保在进入机器学习之前存储阵列已满,以便数据集保持一致。
在本系列的第 2 部分中,将建立一个机器学习基础,以允许开发人员在我们继续开发该平台的过程中从脑电波中提取模式。
如前所述, HTIL 开发的代码是实验室的专有信息,只有在确认来源的情况下才能使用。请联系我了解更多详情,以便您可以使用这些文件将 Muse 连接到网络应用程序。
请继续关注第 2 部分!
具有 NP 完全性的连接艺术
玩数学艺术可能会上瘾

原始图像:神奈川外的巨浪,由 Hokusai 拍摄(约 1829 年),TSP 图像由 Tumas Rackaitis 拍摄(2020 年)
我们总是着迷于将点点滴滴联系起来。纵观整个世界,各种文化都观察过天空,发现过星座。数学家们长期以来一直试图形式化在任意一组位置中寻找最短路径的最佳方式。这导致了旅行推销员问题(TSP)。
你以前遇到过这个问题,例如,通过问:我应该按什么顺序做我的差事,这样他们能最快地完成?我们可以把这个问题简化成一个图:每个任务是一个点,一个顶点,完成这个任务需要多长时间,这个点到另一个点的距离就是它们之间的距离。寻找最短的路径将是最快的差事序列。
点画是一种艺术技巧,它用一组点来表现一幅画,给人以色调的印象。我们可以用它来连接点状图像,其中的点根据图像的暗程度聚集在一起。这样做,我们可以创建一个 TSP 问题和一些数学上有趣的艺术。

原始的,点画的,TSP 的我的狗狗的图像。您可以在这里单独查看图片
如果你想自己尝试一下,我在 Github 上做了一个简单易用的 python 库!自己试试。
这种艺术的算法在高层次上看似简单:
- 从图像中生成点。(点画)
- 连接这些点来解决 TSP 问题。(求解)
为了演示如何制作 TSP 艺术,让我们首先选择一个图像。我将改变这个企鹅形象。

企鹅被点画和茶匙
让我们从生成一些点开始。在我的回购中使用的技术被称为加权 Vornoi 点画,由 Adrian Secord 首先介绍,在此为 。
您可以复制我的 repo,并按照自述文件中的设置说明进行操作。
要生成点画图像,需要在调用 python 文件后指定图像的路径。
python3 stippler.py <image path> --n_point 20000 --force --save --display

企鹅点画后加权 Voronoi 点画。
这将绘制一个 20k 点的图像,强制重新计算,保存输出,并显示在屏幕上。
在一小段时间后,文件将被生成,它将输出 3 个文件:一个 PDF,一个 PNG 和一个 NPY 文件。PNG 很有趣,但是 stippler.py 文件中真正重要的是它创建的。npy 文件,它包含图像中每个点的 X 和 Y 坐标。我们可以把它直接输入 TSP 求解器。
为此,我们指定。TSP 求解器脚本中的 npy 文件:
python3 tsp.py <.npy_file_path> #produces TSP solution. 
这将产生一个 TSP 图像!

企鹅的 TSP 表示
您可以配置这些,并在下面的存储库中找到更多配置。
我从我的数学专业导师博施博士那里学到了这种艺术。他写了一本关于优化艺术领域的书。这本书通俗易懂,读起来很有意思,有很多漂亮的图片。
你可以在这里查阅他的书:
罗伯特·博施。 Opt Art:从数学优化到视觉设计。普林斯顿大学出版社,2019。
以下是我做过的一些我最喜欢的 TSP 图片:
我在这里使用的 Psyduck 作为我的缩放背景。我试图将它转换成 TSP 表示,结果好坏参半。我对它的点画非常满意,但作为 TSP 它看起来非常奇怪。

Psyduck,原始图像

Psyduck 在加权 Voronoi 点画后点画

Psyduck TSP:模糊。我喜欢眼睛的表现方式。前景中细节的缺乏使得点稀疏分布,这意味着 TSP 表示看起来很怪异。
另一个我最喜欢的是北斋的《女性浪潮》(约 1830)。日本木刻画往往点画得非常好,这是由于如何通过媒介处理色调,这是我越来越欣赏的东西。

《女性的浪潮》,作者北斋(~1829)

女性波,由北斋,点画加权 Voronoi 点画。两万个点。

20,000 点 TSP 代表女性波由 Hokusai。
如果你想看更多我的作品,你可以看看我的作品集网站。
下面这篇文章的代码就在这里的这个库中。
试试吧,在下面的评论里分享你的作品吧!
将模型误认为软件的后果(第二部分)
数据科学家来自火星,软件工程师来自金星(下)
构建和部署模型时要避免的十二个陷阱

在数据系列的第一部分中,科学家来自火星,软件工程师来自金星,我们研究了软件和模型之间差异的五个关键维度。接下来自然要问的问题是— 那又怎样?如果模型与软件混为一谈,数据科学家被视为软件工程师,这真的有关系吗?毕竟,对于大多数人来说,更重要的是对于商业世界来说,他们之间的相似之处要比不同之处明显得多。事实上,Andrej Karpathy 将这种使用模型解决问题的新方法称为软件 2.0 。如果它们真的是软件的下一个迭代,那么这些差异真的重要吗?
当我们将模型和软件融合在一起时,构建模型的挑战就变得更加严峻了。在这篇博客中,我们描述了当我们将两者合并时所面临的 12 个“陷阱”,并认为我们需要认识到它们的差异并相应地解决它们。
数据陷阱
正如我们在之前的博客中所讨论的,模型是正式的数学表示,可以应用于或校准以适应数据。因此,数据是构建模型的起点。虽然测试数据对于构建软件至关重要,但是在收集或准备测试数据之前,可以从给定的规范开始构建算法。
然而,当涉及到构建模型时,数据必须是高质量的(即,垃圾输入,垃圾输出),足够数量的可用数据,并且对于监督学习模型也是有标签的(即,标签是模型预测的响应变量)。数据也需要符合目的。这方面的一个例子是,当模型被部署到生产中时,数据应该代表我们将使用的人群。最近面部识别模型的皮肤类型和性别偏见的例子强调了拥有一个有代表性的(和有统计学意义的)数据集对于建立模型的重要性。这种数据偏差在实践中出奇的普遍。
我们已经看到,未能解决收集、管理和标记构建模型所需的必要数据的挑战,是将模型误认为类似于软件的重要陷阱之一。许多渴望推出人工智能或人工智能项目的公司很少关注这一方面,并开始用很少的数据建立模型。例如,一家公司最近想要构建一个 NLP(自然语言处理)模型,从只有八个 PDF 文档的文档中提取结构化信息。所需的成本和时间(尤其是来自领域专家(例如法律专家或临床医生)的成本和时间)使得标记成为一项重大挑战。虽然技术正在发展,以从更少的数据中学习,并帮助专家将标记数据作为他们正常工作的一部分,但拥有足够的、良好的标记数据仍然与构建模型的方式和传统的软件开发方式有很大的不同。
综上所述,数据陷阱可以进一步分为数据量陷阱、数据质量陷阱、数据偏差陷阱和数据标签陷阱。一家公司可能会遭遇一个或多个这样的陷阱。对数据陷阱有一个现实的感觉是至关重要的,以确保您不会走上错误的道路,在建模工作上花费数百万,而没有实现预期的回报。此外,了解这些陷阱还可以改变您处理建模工作的方式,首先收集更多的标记数据或寻找基于规则的替代方法来解决问题。
范围陷阱
有了三四十年的软件工程实践和方法,软件开发人员和系统分析人员在估计构建和测试软件所需的时间方面已经变得相当不错(或者至少比模型开发人员好得多)。使用敏捷软件开发方法,软件可以在固定的时间周期内增量和迭代地开发——通常在两周或四周的冲刺阶段。
假设我们希望我们的模型满足某些性能标准(例如,准确度、精确度、召回率等),那么很难估计实现这些结果所需要的努力和持续时间。更糟糕的是,我们可能无法先验地告诉我们实际上是否能够成功地满足性能标准。此外,满足性能标准的难度可能是非线性的。例如,在我们最近的一个客户项目中,我们能够在几周内用决策树模型达到 90%的准确率。然而,客户的目标是 99%的准确率。经过几个月的时间,神经网络模型的准确率不会超过 93%。
Lukas Biewald 给出了另一个经典的例子,在 Kaggle 的一次比赛中,全球数千人参加了一场比赛,旨在在短短一周内将模型的准确性从基线的 35%提高到 65%。然而,随后,即使在几个月和几千人试图改善这一结果后,他们所能做到的最好结果是 68%的准确率——仅仅提高了 3%。
我们称之为范围陷阱,在这种情况下,数据科学家无法确定实现特定性能标准(例如,准确性)所需的工作量和持续时间(或时间)、所需的数据以及所需的计算资源。这个范围陷阱可能发生在模型的不同阶段。在构建模型之前,可能很难确定模型的范围以实现某种性能——我们称之为预构建范围陷阱。训练范围陷阱是指数据科学家无法判断他们应该继续训练模型多长时间——使用新数据、新技术、额外资源等——以便在训练阶段达到性能标准。
当谈到在传统软件中嵌入模型或交付数据科学项目时,这两个陷阱会让产品经理、scrum master 或项目经理抓狂。在大型软件开发工作中,我们经常看到数据科学家的“声音”被忽略,由于紧张和固定的截止日期,迫使数据科学家执行简单的描述性分析,并且不能从模型中产生任何见解。或者,他们可能开发基于规则的模型,而不是脆弱的真正的 ML 模型。我们认为,这是许多 AI/ML 项目无法实现其声明的 ROI(投资回报)的重要问题之一。
当一个人构建能够持续学习的模型时,我们面临着一个额外的挑战。假设业务部门为模型部署确定的目标准确度是 90%,而经过训练的模型已经达到了 86%。业务和数据科学家可以一起做出部署模型的决定,让模型不断学习,并希望准确性超过 90%的阈值。同样,数据科学家将无法确定模型是否、何时以及在什么条件下会跨越这个阈值。我们将这种变体称为部署范围陷阱。
最后,模型可能遭受模型漂移,因为底层条件的改变,生产模型的性能下降。这种模型漂移可能突然发生,也可能连续发生。同样,数据科学家将无法确定模型准确性的性质、时间和恶化程度。我们称之为漂移范围陷阱。因此,需要建立模型监控实践来测量和处理这种模型漂移。
总之,作用域陷阱可以进一步分为预构建作用域、培训作用域、部署作用域和漂移作用域陷阱。下图通过一个示例强调了这些不同类型的作用域陷阱。

范围陷阱及其在构建前、培训期间和部署后的表现
回收凝汽阀
在着手构建任何大型软件之前,业务发起人和项目经理通常必须展示预期的 ROI。随着数据科学项目在企业中变得越来越普遍,企业领导者在进行投资或确定投资优先级之前,自然希望了解预期的投资回报。虽然估计一个新软件的回报并不容易,但当涉及到模型的预期 ROI 时,这项任务变得更加复杂。
从概念上讲,ROI 是一个相对简单的计算方法——它是相对于成本的净收益
ROI =(模型收益-模型成本)/模型成本
AI/ML 模型在公司中的好处通常分为两大类——效率和效率。当公司自动执行重复性的手动或认知任务时,他们可以提高流程的效率,减少执行这些任务所需的时间,并提高劳动力的生产率。当公司使用模型来制定更好的决策,以增强人类的决策能力时,他们就提高了决策的有效性。换句话说,好处来自于更快的和更好的。我们需要问的问题是——相对于什么基准更快更好?正是在估算这一基线时,公司常常做不到。****
当自动化一项任务时,我们需要有一个基线人类完成这项任务需要多长时间?不幸的是,估计一个人完成一项任务需要多长时间——尤其是当它是一项认知任务(例如评估客户的风险)或非重复性任务(例如处理费用审批中的异常)时,并不容易。具有不同技能、背景和任期的人可能需要不同的时间来完成任务。对所有这些因素进行适当的分析以确定一项任务的真实持续时间是一项重要的工作,而且在一个服务组织或知识型组织中也是不切实际的,因为这些组织的任务种类繁多,复杂程度各异。
在推导效率基线时的另一个常见问题是,可能很难将给定任务的评估与所有其他任务区分开来。以采购经理为例,她在一天的不同活动中检查系统中的采购订单,与包装单和供应商发票进行交叉检查,以确定交易是否准确。假设我们已经构建了一个 NLP 模型来从发票中提取关键字段,以便它们可以与采购订单相一致。即使对于这一个人来说,估计他们在发票处理上花费的总时间也可能是难以计算的,因为这项任务与其他任务相结合,例如参加会议、检查货物等。并且取决于采购订单、发票和包装单的复杂性(例如,如果装运是跨多个采购订单或多个发票的交付,则复杂性和时间会增加)。
当谈到获得有效性的基线时,我们进入了一个更具挑战性的努力。效率是针对任务计算的,这些离散的活动可以用持续时间来衡量。然而,说到有效性,我们正在评估的决策和的行动。我们如何确定一个动作是否比另一个动作更好?一个行动的结果是多维的,可能是不确定的,并且在效果上是滞后的。假设你正在驾驶你的汽车,当你接近一个信号灯时,绿灯变成了琥珀色。你会利用刹车来阻止后面紧跟的车有可能撞上你的危险吗?或者你会闯红灯(仍然合法)?哪种行动更好,以什么方式——对你后面的车辆更好,在油耗方面更好,在更严格地遵守法律方面更好。虽然这是一个简单的操作,但是估计决策的基线甚至更加复杂。**
到目前为止,我们刚刚检查了效率和有效性基线的估计。这必须在我们开始构建模型之前发生,以便我们对我们的模型需要什么性能有一个好的想法。我们称此为 收益估计陷阱 。还有另一种类型的回报陷阱,当我们已经构建并部署了我们的模型,现在正试图实现其好处时,就会出现这种情况。我们称之为 返回实现陷阱 。
在计算回报时,我们再次遇到问题。在效率收益的情况下,我们可以明确地表明自动化能够减少完成任务所需的时间。假设您的自动化发票处理模型将处理发票的平均时间从 30 分钟减少到了 15 分钟。如果这个人一天处理四张发票,他一天可以节省一个小时,一周可以节省五个小时。现在让我们假设这个人一天工作十个小时或者一周工作五十个小时。时间上的节约是 10%。然而,这可能不会给公司带来切实的经济利益。这可能是由多种原因造成的。员工已经每周工作 50 小时,节省了 5 小时,他们可能只是减少了工作时间。就员工满意度和保留率而言,这可能仍然是组织的总体收益——但我们可能没有将这一点纳入我们的收益估算。即使他们每周只工作所需的 40 个小时,而我们由于自动化节省了他们 5 个小时,他们可能会找到其他事情来填补这一缺口,而不是让组织能够将节省的时间转化为金钱。这是 RPA(机器人流程自动化)和 IPA(智能流程自动化)面临的最大挑战之一,在 RPA 和 IPA 中,为个人节省了时间,因此 FTE(相当于全职)有所减少,但这种节省并没有转化为员工数量的减少,在这种情况下,您可以清楚地展示自动化带来的回报。
当谈到实现决策或行动有效性的好处时,我们也会遇到类似的问题。在这些案例中,最大的挑战是归属的挑战。当一个行动可以被证明比一个备选方案好得多时,并不总是有可能隔离出执行这个行动的整个环境。例如,在停止车辆或穿过琥珀色的十字路口的情况下,当天气干燥和晴朗时,突然停止的行为可能更好,而在潮湿、湿滑、下雪天,这可能是错误的选择。在这种情况下,你不能把在干燥晴朗的日子停下来的好处完全归功于你的行动——部分功劳归于大自然母亲为你的行动提供了合适的环境。在评估决策和行动时,这种归因挑战太常见了,因为竞争对手、客户、供应商、法规和许多其他利益相关者可能会参与将行动或决策变得“更好”或“更坏”。
与软件相比,模型的回报陷阱更严重的原因是因为我们将这些模型的表现与人的表现进行比较。如果人类无法以自动化模型的速度执行某些任务(例如,算法交易),或者模型可以评估人类无法做出的选择并做出正确的决定(例如,玩围棋或象棋),模型的价值将相当明显。然而,在大多数情况下,当模型自动化任务或增加人类决策或行动时,价值陷阱是一个需要应对的重大挑战。
综上所述,我们最终得到四种不同类型的回报陷阱 — 回报效率估算、回报效率估算、回报效率实现、回报效率 实现陷阱。
摘要
我们已经了解了陷阱的三大类别以及总共十二个不同的子类别,如下所示。

跨越三个不同类别的十二个模型陷阱
在随后的博客中,我们将会看到一些最佳实践来解决这些陷阱以及界定、构建和交付模型的挑战。
作者: Anand S. Rao 和 Joseph Voyles
考虑从事欺诈分析工作

Bermix 工作室在 Unsplash 拍摄的照片
为什么欺诈可能是数据和分析中最热门的领域
与我交谈的人越多,我就越相信欺诈是并将继续是数据和分析领域最热门的细分市场。
我的推理相对简单:
每个企业都遭受欺诈
从传统企业到最前沿的企业,没有一个企业不遭受欺诈。这项针对亚太地区企业的研究发现,欺诈平均会让企业损失 1.75%的销售额——这是最高销售额,而不是利润!
在美国,这个数字可能更少,但即使欺诈成本占所有收入的 1%,这也意味着欺诈是一个每年 2000 亿美元的问题(美国 2019 年的 GDP 是 21 万亿美元)。
欺诈的例子包括:

斯蒂芬·菲利普斯-Hostreviews.co.uk 在 Unsplash 上拍摄的照片
- 信用卡诈骗。如果信用卡公司不能及时阻止欺诈交易,它将不得不买单。
- 卖家在亚马逊市场、Ebay 或 Etsy 等第三方卖家匹配买家的在线市场上敲诈买家(反之亦然)。在卖家方面,它可以是任何事情,从发送一个装满石头的盒子到建立一个虚假的在线商店来窃取姓名和信用卡号码。在买方方面,退款是一种常见的情况,即客户收到了购买的商品,但声称他们没有收到。
- 与在线市场类似,零工和共享经济尤其容易出现欺诈。它可以是复杂的计划,洗钱者可以预订幽灵车(在拼车平台上)或幽灵住宿(在度假租赁平台上),也可以是更简单的计划,如虚假评论以推高评级,并利用推荐算法。
- 不太明显的欺诈也比比皆是。例如,当合同规定送餐员有义务送货上门时,他可能会拒绝全程送餐(为了节省时间和多送几次)。或者,Instacart 购物者可能会超慢地购物(以增加每单位努力的报酬)。同一购物者也可能挑选出某一类别中最贵、利润最高的商品(作为对店主回扣的回报)。这些事情既会对公司造成财务损失,也会对用户体验产生负面影响。
- 促销和优惠券是骗子和普通人最喜欢的目标。数字业务通常急于扩大规模。因此,向潜在客户提供金钱、赠品和折扣的做法非常普遍。这些类型的促销活动会像蜜蜂吸引蜂蜜一样吸引骗子。
- 保险欺诈规模巨大,每年损失数百亿美元。
欺诈可以用数据来对抗
欺诈是可以预测的。许多公司对数据科学和机器学习的一个批评是,积极的影响很难观察到。部署机器学习模型来预测未来趋势或揭示令人眼花缭乱的前所未见的客户行为洞察的宏伟计划往往无法推动变革(因为预测未来很难)。
然而,利用正确的数据和模型,欺诈是最有可能被识别和预测的。这可能看起来没有预测下一个大的消费趋势或让汽车自动驾驶那么迷人,但你更有可能相对快速地产生真正的物质影响(对于快速增长的公司来说,欺诈可能会让他们付出比我上面提到的 1%—2%的收入更多的代价)。
如果你的模式能够帮助你的公司每年从快速扩张的收入中赚回 1%,那就是很大的增值。
好吧,那么我需要知道些什么来对抗诈骗呢?
数据科学在打击欺诈中的作用应该是显而易见的——创建一个流程或系统来标记很可能存在欺诈的用户、交易或交互。
你最有可能做到这一点的方法是将大量的数据输入到一个模型中,这个模型会显示出欺诈的可能性。然后你将不得不决定什么构成足够高的概率来拉响警报。
这里有一个非详尽的技能列表,我认为对帮助公司打击欺诈会有帮助。
- 大数据操作、提取和清理:在线支付公司、电子商务公司或在线市场公司的数据集非常庞大。为了运行实验,您需要能够访问、清理和组织这些数据(如果数据工程师很忙,您将不得不自己动手)。
- 处理不平衡的数据集:类别不平衡将是欺诈分析中的常见障碍。合法用户或交易的数量远远大于欺诈用户或交易的数量。因此,了解类不平衡如何可能使你的模型产生偏差,以及如何对抗它的一些负面影响(重采样、SMOTE 等)。)很关键。
- 【自然语言处理】 :所有的数据都要很好地结构化、量化,这是极不可能的。相反,很多数据很可能是文本格式的,如电子邮件、客户反馈等。您将需要能够将这些文本数据转换成您的模型能够接收和理解的东西。
- 建模:无论是逻辑回归还是神经网络,一旦数据准备就绪,你将需要知道如何正确地训练一个模型,评估它的性能(包括它如何对未经训练的数据进行归纳),并在新数据到来时更新它。
- 适应性(你和你的模型):欺诈者会不断调整他们的方法和算法(是的,他们也精通数据)以避免被发现——考虑到所涉及的金额,他们非常有动力快速行动。因此,你上个月运行良好的模型可能会突然被欺诈者的创新所淘汰。你对问题的理解和你的模型都需要高度灵活才能跟上。
- 大规模部署模型:欺诈模型需要快速、可靠。欺诈者不会等着你慢慢做出决定。您还需要能够在保持模型更新和保护客户免受模型失控之间走钢丝。考虑到显著的类不平衡,新数据可能会彻底改变您的模型的行为,您将需要有一个适当的过程来快速评估模型更新的影响,并确定模型何时看起来可能会脱离轨道。
- 商业直觉、产品知识和客户同理心:这很重要,有两个原因。你越了解产品,就越能发现潜在欺诈和滥用的漏洞和领域。但是同样重要的是要认识到,将一堆账户或交易标记为潜在欺诈会给业务带来摩擦,并降低被错误识别的用户的体验。知道如何以及在哪里划清界限将是至关重要的——否则你将面临减少欺诈的风险,但代价是超出平均水平的用户流失。
结论
如果我重新开始我的数据职业生涯,我会认真考虑欺诈分析。在这个领域,数据和模型可以对利润产生更直接和更重要的影响。而造假是每个行业都面临的问题。
随着当代科技公司不断发明新的市场和服务,我们日常涉及的交易数量将大幅增加。每一笔新交易都代表着欺诈者盗窃的机会,也代表着聪明的分析师化险为夷的机会。干杯!
是否在我的模型中考虑多重共线性?
机器学习
简要讨论是否有必要修复特征空间中的多重共线性。希望对你设计下一个计算实验有帮助。

马丁·桑切斯在 Unsplash 上的照片
作为一个超级篮球迷,我曾经写过一篇博客,讲述如何使用线性回归模型根据一名 NBA 球员的场均数据来预测他的工资。我被我的一个粉丝挑战了。
他坚持认为我的管道是错误的,因为独立变量之间的多重共线性在将它们汇集到回归模型之前没有得到解决。
我会说,他一定是一个好学生,记得统计课程中所教的内容。但是无论何时建模,我们真的应该关心我们的特征空间中的多重共线性吗?
看情况。
我想通过一个小而有趣的实验来讨论这个话题。在这个例子中,我不会从理论统计的角度来评论任何事情。让我们只阅读代码并检查结果。
问题。
谁是 NBA 2019-2020 赛季的年度最佳新秀(罗伊)?
特征空间。
NBA 新秀从 1990 年到 2019 年的基本数据,包括新秀赛季的比赛(' G ')、场均上场时间(' MP ')、场均得分(' PTS ')、场均篮板总数(' AST ')、场均助攻数(' STL ')、场均盖帽数(' BLK ')、投篮命中率(' FG% ')、三分球命中率(' 3P% ')和罚球命中率(' FT% ')。

玩具数据的头
因变量。
我用罗伊投票份额作为因变量,范围从 0 到 1。投票份额越高,成为罗伊的几率就越高。我没有把它框架到一个分类问题上,为了避免一个不平衡的训练集。对如何从头到尾生成一个 ML 项目的细节感兴趣的可以参考我的另一个帖子。
回归模型。
我尝试了两个机器学习回归模型(弹性网和随机森林)和一个经典线性回归模型。

超参数调谐功能。
上面的函数显示了超参数调整函数,其中我可以使用交叉验证来确定每个模型的超参数。调整后的超参数将用于最终的模型训练。
基本线性回归在管道内定义如下。

基本线性回归模型
超参数调整和模型训练。
我使用分割数据集训练上述模型。数据分割和超参数调整代码如下所示。

数据分割代码。

调谐过程。
上面代码中调整过的超参数然后用于训练最终的模型。

最终模型训练函数。

训练最终模型。
我也训练了基本的线性回归模型如下。

拟合基本线性回归模型。
绩效评估。
我在测试数据上评估了三个模型的性能。我计算回归模型的 MSE。

具有所有特征的三个模型的 MSE。
我发现模型 2(随机森林回归)是所有三个模型中最好的,测试数据 MSE 等于 0.055。
整个建模过程已经完成。
从特征空间中移除相关变量。
现在,让我们处理数据,以便排除彼此相关的特征。我简单地检查了下面的相关性。

数据集相关性。
似乎每场比赛的得分(PTS)和上场时间(MP)与皮尔森的相关系数等于 82%高度相关。因此,我首先从数据集中删除 MP,然后再次重复整个建模过程。以下是测试数据 MSE:

从特征集中移除 MP 后三个模型的 MSE。
我还从整个特征空间中移除 PTS,并重复整个建模过程。这是测试数据的 MSE 结果:

从特征集中移除 PTS 后三个模型的 MSE。
如上图所示,在解决了特征集中的共线性后,三个模型的性能几乎与完整模型相同。
这些结果表明,从特征空间中移除共线性对我们数据集中的罗伊的预测没有帮助。
顺便说一句,所有的模型对我们的问题给出了相同的答案。预测 Ja 莫兰特 将是 NBA 2019–2020 赛季的罗伊。
何时考虑多重共线性?
然而,为什么人们强调解决回归模型中多重共线性的重要性,而它对预测没有影响呢?这与模型的解释有关。
假设我们感兴趣的是检测球员的哪些基本数据对预测贡献最大。然后,我们需要小心多重共线性。
高度相关的变量可以相互竞争来解释模型中因变量的方差。具体来说,这种竞争将体现在弹性净回归和线性回归中的系数上,或者体现在随机森林回归中的特征重要性上。
由于第二个模型(随机森林回归器)是我实验中最好的模型,我将用它作为例子来解释。
我分别画出了随机森林模型 1) 具有所有特征、 2) 具有除 MP 之外的所有特征和 3) 具有除 PTS 之外的所有特征的特征重要性。

所有变量的特征重要性。
当我使用所有特征时,我发现 PTS 是模型中最重要的变量,而 MP 看起来一点也不重要。

没有 MP 的特性重要性。
如果我从特征空间中移除 MP,模型仍然给出 PTS 作为最重要的变量。

不含 PTS 的特征重要性。
但是,如果我把 PTS 从特征空间中去掉,如上图所示,MP 就成了最重要的变量。
从这些结果中我们可以看出,MP 和 PTS 对于预测都是重要的,但是如果将它们作为独立变量一起放入模型中,它们在特征重要性上是相互竞争的。
结论
我们需要修复回归模型中的多重共线性吗?
看情况。
如果回归器的目的是纯粹的预测,数据集中存在的多重共线性根本没有害处。它的预测能力不受相关变量的影响。
如果回归变量的目的是了解自变量和因变量之间的关系,则需要解决多重共线性问题,因为相关变量会在解释因变量时相互竞争。
希望这篇文章能对你的机器学习项目有所帮助。如果你喜欢读这篇文章,请在媒体上 跟随我 。如果您对数据科学文章感兴趣,这些帖子可能会对您有所帮助:
一个关于如何在 r 中用 pheatmap 生成漂亮的热图的教程。
towardsdatascience.com](/pheatmap-draws-pretty-heatmaps-483dab9a3cc) [## 给出了随机森林分类器的特征重要性
如何建立一个随机森林分类器,提取特征重要性,并漂亮地呈现出来。
towardsdatascience.com](/present-the-feature-importance-of-the-random-forest-classifier-99bb042be4cc) [## 线性回归中的一个实用建议
从弹性网开始,记得调好定义 l1 范数之比的超参数。
towardsdatascience.com](/a-practical-suggestion-in-linear-regression-cb639fd5ccdb) 
内森·杜姆劳在 Unsplash 上拍摄的照片
关于数据和科学在数据科学中重要性的思考

又一个新冠肺炎仪表板。这可能不是很有见地,但我喜欢它,因为它是我的
我必须承认:在封锁的这些天里,我一直在摆弄关于新冠肺炎疫情的仪表盘和信息图表,就像其他人一样。我已经制作了一个交互式仪表盘,有漂亮的图表,一些格式化的表格,甚至一些(基本的)地图。它闪亮而漂亮,你可以花一些时间玩它打开和关闭图层,悬停,缩放…而且,老实说,我必须承认,我为我取得的一些成果感到自豪。然而,它有缺陷。就像其他人的(或者差不多)。然而,我会继续改进它,即使我担心它会一直有缺陷,即使我承认它永远不会有助于提高这方面的知识。
那么,如果我知道我不能改变它的命运,为什么我还要坚持不懈地努力呢?诚然,在某些时候,我问过自己这个问题,我甚至考虑过辞职。我不仅不想浪费时间(即使在我们被锁在家里的这些日子里,我们也有很多事情可以做),而且我不想制造噪音、错误信息,甚至对一部已经很重要的戏剧进行更多的渲染。因为有缺陷的图形就是这样。但最终,我意识到在这样的仪表盘上工作是一个边做边学的好机会。
从中可以学到许多类型的课程,比如与技术技能相关的课程,或者与如何收集、可视化和分析数据相关的课程。今天,当新冠肺炎的数据和数字到处都是时,我想分享一些关于数据科学中的科学(或缺乏科学)的思考。
1.获得(正确的)数据。
听起来很明显,没有数据来可视化或分析,就没有数据可视化或数据科学。因此,任何想要进行任何类型的数据可视化或数据分析的人首先要做的就是获取数据。第二:我们不能使用任何类型的数据。我们需要使用好的数据,我说的好的是指可靠的、可用的(就许可证、格式和结构而言)、最新的和频繁更新的,并且希望官方的数据,即代表性的足以解释我们正在研究的现象。虽然这通常不是小事,但如果我们要解释一个全新的现象,就在我们说话的时候,它正在发生,而且像新冠肺炎一样在全球范围内发生,这就更重要了。
通常情况下,只有两种可能的选择:要么自己收集数据,要么依赖他人的数据。虽然收集我们自己的数据可能是某些情况下的最佳选择,但在新冠肺炎的情况下,我们不太可能收集到对我们有用的数据(事实上,正如我们将看到的那样,甚至政府也在努力这样做)。因此,我们只有一个选择。当然,我们不能依赖某个随机的人或机构,我们需要依赖我们可以信任的人,比如大学(因为他们倾向于提供严格的数据)、政府(因为他们提供官方数据)或组织(比如世界卫生组织)。但是我们从哪里得到数据呢?

世卫组织的新冠肺炎仪表盘(截图自 2020 年 4 月 2 日)
虽然大多数政府以开放许可证的形式提供数据,允许出于任何目的使用和重新使用数据,但它们通常未能解决另一个重要问题:文件格式和数据结构。因此,在英国或西班牙等国家,如果没有手动操作,数据无法直接消费。其他公司,如世卫组织,提供了很好的数据仪表板,但他们不提供供第三方使用的原始数据。好消息是,有些人和机构正致力于使用开放许可证发布干净的数据,例如西班牙的data dista、约翰·霍普斯金的大学收集来自不同政府和世卫组织的数据,甚至还有像 R 这样的包,它从 UJH 获取数据,并为开发人员和数据科学家在他们的项目中使用提供一个很好的数据框架。
因此,难怪全球大多数信息图和仪表板依赖于相同的数据源。这似乎是一个明智的决定:我们不仅得到现成可用的数据,而且我们是从可信的来源得到这些数据的。然而,正如我要说的,正是因为这个原因,他们中的大多数人都错了。但是有什么可能出错呢?
2.不要把数据看得太重
既然我们知道从哪里获取数据,我们必须意识到:通过依赖他人生成的数据,我们没有解决数据收集的(主要)问题,我们只是将责任转移给了其他人,但有人仍然必须处理我们一直试图避免的事情。令人惊讶的是,并非每个国家都以相同的方式收集数据。
就拿最基本最关键的问题来说:确诊病例数是怎么定义的。因为新冠肺炎的症状与流感非常相似,而知道某人是否被感染的唯一方法是通过测试呈阳性。这似乎是一个很好的定义:我们有一个对每个人都一样的客观测试,所有国家似乎都使用相同的标准。不幸的是,这些测试需要稀缺的设备(与当前全球需求相比),只能在医院进行,并且需要两天才能得到结果。因此,有许多其他场景没有在本测试中考虑,如图 2a 所示。所以,是的,每个政府都提供了这个数字,然而他们都比真实数字低得多。低多少?没有办法知道。

确认数字≠实际数字。案例比官方的多很多。还有多少?我们不可能知道
让我们关注另一个例子:死亡人数由 COVID 19 。显然,这应该更容易。每个国家都有每天死亡人数和死亡原因的记录,所以应该很容易从所有可能的原因中筛选出死于新冠肺炎的人。嗯,不。正如我们所看到的,如果我们不能准确地定义感染新冠肺炎病毒的人数,我们就无法知道因此而死亡的人数。
但是,如果我们考虑到每个国家对如何统计检测呈阳性的死亡人数有不同的标准,情况可能会更加棘手。以英国的定义为例:
死亡数字几乎在所有情况下都与死于医院且新冠肺炎检测呈阳性的患者有关。[……]这些数字不包括院外死亡,如护理院内的死亡,除非上文另有说明。( [来源*](https://www.gov.uk/guidance/coronavirus-covid-19-information-for-the-public ↩︎) )*
所以,再一次:无论哪个国家进行测量,真实数字都比报道的要高得多。他们都错了。一些人认为,政府不想提供真实的数字,以免引起更多的社会恐慌,失去选民的支持,甚至比其他国家更好看。然而,通常更简单的答案是最有可能的答案:不是政府想对我们隐瞒信息,只是没有一个国家有能力面对这场疫情,更不用说采取准确的衡量标准了。新冠肺炎的另一出戏剧超越了个人悲剧:世界上没有一个国家准备好应对新冠肺炎所代表的压力测试。

这并不是说政府在密谋隐藏信息,只是他们没有办法获得更好的数据。戏剧就在这里!
但是让我们回到我们的道路:数据和数字的道路。在这一点上,我们不得不承认所有的数据都是有缺陷的,我们无法从中获得真实情况的完美画面。如果我们想这样做,我们需要使用其他数据来源,比如将每日死亡总数的记录与去年同期进行比较。当然,这将需要更多的时间,反过来,这也有其他影响和问题(例如,它不会给出新冠肺炎死亡的准确数字,因为没有办法知道他们的死因,但不同时期之间的显著差异可能是一个很好的代理)。
因此,我们现在有两个选择,要么对所有新冠肺炎信息图表和指标完全失去信心,要么承认它们的局限性,并假设它们只是对现实的粗略近似。

我们有两个选择:要么对所有新冠肺炎的信息图表完全失去信心,要么假设它们只不过是对现实的粗略近似
3.选择正确的图形和图像
太好了!如果你正在读这篇文章,这意味着你可以假设现实(一如既往)远比漂亮的仪表盘所能显示的更复杂,不管它们有多花哨。说到这里:当心花哨的视觉效果!









我敢肯定,在这一点上,你可能已经看到了许多任何类型的整洁的信息图,就像上面的那些:其中一些使用箱线图,其他线条,其他散点图…其中一些有平滑的边缘,其他的有对数刻度的轴。甚至有一些想要引入地理空间分析并呈现不同种类的地图(choropleths,bubbles,sizes 如果你像我一样,你可以享受观看它们并与它们互动数小时的乐趣。但是它们真的能有效显示有用的数据吗?可惜大部分都不是(甚至包括我自己的一些)。
最基本也是最常见的一种方法是显示一个国家内的大部分病例。大数字确实很吸引人,也很容易理解,但它们通常缺乏一些上下文,使它们真正有意义。当然,谁都能理解一个 7 位数的数字是个大数字,但要知道它到底有多大真的很难。我们需要将它与其他东西进行比较,以全面掌握它的真正重要性。此外,由于我们处理的是一个累积数字,因此必须了解所分析的时间跨度,因为在一天、一周、一个月或一年内达到某个数字是不一样的。

大数字很容易记,也很容易理解,但单独使用时并不真正有意义。这是我试图通过添加一些背景来解决的问题。来源:卡洛斯·卡马拉
一种常见的变化是提供累积案例的折线图,X 轴表示日期,Y 轴表示案例总数。有几种变化:显示相对数据(例如:病例数/人口数),用对数标度(以便更容易看到第一天与最近几天相比的变化),显示几个类别,代表不同地区或不同类型的病例…虽然它们真的很有效,而且从技术角度来看大多数都是正确的(特别是考虑到其中一些变化会产生很大的差异),但数字本身的表示可能很少或没有用处。真的能代表什么吗?通过提供一个根据定义将会一直增长的数字,我们可以回答什么样的问题?

显示累积案例的折线图。真的有意义吗?来源:世卫组织
正是出于这个原因,一些人决定使用其他指标来评估疫情的发展情况,例如每天新增病例的数量,因为这些指标使我们能够很容易地确定数字是好还是坏于前一天。同样,可以对前面的图进行同样的变化,以使其更有洞察力,例如下面的条形图,它显示了按类型分组的每日病例变化。我们不仅可以看到它们开始下降,而且,与死亡或活动病例相比,康复病例的数量正在增加。

每日案例,按类型。来源:卡洛斯·卡马拉
这确实比累积案例更有用,尽管它有些不稳定并可能导致混乱。以下图为例:

你能看到那种奇怪的意想不到的变化吗?就是周末效应!(这就是为什么在下一个仪表板版本中,我将突出周末)。来源:卡洛斯·卡马拉
你能看到 3 月 29 日和 30 日的所有数据在第二天再次达到峰值之前是如何急剧下降的吗?这确实是一种意想不到的行为,也很难解释。除非我们意识到这两天是周六和周日,并且由于在医院工作的人较少,数据不会像往常一样快,因此会在周一累积。这种现象被称为“周末效应”(我提到过你不应该把数据看得太重吗?😉)
正因为如此,其他一些人,如《金融时报》的约翰·伯恩-默多克(John Burn-Murdoch)更喜欢使用固定期限(如 3 天或 5 天)的滚动平均值,这是一个更稳定的数字,如下图所示。

来源:金融时报
其他用于修复这些异常值和显示趋势的技术是使用基于实际数据的平滑线图,就像下面由 Pablo Rey 制作的图。

基于实际数据的平滑线。来源:蒙特拉 34
4.不要仓促比较
当然,最常见的视觉类型是比较新冠肺炎是如何影响不同地区的,无论是在一个国家内还是在不同国家之间(通常以中国或武汉为参照——毕竟,这是一切开始的地方)。虽然这种图表可以回答一些问题,比如一个特定地区如何对待另一个地区(因此,例如,复制或避免他们针对新冠肺炎的措施),但这些比较确实存在问题。首先,人口或规模差异巨大的事实使得任何绝对比较都是无效的。
但是,即使在使用相对值(例如:每个居民的病例数)时,还有其他对疾病演变有直接影响的关键因素,其影响被认为是相同的,而实际上可能有几个数量级的差异,例如人口统计、地理、城市住区或卫生系统(就人力和财政资源而言),等等。

按年龄和国家分列的病例和死亡率柱状图。意大利的大多数病例发生在 70-90 岁之间,而韩国的病例发生在 20-30 岁之间,因此死亡总数存在巨大差异。来源: Diario.es
然而,在所有这些差异中,最相关的可能是检测新冠肺炎病毒的测试次数,这个数字不仅不同,而且通常是未知的。这绝不是微不足道的,因为我们已经看到案例的数量是使用这个参数定义的。因此,如果一个国家进行的检测数量非常少,那么它的病例数量也将非常少。眼不见心不烦。
因此,这里最大的问题是,如果不考虑这些因素,比较可能会得出大量与现实无关的有偏见的结论,例如,一些国家可能会避开新冠肺炎,或者具有某种免疫力,或者新冠肺炎只会影响卫生系统糟糕的国家,无组织的政府或不发达国家。因此,就像一些政客为了自身利益已经开始做的那样,一种建立在完全错误的基础上的道德优越感的叙事存在风险。

不要像 Wopke Hoekstra 那样做草率的比较,否则你可能会像他一样得出令人反感的结论。图片:维基共享资源
5.更不要把预测当回事
最后一组图,也是最复杂的一组图,是那些进行预测的图。虽然它们真的很吸引人,而且显然为我们的一个主要担忧提供了答案()“这什么时候会结束?”/“这种情况还会持续下去吗?”)用一种非常可以理解的方式来说,他们真的很狡猾。有几种方法可以进行预测,例如使用线性回归或模型。虽然模型可以像我们希望的那样简单或复杂(因此,它们的准确性会有很大差异),但它们主要依赖于拥有一组良好的历史数据或了解它们想要描述的现象的逻辑。不幸的是,由于新冠肺炎是一个新现象,我们两者都缺乏,因此,现阶段的预测容易出错。一些预测是基于以前爆发疫情的其他地方发生的情况,但我们已经看到这可能会产生多大的问题。

如果我们看不到(也不理解)预测背后的模型,它可以像算命师一样有用和可靠。图片:算命先生,由* 阿尔伯特·安克尔(维基共享资源)*
最后,但同样重要的是,我们不应该忘记,它们需要特定领域的大量知识,就像大多数做这些漂亮可视化的人(包括我自己)所缺乏的一样。因此,既然我已经公开承认这超出了我的知识范围,在这一点上,我只能建议对任何不是由该领域权威人士作出的预测持怀疑态度。即使在这种情况下(如果我能理解的话),我也会建议谨慎行事。或者更好的是:如果预测是由流行病学家做出的,而你是其中一员,那就依靠预测。
包扎
如前所述,如果数据可视化从来都不容易,那么对于像新冠肺炎这样的新现象就更不容易了。因此,当面对任何类型的视觉效果时,我们都应该谨慎行事。如果你正在做(或计划做)任何类型的可视化,问问你自己你想回答什么问题,最好的方法是什么,考虑前面提到的注意事项,并让它们对你的读者显而易见。此外,让你的分析具有可重复性,这样任何人都可以告诉你是否做错了什么,甚至可以自己解决。如果你只是简单地看着它们,去寻找所有那些解释,如果你找不到,去寻求它们,帮助作者或者干脆忽略它,去寻找一个替代方案。但无论如何,你要时刻记住,不要把数据看得太重,也不要太盲目。数据本身并不重要,重要的是我们用它做什么,以及如何做才能获得真正重要的知识。这就是科学发挥作用的时候了。
原载于https://www . carloscamara . es2020 年 4 月 4 日。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
一致值函数定义
从数学上定义价值函数和策略,以及这些定义的一些涌现属性。

安托万·道特里在 Unsplash 上拍摄的照片
在这篇文章中,我将定义强化学习中使用的标准γ-贴现值函数。从这些定义出发,我将讨论值函数的两个重要涌现性质,它们证明了定义的自洽性。我将从数学上构建这些概念,重点是写出推导中的每一步,并讨论每一步的含义。这些方程是 RL 中许多重要数学证明的基础,完全理解它们对于建立 RL 的理论理解是重要的。
价值函数
价值函数是强化学习的核心。对于任何给定的状态,代理可以查询值函数来确定与处于该状态相关联的“值”。我们传统上将“价值”定义为未来获得的回报的总和。因为它依赖于代理人在未来会看到什么样的回报,所以必须为给定的行为策略定义一个价值函数;一项政策。也就是说,状态的值取决于代理在访问该状态后的行为;一个独立于行为的“价值”的讨论是没有意义的。
我们将策略定义为将状态映射到动作概率分布的函数。形式上,

其中𝒮表示代理可以访问的所有可能状态的集合(通常称为“状态空间”),𝒜表示所有可能动作的集合(通常称为“动作空间”),δ(𝒜表示动作集合上的标准单形。标准单纯形只是在动作空间上书写概率分布的一种形式方法。简而言之,策略接受一个状态,并返回代理在该状态下应该采取哪些操作的权重。大的权重导致选择该动作的频率,小的权重导致低的频率。
有了价值函数的直观定义和策略的正式定义,我们可以写出价值函数的正式定义:

请注意,在偶发性问题设置中,我们可以假设终止后所有转换的γ=0,因此这些转换对总和仅贡献一系列零,可以忽略不计。
值得注意的是,上面给出的定义只是价值函数的一个可能的定义。强化学习的大部分理论是围绕上面定义的γ-折扣奖励值函数发展的,这也是我们将使用的定义。然而,也考虑了其他替代方法,尤其是最近,包括平均奖励值函数。

一个简单的三态 MDP。图片作者。
上面定义的价值函数提出了这样一个问题:“假设我们处于某个状态,我们可以期望在未来看到多少回报?”为了使用这个值函数来做出决策,代理可以查询可能的下一个状态的值。例如,假设一个代理处于状态 A,有两个动作分别指向状态 B 和 C,代理可以问 v(B) :“在状态 B 我会看到多少奖励?”或者 v(C) :“在 C 国我会看到多少奖励?”
使用状态值函数来选择动作的困难在于它(a)要求代理提前知道动作的结果(例如,采取“向左”动作导致进入状态 B ),以及(B)要求动作每次都确定性地导致相同的状态。强化学习框架旨在处理非常一般的问题设置,其中(a)和(b)不一定都为真。
为了克服这些挑战,我们引入了状态-动作值函数(一般简称为“动作值函数”);但是我不喜欢这种命名约定)。状态-动作值函数定义为

首先注意(顾名思义)这现在是一个状态和动作的函数,并返回未来奖励的平均值。这个和状态值函数的区别只在于(S,A,R,S,A,R,…)序列的第一个动作。在状态值函数的情况下,第一个动作是根据π(S₀分布的随机变量);然而,在状态-动作值函数的情况下,第一个动作是非随机的并且是给定的。
通过访问状态-动作值函数,代理不需要知道其环境的状态动态。它不需要知道在状态 A 中采取“左”的动作会导致状态 B,状态 B 具有值 v(B) 。相反,代理可以查询其状态-动作值函数,以获得在状态 A 中采取“向左”动作的值, q(A,left) 。正如我们将在下一节中看到的,代理人不再需要访问环境模型来做出决策这一事实是状态-动作值函数的一个定义性特征。直观地,状态-动作值函数隐含地学习环境动态的模型。
贝尔曼一致性方程
v 和 q 都满足一组所谓的贝尔曼一致性方程。也就是说,对于我们的任何状态和行为

其中 r(s,a) 是返回平均一步奖励给定状态 s 和动作 a 的函数。这有点复杂,所以让我们一点一点地来看。
等式(1)简单地说:如果对动作求平均,则状态 s 的状态值函数等于状态 s 的状态-动作值函数。将期望值写成一个和,我们得到

即:在 s 处的状态值函数是状态-动作值函数的所有可能动作的总和,由采取每个动作的概率加权。这是有道理的。两个价值函数对未来奖励有相同的定义,遵循政策π,并对γ折扣奖励进行求和。价值函数仅在第一步有所不同,无论行动是给我们的(状态-行动价值函数),还是我们根据π(状态价值函数)对行动取平均值。
通过访问状态-动作值函数,我们可以通过考虑序列中第一个动作的备选策略来构建任意多个可能的状态值函数。例如,假设代理想要考虑这样一个场景,其中(S,Aμ,R,S,Aπ,R,S,A~π,R,…)第一个动作是从策略μ中选择的,所有未来的动作都是根据策略π选择的。这种策略组合的价值函数可以写成

这种情况发生在策略改进设置中,代理一次只能在一个状态下改进其策略。代理为第一个状态寻找更好的策略,假设它为所有未来状态遵循π,那么根据策略改进定理,我们可以证明这个新的联合策略至少与π一样好,如果不是更好的话。
等式(2)更复杂。让我们从简单的部分开始,这个新函数的定义 r(s,a) ,

这就是我在下一次从状态 s 开始并采取行动a的转变中看到的平均回报
为了理解等式(2)的第二部分,我们先回忆一下价值函数的定义。

其中期望是以 s 和 a、为条件的,但是为了空间和清晰起见,我省略了这个细节。我还将第一次奖励的期望值替换为我们上面计算的平均奖励函数。
现在利用我们从第一个一致性方程(方程 1)中学到的知识,我们可以写出

我们恢复第二个一致性方程,方程(2)。
这个第二一致性方程给了我们一些关于状态-动作值函数和状态值函数之间的区别的直觉。特别是,国家行为价值函数的不同之处仅在于序列的第一个奖励。在状态-行动的情况下,我们考虑由确定性行动 a 选择的奖励,而不管该行动可能来自哪里(它可能被策略π采样,它可能被其他策略采样,等等)。).在状态值案例中,我们考虑平均奖励,其中根据策略π对所有行为取平均值。我们不能考虑作用的其他分布,也不能考虑特定的单个作用(这当然是作用分布的一个特例,所有的概率质量都在一个作用上)。
理解价值函数和策略的定义,以及它们的一些核心涌现性质,对于 RL 证明中更严格的构造是至关重要的。拥有直觉有助于阅读证明和获得“基本想法”,但直觉本身不足以抓住 RL 中的一些微妙之处;因此经常导致不一致的误解或代码中的错误。例如,一个常见的误解是,访问一个国家行动价值函数意味着一个人可以通过对该国家行动价值函数的行动进行预期来计算任何其他政策的值。然而,很明显(希望如此),这种期望仅仅改变了轨迹中的第一步,但是所有以后的步骤都是关于原始策略π的。
约束线性回归
市场组合建模和价格促销优化

里卡多·戈麦斯·安吉尔在 Unsplash 上的照片
当我第一次听说“约束回归”的时候,我很困惑,为什么有人想要约束为你的数据提供最优估计的系数?事实证明,这是市场组合建模和价格促销优化的一个重要方面,尤其是在零售和 CPG 行业。我把这篇文章分成两个部分,这样容易理解。
1.为什么我们需要约束回归?
2.执行约束回归的方法
为什么我们需要约束回归?
对于实际应用,我们转向一个叫做市场组合建模(MMM)的概念。它有助于量化各种营销因素对销售额或销量份额的影响。这些营销因素主要是营销的 4p,即价格、促销、产品和地点。
如果我提高一种产品的价格,我的销售额会有什么变化?如果我开展特定的促销活动,我的销售额会有怎样的变化?事实证明,“价格”和“促销”(持续)很容易量化,而“产品”和“地点”(分类)可能需要混合模型。
以销售为因变量,营销因素为自变量,进行多元线性回归,可以很容易地找到量化各因素影响的系数。
注:在行业中,专业人士喜欢交替使用市场组合模型、媒体组合模型、价格弹性模型等。在所有三个模块中工作过之后,我认为市场组合建模是母模块,媒体组合建模和价格弹性建模是子模块。

如果我们将销售量作为因变量,在将其他因素对销售的影响隔离后,只有营销支出作为自变量,我们通过模型系数估计 ROI,如果我们将销售量作为因变量,将价格作为自变量,在将其他因素对销售的影响隔离后,我们通过模型系数估计价格弹性。
假设我们正在对一些零售数据进行建模,将销售额作为因变量,将其中一种产品的价格作为其他几个变量中的一个自变量。该等式将如下所示,

来源:https://medium.com/@harsha.ft194029 @作者
约束回归的不同方法
二次规划:

https://en.wikipedia.org/wiki/Quadratic_programming
OLS 的成本函数可以很容易地转换成上述方程,因此 QP 可以应用于解决线性等式和不等式约束。
贝叶斯线性回归:
如果我们约束一些系数,这意味着我们对估计有一些先验知识,这就是贝叶斯统计所处理的。我们可以指定估计值的先验分布,并执行贝叶斯回归来获得期望的结果。
上面提到的并不是一个详尽的列表,请在评论区随意添加任何其他方法。
让我们考虑一些假设的销售和促销支出数据,如下所示。

来源:https://www . ka ggle . com/djokester/三年洗发水销售额(已修改)
我们将执行普通的最小二乘法,看看结果是什么。

来源:作者@https://medium.com/@harsha.ft194029
让我们把“花”的系数约束到一个任意的上界,比如说 0.5?

来源:作者@ https://medium.com/@harsha.ft194029
比较以下表格格式的结果,

来源:作者@https://medium.com/@harsha.ft194029
就 r2 而言,约束系数让我们付出了太多。上面用于约束回归的包是为营销组合模型工具定制的库。
如果您有任何疑问/反馈或者您想在数据科学项目上合作,请通过 LinkedIn 联系我
去神秘化的约束优化,用 Python 实现。
设计一种新的鲁棒约束优化算法。

非线性约束优化问题是一类重要的问题,具有广泛的工程和科学应用。在本文中,我们将看到简单无约束优化技术的改造如何导致约束优化问题的混合算法。稍后,我们将通过对一个问题集的详细分析来观察算法的健壮性,并通过将结果与 python 中的一些内置函数进行比较来监控 optima 的性能。
关键词 —约束优化、多变量优化、单变量优化。
许多工程设计和决策问题的目标是优化功能,同时要求满足由于空间、强度或稳定性考虑而产生的一些约束。因此,约束优化是指在某些变量存在约束的情况下,针对这些变量优化目标函数的过程。
具有 N 个变量的约束优化问题由下式给出:

——其中gⱼ(x)为 j 个不等式约束,【hₖ(x)为 k 个等式约束, f(x) 为待优化的目标函数。让我们了解一些优化中常用的术语。
理论
用数学最优化的说法,有两条途径可以找到最优值(数值上):
1。使用直接搜索法:这里只使用给定点的函数值来寻找最优。它的工作原理是比较一个点的邻域中的函数值,然后向导致函数值减少的方向移动(对于最小化问题)。当函数是不连续的,*因此导数在该点不可用时,通常使用直接搜索方法。*
2。使用基于梯度的方法:这里,我们使用一阶和二阶导数来定位最优值。这些方法考虑了梯度信息,因此具有更快地收敛到最优值的优点。**
如何用数值求特定点的导数?我们使用中心差分法,数学上给出为{limit h - > 0}:

我们提出的约束优化算法采用了两种单变量优化方法和一种多变量优化方法。我们的主要目的是将这个多变量约束优化问题转化为一个无约束的多变量优化问题,然后这个无约束问题可以用单变量优化方法来解决。
单变量优化
同样,有两种途径可以找到单个变量的线性或非线性函数的最优解,一种是使用直接搜索方法,另一种是通过基于梯度的技术。人们可以单独使用这两种方法中的任何一种来找到最优解。我们的约束优化算法使用了这两种方法。使用直接搜索方法,我们将包围最优值,一旦我们有了最优值的特定界限,我们就可以使用基于梯度的方法(对于单变量函数)找到精确的最优值。
有许多直接搜索和基于梯度的方法来获得单变量函数的最优值。我们的方法使用边界相位法和割线法。
注意:所有描述的优化方法都是迭代的。经过一系列迭代,我们逐渐收敛到最优值。
边界相位法:一种直接搜索法,用于寻找单变量无约束优化中最小值的上下界。算法给出为(f’指 1ˢᵗ阶在一点的导数):
- 选择初始猜测 x⁽⁰⁾,一个增量δ(~ 0),设置迭代计数器 k=0 。
- 若f(x⁽⁰⁾-δ)>f(x⁽⁰⁾+δ),则δ为正。否则如果,f(x⁽⁰⁾-δ)<f(x⁽⁰⁾+δ),那么,δ就是负。否则转到步骤 1 。
- 设定x⁽ᵏ⁺⁾=x⁽ᵏ⁾+2ᵏδ.(指数扰动)。
- 如果 f(x⁽ᵏ⁺ ⁾) < f(x⁽ᵏ⁾) ,设置 k = k+1 并转到 step_3 。否则,最小值出现在 (x⁽ᵏ⁻ ⁾,x⁽ᵏ⁺ ⁾) 和终止。**
割线法:一种非常流行的基于梯度的单变量优化方法。终止条件是当函数在一点的梯度很小(~0)时。该方法如下:
- 选择两点 a,b 使得f’(a)和f’(b)符号相反。换句话说,f’(a)。f'(b) < 0。选择 ε{epsilon} 一个小数字,又名终止因子。设定 x₁= a 和 x₂ = b 。**
- 计算一个新的点z = x₂-(f'(x₂)(x₂-x₁))/(f'(x₂)-f'(x₁))并找到f’(z)。*
- 如果 |f'(z)| ≤ ε ,终止。* 否则如果 f'(z) < 0,置位 x₁= z 并转到 step_2 ,
 否则如果 f'(z) ≥ 0,置位 x₂ = z 并转到 step_2 。***
其他流行的基于梯度的单变量优化方法有二分法、牛顿-拉普森法等。
单向搜索:这里的目标是找到在特定方向上函数值将最小的地方。数学上,我们需要找到标量α(α)使得, f(α) = f(x⁽ᵗ⁾+α.s⁽ᵗ⁾) 被最小化,这是使用单变量优化方法实现的。{s⁽ᵗ⁾ =搜索方向}。**

注意:许多多变量优化技术不过是连续的单向搜索,沿着特定方向寻找最小点。
可变度量方法(大卫顿-弗莱彻-鲍威尔方法):
DFP 方法是一种基于梯度的多变量优化算法。它通过不考虑用于创建搜索方向的 hessian 而更快地收敛到最优值,从而克服了其他几种多变量优化算法的局限性。搜索方向由下式给出:**

其中矩阵 A 由下式给出:

e(x ⁽ᵏ⁾ ) 代表函数在点 x ⁽ᵏ⁾.的梯度单向搜索涉及割线方法和边界相位方法,以在搜索空间中找到 α 的值。单向搜索后获得的新的点是:

- 选择初始猜测 x⁽⁰⁾ 和终止参数 ε₁、ε₂ 。(注意,这里的 x⁽⁰⁾ 是矢量)。
- 在 x⁽⁰⁾ 找到∇f(x⁽⁰⁾){梯度ft5 }并设置s⁽⁰⁾=-∇f(x⁽⁰⁾){初始搜索方向}。****
- 找到α(α)使得 f(x⁽⁰⁾ + α。s⁽⁰⁾) 最小,终止参数 ε₁ 。用 α*表示。设定 x⁽ ⁾ = x⁽⁰⁾ + α*s⁽⁰⁾ 且 k=1。算算∇f(x⁽⁾。{f(x⁽⁰⁾ + α。s⁽⁰⁾)是 α,的函数,我们将通过单变量优化方法找到这个 α 。***
- 使用eq【1】和 eq【2】中的公式找到 s⁽ᵏ⁾ 。****
- 找到λ⁽ᵏ⁾,让 f(x⁽ᵏ⁾ +λ⁽ᵏ⁾.s⁽ᵏ⁾) 最小,终止因子 ε₁ 。套, x⁽ᵏ⁺ ⁾ = x⁽ᵏ⁾ +λ⁽ᵏ⁾.s⁽ᵏ⁾ 。
- 检查终止条件。||x⁽ᵏ⁺ ⁾ -x⁽ᵏ⁾||/||x⁽ᵏ⁾|| ≤ ε₂吗?
 {||。||表示向量的范数}。
- 如果是,终止;否则设置 k = k+1,转到 step_4。**
罚函数法:
一个罚值(正则项)是我们添加到目标函数中的附加项,它有助于控制目标函数的过度波动。通过添加这些惩罚项,我们将约束问题转化为无约束问题,其结构使得最小化有利于满足约束,如下图所示。**

简而言之,这种技术是在目标函数中增加一项,使得违反约束会产生很高的代价。这就是所谓的罚函数法。数学上,**

其中 R 是罚参数,P(x,R)是罚函数,ω是罚项。根据约束的可行性和类型,有各种类型的惩罚条款。这一项被称为括号操作符罚项。在哪里,**

该方法给出如下:
- 选择初始解 x⁽⁰⁾ 和终止参数 ε₁,惩罚参数 R⁽⁰⁾,和一个更新因子 c 。
- 形成罚函数p(x⁽ᵗ⁾,r⁽ᵗ⁾)=f(x⁽ᵗ⁾)+ω(r⁽ᵗ⁾,g(x⁽ᵗ⁾),h(x⁽ᵗ⁾))* 。***
- 使用 DFP 方法找到 r⁽ᵗ⁾).p(x⁽ᵗ⁾的最小值设解为 x⁽ᵗ⁺ ⁾ 。{这个特殊的步骤是一个无约束优化问题}。对于 DFP,我们最初的猜测是 x⁽ᵗ⁾.**
- 检查终止条件:
 是 |P(x⁽ᵗ⁺ ⁾,R⁽ᵗ⁾) - P(x⁽ᵗ⁾,R⁽ᵗ⁻ ⁾)| ≤ ε₂ ?{|.|是 mod 函数}。
 如果是,设置 xᵀ= x⁽ᵗ⁺ ⁾ 和终止,否则
 转到 step_5。**
- 更新 **R⁽ᵗ⁺ ⁾= cR⁽ᵗ⁾ 。设置,t=t+1,转到 step_2 。***
注意:所有描述的优化方法都是迭代的。经过一系列迭代,我们逐渐收敛到最优值。我们在每次迭代中更新这个 R 值。
使用罚函数法有几个限制。首先,它导致轮廓的变形,由于这种变形,算法需要更长的时间来收敛。而且,这导致了人为的局部最优值的存在。

为了实现的目的,我们将只坚持罚函数法。还有另一种方法称为 乘数法用来克服失真的局限性。它基本上是对罚函数法的一个微小修改。该方法不会扭曲轮廓,而是具有将轮廓移向约束最佳点的效果。所以,这里人为的局部最优值为零。****
乘数的方法和罚函数方法*都将约束优化问题转化为无约束问题,进而可以用任何多变量优化方法求解。*******
嗯,就这样吧!!!如果你已经走了这么远,太好了!现在,让我们看看我们的方法的流程图,然后去实现。为了便于实现,我们将只讨论罚函数法。

Himmelblau 函数:
为了说明我们的方法,我们将使用著名的 Himmelblau 函数(见图),给出为 f(x,y)=(x+y11)+(x+y7)。

让我们使用我们提出的算法来解决下面的约束优化问题。

履行
DFP 方法用于多变量优化,结合边界相位和割线方法用于获得单向搜索。代码用 python 实现,托管在我的 GitHub repo 中。我们现在简要演示所使用的每个函数:**
multi_f: 该函数取一个输入向量 x* (搜索空间中的一个点)并返回该点的函数值(惩罚函数值)。***
grad _ multi _ f:该函数使用中心差分方法计算搜索空间中特定点的梯度向量。**
包围 _: 该函数实现了用于包围 α (通过执行单向搜索获得的最小值)的边界相位方法。它取一个矢量 x 和矢量 s (搜索方向)并输出一个区间,基于该区间可以对 α 进行评估。*
f _dash: 该函数用于使用中心差分法得到单变量函数的一阶微分。(代表f’)。
割线 _ 最小值: 该函数将从边界相位方法中找到的边界、一个点 x 和一个搜索方向 s 作为输入,并评估 alphastar。
compute_z: 该函数用于计算割线法中使用的公式:

DFP: 它只取输入向量 x 作为自变量,返回解向量。在主函数内调用该函数,直到满足罚函数法的终止条件。**
它首先从输入向量【x,*** 中找到一个搜索方向 s ,然后通过调用边界相位和割线方法执行单向搜索,以找到最优的 α 。然后,它通过评估等式(1)和(2)找到新的搜索方向。该过程继续进行,直到满足 DFP 的终止条件,并且我们已经为该特定序列找到了该无约束问题的最优解。***
结果
完整的代码在我的 Github repo 上。注意,我们的方法是一般化的,并且适用于人们想要工作的任何数量的维度。
我们算法的参数设置是:
*** m = 2,
*** R=0.1* {panalty 参数},
*** c = 1.55{用于更新 r 的因子},
*** x_ip (初始猜测)= (0.11,0.1)ᵀ 。***
我建议读者尝试使用不同的初始猜测,并使用这些参数值。因此,我们的算法在 14 个序列后收敛。我们得到了约束优化问题的最优解。

让我们将我们的结果与从 python 中 scipy 库的 优化 模块中找到的结果进行比较。{使用相同的初始猜测}:

结果非常接近,为了得到更接近的结果,我们可以尝试非常小的终止因子。我还尝试了一系列不同的初始猜测。是的,它们都融合了!!

“过早优化是万恶之源” —唐纳德·克努特(Donald Knuth)。
我希望你和我一起享受约束优化的过程。我很想知道任何阅读这篇文章的人的反馈(鼓掌👏🏼也会得到很好的反馈😇).我很乐意回答对上述任何概念的疑问/问题。你可以通过 Linkedin 联系我。
最后,特别感谢迪帕克·夏尔马教授,IIT·古瓦哈蒂,他教了我优化课程,作为我课程的一部分。
谢谢大家!!
Oracle 中的约束
保证数据完整性的方法

任何数据库管理系统的核心功能之一是确保数据在其生命周期中的完整性。数据完整性,简单来说,就是随着时间的推移,数据应该保持【准确】 。不一致或损坏的数据对企业来说用处有限或毫无用处,在某些情况下甚至会造成威胁。损害数据完整性的方式有多种,例如在复制或传输过程中、来自多个来源的集成、人为数据输入错误等
在 Oracle 中,“约束条件”是一种实施规则的工具,以确保只有允许的数据值存储在数据库中。顾名思义,约束对可以存储在数据库表中的数据的类型或值进行限制/检查。Oracle 提供了以下约束来加强数据完整性:
- NOT NULL: 如果根据业务逻辑,表中的一列或一组列不允许空值,那么可以使用 NOT NULL 约束来防止这种情况。
***alter table SALES modify (cust_id NOT NULL);***
如果在销售表的 cust_id 列中输入空值,数据库将抛出错误
2。UNIQUE: 如果根据业务逻辑,一个表中的一列或一组列需要存储唯一的值,那么可以使用 UNIQUE 约束来实施这个规则。
***create table test (col1 number UNIQUE);***
唯一约束允许存储空值。
3。主键:主键约束是非空约束和唯一约束的组合。定义主键的一列或一组列只允许唯一值,不能为空值。
***create table test (col1 number PRIMARY KEY);***
一个 Oracle 表中只能有一个(且只能有一个)主键。
4。外键:经常需要将一个表中的数据与另一个表中的数据进行比较来验证。(例如,如果您在订单表中添加一个新订单,您必须交叉检查对应于该订单的有效产品是否出现在您的产品表中)。为了实现这种数据完整性,使用了外键约束。这种类型的验证也被称为。外键约束总是引用其他表的主键或唯一约束。定义了外键的表称为引用表的。定义了主键或唯一约束的表称为引用表**。****
****create table orders
(
 order_no number primary key,
 customer_name varchar2(10),
 constraint cons_prod_fk foreign key(prod_no) references product(prod_no)
);****
如果使用' ON DELETE CASCADE '选项定义外键约束,则当从被引用表中删除任何行时,相应的行也将从引用表中删除。
外键列中允许空值。
5。CHECK: Check 约束用于强制数据行的一个或多个检查条件。
****alter table customers add constraint customer_credit_limit CHECK (credit_limit <= 1000)****
这将确保 credit_limit 始终小于或等于 1000。否则,系统将引发错误

在 Oracle 中使用约束时,以下信息会非常有用:
- 约束名称存储在 ALL_CONSTRAINTS 表中。定义约束的列名可以在 ALL _ CONS _ 列中找到。
- 可以随时启用或禁用约束。创建、禁用或启用约束时,可以指定一些有关约束行为的其他信息。启用的约束可以有两个选项 VALIDATE 和 NOVALIDATE。VALIDATE 将验证表中的现有数据,而 NOVALIDATE 在启用约束后不会验证现有数据。
- 当您创建或启用主键或唯一约束时,Oracle 将在该约束的列上创建唯一索引。外键约束不强制自动创建索引。但是,在每个外键约束的列上建立索引是值得的。如果子表中的相应列没有索引,Oracle 在对父表执行删除操作时,将被迫对子表解除表锁。如果存在索引,Oracle 使用它来标识和锁定子代中的必要行,同时删除父代行。
数据完整性不应与数据安全性混淆。数据安全性涉及保护数据免受未经授权的访问,而数据完整性指的是存储数据的准确性和一致性。
数据完整性是衡量数字信息健康程度的标准。在数据管道中尽可能早地实施这些规则或检查,将导致最少的损坏,并提高业务流程的效率。
下面这段话通常适用于数据库设计和生活。
"诚信,在方便和正确之间的选择!"
构建决策树以及如何处理过度拟合

介绍
决策树是一种用于监督学习的算法。它使用树结构,其中有两种类型的节点:决策节点和叶节点。决策节点通过对某个特征提出布尔问题,将数据分成两个分支。一个叶节点代表一个类。训练过程是关于在具有特定值的特定特征上找到“最佳”分割。预测过程是通过在路径上的每个决策节点回答问题从根节点到达叶节点。
基尼杂质与熵
术语“最佳”分裂是指在分裂之后,两个分支比任何其他可能的分裂更“有序”。我们如何定义更有序?这取决于我们选择哪个指标。一般来说,有两种类型的指标:基尼系数和熵。这些指标越小,数据集就越“有序”。
这两个指标之间的差异非常微妙。要了解更多,你可以阅读这篇帖子。在大多数应用中,这两种度量的表现相似。下面是计算每个指标的代码。
建造这棵树
培训过程实质上就是构建树。关键的一步是确定“最佳”分割。过程如下:我们尝试在每个特征的每个唯一值处分割数据,并选择产生最少无序的最佳值。现在让我们把这个过程翻译成 Python 代码。
在构建树之前,让我们定义决策节点和叶节点。决策节点指定将根据其进行分割的要素和值。它还指向它的左右子节点。叶节点包括一个类似于Counter对象的字典,显示每个类有多少训练样本。这对于计算训练的准确度是有用的(尽管这不是必需的,因为我们可以在模型被训练后通过预测每个例子来获得准确度)。此外,它还会导致到达该叶子的每个示例的结果预测。
给定它的结构,通过递归构造树是最方便的。递归的出口是一个叶节点。当我们无法通过拆分来提高数据的纯度时,就会出现这种情况。如果我们能找到一个“最佳”的分割,这就变成了一个决策节点。接下来,我们对它的左右孩子递归地做同样的事情。
预言;预测;预告
现在我们可以通过遍历树直到一个叶节点来预测一个例子。
事实证明,训练准确率为 100%,决策边界看起来很奇怪!显然,模型过度拟合了训练数据。好吧,如果你想一想,如果我们继续分裂直到数据集不能更纯,决策树将会过度拟合数据。换句话说,如果我们不停止分裂,模型将正确地分类每一个例子!训练准确率是 100%(除非有完全相同特征的不同类的例子),没有意外。

如何应对过度拟合?
从上一节,我们知道了决策树过度拟合的幕后原因。为了防止过度拟合,有两种方法:1 .我们在某个点停止分裂树。2.我们先生成一棵完整的树,然后去掉一些分支。我将用第一种方法作为例子。为了更早的停止分裂,我们需要引入两个超参数进行训练。它们是:树的最大深度和叶子的最小尺寸。让我们重写树构建部分。
现在,我们可以重新训练数据并绘制决策边界。

想象这棵树
接下来,我们将通过打印节点来可视化决策树。节点的缩进与其深度成比例。
|---feature_1 <= 1.87
|   |---feature_1 <= -0.74
|   |   |---feature_1 <= -1.79
|   |   |   |---feature_1 <= -2.1
|   |   |   |   |---Class: 2
|   |   |   |---feature_1 > -2.1
|   |   |   |   |---Class: 2
|   |   |---feature_1 > -1.79
|   |   |   |---feature_0 <= 1.62
|   |   |   |   |---feature_0 <= -1.31
|   |   |   |   |   |---Class: 2
|   |   |   |   |---feature_0 > -1.31
|   |   |   |   |   |---feature_1 <= -1.49
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |   |---feature_1 > -1.49
|   |   |   |   |   |   |---Class: 1
|   |   |   |---feature_0 > 1.62
|   |   |   |   |---Class: 2
|   |---feature_1 > -0.74
|   |   |---feature_1 <= 0.76
|   |   |   |---feature_0 <= 0.89
|   |   |   |   |---feature_0 <= -0.86
|   |   |   |   |   |---feature_0 <= -2.24
|   |   |   |   |   |   |---Class: 2
|   |   |   |   |   |---feature_0 > -2.24
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |---feature_0 > -0.86
|   |   |   |   |   |---Class: 0
|   |   |   |---feature_0 > 0.89
|   |   |   |   |---feature_0 <= 2.13
|   |   |   |   |   |---Class: 1
|   |   |   |   |---feature_0 > 2.13
|   |   |   |   |   |---Class: 2
|   |   |---feature_1 > 0.76
|   |   |   |---feature_0 <= -1.6
|   |   |   |   |---Class: 2
|   |   |   |---feature_0 > -1.6
|   |   |   |   |---feature_0 <= 1.35
|   |   |   |   |   |---feature_1 <= 1.66
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |   |---feature_1 > 1.66
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |---feature_0 > 1.35
|   |   |   |   |   |---Class: 2
|---feature_1 > 1.87
|   |---Class: 2
非线性特征
上面的例子清楚地显示了决策树的一个特征:决策边界在特征空间中是线性的。虽然该树能够对不可线性分离的数据集进行分类,但它严重依赖于训练数据的质量,并且其准确性在决策边界附近会降低。解决这个缺点的一个方法是特征工程。类似于逻辑回归中的例子,我们可以扩展特征以包括非线性项。如果我们添加像【x1 x2】x1和 x2 这样的术语,这里有一个例子。

结论
与其他回归模型不同,决策树不使用正则化来对抗过度拟合。相反,它采用了树木修剪。选择正确的超参数(树深度和叶子大小)也需要实验,例如使用超参数矩阵进行交叉验证。
关于完整的工作流程,包括数据生成和绘制决策边界,您可以访问 my github 。
构建强化学习政策的轴心
向研究界最不透明的框架迈了一小步。
强化学习(RL)是一种与环境交互的框架,用于学习策略以采取行动并实现某些目标,在许多领域都有所发展。有些领域是有意义的,有些永远不会起作用,而大多数注定会落在中间的某个地方。RL 之所以如此受欢迎,是因为它的优雅: 我们知道,大多数生物都是通过与它们的环境 互动来学习的(但是,我们应该注意到,进化实际上是对这些我们不知道如何建模的行为的强先验)。问题是 RL 在个人和群体层面上对我们的社会产生了有害的影响(见我写的关于推荐系统)——这不是一篇描述这些问题的文章,而是在取笑我们如何以更精确的方式解决它们。
然而,推荐系统极大地提醒了我们,将社会建模为强化学习问题是多么困难(有时甚至是倒退)。从工程师的角度来看,推荐系统是一个 RL 循环,其中消费者(我们)是环境,应用程序是代理,金钱是回报。令人不安的是,科技公司将探索你的内在工作方式,作为他们研究的环境。工程问题最终变成了设计师/用户和代理人之间的游戏——RL 传统上对行为的调整和突然、意想不到的变化非常敏感。在列举了几个现实世界的 RL 的例子之后,我提出了三个初始轴来开始关于 RL 策略的讨论:
- 模拟目标动态的能力,
- 目标动力学抽象的封闭性,
- 关于目标动态的现有规定。
我参加了一个阅读小组,该小组由伯克利的毕业生组成,他们在计算和工程领域获得了参与和扩展奖学金(googes)和长期网络安全中心( CLTC ),希望最终能就这个主题发表一份白皮书(感谢合作者)。

照片由Kendall Hoopes从 Pexels
真实世界 RL:现在和未来
RL 正以积极的方式在现实世界中使用,我乐观地认为它将在比最初预期更多的领域发挥作用。这里有几个例子可以让你开始思考:
示例:电网
电力系统已经被证明是非常易于强化学习的。它可以用于管理网络负载,计划不同工厂的生产,等等。许多论文利用动力学和故障保险的精确模拟,以便学习过程不会导致任何奇怪的行为。在电气工程中研究功率动力学的历史为新的基于学习的技术提供了明确的界限和故障保险。电网背后的这些主题就是为什么一些大型技术公司已经将 RL 应用于数据中心运营的优化。该系统在其抽象中是封闭的,具有精确的建模动态和合理的建模目标。【参见 1 、 2 、 3
示例:自动交易
自动化交易可能是 RL 最符合逻辑的问题空间——奖励功能就是赚钱,不停赚钱。行动是购买和销售,但如何在底层环境上放置一个边界框—它不仅仅是将资金转移到所有最终用户的银行账户。另一个问题是,行动空间是如此多样和难以约束(约束是由人类过去犯下的错误设定的,以及一闪而过的崩溃),动态非常未知。不确定性并没有阻止人们在这个领域赚很多钱,但我不认为我们已经结束了股票市场似乎脱离现实的闪电崩盘和其他时期。这里有一篇关于领域的好博文。我认为人们将继续在这一领域赚钱,除非真的出现问题,否则我们不会看到太多的监管,货币市场到最终用户的动态将无法捕捉(人们不总是喜欢谈论他们的钱在哪里)。
例如:医疗
用于医疗的 RL 很可能看起来像一台计算机,它计算出如何制造新药或管理以前未研究过的组合(在离线时尚中听起来不错),但它也可以采取多种形式,并故意模糊(突出挑战)。当前的医疗政策可以为未来的数据驱动的医疗政策提供信息,但这可能过于乐观了。具体来说,我提出医学治疗,因为 RL 在医学之外的应用领域有很多东西需要学习。医学临床试验将任何新方法与当前的护理标准进行比较。在 RL 中实现这一标准将是从当前以人为中心的医疗系统(或其他流程)到数据驱动系统的转变不会降低个人层面的性能(即使给某人送一台计算机更便宜)。这种性能比较应该在不考虑可及性的情况下进行(人类医生更昂贵,因为他们稀缺),并防止过早采用计算机医生。如果你对数据医学是否准备好了感兴趣,这篇博客调查了Reddit-corpus GPT 3 对医学思想的直觉,结果并不理想。
RL 策略的轴
这是我第一次草拟 RL 政策。这样的政策不应该禁止在这些轴之一的极端区域研究系统中的 RL,但是它应该要求额外的监督或更好的数据实践。减缓 RL 反馈循环的一个令人兴奋的领域是将一些研究领域限制为离线 RL——将记录的数据提取到策略中并偶尔更新而不是持续更新的过程。
轴 1:对目标代理建模的能力
考虑从电力系统到医疗的不同:我们知道麦克斯韦方程以及它们在工程系统中如何工作,但我们不知道人体(相对而言)。对电力系统背后的物理进行建模,让机器学习工程师受到物理现实的限制——我们知道何时某些动作会关闭电源,并且它们会从动作空间中移除(尽管这确实会使学习问题稍微困难一些)。
这个界限确保我们不会对我们所控制的系统的底层造成伤害。
轴 2:目标端点处抽象的准确性
我对现实世界中的强化学习最大的不满是,当目标终端用户,例如社交网络中的新闻源,显然不仅仅是单个用户。这个轴看起来和上面很像,但是我故意把它分开。例如,运行成本更高的数据中心和电网的潜在危害是非常不同的(尽管我们不一定知道谷歌倒闭实际上会造成多大的危害)。这条轴线还可以进一步细化,但我认为一些尺度感是重要的。我们可以将 RL 应用于我们的智能冰箱,但可能不会应用于食品配送基础设施。
这个框让我们考虑控制算法界限之外的潜在损害的规模。
轴 3:脚手架的现有规定
这个医学例子强调了现有的规范性行为和法规帮助定义问题空间的潜力。电力系统在这一点上处于中间位置:作为一家公用事业公司有助于约束一些行为。其他例子就更离谱了。RL 政策和其他技术诉讼应该依靠这个现有的脚手架。我知道食品药品监督管理局(FDA)和其他政府机构是通过修补历史错误而建立的,我认为这是一个难以前进的趋势,但我支持建立一个数据和算法管理局。
这个轴让我们记住,当环境和代理是相对自由的系统(咳咳,社交媒体)时,应用 RL 会有更多的潜在危害。
示例:运输
谷歌地图算法可能会被 RL 改进(也许他们是?让我知道)。运输和路线有很好的抽象、奖励和动态。首先,考虑一下医药与交通:医药是一个更受监管、更正式的规范结构。人们很高兴将 RL 应用于交通运输——交通运输对于提取奖励功能来说已经成熟,并且比药物等具有更规范内容的东西更容易。规范性内容与人们想要保持“人性”的强烈程度相关。交通是一个例子,其中大规模的 RL 可以转化为复杂的多智能体交互和意外问题(例如,自动驾驶汽车使行人无法使用通常不行驶的道路)。
运输我会把安全放在轴 1 上(本质上是遍历一个图),中等放在轴 2 上(不确定自主路由会如何进行,因为它是一个如此庞大的系统),安全放在轴 3 上(我们已经有很多汽车要遵循的规则)。
大规模 RL 成为多代理 RL
多主体强化学习(MARL)可以以多种方式定义,但它是带有个体微调的大规模强化学习的发展方向。讨论一些记录在案的 MARL 关键挑战是很有意义的(参见概述):
- 非唯一学习目标:每个智能体都有不同的目标,我们如何优化全局?
- 非平稳性:数据分布在每一步都会发生难以置信的变化,因为个体不再控制一切。
- 可扩展性问题:如果智能体考虑到附近每个智能体的行为,复杂性会呈指数级增长。
- 变化的信息结构:代理和环境数据在代理之间是异构的(我认为这是最接近解决的一个)。
我有兴趣通过合作与竞争游戏(只有一些是零和游戏)、顺序与并行 MDP、集中与去集中控制等等来阐述我对社交媒体世界的看法。从研究的角度来看,我对 MARL 越来越感兴趣,因为这是大多数基于应用的算法正在对我们做的事情。MARL 目前的共识是,几乎任何复杂的奖励函数都是难以处理的,所以这是所有科技公司决定进行的实验!
最后那句话是故意很多,为了某种戏剧而翻转了主宾顺序。但是,看起来确实是这样的:科技公司每天决定着我们越来越多的互动,尽管它们在优化利润,但每个个体代理人(我们)都有我们的奖励功能。下游效应正在显现。
我相信我会在未来更多地触及这一点,就像我过去在推荐系统的帖子中所做的那样。这个选举季更加强调我在社区和公益建筑技术方面的工作需求,也许我应该从改善心理健康和长期导向的社交网络开始。
我们都需要更多的正能量和更多的讨论。
这是我关于机器人学&自动化、 民主化自动化 的免费时事通讯的一个高峰。
一个关于机器人和人工智能的博客,让它们对每个人都有益,以及即将到来的自动化浪潮…
robotic.substack.com](https://robotic.substack.com/)
构建协整的加密货币投资组合
加密货币
利用协整检验构建加密投资组合

像水一样,隐花倾向于一起移动,但要小心湍流!里奥·霍奇斯在 Unsplash 上的照片
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
2009 年,比特币(BTC)问世,成为第一种去中心化的数字货币。从那时起,比特币已经被成千上万的商家和供应商接受,加密货币市场也出现了巨大的增长。如今,除了比特币之外,还有一千多种加密货币,如以太坊(ETH)、莱特币(LTC)、比特币现金(BCH),以及更多正在国际主要加密货币交易所交易的货币。
多年来,由于各种原因,比特币的许多替代品被发明出来。例如,以太坊是一个运行智能合约的去中心化平台,它的创建是为了解决应用程序开发缺乏比特币脚本语言的问题。作为另一个例子,比特币现金是为了解决比特币的可扩展性问题而发明的,因为它的块大小限制了比特币网络在给定时间框架内可以处理的交易量,等等。这种创新导致了许多替代硬币的快速增长,以及加密货币市场规模的上升。尽管如此,比特币在交易量、价格和市值方面一直名列前茅。
比特币和许多替代币的兴起为投资者和投机者创造了交易机会。事实上,研究表明,加密货币指数(CRIX)和传统资产之间的相关性非常低。此外,实证结果表明,将 CRIX 纳入仅包含传统资产的初始投资组合可以扩大有效边界,并为投资者提供额外的效用。
回想一下,标准普尔 500 在 2017 年的回报率约为 21%,但比特币在一年内的回报率达到了惊人的 1318%。2017 年 12 月中旬,比特币价格达到历史最高水平,超过 1.9 万美元,24 小时交易量约为 230 亿美元。然而,随着价格在 2 月初跌至 6000 美元的低点。比特币曾被认为是最大的泡沫之一,在新冠肺炎疫情期间飙升,并像黄金一样被更广泛地接受为通胀对冲工具。
许多加密交易所允许投资者直接从他们的银行账户购买硬币,然后将硬币转移到不同的数字钱包和交易所进行交易。一些交易所甚至允许卖空加密货币。
与传统股票市场不同,大多数加密货币高度相关。例如,4 种数字货币(即 BTC、瑞士法郎、长期货币和 BCH)之间的日收益率相关性超过 75%。加密货币的高波动性要求开发市场中性的交易策略,它们的高相关性促使我们研究协整或均值回归策略。

密码之间日收益率的相关性。
寻求从价差的均值回复特性理解不同资产价格和套利的长期共同运动的学术研究者和实践者已经广泛地研究和学习了均值回复交易策略。
传统金融的许多领域都制定了策略,但对交易对或一篮子加密货币的研究却很少。事实上,大多数关于交易加密货币的研究都是基于技术分析,或者从交易所之间的价格差异中套利。
通过一系列统计测试,我们创建了一个可交易的投资组合,包括比特币(BTC)、以太坊(ETH)、比特币现金(BCH)和莱特币(LTC),具有快速的均值回复价格运动。
在我们最近的研究( Nguyen 和 Leung,2019 )中,我们分析了构建一组共同整合的加密货币的过程。这个过程涉及一系列的统计检验,包括(i) Johansen 协整检验,和(ii)Engle 和 Granger 开发的经典两步方法。
在我们的结果中,我们构建了涉及四种加密货币的协整投资组合:比特币(BTC)、以太坊(ETH)、比特币现金(BCH)和莱特币(LTC)。协整检验给出了一个可交易的投资组合,因为快速均值回复利差会经常跨越进场和出场水平。该策略基于交易的价差形式:
利差= BTC- aETH- bLTC- c*BCH,
有趣的是,这两个协整检验实际上产生了不同的均值回复投资组合。

价差由两种方法构成:(左)Johansen 方法,(右)Engle-Granger 方法
从上图可以看出,很难看出哪个利差看起来更稳定或均值回复。然而,从交易的角度来看,导致频繁 从两个方向跨越均衡水平的周期性运动或波动是可取的,特别是考虑到进入和退出规则是基于对平均价格的偏离。
由于这个原因,恩格尔-格兰杰方法在这个例子中更适合交易,因此被用来对我们的交易系统进行回溯测试。我们还设置了进出价格阈值,以均值的标准差来衡量。为了减轻更高提款的影响,我们还增加了 5%的止损单。

交易系统有 5%的止损,设置进场/出场水平为均值的负/正 1 个标准差。从上到下,我们绘制了价差、位置(-1,0,+1;蓝色)、累积 PnL(绿色)和下降(红色)。
我们发现设置更高的进场和出场水平会带来更高的利润(见下表)。与带止损出场或跟踪止损的交易系统相比,不带止损出场或跟踪止损的交易系统利润最高,但亏损也最大。除了建立这种新的加密投资组合,我们的方法可以更广泛地应用于使用其他加密货币创建新的共同整合投资组合。

测试期间跟踪止损 5%的交易策略统计。贸易统计词汇如下。

净值曲线:(上)没有止损/跟踪止损,(中)有 5%的跟踪止损,(下)有 5%的止损。
随着加密货币市场随着新硬币和新交易所的不断增长,个人投资者、加密基金经理和监管机构了解所有加密货币及其衍生品之间的价格依赖性非常重要。未来的研究有很多方向。我们的一个项目涉及开发一种机器学习方法,从大量加密货币中自动选择一个小的投资组合。
点击此处对上述综合加密组合的全面分析(免费 pdf)。

贸易统计解释
参考
H.Nguyen 和 T. Leung (2019),构建用于统计套利的协整加密货币投资组合,经济学和金融学研究,第 36 卷第 3 期,第 581–599 页
T.Leung T .和 X. Li (2016),最优均值回复交易:数学分析和实际应用,世界科学图书,
J.张,A. Aravkin,和 T. Leung (2020),通过惩罚似然优化的稀疏均值回复投资组合,自动化,第 111 卷,108651。https://doi.org/10.1016/j.automatica.2019.108651
T.Leung T .和 X. Li (2015),【有交易费用的最优均值回复交易&止损退出,国际理论杂志&应用金融,第 18 卷,第 3 期,第 1550020 页。
了解更多关于华盛顿大学的 计算金融&风险管理 项目
利用数字信号处理和机器学习构建、操作、分类和生成音频

作者图片
"我们生活在一个信息越来越多,而意义越来越少的世界里."
“所以艺术无处不在,因为技巧是现实的核心。所以艺术死了,不仅因为它的批判性超越性消失了,还因为现实本身,完全被一种与其自身结构不可分割的美学所浸透,已经与它自己的形象混淆了。现实不再有时间呈现现实的外表。它甚至不再超越小说:它捕捉了每一个梦,甚至在它呈现出梦的外观之前。”
写于 1981 年,这两段引文都来自让·波德里亚的模拟和拟像。相邻的是超现实的观念;定义为:
超现实,在符号学和后现代主义中,是意识无法区分现实和对现实的模拟,尤其是在技术先进的后现代社会中
如果你在谷歌上搜索什么是音频?— 你会得到几个结果。
- 它源于拉丁语:' audire '。意思是:听到
- 直到 19 世纪末 20 世纪初,这个词才在英语语料库中广泛使用。在 1880 年之前开始有一个轻微的上升趋势,然后在 1900 年到 1910 年之间突然上升。

谷歌图书 ngram 浏览器,word: 音频1800–2019
1857 年 3 月,爱德华·莱昂·斯科特·德·马丁维尔在法国首次为唱机签名申请专利。这是已知的最早的录音设备。它将声波描绘成被烟熏黑的纸张或玻璃上的线条。这种表现是声波在空气中传播的物理现象的直接反应。最初用于研究声学,它不是一种将声音作为媒介传播的方法,但在某些情况下,它被用于检测音乐音高,方法是记录一个人演奏乐器或唱歌的音高,然后将该转录与另一个音叉转录进行比较。使用表示对表示进行分类。

É.-L. Scott,1857 年 3 月 25 日的“第 31470 号专利申请”。6 页,包括证书和一张亲笔签名| 来源
下面是 1860 年 4 月 9 日用留声机录制的一段录音。据信是斯科特本人在唱一首法国民歌:“ Au clair de la lune
1860 年 4 月 9 日的录音
一个有趣的提示:当斯科特录制这些录音时,据说他并不想让任何人听到,而只是想做一个分析工具。直到 1877 年,有人认为声音可以通过再现来再现。
在 2008 年之前,Scott 的录音还没有被听到,最古老的录音是 Edison 的留声机——黄色石蜡缸在 1888 年 6 月 29 日的音乐会上,4000 个声音在英国伦敦的 Handel Festival 上表演了 Israel in Egypt 。
然而,第一个入耳式耳机是在 19 世纪 50 年代发明的。医用听诊器。第一个听诊器于 1816 年问世。那时候它还不是我们现在知道的入耳式设备。这是一种放大胸腔声音的工具。1816 年的听诊器不是用电,而是医生放在病人胸部的纸漏斗。1850 年的听诊器没有太大的不同,但作为双耳装置变得更加精致;允许来自病人胸腔的声音直接进入医生的双耳。
1895 年,我们首次使用无线电信号进行传输。虽然,第一次传输是字母“S”的电报。所以,不是真的处理声音。但是在 1910 年,纳撒尼尔·鲍德温发明了我们现在所知的现代耳机。最初是为了海军作为一种通信方式使用,它被设计成包含一英里长的连接到操作员头带的铜线。这允许模拟信号通过电在铜中传播。
当你搜索“什么是音频”时,你会发现第三个结果是这个定义:
音频信号是声音的表示,通常使用电压电平表示模拟信号,使用一系列二进制数表示数字信号。音频信号具有大约 20 到 20,000 Hz 的音频范围内的频率,这对应于人类听觉的下限和上限。
就本文而言,让我们来探索数字音频和机器用来表现声音的方法。
如何用数字信号处理模拟声音?
像模拟信号一样,数字信号也是声音的表现形式。允许机器再现的数字信号有两个主要方面。这两个方面可以认为是 X 和 Y;时间和幅度分别为:采样率和位深。
抽样率
如果您曾经打开过数字音频工作区(DAW ),或者检查过音频文件,您会对数字 44100 很熟悉。如果您还没有,44100Hz 是音频数字渲染的标准采样率。有不同的费率,如 88200,96000 和 196000,以及。但是 44100 是一个特定的数字,是信号采样的最低阈值。
采样率是指机器在录音时每秒钟分析输入声音或模拟信号的次数。网上定义为:空间频率 T5数字 采样 。相邻像素间中心距的倒数。这意味着,对于每个样本 (1/44100 秒),机器记录信号,并以一个数字表示每个箱。音频也可以用这些原则来构建。**

低到高(从左到右)采样率对呈现数字表示的影响的图示| 来源
在上图中,您可以看到,在低采样速率下,这种表示会丢失信号中的许多信息;并且随着采样率的增加,产生更精确的表示。将其分解:将声音或模拟信号转换为数字信号的任务是获取连续信号并将其转换为离散值,当随着时间的推移将这些离散值放在一起时,呈现连续信号的表示,然后可以转换回来。基本上,采样率越高,机器可以渲染得越准确和精确。但是为什么是 44100Hz 呢?
奈奎斯特采样定理陈述:要达到最准确呈现某一频率信号的采样率,采样率必须是频率的两倍。
Sampling_Rate_Needed = frequency * 2
因此,如果频率为 440Hz,那么最精确地呈现频率的数字表示所需的采样速率应为 880Hz。据此推断,人类的听觉范围上限约为 20,000 赫兹。因此,为了准确地呈现人类可以听到的任何声音的数字表示,我们至少需要 40,000Hz 的采样率。似乎为了安全起见,增加了 4100 赫兹。额外的采样速率用于特定情况,如环绕声、电影音频、音乐制作和其他需要更精确处理渲染信号的高保真体验。但是为了准确地表现人类能听到的任何东西,44100 赫兹是必要的频率。
钻头深度
位深度类似于采样率,因为它是一种分辨率。但是它不是沿着时间渲染,而是沿着振幅渲染。低分辨率位深度 1 只能将振幅渲染为两种动态:开或关。声音或寂静。类似于黑白图像。

不同位深度量化对呈现数字表示的影响的图示| 来源
您可以看到,随着位深度的增加,机器可以使用更多的离散值来呈现幅度。我们可以用以下公式计算每比特深度的离散值数量:
2^bit_depth
对于 8 位的深度,有 256 个离散值的范围(0–255)。对于 16 位,有 65536 个离散值的范围。对于 24 位,有 16777216 个离散值的范围。概念化该离散值范围的另一种方式是:机器渲染给定样本体积可用的振幅的可能动态范围。使用下面的公式,我们可以理解以分贝为单位的比特深度的动态范围。
n = bit_depth
20 * log10(2^n) = db
位深度为 8 时,动态范围为 48db。然而,比特深度为 16 时,我们有 96db 当我们考虑抖动因素时,我们可以达到 115db。对于一个人来说,140 分贝是我们的耳朵受到痛苦伤害之前的极限。因此,在渲染声音时,115db 的动态范围足以满足大多数制作需求。

声音信号被表示成离散数字数组的基本概述|原始 Aquegg |维基共享
上图显示信号为一条红线,由机器用蓝点进行分析,并用数字渲染模拟为右边的阵列。假设该图为一秒钟,则采样率为 21Hz,位深度为 4 位。不确定这听起来会不会太好。
我们听的大多数音乐都是 44.1KHz,16 位深度。因此,下一次您收听来自数字源的音频时,请尝试想象频率(每秒 44100 个样本)和振幅 (65536 级动态范围)上的所有离散值,这些值紧密地呈现在一个复合信号中,模拟了收听连续声波的体验。
构建数字音频
工程频率
在我的上一篇文章发音 PI 中,我构建了一个基于频率 3.14Hz 的泛音序列,并从该序列生成音频文件。在这里,我将更深入地介绍我为实现这一目标而构建的模块的功能。此外,我将解释如何构建西方音乐体系以及更复杂、独特的音乐体系。
当我们思考什么是音高时,我们经常会想到它们的字母名称。A,A#,G,G#' …等等。但这些只是帮助我们理解音乐作为一种语言,并象征性地通过声波景观导航的抽象表示。“a”代表一类频率。为了引用“A”的更具体的实例,我们可以调用音高以及与其在序列中的位置相关联的八度音程数。这叫做科学记数法。“A”最常见的例子是“A4”。这个特定实例表示频率 440Hz,并且位于第四个八度音阶的位置。目前的调谐标准是基于 440 赫兹。每个八度音程位置正好是它上面八度音程的 1/2 和它下面八度音程的两倍。例如,“A5”代表频率 880 赫兹,“A3”代表频率 220 赫兹。在每个八度之间,有一个西方体系中所有十二个音符的实例。两个频率之间的最小间隔称为小秒。小秒针也是一种象征性的抽象概念,在西方语境中具有质量和定义的内涵;而仅仅是两个频率之间的数值距离的表示。有很多方法可以评估这个距离。
在下面的笔记本中有一些例子,它们是发展西方十二平均律音乐体系中使用的八度音阶所必需的数学关系。
分解西方音乐体系中音程的数学因素的笔记本
这本笔记本的目的是揭露一些简单的事实。
- 八度音程是 1 和 2 之间的空间的表示。
- 分八度就是把 1 和 2 之间的空间分成相等的音程。
- 等同于这些划分的音程由抽象的特征表示,并且对于它们所属的八度音阶是唯一的,但是共享共同的因子分解。
但是让我们用这个逻辑,调查一些另类的音乐系统。
笔记本打破另类音乐系统的发展
对于那些不熟悉西方音乐体系的人来说,本笔记本的目的是展示一个练习,这个练习描述了采用离散值的符号结构、解构它并将其整体嵌套在每个离散值的空间内的数学过程。在上部结构的空间内嵌入镜像下部结构。
建造 Wav
从这里开始,让我们使用这些系统的频率来制作它们音调的数字表示。
下面的笔记本简要概述了从一个数字开始构建音调,然后基于这些音调构建系统,并分析几个文件以验证它们是否被准确渲染的过程。
笔记本调查的建设和检查一个数字化建设的音频
用音调的数字表示构建功能音乐
图 1 是根据上述代码渲染的音调构建的三个音阶。
- 半音四分之一音阶从 440 赫兹移动到 880 赫兹
- 半音半音音阶从 880 赫兹移动到 440 赫兹
- 全音阶半音音阶从 440 赫兹移动到 880 赫兹
音频图 1

图表图 1 |由作者代码生成的图像
根据我们掌握的音调,我们可以做出任何音阶。我使用这三个只是为了证明概念。除了音阶,我们还可以构建和弦和级数。图 2 是从渲染音调构建的一个进程。
- 这个过程从一个第二代第六代开始
- 然后进入微音和弦,利用全音阶和微音系统的音调以混合和弦结束
音频图 2

图 2 |由作者代码生成的图像
数字音频的新结构
下面是我几年前画的一幅铅笔画的扫描图。下面是转换成数字音频并通过摄谱仪显示的同一幅图像。每个像素中的数据用于构建音频信息的频率和振幅。在未来的博客中,我可以详细说明这个过程是如何工作的,但现在,我只是想展示这个来进一步阐述数字音频是一种表现,而不是声音本身。

作者的墨水和铅笔画扫描|作者的图像

一段时间内以一系列频率呈现的图像|作者提供的图像
ps。当通过扬声器播放时,它发出的实际声音令人难以忍受
操纵数字音频
开发模拟音乐数学关系的数字渲染是一回事。很高兴知道机器可以实例化音调。但是当机器使用纯数学精度时,对声音的感知是熟悉的,但缺乏我们在物理世界中听到声音时所期望的品质。
让我们探索模块的更多功能,并使用录制的音频。在这一部分,我们将使用 20 世纪 70 年代公共领域购物中心 muzak 的舒缓声音。
70 年代的公共领域音乐
操作音频类似于 python 中的基本字符串和数组操作。
#to know how many elements are in an given array of audionum_of_ele = seconds*sample_rate
每个元素都是包含音频数据快照的样本。
下面是一个笔记本,它通过对音频阵列的一系列单个操作,并以一个将效果按顺序链接并传递音频的操作结束。所有产生的例子都可以在笔记本下面找到。
笔记本经历一系列操作
音频处理的结果
反向音频
python 中有几种反转音频的方法,但主要原理是将音频数据提取到一个数组中,反转数组,然后将其转换回音频文件。一个简单的方法是:
sr, y = read(wav)
reverse_y = y[::-1]
write(outfile, sr, reverse_y)
时间拉伸音频
这里有三个时间拉伸算法的例子,可以减缓音频的速度。根据您如何考虑采样率的变化,您可以获得不同的探测范围。
将采样速率乘以一个大于 1 的因子来减慢的算法
将采样速率除以小于 1 的因子以降低速度的算法
使用 scipy 的算法,当读入 wav 时,在 scipy 的数据输出中广播一个倍增因子
基本的想法是,你想让采样率更长,以便在更长的时间间隔上伸展音乐内容。为了加速音频,你做相反的事情。
下面是两个加速音频的例子。
将采样速率乘以小于 1 的因子来加速的算法
将采样速率除以一个大于 1 的因子来加速的算法
音高移位音频
音高上移 100 赫兹
找到音高移位方法的关键是改变采样速率的速度,而不导致 wav 时间拉伸。为了实现这一点,我将我想改变音高的赫兹数除以采样率,然后将文件分成左右声道。一旦我这样做了,我就对每个通道中的每个样本使用快速傅立叶变换并调换频率,然后反转该过程并将两个通道缝合在一起。
延迟音频
延迟设置为 1000 毫秒,因子为 0.5,重复次数为 3 次
如果您查看绘制该文件音频的摄谱仪,可以看到延迟如何模糊了文件的中间,但在开头和结尾有明显的三次重复,信号的重叠迭代明显较少。为了延迟数字音频,您必须通过生成偏移来处理字节。这样做的逻辑如下:
**def** delay(bytes,params,offset_ms):
    # generate offset
    offset= params.sampwidth*offset_ms*int(params.framerate/1000)
    # add silence in the beginning
    beginning= b'**\0**'*offset *#remove space from the end*
    end= bytes[:-offset] # concat bytes with both beginning and end with sample width
    **return** add(bytes, beginning+end, params.sampwidth)
使用代码块,如链式效果踏板
在序列中构建算法后:
反向- >延迟- >拉伸- >延迟半速- >层反向
并通过数字信号,下面的音频结果。
顺序算法效果
数字音频分类
到目前为止,一切都是在干扰呈现数字音频的数学机制。实质上是在玩音频阵列。在本文的其余部分,我们将更深入地研究使用特征提取的音频分析,以及机器如何使用这些特征来模拟音频。

可视化的 FFT | 来源
为了提取这些特征,我们将使用傅立叶分析中的方法;主要是:快速傅立叶变换。FFT 是一种提取复杂波形并将其解析为后续简单波形的方法。它允许我们访问基于频域的特征。在下面的笔记本中,我们将探索:
- 光谱质心
- 光谱带宽
- 梅尔频率倒谱系数
- 色度特征
简单解释一下:
光谱质心
频谱质心可以被认为是测量给定声音的'亮度。它计算给定样本中频率的加权平均值。

其中 x(n) 表示仓号 n 的加权频率值或幅度, f(n) 表示该仓的中心频率。
光谱带宽
频谱带宽与声音的频谱分辨率相关。它被定义为每个样本最大峰值频率一半处的频带宽度。
梅尔频率倒谱系数
梅尔频率倒谱是从功率倒谱构建的,是声音的短期功率谱的表示。系数是通过 FFT 检查样本,然后将功率映射到 mel 标度上而得到的。然后记录每个 mel 频率。之后,执行离散余弦变换,就好像对数梅尔功率列表是一个孤立的信号。即信号的合成振幅就是系数。这些系数精确地描述了给定样本的频谱包络的形状。
色度
色度提取沿着 0-11 的向量显示强度。该向量表示音高类别集,并揭示存在的每个音高类别的数量。换句话说,它告诉我们信号中存在哪些谐波频率。
这只是信号中存在的一小部分特性,但有了这些特性以及本笔记本中显示的其他一些特性,我们就可以开始对信号进行建模了。
显示信号建模特征提取示例的笔记本
有趣的是,对数字音频信号进行建模就是提取已有表征的特征的表征。
建模
使用 G. Tzanetakis 的集合,让我们使用这些特征提取来模拟音乐流派。该合集由 1000 首音轨组成,每首音轨时长 30 秒。它包含 10 个流派,每个流派代表 100 首曲目。轨道都是 22050 赫兹单声道 16 位 wav 文件。一旦我们从集合中提取了所有的数据,让我们从我们简单但很好的朋友开始,人工深度神经网络。

DeepANN 分类器| 来源
笔记本构建和实现一个简单的 DeepANN
该模型达到了 69.50%的准确率。
考虑到建模是信号工程表示的一种方法,让我们看看我们可以对数据建模的其他方式。让我们使用数字音频的图像,而不是提取代表样本中不同值的特征。为此,我们可以获取每个 wav 文件并生成一个摄谱仪。然后我们可以在光谱图上使用卷积神经网络(CNN)。
这是一个我们将用来训练 CNN 的光谱仪的例子。

gtzan collection 的 10 秒 wav 文件的摄谱仪,流派:hip hop |由作者代码生成的图像
CNN 将学习文件的视觉印象来理解一个流派的特征,而不是对提取的特征本身进行训练来理解一个流派的特征。

CNN 分类器| 来源
笔记本电脑建设和实施有线电视新闻网
虽然使用摄谱仪来表示音频的想法很有趣,但使用它来训练分类器似乎不是最有效的方法。更不用说 CNN 的训练时间在记忆和时间上要昂贵得多。
说到记忆和时间,让我们回到使用原始音频的特征提取,并尝试一个长短期记忆(LSTM)模型。

LSTM | 来源
笔记本电脑建设和实施 LSTM
我们达到了 66.66%的准确率——比我们在本节开始时使用的原始神经网络模型的准确率略低 3%。在这里,LSTM 是有效的,但对于分类问题,我仍然会选择人工神经网络。但是让我们看看 LSTMs 还能做什么。
生成数字音频
在我的文章生成性人工智能:故障、缺陷、非理性和表达中,我讨论了一些关于人工智能是否以及如何能够’生成的观点。简而言之,总的来说,我认为 AI 可以产生,从而创造,在某些情况下。在我们尝试让人工智能生成音乐之前,让我们使用预测功能来看看它复制音乐的能力有多强。
在这个笔记本里,我取了三个音频文件。
- 《魔法咒语》水晶城堡
- 《两个人就能赢》J·迪拉
- 《十二声部音乐第三部》,菲利普·格拉斯乐团(1975 年现场演出)
这里的任务是使用更高维度的音频数据,即我们在处理数字音频时使用的幅度阵列,来看看 LSTM 是否可以预测每个样本的正确幅度序列。
用于音频生成的笔记本构建和实现 LSTM
正如我们所看到的,LSTM 可以被训练得相当精确。下面生成的音频:
拼接在一起的三个音轨的真实音频
表演中的模型


第一个模型的训练性能|由作者代码生成的图像
模型学习但不太理解卷


第二模型的训练性能|由作者代码生成的图像
模型学习准确预测振幅并重现音频


第三模型的训练性能|由作者代码生成的图像
对于下一个生成音频的实验,我不会展示笔记本电脑,因为每个模型都需要几天甚至更长时间来训练(一个模型用 GPU 花费了近一周的时间!).但是对于那些有兴趣阅读模型代码的人来说,这里是最终模型的架构。根据我预处理数据的方式,这个模型最终产生了一些有趣的结果。
最终 LSTM 建筑
除了众多添加的层之外,该模型的主要区别之一是时间分布的密集分层,所有内容都构建在其中。这允许 LSTM 在训练时一致地返回层内和层间每一步的完整序列。它还产生一个完整序列的输出,而不是一次一个样本。
在这个实验中,我不想停留在振幅的表面水平,而是想在通过傅立叶分析提取的低维光谱特征上训练模型。我不是在一个复杂的音频文件上训练,而是在一个艺术家的完整专辑和现场表演上训练。我继续使用水晶城堡,J 迪拉和菲利普·格拉斯合奏。最初,我把整张专辑分成 10 秒钟的音频片段,最终在完整的音轨上训练了一个模型。
在训练过程中,我保存了不同模型开发的不同阶段,并从每个艺术家提取的特征数据中构建了一个随机序列生成器。然后,我让每个模型在不同的学习阶段尽可能地产生声音。下面是不同阶段的输出。音频不会以任何方式改变,而是按照模型学习阶段的时间顺序排列。
*警告:开始时有点吵
隔离后,你可能会发现这个 LSTM 在杰弗逊南部的约翰逊大街&法拉盛附近的布什维克闲逛

AI _ LSTM _ 生成 _ 水晶城堡的摄谱仪|图片由作者代码生成
** 50 秒到 1:40 秒之间发生了一些美丽而不完美的事情*
隔离后,你可能会发现这个 LSTM 每天都在伍德布里奇的盟友那里闲逛

AI_LSTM_Generated_J Dilla |图片由作者代码生成的摄谱仪
真正让我惊讶的是:在给定数据的情况下,模型首先学习音频的哪些部分,以及它从看似只是音调和声音到对节奏的明确理解有多快。这个输出让我想起了开车去北部时,通过调频广播信号调谐,寻找正确的电台。
现在,在这两个例子中,有一些严重的过度拟合,以及一些细微差别。事实上,在 J Dilla 赛道上,有趣的是这个模型不停地吐出循环。我想知道这是否与 J Dilla 使用样本有关。而在水晶城堡的音乐中,有重复,但它是人们重复的话,节奏和旋律,而 J Dilla 的轨道是录音和磁带的逐字取样重复。我还发现令人着迷的是,在 J Dilla 的音轨中,人声清晰,可以听到明确的词语——你可以在摄谱仪的后半部分看到人声的弦外之音;而在水晶城堡的音轨中,声音是存在的,但看起来是一个模糊的单词轮廓,与乐器天衣无缝地融合在一起,而不是明确的话语。
最后,让我们听听菲利普·格拉斯系综模型。有趣的事情正在这里发生。我不想听从白噪音到智能模拟的演变,我想放大来听这里发生的音乐选择,以及音色。在 Crystal Castles 和 J Dilla 中,每一个成熟的 AI 都会在逐字模拟和误差边缘之间振荡,误差边缘允许一些令人想起决策的故障缺陷。不过,在谷歌眼镜模式下,所有的输出都是现场录音中找不到的音乐片段。
艾 LSTM 菲利普·格拉斯合奏

AI _ LSTM _ 玻璃 _2_1_6 |图像的摄谱仪由作者代码生成
下面是一些我觉得很漂亮的光谱图。人工智能正在理解频率和节奏,并产生信号,这些信号在它接受训练的实际记录中并不存在。然而,它缺乏音色,听起来像一个模拟合成器;但对我来说,感觉奇怪的真实,有菲利普·格拉斯的幽灵般的影响,但却是增强的,创造性的和独特的。













由作者代码生成的所有光谱图
下面是我觉得有趣的输出汇编。音频没有改变。
GlassAI 汇编
结论
“所以艺术无处不在,因为技巧是现实的核心。所以艺术死了,不仅因为它的批判性超越性消失了,还因为现实本身,完全被一种与其自身结构不可分割的美学所浸透,已经与它自己的形象混淆了。现实不再有时间呈现现实的外表。它甚至不再超越小说:它捕捉了每一个梦,甚至在它呈现出梦的外观之前。”
这一点,连同 超现实 的概念,一直贯穿在我的脑海深处。虽然我知道鲍德里亚指的是作为一个对象系统的现实的理论讨论,但当我开始探索 DSP 时,这方面的一些东西引起了我的共鸣。我认为数字音频是一种表现,一种声音的模拟。无法与自然产生的声音区分开来的声音。技术上来说,是的。但以玻璃为结尾,让我觉得通过模拟和复制媒介的有意开发,我们可能会发现一个没有技术桥梁我们根本无法达到的创造力维度。回到人工智能可以成为一种创造性和协作性工具的想法。我以这本笔记本作为结束。
通过顺序算法发送玻璃信号的笔记本
通过顺序算法从玻璃信号输出
建筑和机器学习
Autodesk BIM 360 + Construction IQ 为施工现场带来了预测和分析能力。

来源:Franki Chamaki 通过 Unsplash。
介绍
承包商的工作性质是反动的。他们不断地在现场扑灭一场又一场大火,以确保施工尽可能顺利地进行。但是,如果通过机器学习,我们可以改变这种被动的工作流程,变得积极主动呢?机器学习可以消化承包商必须处理的大量项目数据,并可以将承包商每天需要关注的最关键问题浮出水面,以避免挫折。
要了解数据分析和预测如何帮助建筑行业,站在承包商的角度很重要。从承包商的角度来看,一个虚构的情况可以帮助我们。
承包商的生活
1.故事
想象一下:你是一个大型建筑工地的总承包商主管。你从头到尾监督现场的所有操作。您负责安排日常工作,监督所有正在进行的活动和任务,并确保现场每个人的安全。您的角色对于项目的成功和他人的安全至关重要。
你工作的建筑公司负责为一家大牌科技公司建造下一个主要总部(假设是亚马逊 HQ3)。建筑师完成了所有的图纸,加快了建筑许可过程,并将批准的计划发送给您,以便开始施工。施工时间表非常紧迫,您马上开始匆忙确保您的施工团队按时将建筑交付给客户。有数亿美元悬而未决,你开始感到压力。你知道亚马逊想要一个完美的建筑,而且他们希望尽快得到它。

作为施工主管或项目经理,需要管理很多很多的团队。这只是众多参与建设的人之间紧密合作的一个例子。资料来源:Josue Isai Ramos Figueroa。
2.压力是存在的
施工几个月后,齿轮正全速运转。混凝土地基已经浇筑完毕,大楼开始上升。现场有多个分包商(subs ),每个都在做自己的工作。作为总承包商,你要协调分包商工作的时间,评估他们工作的质量,并确保他们遵守现场安全措施。最重要的是,代课老师不停地问你问题:
防水部分:基础墙的防水是水泥防水还是液体防水?
钢接头:一些钢柱刚刚到达现场,它们看起来有点生锈了。他们应该是工厂准备好的吗?
混凝土分包商:结构工程师有没有回复我们,每层楼的混凝土桥面允许多少粉煤灰?

主管必须确保现场的每个人都遵守安全协议,以保证现场每个人的安全。来源:Josue Isai Ramos Figueroaviaun splash。
一天中没有足够的时间让你监视每个人并及时解决问题。结果,分包商问的几个问题被漏掉了,你没有定期与每个分包商联系,工作继续进行。
3.问题
一个月后,你注意到混凝土接头只完成了 2 层,而他们现在应该已经完成了 4 层。混凝土是施工关键路径的一部分——在混凝土完成之前,其他人员无法开始工作。
混凝土小组说他们不知道混凝土中允许有多少粉煤灰。他们几周前就问你了,但是因为你有太多其他的事情要做,你从来没有回答过。因此,他们没有永远等待你的回答,而是比平时更多地使用混音来省钱。这种混凝土比含有较少粉煤灰的混凝土需要更长的时间来固化——因此会延迟。现在你要么想办法补上一个月,要么向客户要求更多时间,失去信誉,并承受巨大损失。
你感到压力了吗?

承包商要管理的东西很多,可能会让人不堪重负。来源: 路易斯·比利亚斯米尔转自 Unsplash。
施工文件
好了,现在我们理解了承包商的一些潜在压力和挫折,我们可以同情他们的处境了。如果听之任之,很容易忽略一个小细节而造成严重后果。如果文档和数据没有组织好,就更难掌握细节。这句来自 BIM 360 博客的话完美地抓住了这一点:
“建设因为没有记录事情而受到很多批评,但我认为这是错误的。我们非常擅长记录。我们只是不善于把它放在可以被发现和分享的地方。”-杰夫样本,Contechcrew

在匆忙中,有时承包商文件的组织会变得有点混乱。(但我敢肯定,在这个问题上,他们并不孤单。来源:托马斯·耶茨通过 Unsplash 。
这句话揭示了承包商有时很难保持他们的信息有条理,以便它有用——这并不奇怪,因为有太多的因素会使施工过程变得混乱。在整个施工过程中,图纸、产品、系统、规范和分包商都可以修改和变更。行动项目可能存在于贴在墙上的便利贴上,要提取的标记可能存在于分散在桌子上的打印纸上,数字文件可能保存在内部文件夹结构中的任何地方(或在您的桌面上)。
Autodesk 知道这一点,他们为文档和数据创建了一个单一的数字环境,与承包商使用的其他建筑信息建模(BIM)软件共存。这个环境就是 Autodesk BIM 360。
BIM 360
为了避免多个流程和不同软件处理管理文档的复杂性,Autodesk BIM 360 允许承包商在一个地方管理所有文档和数据。
可以在此发送、接收、跟踪和组织 RFI 和提交文件。可以在这里管理、更新和共享图纸和规格。不同行业的三维模型可以在这里进行协调和分析。这里可以记录安全规则和观察结果。可以在这里管理检查表、问题列表和竣工查核事项表,以确保项目的质量。带有注释和标记的设计评审也在这里完成。
除了有助于保持承包商信息有序的明显优势,BIM 360 还允许承包商通过可定制的报告和仪表板受益于数据分析。BIM 360 还通过 建筑智商 提供来自机器学习模型的预测能力。

BIM 360 中的示例分析仪表板显示了正在建设的项目的数据。来源:Autodesk 截屏转载,由 Autodesk,Inc .提供
建筑智商
现在,我们开始了解 Autodesk BIM 360 激动人心的高科技功能。由于 BIM 360 是存储信息的单一位置,因此 Construction IQ 从这些数据中提取信息,利用机器学习来识别施工期间的风险。但是为什么预测“风险”很重要呢?
让我们回到我们虚构的故事,关于我们的主管管理亚马逊 HQ3 项目的建设。负责人发现太多的粉煤灰在混凝土混合物中使用得太晚,这导致了代价高昂的延误。但是如果我们能够预测像这样的潜在风险会怎么样呢?
如果有一个分包商经常出错,并且这些错误被记录在 BIM 360 中,Construction IQ 会分析这些数据并为每个分包商生成预测。它提供了一种“分包商预测”,告诉我们某个分包商今天是处于低风险还是高风险。这可以帮助我们防止“危险倾向”的潜艇犯错误。使用风险预测,总承包商可以更关注需要更多监督的分包商。
Construction IQ 还引起了我们对其他风险的关注,例如安全风险和设计风险。在仪表盘的帮助下,这一切都以干净、快速、直观、易读的快照呈现给忙碌的承包商。我们可以拖放“卡片”来获取我们想看的信息。

在右侧,有一些“卡片”,可以选择这些卡片向仪表板添加不同类别的项目信息,如设计、质量、安全、进度和问题。来源:Autodesk 屏幕截图,由 Autodesk,Inc .提供转载
引擎盖下的建筑智商
Construction IQ 从记录的项目问题、现场观察、清单、分包商任务、历史数据和其他元数据中提取用于风险预测的数据,以预测日常风险。
了解允许 Autodesk 预测分包商是否具有高风险的独特算法组合将是一件有趣的事情。从承包商那里收集的许多数据可能是文本——观察、问题、清单、任务,都是文本。这是除了图纸和标记的 pdf 之外,还有记录在 BIM 360 中的现场图像,机器学习算法可以使用这些图像来预测风险。
为了确定分包商的质量和安全风险,在幕后,可能正在对分包商工作的每个文本观察进行自然语言处理,以提取情感,然后通过分类模型(如逻辑回归、朴素贝叶斯或支持向量机(SVM))将其分为“好”或“坏”类别。如果有更多的“坏”观察,那么该潜艇的风险可能会更高。“高风险”和“低风险”观察的最终分类也可以通过查看现场拍摄的质量检查照片的 CNN 来增强。像 LSTMs 这样的时间序列建模可以对更近期的分包商观察进行更重要的加权,以更准确地预测分包商的风险。
为了确定设计风险,计算机视觉可用于检测图纸和标记 pdf 中的设计风险——搜索代码合规性问题和图纸中的任何错误。

展示 Construction IQ 的风险预测的示例建筑项目仪表板。来源:Autodesk 截屏转载,由 Autodesk,Inc .提供
下一步是什么?
机器学习只有在数据存在的情况下才有可能。没有数据就意味着没有机器学习。因此,只有通过将所有建筑信息数字化到我们的计算机中,并将这些信息集中在一个平台上(如 BIM 360),建筑中机器学习的出现才有可能。通过将信息组织、整理并存放在一个地方,文档很容易找到。
那么下一步是什么?在工作现场,机器学习还能帮助我们做什么?除了风险,这篇 BIM 360 博客提出,通过计算机视觉对不遵守安全标准的建筑工人进行图像标记有助于确保现场人员的安全。像 SmartVid.io 这样的公司处于这一努力的最前沿。
计算机视觉也可能有助于检测有缺陷的建筑,如开裂的混凝土、瓷砖、干墙和其他材料。查看我创建的这个 web-app 演示 ,它使用卷积神经网络(CNN)来检测建筑材料中的缺陷,并查看我的 GitHub 库 以了解开发该工具的详细信息。

我构建的这个网络应用程序使用了三个卷积神经网络(CNN ),可以帮助检测建筑工地上有裂缝的材料。它首先分类照片是“一般”还是“特定”。如果它是“特定的”,那么它会检测照片中材料的类型。最后,它决定了材料是“开裂”还是“未开裂”。来源:作者。
此外,机器学习可能有潜力为与承包商合作的建筑师和工程师提供进一步的设计帮助,例如当我们在 Autodesk Revit 模型中进行设计时,使用 AI 插件来确定代码合规性。 看看我的博客上与此相关的升级代码。
结论
机器学习有可能改变建筑工地上解决问题的方式,只要承包商将他们的数据数字化、组织化并全部放在一个地方。由于 Autodesk 的 BIM 360 环境中的Construction IQ等工具,承包商将能够在潜在风险变成重大问题之前对其做出反应。
机器学习在建筑、工程和施工方面还有许多应用有待开发。我们必须深入了解行业的努力和斗争,以便将机器学习最有意义的应用带到他们的工作中。
AI +消费级无人机技术将改变国防部
无人驾驶飞机,成群结队,无人驾驶飞机的风暴。
这最初出现在我的时事通讯《民主化自动化》上。 没有我想遇到来历不明的无人机的情况,而在未来,我们将会看到数百架。
2016 年消费级无人机购买量为 240 万架;高于 2015 年的 110 万辆[ 来源,来源 ]。继续这一趋势(悲观地说), 2020 年将有大约 1000-2000 万架无人机售出。小型无人机的消费化为稳定社会和战争的未来带来了大问题。
不相信我?点击下面的快速介绍。
无人机便宜、用途广泛,而且难以追踪。如果你认为,“嗯,也许人们不会利用它们,”这是美国军方释放微型飞行器(MAV)群的实践。分类内容肯定是比较极端的。
最重要的是,土耳其有可以出口的自杀式无人驾驶飞机(在以色列设计)。这款名为 Kargu 的无人机是以土耳其语中的 hawk 命名的。阅读一些技术规范:
15 磅重的 Kargu-2 能以每小时 90 英里的速度飞行,并能在空中停留长达 30 分钟。它有一个视距控制链路,范围约为 6 英里。
无人机可以携带三种不同类型的弹头中的一种,包括一种用于在户外攻击人员和其他无装甲目标的高爆炸碎片弹头,一种适用于建筑物或洞穴等封闭空间目标的温压型弹头,以及一种用于攻击轻装甲威胁的聚能装药。【 来源
鹰的命名现在应该非常清楚了——它有能力消灭任何可见的目标。卖家声称这款无人机是为了反无人机和非对称,现代战争。这些技术落入更具争议的手中只是时间问题。美军中央司令部司令已经宣布:
“美军跟不上廉价无人机的洪流。”
这是我正在写的无人机风暴。10 架、100 架或 1000 架无人机攻击我们在国外的军队或国内的领导人。我们如何防御这种情况?

来源-作者的研究。
无人机和蜂群的定义并不总是很清楚——尤其是蜂群。
嗡嗡声
谷歌默认定义(经过一些噪音和蜜蜂):
遥控无人驾驶飞机或导弹。
我认为谷歌的定义将很快过时,因为无人机是机载/本地控制的。
蜂群
谷歌的默认定义实际上并不太糟糕(比我遇到的许多使用这个词的研究人员要好):
一大群或密集的昆虫,尤指会飞的昆虫
我认为祈使的词是 密 。一个群体是一个密集的物体,在那里不同地控制一个成员可能完全不会被注意到。最后,一个群体由 a)涌现行为或 b)中央控制来管理。
一群机器人会变成 10 个、50 个、100 个或 1000 个代理人的群体吗?2000 年代的群体控制研究主要集中在 10 个机器人的最优控制上。这项研究集中在几个机器人上,每个个体仍然是至关重要的。在我的圈子里,我们不认为这是一个群体,但我们喜欢思考边界在哪里。我的导师克里斯·皮斯特对群体定义的评论:
人们如何做【群体控制】?不知何故,当通信中断或动态的时间常数短于通信/决策等待时间时,我们在所有级别上集中分层规划和分布式决策。军事行动是一个常见的例子(一窝蜂),但管理一个家庭也类似——通常你不会喝完咖啡,通常两个人不会同时买咖啡。(根据内森的定义,家庭不是群体,但军队是,公共卫生也是)。
我们对成群结队的微型机器人感兴趣,它们可以协助应急响应、农业、分布式传感等领域。区别在于微型机器人可以大规模制造(1000 个),所以我们有完全不同的关注点。
我们如何对付一群真正好斗的蜂群?
反无人机
以下是我在反无人机工作领域的发现(和所做的)。
无人机粉碎机?
这是在一系列关于国防工业无人机的电子邮件中。这架无人机(配备了一个旨在撞击其他无人机并破坏转子和马达的表面)被命名为 Anduril (也被称为西方的火焰),令人印象深刻,但我不确定它对预期目标的效果如何。
点击下面观看一个 反无人机无人机 如何表现的演示。
更智能的反无人机无人机?
我不喜欢这个,因为用来提高防守者的技术显然可以提高进攻者。我们正在研究一队防御性无人机如何试图阻止许多攻击者进入突出显示的区域。需要学习的是如何相对于攻击者定位每架无人机,以便它可以在空中向攻击者发射网(摧毁它)。我怀疑我们可能有无人机在机场和许多其他关键的公共区域做这件事。
点击下面的视频,展示了一个防御性的蜂群(红色)抵御来自基地区域的入侵无人机(蓝色)。这是基于 的 Boids 群集算法 ,也是一个活跃的研究领域。
头脑风暴反无人机技术得出了一些常见的答案:
- 无人机本身:一架装备了网的无人机可以轻松放倒另一架无人机。无人机也可以撞击目标来摧毁易碎的飞行部件(见下文,无人机击碎器)。一个军用物品与一个消费级无人机的一对一交易是非常昂贵的(我估计是 1000 倍)。
- 电磁脉冲:电磁脉冲受到欢迎,因为它会破坏电子设备,使电动汽车(及其控制电子设备)无法使用。问题是 EMP 脉冲通常对目标之外的东西有严重的影响,而移动/手持 EMP 的开发既昂贵又缓慢。
- 激光/其他地面防御:我不确定这些有多大用处,但是有些军用车辆装备了高功率激光,可以融化迎面而来的无人机。问题还是在于,这种防御比进攻(潜在的消费级无人机)成本更高。
反无人机技术工作的道德底线是什么?
我们在我的研究小组模拟“无人机斗狗”,我想知道我们是否应该这样做。尚不清楚如何控制作品的传播,但如何在不使世界变得更加危险的情况下,各国在技术上缩小从进攻到防御的差距(见军备竞赛)。历史表明,这是难以避免的。
我有一个未来的帖子,关于加速技术进步的问题以及进攻和防守能力之间的差异。我认为我们需要条约和积极的网络意识。无人机很难研究,因为消费者喜欢无人机!人们希望无人机能够稳健地交付披萨。如果消费者驱动市场,我们如何阻止进步和可用性?我们没有。
其他风暴正在酝酿
- 夜间,成群的无人机在科罗拉多和内布拉斯加大草原上空形成并移动。几乎无法追踪,侵犯了隐私,并引起了一些州级反应。
- 美国空军测试并利用无人驾驶飞机执行秘密任务。这并不奇怪。
- 美国空军测试无人驾驶战斗机。他们希望持续 g 力的优势会有所帮助,但老实说,现代自主系统的处理速度仍然落后几年。几年后,是的,这将成为现实。
你们中有多少人真的听过一群四旋翼无人机在附近飞行?烂透了。很多。以至于甚至考虑广泛使用四旋翼飞机进行本地运输对我来说听起来都很荒谬。
在我的研究小组中,我们正在开发一种静音无人机,但它距离实用还有很多步,所以我们不担心军事化(是的,我们讨论它的伦理)。可以看下面的视频。
一个关于机器人和人工智能的博客,让它们对每个人都有益,以及即将到来的自动化浪潮…
robotic.substack.com](https://robotic.substack.com/)*
消费者选择模型的 5 个技巧
本文分享了构建集体消费者选择模型时需要牢记的 5 大技巧!
你有没有想过——我们人类一天会做出多少选择?———估计有 35000 人!

照片由 Nathália Rosa 在 Unsplash 上拍摄
随着每天都有新产品推出,这些选择不断增加,如果不是呈指数增长的话。从在杂货店选择牛奶罐到选择下一个最佳职业步骤,一方面“是什么”对决策者来说很重要,另一方面,公司继续寻找背后的“为什么”。每个零售或制造公司以及服务提供机构都很想知道他们的客户是如何“做出选择”的
- 是什么让他们选择一瓶洗发水而不是旁边的另一瓶?
- 是什么促使他们购买一辆车而不是另一辆?
- 他们更愿意为产品支付什么,为什么?
- 他们可以为某些功能多付多少钱,为什么?
- 他们最喜欢在什么时候购买?
- 他们想买多少?
这些长长的问题列表令人兴奋,消费者模型也是如此。总体而言,有 3 种类型的消费者模型:
- 数数——他们买了多少
- 时机——他们什么时候买
- 选择——他们买什么
本文阐述了 【消费者选择车型】 。选择模型试图找到答案——顾客如何选择最终产品,即顾客如何根据某种潜在的尺度(如“效用”,即顾客对每件商品的“感知价值”)对给定商品进行排序。例如,给定一组 4 个项目,比如洗发水,消费者倾向于评估每个选项的效用,并根据个人偏好对它们进行排序。价格、是否无硫、香味、品牌的商誉、公众评价、个人经历等因素起着重要作用。显然,这种“效用”会因人而异。同样,对于消费者来说,它还取决于其他因素,比如年龄、性别、所在地、职业、收入,有时还有像性格这样复杂的因素。
这些具有挑战性的问题使得“消费者选择建模”变得更加有趣。虽然寻找这些问题的答案是令人兴奋的,但为客户提供更好的选择同样重要,如果不是更重要的话。最终, 一个消费者想要最好的产品,一个公司最想卖出去!
目标很简单——做的越多,卖的就越多!
因此,选择建模使数据科学家能够利用购买交易、调查和产品评论的大量消费者数据,找出描述、规定和预测消费者选择决策行为的模型,使企业能够制定更好的战略。随着人工智能高级应用的出现,出现了许多新的想法。例如,研究消费者在扫描货架时不断变化的面部表情,分析消费者在发现感兴趣的产品时的肢体语言,或者理解消费者在购物时的总体兴奋感,这提供了更深入的见解,从而提供了更深入的知识来改善消费者体验。
正如维基百科的定义一样, 选择建模 试图通过在一个或多个特定上下文中显示的偏好或陈述的偏好,对个人或群体的决策过程进行建模。
现在,想到的下一个有趣的问题是— 如何定义这些客户? —一个群体的整体?在一定数量的细分市场中(如果是,那么是多少)?还是在个人层面上分别对待每个客户?
毕竟,每个人都是不同的,他们的偏好也是不同的。答案在于高复杂度结果的粒度与更简单结果的一般化之间的权衡。这只不过是他们在机器学习语言中所说的老派“偏差-方差”权衡。人们可以根据最终目标、业务知识以及可用数据的大小和特征来做出决策。
因此,从高层次来看,有 3 种类型的消费者选择模型:
集体选择模型:它假设每个顾客都会以同样的顺序排列某些商品——例如,当一家快速消费品公司创造一种新产品时——通常它是基于假定的集体消费者偏好
2)基于细分市场的选择模型:一旦在给定业务、业务问题和最终目标的情况下找到最佳数量的细分市场,它就将整个消费者群划分为这些细分市场,并假设同一细分市场中的每个客户都会以相同的顺序对集合中的项目进行排序,而不同细分市场中的客户会对项目进行排序,使得至少有一个排序会不同。例如,当不同的优惠、折扣和产品被提供给根据各种标准(如忠诚度、频率、新近度、位置、性别等)分组的客户时。
3)个人选择模型:它假设每个顾客会对商品进行不同的排序。虽然,即使只有有限的排列是可能的,每个人都被单独评估和对待,在更精细的层次上考虑个人偏好和个性,例如电子商务网站上的推荐系统
让我分享一下我最近创建消费者偏好模型的经验吧!
作为哥伦比亚大学 capstone 项目的一部分,我有机会与四位出色的队友一起为北美一家顶级汽车制造公司创建消费者偏好模型。在这篇文章中,我分享了我从每个数据科学家那里学到的,基于这个项目建立一个成功的集体消费者偏好模型的 5 大技巧。
目标是什么?
许多公司的生产不是基于订单,而是一次批量生产——因此,就像任何企业一样,他们也想知道他们的客户偏好。因此,我们开始创建集体消费者偏好模型,以了解消费者更喜欢哪种汽车功能!
因此,以下是我们在从事该项目时发现的 5 大经验,我们很乐意与大家分享:
1)选择影响选择模型 —数据科学家应该问自己的第一个问题,“消费者的购买决策之旅是什么?”不清楚对不对?以这种方式思考——你买牛奶和买笔记本电脑有什么不同?这两种情况都涉及到消费者。两种情况下都要做出选择。在这两种情况下,有多种选择,可能有相似数量的选择。那么这两种决策和购买行为有什么不同呢?—区别在于这个问题的答案——消费者如何以及通过什么心理决策做出最终决定?虽然牛奶罐的选择取决于商店的库存,消费者通常会从货架上的所有选项中选择任何一个选项,但购买笔记本电脑需要几天的研究、集思广益,而不是只去预计会有所需选项的商店。此外,某些选择依赖于商店库存,然而,在所选择的备选项中,诸如品牌、RAM、价格等特征的某些其他选择比库存本身更依赖于消费者的“预设”偏好。选择模型假设消费者在特定时间看到所有的选择,并根据他们的效用排序做出决定。因此,根据业务设置,人们可能需要改变选择模型,创建复杂的约束,假设复杂的功能来创建一个好的模型。阅读更多关于选择模型的理论,解决它们的每种方法背后的假设,并加强自己的概率和统计游戏,例如, 这 是一个很好的汇编,可以带来很大的见解和更好的建模。
2)变量缩减 v/s 信息损失 —这种权衡是数据科学家在解决业务问题时需要做出的最重要的决策之一。假设您的数据集有汽车的颜色,并且有 100 多种独特的颜色。作为一名数据科学家,您首先想到的是什么?让我们减少这些颜色类别,因为在一次性编码之后,它将导致 100 列!!!
什么!!100 列只是为了颜色!?
来源: Giphy
我们,数据科学家,有这种尽可能减少变量的诱惑,因为 我们的 目标是做出稳健的模型。这仍然是一个从较少变量开始的好策略。为了应对这个充满变数的问题,我们的旅程是这样的:
我们的第一个想法是根据 RGB 分数将颜色分组但是!等一下!…消费者在购买汽车时是否会将黑色、深蓝色和深绿色汽车视为相似颜色的汽车?不要!他们的看法完全不同!因此,尽管“数学上”基于 RGB 代码对颜色进行分组听起来是最符合逻辑的论点,但它去除了“顾客如何感知汽车颜色”的本质。
注意:不正确的分组比没有分组更糟糕(即没有变量约简)!
因此,这种变量的减少会导致信息的丢失,而我们正在某个地方寻找我们的模型来捕捉这些信息,然后希望它学会做出一个好的预测!
我们的下一个想法是根据流行度对颜色进行分组 — 最初,我们认为根据频率进行分组将是最佳选择,因为将所有出现频率极低的颜色分组在一起将减少数据,而不会损失太多“信息”。不管怎样,我们正在去除颜色的纹理,因为数据科学家喜欢不择手段地减少变量 但是,这里也发生了信息丢失! 黑色“哑光”和黑色“金属色”看起来完全不同!
毕竟哑光就是哑光!

照片由 Philippe Oursel 在 Unsplash 上拍摄
通过去除这种粒度来使生活变得更容易是没有意义的,因此,结果是通过保持颜色原样来实现的!
3)选择合适的目标变量 —目标变量是先建模再预测的因变量。有时这个目标不能是明确的,相反,它必须根据要解决的最终目标,在商业敏锐度的基础上创建或决定。选择模型中的目标变量通常是二元变量,如果客户选择了特定的选择,则使用机器学习或最大似然估计对其进行建模。最重要的是,必须确保数据集遵循选择模型背后的基本假设。例如,在我们的班级中,汽车在一个特定的经销店按顺序售完,我们有每天售出多少辆汽车的数据。应用这种简单的选择模型会迫使模型‘假设’每个客户在特定的一天看到了所有的选项,这在现实中是不正确的。事实上,在现实中,‘最受消费者青睐的选项’首先会被抢购一空,我们的模型会将其视为被拒绝。因此,我们最终将“汽车在经销商处的库存时间”作为目标变量,因为它表明了汽车的“受欢迎程度”——汽车停留的时间越长,受欢迎程度就越低,越早售罄,就越受欢迎。其次,在对这个新创建的目标变量应用回归时,模型表现不佳。难道不是很直观吗?对于一个 1000k x k 维的小数据集,模型准确预测一辆汽车将在 35 天或 36 天内售出有意义吗?不仅仅是感觉,更重要的是,我们的业务问题需要解决吗?因此,我们将该变量转换为二元变量,其中根据经销商(即其服务的细分市场)选择临界值或“流行库存时间阈值”。该 XGBoost 模型优于所有模型,给出了 80%的平均 AUC。
4)消费者市场/细分 —集体选择模型,虽然简单,但处理 的问题过于一般化 而丢失了关于—变化的客户行为的重要信息。即使您的客户或业务问题要求创建一个集体消费者偏好模型,数据科学家也应该经常问自己——我能在预测中纳入这些不同的客户行为吗?我们可以选择为每个汽车经销商创建单独的模型,这些经销商通常相距 50 英里以上。如果后者的平均 AUC 有所提高或没有提高,制作一个集体和单独的市场/区域模型并比较它们总是一个好主意。后者的缺点是“数据量减少”。因此,必须仔细分析这种权衡,牢记我们正在解决的业务问题 !
5)现实世界的实现 —当我们创建一个成功的模型时会发生什么?—我们把它交给客户?—对吗?—事情是这样的:在一次通话中,我们告诉团队,我们已经成功地制作了精度为 xx 的模型!有一些赞赏,但缺乏预期的兴奋,这是有道理的?最终使用模型的人,通常不会被期望精通机器学习?—他们能否将新输入转换成所需的格式,然后成功调用预测函数?不是吧?这很有挑战性!因此,我了解到,一个数据科学项目,尤其是解决业务问题的项目,如果没有用户界面是不完整的,即使是 Jupyter 笔记本本身内置的基本界面也是如此。他们必须能够在现实世界中实现该模型。在接下来的通话中,当我们向他们展示了一个非常基本的用户界面,他们可以在其中插入所有输入内容时,他们大吃一惊!型号是一样的,只是 UI 增加了舒适性和便利性,这就完全不同了!
感谢阅读!
希望你喜欢阅读这篇文章,请分享你的宝贵反馈!
使用不到 30 行 Python 代码追踪联系人
机器学习如何帮助应对流行病?

图片由作者提供(使用 Canva 制作)
接触追踪是用于识别那些与传染病检测呈阳性的人接触的人的过程的名称,例如麻疹、艾滋病毒和新冠肺炎病毒。在疫情期间,正确追踪接触者有助于减少感染人数或加快感染者的治疗过程。这样做有助于拯救许多生命。
技术可以帮助实现接触追踪过程的自动化,产生比手动操作更有效、更准确的结果。一项可以帮助这一过程的技术是机器学习。更准确地说,集群。聚类是机器学习算法的一个子类,用于根据这些特征将共享某些特征的数据划分到不同的聚类中。
如何在混乱中找到模式?
towardsdatascience.com](/5-data-mining-techniques-every-data-scientist-should-know-be06426a4ed9)
有各种类型的聚类算法,如 K-means、Mean-Shift、谱聚类、BIRCH、DBSCAN 等等。这些不同的算法可以分为三类:
- 基于密度的聚类:基于区域的密度形成聚类——这种类型的例子有: DBSCAN (基于密度的有噪声应用的空间聚类)和 OPTICS (对点进行排序以识别聚类结构)。
- 基于等级的聚类:使用树型结构形成聚类。一些聚类是预定义的,然后用于创建新的聚类—这种类型的示例: CURE (使用代表的聚类)、 BIRCH (平衡迭代减少聚类,并使用层次)。
- 基于划分的聚类:通过将输入数据划分为 K 个聚类来形成聚类——这种类型的示例: K-means , CLARANS (基于随机搜索对大型应用进行聚类)。
对于接触追踪,我们需要使用基于密度的聚类算法。原因是,当感染者与他人接触时,疾病就会传播。因此,更加拥挤——密集——的地区会比不那么拥挤的地区有更多的病例。
为了追踪受感染者的活动,科学家们经常使用 GPS 数据集,这些数据集包含一个人在任何给定时间范围内的时间和位置信息。位置数据通常表示为经度和纬度坐标。
接触追踪算法
为了建立一个接触追踪算法,我们需要做三个步骤:
- 获取特定时间和地点内不同用户的位置数据。
- 对数据应用基于密度的聚类算法。
- 使用分类来预测受感染的人。
那么,让我们开始吧…
我们将编写一个 Python 代码,它使用 DBSCAN 聚类算法来预测谁可能会因为与感染者接触而被感染。
步骤№1:获取数据。
不幸的是,我们无法从 GPS 位置获得真实生活的数据。因此,我们将构建一个模拟数据集来应用我们的算法。对于本文,我使用了一个模拟数据生成器来生成一个 JSON 数据集,其中包含 10 个用户的 100 个位置条目。如果要尝试另一个数据集,请确保满足以下条件:
- 每个用户有多个条目。
- 用户彼此之间的距离很近,并且在一个时间范围内(例如,一天或特定的小时数)。
首先,让我们导入我们将使用的所有库。我们将需要Pandas和Sklearn来处理数据和Pygal来显示数据。
*import pandas as pd
import pygal
from sklearn.cluster import DBSCAN*
注意:如果您没有这些库,您可以使用pip从命令行安装它们。此外,如果你正在使用 Jupyter 笔记本,你需要添加这个单元格来显示Pygal图:
*from IPython.display import display, HTML
base_html = """
<!DOCTYPE html>
<html>
  <head>
  <script type="text/javascript" src="[http://kozea.github.com/pygal.js/javascripts/svg.jquery.js](http://kozea.github.com/pygal.js/javascripts/svg.jquery.js)"></script>
  <script type="text/javascript" src="[https://kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js](https://kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js)""></script>
  </head>
  <body>
    <figure>
      {rendered_chart}
    </figure>
  </body>
</html>
"""*
现在,我们可以加载数据集并显示前 5 行,以了解它是如何构建的。
*dataFrame = pd.read_json(r"Location_Of_Your_Dataset\MOCK_DATA.json")
dataFrame.head()*

为了更好地理解数据,我们将使用 Pygal 散点图来绘制它。我们可以提取每个用户的不同位置,并将其存储在一个字典中,然后使用这个字典来绘制数据。
*disp_dict = {}
for index, row in dataFram.iterrows():
    if row['User'] not in disp_dict.keys():
        disp_dict[row['User']] = [(row['Latitude'], row['Longitude'])]
    else:
        disp_dict[row['User']].append((row['Latitude'], row['Longitude']))
xy_chart = pygal.XY(stroke=False)
[xy_chart.add(k,v) for k,v in sorted(disp_dict.items())]
display(HTML(base_html.format(rendered_chart=xy_chart.render(is_unicode=True))))*
运行这段代码,我们得到…

原始数据散点图。
步骤№2:应用 DBSCAN 算法。
太棒了。现在我们有了数据集,我们可以对其应用聚类算法,然后使用它来预测潜在的感染。为此,我们将使用 DBSCAN 算法。
DBSCAN 算法将聚类视为由低密度区域分隔的高密度区域。因此,与 k-means 相反,DBSCAN 发现的聚类可以是任何形状,k-means 假设所有的聚类都是凸形的。
Sklearn 有一个预定义的 DBSCAN 算法;要使用它,您只需知道三个参数:
- eps: 该因子表示同一聚类中不同点之间的距离。在我们的例子中,我们将使用 CDC 推荐的距离,即 6 英尺(或 0.0018288 公里)。
- min_samples: 聚类中的最小样本数。如果数据集较大且有噪声,请增加该数值。
- 度量:设置数据点之间的距离度量。Sklearn 有很多距离度量,比如欧几里德,曼哈顿,闵可夫斯基。然而,对于我们的例子,我们需要一个距离度量来描述一个密码(地球)上的距离。这个度量叫做 哈弗辛。
我们现在可以将我们的模型应用到数据集。
*safe_distance = 0.0018288 # a radial distance of 6 feet in kilometers
model = DBSCAN(eps=safe_distance, min_samples=2, metric='haversine').fit(dataFram[['Latitude', 'Longitude']])
core_samples_mask = np.zeros_like(model.labels_, dtype=bool)
core_samples_mask[model.core_sample_indices_] = True
labels = model.labels_
dataFram['Cluster'] = model.labels_.tolist()*
应用具有这些参数的模型导致 18 个聚类。我们可以使用这段代码显示这些集群…
*disp_dict_clust = {}
for index, row in dataFram.iterrows():
    if row['Cluster'] not in disp_dict_clust.keys():
        disp_dict_clust[row['Cluster']] = [(row['Latitude'], row['Longitude'])]
    else:
        disp_dict_clust[row['Cluster']].append((row['Latitude'], row['Longitude']))
print(len(disp_dict_clust.keys()))
from pygal.style import LightenStyle
dark_lighten_style = LightenStyle('#F35548')
xy_chart = pygal.XY(stroke=False, style=dark_lighten_style)
[xy_chart.add(str(k),v) for k,v in disp_dict_clust.items()]
display(HTML(base_html.format(rendered_chart=xy_chart.render(is_unicode=True))))*

聚类散点图
在算法完成之后,如果有任何没有聚类的数据点,它们将被聚类为噪声或聚类-1。通常,您会发现这个数据集中的所有用户都是-1 集群以及其他集群的一部分。
步骤№3:预测感染者。
如果我们有一个被感染的人的名字,我们就可以用它来得到这个人所属的所有集群。从那里,我们可以看到这些集群中的其他人。这些人被感染的概率会比没有被感染的人高。
- 获取特定人所属的所有聚类
给定一个名字inputName例如, William ,我们希望得到 William 所属的所有集群。
*inputName = "William"
inputNameClusters = set()
    for i in range(len(dataFrame)):
        if dataFrame['User'][i] == inputName:
            inputNameClusters.add(dataFrame['Cluster'][i])*
执行完这段代码后,inputNameCluster将变成{2,4,5,-1}。
- 将人们聚集在一个特定的群体中。
现在,我们想要属于这个特定集群集合的其他人。
*infected = set()
    for cluster in inputNameClusters:
        if cluster != -1:
            namesInCluster = dataFrame.loc[dataFrame['Cluster'] == cluster, 'User']
            for i in range(len(namesInCluster)):
                name = namesInCluster.iloc[i]
                if name != inputName:
                    infected.add(name)*
在这两个部分中,当inputName在每个集群的名称列表中时,我使用集合来避免额外的 if-else 语句。
Voilà ,代码会返回{'Doreen ',' James ',' John'},也就是说,那三个人有可能被感染,因为他们在某个时间某个地点接触过 William。
我将核心代码放入一个函数中,该函数获取数据帧和用户名,并对该用户进行接触追踪,最后打印出潜在的感染者。该函数将首先检查inputName是否有效;否则,它将引发一个断言错误。最重要的是,它只有不到 30 行代码!!
联系人追踪功能的完整代码:
结论
接触追踪是我们可以利用技术挽救人们的生命并尽快为他们提供治疗的方法之一。政府和医务人员经常可以访问一些患者的 GPS 位置。我们在这篇文章中走过的过程,基本上与他们获取潜在感染的过程相同。幸运的是,感谢像 Sklearn 这样的库,我们可以在我们的数据集上使用预定义的模型,并通过几行代码获得结果。
参考
[1]“在带有噪声的大型空间数据库中发现聚类的基于密度的算法”,Ester,m .,H. P. Kriegel,J. Sander 和 X. Xu,载于第二届知识发现和数据挖掘国际会议论文集,俄勒冈州波特兰,出版社,第 226-231 页。1996
[2]“DBSCAN 再访,再访:为什么以及如何应该(仍然)使用 DBS can。舒伯特,e,桑德,j,埃斯特,m,克里格尔,H. P .,,徐,X. (2017)。《美国计算机学会数据库系统学报》(TODS),42(3),19。
[3] Sklearn 文档。
适用于大多数 ML 用例的一个容器
揭开 XgBoost 的神秘面纱
设置 Docker 和远程 Jupyter 服务器(在 GCP 上)
序幕
这是去神秘化 XGBoost 第一部分和第二部分的延续。不过你可以单独阅读。如果您正在使用 XgBoost 进行您的机器学习项目,并且想要深入了解内部工作方式和超级参数,请查看第一部分和第二部分。
在这篇博客中,我将深入构建一个 docker 容器,它可以用于几乎所有的 ML 用例。集装箱内还将设置 Jupyter 笔记本和 Jupyter 实验室。我们将能够在 notebook 的 python 内核中同时使用 Python 和 R 进行编码!(R 有我喜欢使用的非常酷的绘图库,包括 XGBoost 的 plot_tree 的树可视化实现)。我们还将能够在本地浏览器上,通过 ssh 隧道和分别到容器主机和容器的端口转发,远程连接到 Jupyter 实验室服务器。
因为我最近在 GCP 上这么做了,所以我还将添加特定于 GCP 的指令,以便在 GCP 的 VM 上托管和运行这个容器,以及将持久磁盘映射到容器的指令。我们走吧!
安装步骤
安装 Docker
第一步,显然是安装 Docker。对我来说,我在 Debian 上,在 GCP 的一个虚拟机中。我跟着下面的链接,去安装 docker。或者可以关注 Docker 官方链接。
无论你的操作系统是什么,找出正确的安装步骤。谷歌。冰!
文档文件
下面是 docker 文件。我选择了 Ubuntu 18.04 作为基础映像,因为我喜欢 Ubuntu😀。您可以选择 Centos 或其他 linux 版本。不幸的是,所有的运行命令都依赖于操作系统,或者至少选择 Linux。

请从https://github . com/run 2/demystify-xgboost/blob/master/docker file下载该文件
从上面的链接获取文件。
我将运行分割成逻辑单元,这样容器可以一层一层地构建。在层中包含这些(单次运行命令)可以帮助您构建其他这样的 other 文件,在使用上述缓存层时(在您从该 other 文件构建映像之后),为您的用例构建更具体的层。
首先,我有所需的 Ubuntu 包,然后是 python 和 pip,然后是所有常见的 ML 库,然后是安装 graphviz 所需的库(我们需要它进行可视化),然后安装 graphviz 和 R,接着是 rpy2,它基本上允许我们在 python 内核中运行 R,然后是 R 包,然后是 github,最后是可选的 gcloud sdk 和一些清理工作。
它还安装了许多实用程序包,如 Kaggle、Shap、Jupyter extensions、Seaborn、arff2pandas,它们广泛用于 ML 项目。
接下来,Dockefile 创建一个本地目录来映射主机卷。创建一个名为 ml-user 的用户(无密码)。建立了朱庇特和朱庇特实验室。我没有遇到使用密码作为参数的麻烦,但是有一个注释部分显示了如何做。不过我不建议你这么做,因为你真的不需要那个密码。永远不会。
最后,Dockerfile 从主机复制三个文件。一个 jupyter 配置文件,一个允许我们通过 https 运行 Jupyter 服务器的证书文件,以及一个在容器启动时启动 Jupyter 服务器的入口点文件。
主持人
容器主机可以是物理机或虚拟机。从 GitHub 下载 Dockerfile,并将其作为“Dockerfile”保存在您主机上的一个目录中。
要构建这个 docker 容器,您还需要三个文件。一个 Jupyter 配置文件 jupyter_notebook_config.py .一个 ssl 证书文件 mysert.pem 和一个名为 entryPoint.sh 的 entrypoint 文件。
让我们过一遍。
Jupyter 的证书和配置文件
这两个文件没什么内容。如果您遵循 Jupyter 设置中给出的程序,我们基本上需要
- 使用 Openssl 创建一个证书文件。我没有上传这个文件,很明显:)
- 为安全访问 Jupyter 实验室创建散列密码
- 生成 Jupyter 配置,并使用 a)证书文件位置,b)密码哈希 c)Jupyter 为笔记本提供服务的文件夹位置 d)以及允许从所有 IP 访问服务器的一些更改 e)默认端口(8888)等更新配置文件。
您可以在 github 链接上使用配置文件(没有密码散列)。
记得修改配置文件中的
您将需要 config 和 cert 文件(我将其命名为 mycert.pem。如果您想命名为其他名称,您将需要在 config 文件中更改名称),位于您启动 docker build 命令的相同位置。
入口点
最后,让我们谈谈入口点。这个脚本以 root 用户的身份从容器的/root 目录运行(参见 docker 文件的最后一行)。但是我们需要在用户 ml-user 的许可下在后台启动 Jupyter lab(不想暴露在 root 下运行的 Jupyter lab),然后确保容器启动并运行,并将 std 和 error 通过管道返回到一个可以从主机访问的文件中。
下面是执行该操作的命令
#!/bin/bash
echo "$1"
sudo -H -u ml-user -- sh -c '. /home/ml-user/.bashrc ; \
cd ; pwd ; chown -R ml-user /ml-disk ; chgrp -R ml-user /ml-disk ; \
PATH=$PATH:/home/ml-user/.local/bin:/home/ml-user:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin; 
export PATH; nohup jupyter lab 2>&1 > /ml-disk/lab.log &'
bash
H 转到-u ml-user 的主目录。注意用户 ml-用户是在 docker 文件中创建的。然后运行 ml-user 的 bashrc 来设置本地环境变量。接下来移动到 ml-user 的主目录,并将所有需要的路径添加到 path 变量中(记住我们是在 root 用户的身份下运行的)。
最后,我们使用 nohup 在后台运行 Jupyter,并在一个目录中的文件中捕获标准错误和标准输出,该目录是专门创建的(再次在 Dockerfile 文件中),用于映射来自主机的卷。最后的 bash 命令确保容器在运行时不会退出,并保持对交互式会话可用。
用于存储 jupyter 日志文件的/ml-disk 目录是容器中的目录,当运行 docker run 命令时,它将被映射到主机上的一个卷,如下所示。
你可以从https://github . com/run 2/demystify-xgboost/blob/master/entryPoint.sh下载 entry point . sh
建造它
让我们将这个容器命名并标记为 de-神秘化/rnpython:1.0。
我们可以使用下面的命令来构建它
docker build -t 去神秘化/rnpython:1.0。(不要忘记点号或合适的目标路径)
这将需要一些时间,你完成了!!🥳🥳🥳
此时,如果您在 gcp 上,您可以标记它,并将其推入 GCP 容器注册。或者使用自己的容器注册中心。甚至是 dockerhub。
注意,如果您在运行 docker 命令时遇到权限问题,您需要将您的用户添加到 sudo 组。见上面提到的 docker 的官方链接。
标记并推送到容器注册表
首先发射命令 docker 图像
获取刚刚创建的 de-mystic/rn python:1.0 docker 图像的图像 id
为了 GCP
标记一下:docker tag【gcr.io/<你的 gcp 项目名>/demystify-rnpython:1.0
Push:docker Push gcr.io/<你的 gcp 项目名>/dr-神秘化-rn python:1.0
对 Dockerhub 或您自己的回购使用类似的标记和推送。
搞定了。!
运行它
您现在已经准备好运行容器了。
先拉一下。
码头工人拉 gcr.io/
那就运行它。
docker run-v/home/
它使您的容器在伪 tty 上交互,而-d 分离它,这样交互 shell 就不会启动。
这里的-v 命令将/home/ 
-p 命令设置从主机端口 8888 到容器端口 8888 的端口转发(Jupyter 服务器将监听的默认端口,在配置文件中有提及)
就是这样。此时,您应该能够在主机上直接打开 https://localhost 。或者,如果您的主机是一台 VM 或服务器,并且您想从您的本地机器访问 Jupyter,您可以从您的本地主机使用 ssh 隧道,如下所示。
ssh -N -f -L localhost:8888:localhost:8888 [ml-user@host](mailto:dbhatt@addawser.engba.symantec.com)
或者如果你在 GCP,你可以从你的本地主机使用 ssh 隧道
gcloud compute ssh --project <your gcp project id> --zone <zone of your hosting vm> <name of your hosting vm> -- -L 8888:localhost:8888
现在,您应该能够从本地浏览器启动 https://localhost 。它会将本地主机上的 8888 通过隧道传输到 VM 主机或服务器的 8888。从那里,端口转发将负责与容器的端口 8888 交换数据包。
你看到了什么?
一旦你启动了浏览器,它应该像下面这样

在左侧,您应该能够看到您的主机上的卷的所有内容(例如/home/ 
父目录是容器上的/ml-disk,该目录映射到主机的/home/ 
GCP 持久磁盘
这一部分只给在 GCP 做这个的人。并且想要在主机 VM 上安装永久磁盘。
在暴露 IAP 隧道和 Https 的情况下,在主机虚拟机上装载磁盘
在 GCP 拥有共享持久磁盘的最佳方式是通过云文件存储。它就像一个 NFS,如果您在由不同用户运行的许多容器实例上使用训练数据集,那么 CFS 是最佳选择,因为它允许在多个实例上进行读写共享。然而,这有点贵,所以我打算用一个普通的磁盘。
但是请记住,对于普通磁盘,您将无法选择多租户读写。它将被锁定,供主机虚拟机独占使用。
下面是我如何创建一个 500GB 的普通磁盘(SSD 很贵),将其命名为 ml-disk (你可以命名为任何东西)并启动我的主机 VM(命名为 hosting-mlcontainer),这个磁盘连接到它。

创建永久磁盘

在创建虚拟机时将其添加到虚拟机中
此外,记住,你的虚拟机需要比你想给容器的更多的内存,所以,相应地分配,比如下面

选择虚拟机体系结构、虚拟 CPU、内存
我通常还会确保不能通过互联网访问启动虚拟机。因此,关闭短暂的外部 ip 地址。而且要确定!您已经打开了 https 访问——因为我们将通过 https 从本地主机进入我们在容器上的 Jupyter 服务器,以便我们可以在本地运行它!!

将外部 ip 设置为无并允许 https 流量
现在,这不是一个 GCP 的博客,所以我不会进入细节,但要访问没有外部 ip 的虚拟机,你可能需要启用 IAP 转发权限。无论如何,一旦虚拟机启动并运行,登录并启动 lbsck 命令。您应该会看到类似这样的内容

sdb 是外部磁盘卷
最后一行是我们刚刚创建的名为 ml-disk 的外部磁盘的卷。现在我们需要装入这个卷。首先,我们需要使用如下命令格式化驱动器
sudo mkfs . ext 4-m 0-F-E lazy _ ritable _ init = 0,lazy_journal_init=0,discard /dev/sdb
然后,我们需要创建一个目录,并将磁盘装入该目录。但是我们每次都需要这样做,对吗?就像每次我们使用相同的外部磁盘创建或启动虚拟机一样,我们需要挂载该磁盘。所以我们最好在虚拟机上(或者在虚拟机模板上)有一个启动脚本。脚本如下所示。

将/dev/sdb 加载到~/ml-disk 目录并允许它对 ml-user 进行写访问的脚本。这个脚本保存了 fstab 文件的副本,该文件将磁盘挂载到分区中,并修改它以在上述目录中挂载/dev/sdb 卷。你可以在https://github . com/run 2/demystify-xgboost/blob/master/VM-startup找到这个脚本
我们需要将该启动脚本放在虚拟机或虚拟机模板的“管理”选项卡下。

将外部磁盘装入指定文件夹的虚拟机启动脚本
搞定了。
改进 docker 有很多方法和细节可以做。请评论并告诉我你们是如何改进它的。
在本系列的下一部分,我将展示如何通过 Jupyter Lab 内核在这个容器上使用 R 和 Python,并在解开 XGBoost 之谜中使用真实的例子。
参考
序幕
medium.com](https://medium.com/@b.debanjan/de-mystifying-xgboost-part-ii-175252dcdbc5) [## 揭开 XGBoost 第一部分的神秘面纱
为什么为什么?
medium.com](https://medium.com/@b.debanjan/de-mystifying-xgboost-part-i-f37c5e64ec8e) [## 运行笔记本服务器- Jupyter 笔记本 6.0.3 文档
Jupyter notebook web 应用程序基于服务器-客户机结构。笔记本电脑服务器使用两个进程…
jupyter-notebook . readthedocs . io](https://jupyter-notebook.readthedocs.io/en/stable/public_server.html)
使用 tf-idf 构建基于电影内容的推荐系统
基于内容的电影推荐方法

介绍
随着时间的推移,我们越来越依赖在线平台和应用程序,如网飞、亚马逊、Spotify 等。我们发现自己不得不不断地从广泛的选项中进行选择。
有人可能会认为,与选择很少相比,选择多是件好事,但选择过多会导致所谓的“决策瘫痪”。正如巴里·施瓦茨在《选择的悖论》中写道:
“大量的选择可能会使消费者泄气,因为这迫使他们在做决定时付出更多的努力。所以消费者决定不决定,不买产品。或者,如果他们这样做了,做决定所需要的努力会降低从结果中获得的快乐。”
这也导致了另一个更微妙的负面影响:
“大量的选项可能会降低人们实际选择的吸引力,原因是考虑一些未选择的选项的吸引力会降低从选择的选项中获得的快乐。”
一个显而易见的结果是,除非对我们来说变得更容易,否则我们最终不会在多种选择中做任何努力;换句话说,除非这些是根据我们的喜好过滤掉的。
这就是为什么推荐系统已经成为上述平台中的关键组件,在这些平台中,用户有无数的选择。他们的成功将在很大程度上取决于他们缩小选择范围的能力,让我们更容易做出选择。
该领域的一个主要推动力是网飞,它通过研究不断推进最先进的技术,并在 2006 年至 2009 年间赞助了 Netflix 奖,极大地推动了该领域的研究。
此外,网飞的推荐者在平台中有巨大的存在。当我们搜索一部电影时,我们会立即得到一系列我们可能也会喜欢的相似电影:

网飞-个人账户
概述
这篇文章从揭示推荐系统中的不同范例开始,并通过一个基于内容的推荐系统的实践方法。我将使用众所周知的 MovieLens 数据集,并展示我们如何根据它们的特点推荐新电影。
这是关于推荐系统的两篇系列文章(可能更多)中的第一篇,下一篇将是关于协同过滤。
在这里 找到这篇帖子的 jupyter 笔记本版,里面有所有代码 。
推荐系统的类型
大多数推荐系统利用协同过滤和基于内容的过滤中的一种或两种。尽管当前的推荐系统通常将几种方法组合成一个混合系统。以下是这些方法的概述:
- 协同过滤:这些方法背后的主要思想是利用其他用户的偏好和品味向用户推荐新的项目。通常的程序是找到相似的用户(或项目)来推荐那些用户喜欢的新项目,并且被推荐的用户可能也会喜欢。
- 基于内容:基于内容的推荐器将使用关于项目的专有数据。为此,我们需要对用户的偏好有一个最低限度的了解,这样我们就可以推荐与用户指定(或推断)的标签/关键词相似的新项目。
- 混合方法:,顾名思义,包括结合协同过滤、基于内容和其他可能方法的技术。现在大多数推荐系统是混合的,如因式分解机的情况。
电影镜头数据集
测试推荐系统最常用的数据集之一是movie lens 数据集,它包含来自 MovieLens 网站的评级数据集。对于这篇博客,我将使用一个数据集,其中包含 6000 名 MovieLens 用户制作的大约 4000 部电影的 100 万匿名评级,这些电影于 2003 年 2 月上映。
让我们看一下这个数据集中包含的数据。我们有三个。csv 文件:收视率、用户、电影。这些文件将作为熊猫数据帧加载。我们有一个评级文件,看起来像:
ratings.sample(5)
        user_id  movie_id  rating
376907     2201      2115       5
254402     1546      2329       5
520079     3208      3300       3
534583     3300      2248       5
325635     1926      1207       4
movies数据集如下:
movies.sample(5)
      movie_id                           title          genres
948        960     Angel on My Shoulder (1946)     Crime|Drama
645        651           Superweib, Das (1996)          Comedy
3638      3707           Nine 1/2 Weeks (1986)           Drama
511        515  Remains of the Day, The (1993)           Drama
2144      2213      Waltzes from Vienna (1933)  Comedy|Musical
既有一个movie_id、title又有一个字符串,所有的genres都由字符|分隔。
和用户数据集,包含用户的基本信息:
users.head()
   user_id gender zipcode  age_desc              occ_desc
0        1      F   48067  Under 18          K-12 student
1        2      M   70072       56+         self-employed
2        3      M   55117     25-34             scientist
3        4      M   02460     45-49  executive/managerial
4        5      M   55455     25-34                writer
电影类型
正如我们将在下一节中探讨的,类型本身可以用来提供一个相当好的基于内容的推荐。但在此之前,我们需要分析一些重要的方面。
- 最受欢迎的流派有哪些?
这将是在构建基于内容的推荐器时要考虑的相关方面。我们想了解在定义用户的品味时,哪些类型是真正相关的。一个合理的假设是,恰恰是那些不受欢迎的类型更符合用户的口味。
最相关的类型有:
genre_popularity = (movies.genres.str.split('|')
                      .explode()
                      .value_counts()
                      .sort_values(ascending=False))
genre_popularity.head(10)Drama         1603
Comedy        1200
Action         503
Thriller       492
Romance        471
Horror         343
Adventure      283
Sci-Fi         276
Children's     251
Crime          211
Name: genres, dtype: int64
或者,为了更直观地展示,我们可以绘制一个带有流派的词云:
genre_wc = WordCloud(width=1000,height=400,background_color='white')
genre_wc.generate_from_frequencies(genre_popularity.to_dict())plt.figure(figsize=(16, 8))
plt.imshow(genre_wc, interpolation="bilinear")
plt.axis('off')

生成体裁词云
如你所见,最常见的类型是戏剧、喜剧和动作片。然后,我们有一些不太常见的其他类型,如西部片、奇幻片或科幻片。正如我之前指出的,后者是我们在推荐时想要给予最大重视的。但是,这是为什么呢?
作为一个例子,让我们考虑一个用户想要找到一部类似于“ 【善、恶、丑 ”的电影,它是西部片、动作片和冒险片的混合体。你认为哪种类型更适合向用户推荐电影?大概是西方的,因为将会有许多非西方的动作或冒险电影,这可能会导致推荐许多非西方电影。
构建基于内容的推荐器
在这篇文章中,我们将基于电影类型建立一个非常简单的推荐器。解决这个问题的一个相当常见的方法是使用一个 tf-idf 矢量器。
虽然这种方法更常用于文本语料库,但是它拥有一些有趣的属性,这些属性对于获得数据的 向量表示 是有用的。该表达式定义如下:

这里我们有术语频率的乘积,即给定术语(类型)在文档(电影的类型)中出现的次数乘以右侧术语,这基本上根据给定术语在所有文档(电影)中出现的次数来衡量术语频率。
包含给定流派( df_i) 的电影越少,得到的权重越高。对数基本上是为了平滑除法的结果,即避免由于右手项而导致的巨大差异。
那么,为什么这对我们有用呢?
正如已经提到的,tf-idf 将通过给予不太频繁的类型更高的权重来帮助捕捉每部电影的重要类型,这是我们用CountVectorizer无法获得的。
tf-idf
为了获得 tf-idf 向量,我将使用 sklearn 的TfidfVectorizer。然而,我们必须考虑这个问题的一些特殊方面。在处理文本数据时,通常的设置是设置一个word分析器和一个ngram_range,它也将包括指定范围内的 n 元语法。一个例子是:
from sklearn.feature_extraction.text import TfidfVectorizers = "Animation Children's Comedy"
tf_wrong = TfidfVectorizer(analyzer='word', ngram_range=(1,2))
tf_wrong.fit([s])
tf_wrong.get_feature_names()
# ['animation', 'animation children', 'children', 'children comedy', 'comedy']
然而,在这种情况下,这实际上没有意义,因为类型的顺序是不相关的,我们想要考虑给定电影的类型的组合,而不考虑顺序。对于上面的例子,我们想要:
from itertools import combinations
[c for i in range(1,4) for c in combinations(s.split(), r=i)]
[('Animation',),  ("Children's",),  ('Comedy',),  ('Animation', "Children's"),  ('Animation', 'Comedy'),  ("Children's", 'Comedy'),  ('Animation', "Children's", 'Comedy')]
在这里,我们找到了直到k(这里是 4)的流派组合集,或者用数学术语来说,就是超集。包括类型的n>1组合,将意味着 tf-idf 矢量器也将考虑这些组合在所有电影中出现的频率,给出现最少的那些分配较高的分数。
我们可以使用analyser参数应用上面的逻辑,我们可以使用这个参数从原始输入中获得特征序列,使用一个可调用的:
tf = TfidfVectorizer(analyzer=lambda s: (c for i in range(1,4)
                     for c in combinations(s.split('|'), r=i)))
tfidf_matrix = tf.fit_transform(movies['genres'])
tfidf_matrix.shape
# (3883, 353)
这将产生以下 tf-idf 向量(注意,只对列和行的子集进行采样):
pd.DataFrame(tfidf_matrix.todense(), columns=tf.get_feature_names(),   index=movies.title).sample(5, axis=1).sample(10, axis=0)

向量间的相似性
下一步将寻找类似的 tf-idf 向量(电影)。回想一下,我们已经将每部电影的类型编码到一个 tf-idf 表示中,现在我们想要定义一个接近度度量。一个常用的度量是余弦相似度。
这种相似性度量因其等于被比较的两个向量之间角度的余弦值而得名。两个向量之间的角度越小,余弦值就越高,从而产生更高的相似性因子。它表示如下:

图片来源
其中,由于内积可以表示为幅度乘以两个向量之间角度的余弦的乘积,因此很明显,上述内容可以表示为余弦:

图像来源
因此,通过取两个向量之间的内积,并根据它们各自的幅度进行归一化,得到两个向量之间的余弦 。
为了计算所有 tf-idf 向量之间的余弦相似性,我们可以再次使用 scikit-learn。[sklearn.metrics.pairwise](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise_distances.html)包含许多成对的距离度量,其中有[cosine_similarity](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise.cosine_similarity.html) ,它将计算所有输入行之间的余弦相似性,在本例中为 tf-idf 向量:
from sklearn.metrics.pairwise import cosine_similarity
cosine_sim = cosine_similarity(tfidf_matrix)

现在我们必须定义一些逻辑来找到给定电影的最高权重或 tf-idf 分数。为此,我定义了以下函数,该函数将一部给定的电影i、相似度矩阵M、项目数据帧作为输入,并返回多达k的推荐:
通过使用argpartition,我们在给定的索引(电影)i上取M(相似矩阵)中的k最高值。然后,我们对M中的列进行索引,并进一步从最高权重到最低权重进行切片(排除最高权重,这将是同一项,因为矩阵是对称的,因此第二行中有2s)。
测试推荐器
我们用几个例子来测试一下推荐器。由于movies数据集包含了 2000 年以前的电影,我们将不得不使用一些经典电影。
'2001: A Space Odyssey'怎么样?
movies[movies.title.eq('2001: A Space Odyssey (1968)')]
    movie_id                              title              genres
912      924       2001: A Space Odyssey (1968)  Drama|Mystery|Sci-Fi|Thriller

2001:太空漫游
我们可以看到它的流派是Drama|Mystery|Sci-Fi|Thriller。看看有什么推荐吧!😃
genre_recommendations('2001: A Space Odyssey (1968)', cosine_sim_df, movies[['title', 'genres']])

正如所料,最相似的电影是那些分享最多类型的电影。有趣的是,我们看到大部分都是Sci-Fi电影。请注意,这很可能受到以下事实的影响:在上面出现的流派中,Sci-Fi通常具有较高的分数,因为它出现的频率最低,因此具有较高的权重。
让我们看看我最喜欢的科幻电影《T2 》:
movies[movies.title.eq(‘Contact (1997)’)]
      movie_id           title        genres
1543      1584  Contact (1997)  Drama|Sci-Fi

接触
在这种情况下,我们会得到:
genre_recommendations('Contact (1997)', cosine_sim_df, movies[['title', 'genres']])

似乎有许多电影分享相同的类型,因此我们得到了所有Drama|Sci-fi电影的推荐。
现在让我们来看一部像《T4》这样的动画电影:
movies[movies.title.eq('Jungle Book, The (1967)')]
      movie_id                    title                             
2009      2078  Jungle Book, The (1967) 
                             genres
Animation|Children's|Comedy|Musical
我们得到以下建议:
genre_recommendations('Jungle Book, The (1967)', cosine_sim_df, movies[['title', 'genres']])

似乎有两部电影分享了完全相同的类型组合,其余的也类似,但没有Comedy类型。
或者让我们试试最后一个,'Saving private Ryan':
print(movies[movies.title.eq('Saving Private Ryan (1998)')])
      movie_id                       title            genres
1959      2028  Saving Private Ryan (1998)  Action|Drama|Wargenre_recommendations('Saving Private Ryan (1998)', cosine_sim_df, movies)

结束的
总的来说,我们已经看到,一个相当幼稚的基于内容的推荐器可以提供相当好的结果。
基于内容的推荐器的一个明显优势是,它们不会遭受 冷启动 问题,因为我们只需要关于用户的基本信息(在这种情况下是单部电影)来基于项目提供类似的推荐。另一个有趣的优势是,我们能够向具有独特品味的用户推荐,这对于协同过滤方法来说可能更具挑战性。
然而的一个重要缺点是它倾向于向用户推荐相同类型的项目。为了能够推荐不同类型的项目,用户必须已经对新类型的项目进行了评级或者已经表现出了兴趣。这是协同过滤方法没有的问题,因为这里的匹配是在具有相似品味但不同项目评级的相邻用户之间进行的。
非常感谢你花时间阅读这篇文章,希望你喜欢:)
处理高偏差和方差
通过方程解释正则化
内容
在本帖中,我们将了解:
(I)评估机器学习模型性能的方法
㈡适配不足和适配过度的问题
(三)偏差-方差权衡
㈣解决高偏差和高差异问题
在之前的帖子中,我们研究了逻辑回归、数据预处理,并在 Kaggle 上的 titanic 数据集上进行了实际操作,获得了不错的结果。在到目前为止的两篇帖子中,你一定已经注意到我在这里和那里抛出了过度拟合这个术语,并且还提到它会导致机器学习模型的性能不佳。现在,我们将详细了解机器学习模型可能遇到的问题,以及它们可能的解决方案。在这篇文章中,我们不会亲自动手,但是在接下来的文章中,我们将应用我们在这篇文章中学到的概念。
评估模型的性能
在直接进入机器学习模型中出现的问题之前,我们如何知道我们的模型有问题?为此,我们需要一个模型的评估指标。我们已经看过一些了。我们使用决定系数(r 分数)来评估线性回归模型,使用精确度来评估逻辑回归模型。这两个指标都是由我们通过它们各自的成本函数得到的潜在误差计算出来的。为了解决机器学习模型的问题,我们将使用这个错误来做决定。
线性回归和逻辑回归的误差计算方式不同。为了让事情更容易理解和发展直觉,我们将从线性回归的角度来看待事情,但我们将定义的术语和它们扮演的角色将与任何其他机器学习模型完全相同。
一个错误就是一个“错误”,这个错误的程度可以量化为真实值和估计值之间的绝对差值。对于“n”个数量,它可以表示为:

在训练和测试集上计算模型性能的误差有助于我们识别模型所面临的问题。考虑以下场景:
(I)低训练集误差,低测试集误差
(ii)低训练集误差,高测试集误差
(iii)高训练集误差,高测试集误差
(iv)高训练集误差,低测试集误差
让我们一个一个地看看这些场景。
如果训练集误差和测试集误差都很低,这意味着模型在训练集上很好地学习了输入-输出映射,并且也能够很好地将其推广到测试集。这是一个好的机器学习模型的期望输出。我们在上一篇文章中训练的逻辑回归模型就是这种情况。
当训练集误差较低但测试集误差较高时,我们说模型过度拟合了训练集。这意味着在训练集上训练机器学习模型后,它对训练集的输入输出映射学习得非常好,但不能将这些映射推广到测试集。让我们试着理解为什么会发生这种情况。为任何任务收集的数据都不可能没有错误,不管过程有多仔细。一个好的机器学习模型应该总是对噪声进行采样,并且只生成那些排除了噪声数据点的输入-输出映射,即一个好的机器模型对噪声是鲁棒的。考虑一场音乐会的情况,艺术家的旋律和人群的噪音都有,但我们只关注艺术家的旋律(输入),因为它使我们愉快(输出),忽略了人群的所有噪音。如果我们注意所有的声音(艺术家+噪音),我们可能不会那么快乐。过度拟合是指机器学习模型关注每一个声音(艺术家+噪音),而实际上我们只需要专注于旋律。对训练数据过度拟合的机器学习模型据说会遭受高方差。在这篇文章的后面,我们将看到如何处理过度拟合。
如果训练集和测试集的误差都很高,则表明机器学习模型没有正确地学习训练集上的输入-输出映射,并且也不能在测试集上进行推广。换句话说,我们可以说我们的机器学习模型处于原始状态。据说这种模型在训练和测试数据集上都存在不足,并且存在高偏差。
具有高训练集误差和低测试集误差的机器学习模型很少出现,但是当训练和测试数据没有被适当地采样时(例如,在分类问题中每个类的样本数量几乎相等)会出现这种情况,这导致测试集的统计特性的显著差异。考虑一个二元分类问题,其中对 A 类的预测给出 10%的训练误差,而对 B 类的预测给出 40%的训练误差。平均分类精度在整个数据集上给我们 25%的误差。但这不是一个好的措施。在现实生活的测试集中,A 类的出现次数很可能多于 B 类。这意味着现实生活数据集中的实际预测误差将大大低于 25%。发生这种情况的另一种情况是,当测试集明显小于训练集时,尽管与训练集相似,但我们在测试集上得到的错误较少,因为与训练集相比,它没有那么多噪声。除了在对数据应用任何机器学习算法之前研究和采样数据之外,没有什么可以避免这种情况。

高偏差、完美拟合和高方差的直观表示(来源
偏差和方差的定义
对于熟悉统计学的读者来说,偏差和方差这两个术语一定不陌生。标准差衡量的是数据点离中心位置有多近或多远,从数学上来说,方差就是标准差的平方。因此,方差衡量的是一组数据分布的范围。用于机器学习任务的数据没有特定的输入-输出映射,这些模型的任务是找到足够好的映射来概括结果。一个机器学习模型,它(过)适合所有的数据点,包括有噪声的数据点,或者换句话说,适合所有的数据点,不管它们分布得有多广,都被称为遭受高方差。
在统计学中,估计量(这里是机器学习模型)的偏差(或偏差函数)是估计量的期望值和给定输入的真实值之间的差异。零偏差的估计量或决策规则称为无偏的。机器学习模型的高偏差是机器学习模型的输出与实际输出相差甚远的情况。这是因为模型简单。我们之前看到,具有高偏差的模型在训练集和测试集上都具有高误差。
偏差-方差权衡
偏差-方差权衡是监督机器学习算法的核心属性。理想情况下,我们需要一个机器学习模型,它考虑所有模式以及训练数据中的异常值,并将它们推广到测试(看不见的真实世界)数据,以便实现非常小的误差和非常高的准确性。我们之前看到,高方差模型非常复杂,可以很好地表示训练集的所有特征,从而使训练集的误差最小,但无法推广到看不见的数据。相比之下,高偏差模型表示极其简单的映射,并且可以将一些特征推广到看不见的数据,但是这些模型的简单性导致对训练集的欠拟合,并且当应用于训练集之外的数据时,生成具有较低方差(高偏差)的预测。特定机器学习模型应该具有的偏差和方差的理想量取决于误差(包括偏差误差、方差误差和噪声)的最小化。

偏差-方差权衡(来源
解决机器学习中的问题
建立机器学习模型是一个迭代的过程。看过数据集后,我们应该总是从简单的模型开始,然后不断增加它们的复杂性,直到我们在看不见的数据上得到想要的结果。极其简单的机器学习模型遭受高偏差,而极其复杂的机器学习模型遭受高方差。由于我们是从简单模型逐步过渡到复杂模型的,因此我们消除了高偏差的问题,但消除高方差并不容易,因为有些情况下,使用给定的一组参数和方法,我们无法获得最佳模型,而高方差模型需要经过处理才能获得最佳模型。所以先讨论几种解决方差大的方法。
解决高差异
考虑逻辑回归分类器的例子。如果我们说分类器过拟合训练数据,这意味着等式 y = sigmoid(Wx + b)的输出非常接近实际训练数据值。那么,过度拟合的根本原因是什么呢?显然,是我们在构建分类器时训练的参数值造成了机器学习模型的高方差(过拟合)。调整这些参数值有助于消除过度拟合,这个过程称为正则化。在正式术语中,正则化是添加信息以解决不适定问题或防止过度拟合的过程。正则化使参数值变小,这可以防止过度拟合。在这篇文章的后面,我们会看到为什么会这样。

高方差模型训练和测试误差
正规化
因为我们正在修改参数值,所以我们需要更新我们的成本函数,以便看到正则化的效果。 L2 正则化最常用于成本函数,并产生相当好的结果。L2 正则化后的成本函数为:

其中 y(i)表示训练示例 I 的实际输出,φ(z(I))是通过逻辑回归对训练示例 I 的预测值, λ 是正则化参数,||w||是权重 w 的向量的 L2 范数。权重的矢量化只不过是由向量包围的彼此堆叠的所有 Wi。Python 的机器库使用矢量化参数方程来加速计算。
假设向量 W 具有 3 个值 W1、W2、W3,则向量 W 的 L2 范数计算如下:
||W|| = sqrt(W1 + W2 + W3)
请注意,我们没有像在一般逻辑回归分类器中那样正则化参数 b,每个特征都有一个相应的 W 值,因此有多个 W 值和一个偏差 b 值,正则化偏差参数实际上没有任何区别。为了完整起见,我们通过在成本函数的末尾添加( λ/2)*||b|| 来正则化 b,并对两者使用单独的 λ 值。
为什么以及如何正规化工作?
现在,在执行梯度下降以更新参数时,更新将如下所示:
W = W — alpha * dJ/dW
b = b—α* dJ/db
dJ/dW 项由由于正则化而产生的附加项组成,并且在数值上是正值,即当代价函数被正则化时,代价函数 wrt W 的偏导数值比没有正则化时更大。因此,在更新参数时,我们从 W 的先前状态中减去一个更大的块,并且我们进行多次迭代,W 的最终值小于我们在没有正则化的情况下获得的值。同样,我们可以通过正则化来计算 dJ/db。
现在我们知道,正则化导致小的参数值,这导致收敛于代价函数的全局最小值。我们将使用带有“n”个例子的逻辑回归来看看为什么正则化有效。我们知道 y = sigmoid(w1x 1+w2x 2+w3x 3+…………+ Wnxn + b)。因为正则化导致比非正则化更小的参数值,所以让我们考虑参数值 Wi 非常小并且非常接近于 0。因此,逻辑回归分类器的输出将简单地为 y = sigmoid(b ),这是一个非常简单且非常差的输出估计。换句话说,这个输出非常简单,以至于它有一个极高的偏置。从偏差-方差权衡中,我们知道高偏差和高方差是机器学习模型的两个相反端,理想情况下,我们希望我们的模型处于两者之间。为了实现这一点,我们必须非常明智地选择正则化参数 λ 。 λ 是使用开发集设置的,其中我们尝试各种 λ 值,并选择一个为我们的机器学习模型产生最佳性能的值,然后使用该模型在测试集上进行预测。
像 L2 正则化一样,还有另一种类型的正则化项可以添加到成本函数中。添加此 L1 正则化项后的成本函数如下:

其中||w||是矢量 W 的 L1 范数,具有 3 个元素的矢量 W 的 L1 范数计算如下:
||W|| = |W1| + |W2| + |W3|其中,|Wi|代表 Wi 的绝对值。
添加更多培训数据
处理机器学习模型过度拟合的另一种方式是向训练集添加更多数据。这背后的原因很简单。如果机器学习模型在训练集上过度拟合,它也在学习数据中的噪声输入。添加更多的数据将导致数据中更多的噪声,并且机器学习模型变得难以考虑如此多的噪声,以至于它最终离开有噪声的输入,并且更加关注输入-输出对的一般模式。这导致模型不会过度拟合训练数据,并且没有高方差的问题。要添加到训练集中的数据量取决于机器学习模型的过拟合程度。
解决高偏置
机器学习模型中的偏见不是我们前面看到的大问题,并且很容易消除。由于高偏差导致极其简单的机器学习模型,该模型不能捕获所有必要的特征来进行更准确的预测,因此我们可以做以下事情来消除高偏差:
(I)使用比现有模型更复杂的机器学习模型(通过引入多项式特征而不是线性特征,如 y = Wx + b ),因为它可以很好地捕捉训练数据中的所有重要特征和模式。
(ii)我们看到正则化大幅缩小了参数值,这也可能导致高偏差。因此,减小正则化参数有助于消除高偏差。

高偏差模型训练和测试误差
在这篇文章中,我们首先发展了对高偏差和高方差的直觉,然后理解了当其中一个或两个都出现时,机器学习模型可能遇到的问题。后来,我们研究了从机器学习模型中消除高偏差和高方差的方法,并对正则化的工作提出了见解。
在下一篇文章的中,我们将看看另一种被称为支持向量机的监督机器学习方法,并将使用它来解决 Kaggle 的数据集。
使用 KDWD 的基于上下文的实体链接
由蔚茹·陈、迪安·哈索特、大卫·郑、泰勒·柳撰写
的代码可以在我们的 Github 上找到
我们要感谢ken sho的 Gabriel Altay 和 Georg Kucsko,感谢他们在整个项目中与我们分享时间和资源。**
最后,我们感谢哈佛大学 IACS 的克里斯·坦纳对我们团队的宝贵指导,以及他在整个 顶点 经历中的领导。
简介
命名实体链接,也称为命名实体消歧(NED),是唯一识别文本中提到的实体(如个人、位置、公司或历史事件)的任务。举一个典型的例子,如果给定句子“巴黎是法国的首都”,我们希望能够辨别单词“巴黎”是指法国首都、其他城市、巴黎希尔顿还是许多其他可能性,如下所示。

图 1:维基百科的“巴黎”消歧页面
与命名实体识别(NER)——实际识别文本中提到的这种实体的过程——一起,NED 是自然语言处理(NLP)中最基本的任务之一;能够识别文本谈论的特定事物对于无数的自然语言处理应用来说是必不可少的,包括一般的文本分析、语义搜索系统、构建聊天机器人等等。
让我们考虑一个金融市场中有趣的例子来说明这项任务在 NLP 应用中的重要性。假设我们正在构建一个算法股票交易器,它被设计用来根据新闻事件进行交易。一天早上,我们的算法在一个新闻标题中发现了“特斯拉崩溃,吉姆·克莱姆期待反弹”这句话。马上,我们看到了一个我们的算法交易员必须能够解决的问题:“特斯拉”这个词是指上市公司、特斯拉汽车的一个实例,还是(荒谬地)尼古拉·特斯拉?这个问题对我们的投资组合有非常实际的影响;如果“特斯拉”指的是该公司,那么我们知道标题是说特斯拉股票暴跌,鉴于其目前的低价,这可能是购买特斯拉股票的好时机。另一方面,如果“特斯拉”指的是特斯拉汽车,我们知道这对特斯拉来说是一个坏消息(可能是自动驾驶技术的问题导致特斯拉汽车在高速公路上坠毁),这意味着现在是出售特斯拉股票的好时机。
作为人类,我们发现这个问题微不足道——我们可以很容易和自信地从标题其余部分的上下文中推断出,这实际上是指特斯拉股票,因为提到了实体“Jim Cramer”和“Rally”,以及这些实体与金融的关系。

图 2:维基百科中关于“吉姆·克莱姆”的条目

图 3:维基百科中“反弹(股票市场)”的条目
命名实体消歧的任务旨在对这种基于上下文的推理能力进行建模。
知识图和 KDWD
对基于上下文的推理能力进行建模的重要的第一步是开发可用于从上下文进行推理的知识的表示(例如,单词“集会”具有与“股票”相关的含义)。我们通过知识图来做到这一点,这是一个知识库的图形化表示,节点对应于实体,边对应于实体之间的关系(参见图 4)。
这样的表示有什么帮助呢?嗯,在我们上面的例子中,如果我们可以使用我们的知识图表来识别单词“集会”是股票市场运动的一个例子,我们就更接近于推断“特斯拉”指的是特斯拉股票。
对于我们的方法,我们使用 Kensho 衍生的维基媒体数据集(KDWD),这是由 Kensho 设计的维基数据知识图的结构化、多层呈现,Kensho 是最著名的机器学习和人工智能公司之一,部分专注于 NLP。Kensho 慷慨地向公众提供了 KDWD,为感兴趣的研究人员提供了一种资源来处理 NLP 任务,如消歧。

图 KDWD1 的鸟瞰图
KDWD 由三个数据层组成:维基百科文本、维基百科链接和维基数据图表。第一层,顾名思义,就是来自维基百科大量文章的文本。第二层添加链接标注,第三层是全知识图。KDWD 将图表过滤成 5100 万个条目和 1.4 亿条语句,比如“哈佛马克一号是计算机的一个实例。”我们将参考 KDWD 中包含的文件,这些文件可以在https://www . ka ggle . com/kenshoresearch/ken sho-derived-wikimedia-data上查看和下载
探索性数据分析
对于 KDWD 数据集,我们有两个主要目标:
- 创建带标签的训练/测试数据集
- 减少查找项目所需的时间
标注训练/测试数据集:
我们从'link _ annotated _ text . jsonl文件中生成了一个带标签的数据集。由于文本简介拥有最多的链接,也就是标签实体,我们创建了两个新的 CSV,一个包含 530 万个简介文本,另一个包含 3500 万个标签实体。这些步骤将大小从 17.7 Gb 减少到 3.7 Gb。

图 5:创建了维基百科介绍性文本和标签实体的 CSV 文件。
减少访问次数:
我们为基线模型使用了' item.csv '、 item_aliases.csv '和' page.csv '文件,如下所述。然而,在 item 和 item_aliases 中查找一个“item”的所有实例,然后将item _ id与相应的 page _ ids 链接起来,需要大约 1 分钟的时间。这当然太慢了,所以我们采取了以下步骤来大幅提高性能。
- ****减少数据集大小:我们通过删除在“ page.csv ”中不存在的带有 item_id 的所有项目,过滤了“ item.csv ”和“ item_aliases.csv ”数据集。这是因为我们的训练/测试数据集中的实体仅由 page_id s 标记。这将组合的 item 和 item_aliases 数据集大小减少了 88.2%。

图 6:项目过滤
****2。重构数据以提高性能:减少数据集将每个实体的查找时间减少到大约 10 秒,但这仍然太慢了。因此,我们将数据结构改为字典,以利用 O (1)查找时间:
2.1.合并过滤后的' item.csv 和' item_aliases.csv ',这样我们只有两列: item_id 和 en_label 。
2.2.小写并剥离所有 en_label 条目
2.3.通过 en_label 对数据集进行分组
2.4.将与每个 en_label 相关联的item _ id转换成一个列表,并丢弃重复的item _ id。

图 7:带有 en_label 和相应item _ id的数据帧
2.5.创建一个字典,将 en_label 条目作为键,将相应的 item _ ids 列表作为值。

图 8:创建 2.5 中描述的字典的代码片段
完成上述所有步骤后,我们将项目数据集大小从 4.04Gb 减少到 177Mb。我们现在也有了近乎瞬时的查找时间。
基线建模
我们现在准备为实体链接开发一些简单的基线模型。
人气基线模型
第一个基线模型只是选择最流行的实体作为每次提及的实体预测。对于每个提及,我们从 item_dict 字典中识别出与之相关的所有 item_id 。每个 item_id 都唯一地链接到一个 page_id ,这是我们对实体的引用。对于每个 page_id ,还有一个相关联的页面视图,表明特定实体的受欢迎程度(也就是说,最‘受欢迎’的实体是具有最高维基百科页面视图的实体)。因此,对于每一次提及,我们可以创建一个候选实体列表,并给出它们各自的流行度值。然后,我们的基线模型预测提及的最受欢迎的实体。
第二个基线模型与第一个基线模型相同,只是它选择指向它的链接最多的实体作为每次提及的实体预测。
基线模型的算法: 给定一个提及 w ,其关联的实体候选 e_1 , e_2 ,…, e_k ,以及实体候选 p_1 , p_2 ,…,的页面浏览量(或第二基线模型中指向它的链接数)**

并返回 e_m 。所以对于提 w 的实体的预测是:

带有单词嵌入的基线模型
对于给定段落中的任何提及,我们首先使用 spaCy 包来识别上下文窗口中所有相邻的命名实体。然后,我们从 google2vec 模型中加载预训练的单词嵌入,并将单词嵌入分配给所有实体。通过计算我们在上下文窗口中识别的相邻命名实体的词嵌入和每个提及的实体候选的词嵌入之间的余弦相似度,这个具有词嵌入的基线模型然后用与相邻实体具有最小余弦距离的实体来标记提及。
具有单词嵌入的基线模型的算法: 给定一个提及 w ,其关联的候选实体 e_1 , e_2 ,…, e_k ,以及相对于候选实体 p_1 , p_2 ,…, p_k 的单词嵌入,识别所有邻近的命名实体 n 然后,计算每个候选实体的单词嵌入和相邻命名实体的单词嵌入之间的平均余弦距离:



查找:

并返回 e_m 。所以对于提 w 的实体的预测是:

模型评估的标准:
我们使用总体准确率作为评估所有基线模型的标准。我们从来自 link_annotated_text.jsonl 文件的 combined_entity_df 数据帧中随机抽取 k = 20,000 个项。这些被采样的项目都来自文本数据,它们的实际实体是已知的。因此,对于每个项目 wi,我们从基线模型中获得其预测实体 predi,从组合的 _entity_df 数据框架中获得其实际实体 yi。整体准确率测量为 1 {pred _ I= =y _ I}/k .**
所有基线模型的结果和见解:

图 9:所有基线模型准确性得分
基于来自组合 _ 实体 _df 数据帧的采样项目,我们的第一流行度基线模型具有 61.83%的准确率,第二流行度基线模型具有 59.135%的准确率。这表明我们的流行度基线模型表现相对较好,考虑到它完全基于流行度选择,并且项目的上下文信息(在文本数据中提及)被完全丢弃。
我们的带有单词嵌入的基线模型表现最好,准确率为 66.74%,略好于前两个基线模型。
超越基线:作为组合优化的歧义消除
我们对开发一种更复杂的方法感兴趣,这种方法使用 KDWD 图结构中的连通性信息来消除歧义。具体来说,当我们看到一个目标单词 X 及其候选消歧词 d_1,d_2,…,d_n 时,我们希望图结构告诉我们候选词集中的哪个 d_k 与目标单词的上下文窗口(包含该单词的句子)中的其余单词在语义上最相关。
Hulpus 等人提出了一种有趣的用于图形表示的联合消歧方法。这种方法将消除歧义视为一个组合优化问题。因此,给定一个目标单词 X 和它的上下文窗口,我们识别上下文窗口中的所有歧义术语。然后,我们考虑所有唯一的歧义消除组合,并选择一个最小化所有歧义术语的所有不同成对行走距离总和的组合。
正式地说,对于消歧的唯一组合的集合 D,如果我们将 w(a,b) 定义为在图上从节点 a 移动到节点 b (沿着经由 BFS 找到的最短路径)的行走次数,我们的消歧选择 D 被定义如下:*

作为一个例子,考虑这句话,“本田在电动车行业与捷豹竞争。”三个突出显示的实体及其所指的候选集是:
本田:[H1:企业家,H2:汽车品牌]
捷豹:[J1:汽车品牌,J2:动物]
电动汽车:[E1:电动汽车,E2:期望值]
因此,所有这些可能的组合是:
(H1,J1,E1),(H1,J1,E2),(H1,J2,E1),(H1,J2,E2),(H2,J1,E1),(H2,J1,E2),(H2,J2,E1),(H2,J2,E2)

图 10:显示示例节点之间语义距离的示例图
现在,使用图 10 中显示这些候选的图形部分,我们测量每个组合的成对距离之和,例如,对于第一个组合, D(H1,J1) + D(H1,E1) + D(E1,J1) 。然后,我们选择最小化该度量的组合,从而最大化语义相关度。
结果和未来可能性
在我们测试基线模型的同一数据集上评估该模型,我们实现了 85.5%的准确率,比基线有了显著提高,并且与具有完全不同的高度调整范例的最先进方法相差不远。
速度提升
为了大规模测试这种方法的可行性,必须提高计算效率。我们看到了通过图嵌入和图截断等技术对预测和评估的图遍历瓶颈的潜在改进。
与最先进基线模型的比较
有了更高的计算效率,我们可以在不使用上下文窗口预测的高级消歧学习技术和使用我们的上下文方法的类似技术之间进行富有成效的比较。
参考文献
[1]加布里埃尔·阿勒泰。"介绍 Kensho 衍生的维基媒体数据集."2020 年 2 月 3 日。https://blog . ken sho . com/announcing-the-ken sho-derived-wikimedia-dataset-5d 1197d 72 BCF
[2] OpenAI,“发现实体消歧的类型。”2018 年 2 月 7 日。https://open ai . com/blog/discovering-types-for-entity-diffusion/
[3] Hulpus,Ioana,Narumol Prangnawarat,Conor Hayes。"基于路径的关联数据语义相关度及其在词和实体消歧中的应用."爱尔兰国立大学高威分校数据分析洞察中心(NUIG)。2015.
上下文管理器:数据科学家的观点

python 上下文管理器如何清理您的代码
这篇文章是一个系列的一部分,在这个系列中,我将分享我在干净的 python 代码这个主题上所学到的东西。我是一名数据科学家,希望通过编写更多的 python 代码来提升自己的 python 技能,并找到更好的方法来构建更大的代码库。我正在通读 Python 中的干净代码,并用其他资源丰富材料。我的目标是通过在这里总结来巩固我正在学习的主题,并希望帮助其他人理解这些主题!
这篇文章是关于 Python 中的上下文管理器的。首先,我将描述什么是上下文管理器,以及它们为什么有用。然后,我将带您看一个 web 抓取的实际例子。
什么是上下文管理器?
描述上下文管理器的最好方式是展示一个几乎每个 Python 程序员都在某个时候遇到过的例子,而不知道他们正在使用它!下面的代码片段打开一个. txt 文件,并将这些行存储在 python 列表中。让我们看看这个是什么样子的:
with open('example.txt') as f:
    lines = f.readlines()print(lines)
我们来分析一下。第一行打开一个文件,并将文件对象分配给f。下一行在 file 对象上执行readlines方法,返回一个列表,列表中的每一项代表example.txt文件中的一行文本。最后一行打印列表。使它成为上下文管理器的原因是,一旦 with 语句下的缩进被退出,文件就会被隐式关闭。如果没有上下文管理器,代码将如下所示:
f = open('example.txt')
lines = f.readlines()
f.close()print(lines)
使用上下文管理器使其可读性更好。想象一个打开和关闭多个文件的脚本,它们之间有操作行。它可能会变得难以阅读,并且您很容易忘记关闭文件。即使遇到异常,也要保证成交。
编写自己的上下文管理器
为了说明如何编写自己的代码,我们将使用一个实际的例子。我最近在使用 selenium 进行网络抓取时遇到了一个问题,我无法在现有的浏览器中打开一个新的 url。解决方案是打开一个新的浏览器来访问 url,并在我收集完数据后关闭它。有两种方法可以做到。我们将从定义一个类开始,并使用__enter__和__exit__ dunder 方法。
# imports
from selenium import webdriver
from selenium.webdriver.common.keys import Keys# define context manager class
class OpenBrowser():
    def __enter__(self):
        self.driver = webdriver.Chrome(chrome_driver_path)
        return self.driver
    def __exit__(self, exception_type, exception_value, 
                 exception_traceback):
        self.driver.close()# use context manager
with OpenBrowser() as driver:
    driver.get("[http://www.python.org](http://www.python.org)")
    elem = driver.find_element_by_name("q")
    elem.clear()
    elem.send_keys("pycon")
    elem.send_keys(Keys.RETURN)
    html = driver.page_sourceprint(html)
这看起来很复杂,其实不然。你用__enter()__和__exit()__定义了一个类,它们分别定义了当你通过with调用它时会发生什么,以及当你退出缩进时会发生什么。__exit__的参数是固定的,而__enter__的返回值是可选的。现在读者很清楚,这个驱动程序的存在只是为了在 python.org 搜索 pycon 并获取结果 html。无论发生什么,浏览器都会关闭。
这个语法不算太差,但是有更简单的方法。上下文库提供了一个 decorator 来处理 boiler 板,并用函数替换类。让我们看看上面的例子是如何使用装饰器的:
# imports
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import contextlib[@contextlib](http://twitter.com/contextlib).contextmanager
def open_browser():
    driver = webdriver.Chrome(chrome_driver_path)
    yield driver
    driver.close()with open_browser() as driver:
    driver.get("[http://www.python.org](http://www.python.org)")
    elem = driver.find_element_by_name("q")
    elem.clear()
    elem.send_keys("pycon")
    elem.send_keys(Keys.RETURN)
    html = driver.page_source
这里,我们只是导入contextlib并用 contextlib.contextmanager 修饰一个函数。该函数需要有一个 yield 语句,yield 值是可选的,但它告诉上下文管理器__enter__和__exit__出现的位置,分别在 yield 之前和之后。
结论
我希望我不仅足够好地解释了上下文管理器,而且让你相信它们在你的工具箱中非常有价值。即使作为一名数据科学家,我也发现了许多这些派上用场的案例。欢迎留下任何问题或评论,编码愉快!
法律行业与技术和数据科学的复杂关系

Giammarco Boscaro 在 Unsplash 上拍摄的照片
法律技术中的数据科学
采访阿培尔的斯蒂芬·威尔科克
最近,我们有机会与法律技术公司 Apperio 的首席技术官 Stephen Wilcock 进行了一次交谈。他分享了对整个法律行业的见解;它对创新的立场以及法律技术补充法律的方式。此外,我们还简要讨论了渴望进入法律技术行业的学生应该具备的技能。
为了更好地理解法律技术,最重要的是理解整个法律行业的背景。法律科技是一个新生的新兴行业,仍然比它更著名的兄弟 Fin-Tech(金融科技)落后十年。斯蒂芬将此归因于法律的本质,更具体地说是商法,它构成了英国所有法律支出的绝大部分。商法中的两个主要当事方是律师事务所,另一方是由总法律顾问(“ GC ”)领导的内部法律部门。全科医生向律师事务所支付法律工作和咨询费用;它们是成本中心,因此在它们所属的公司中“不太受欢迎”。因此,他们产生的成本往往被认为是表面价值,并受到审查。

作者图片
没有这种外部压力,律所就缺乏创新的动力。除了极少数例外,它们都是以合伙模式运行的;历史上禁止接受可能损害独立性的外部股东的资本。合作伙伴可能对技术创新不感兴趣,原因有几个。首先,创新是昂贵的——这对一个花股东钱的 CEO 来说可能是微不足道的——合伙人花他们自己的钱;虽然合伙人已经赚了很多钱,但是任何投资都是他们的底线。此外,由于个人合伙人已经获得了巨额收入,他们几乎没有独立改变任何事情的权力。此外,法律领域惯用的按小时计费模式会削弱提高效率的内部商业案例。如果一项法律工作花费的时间更长,客户的账单会更多。这就是 Clayton Christensen 描述的经典的“创新者困境”:在其他人破坏它们之前,公司能否让自己进行昂贵的效率投资,在短期内蚕食其收入,以维护公司的长期竞争力。
根据 Stephen 的说法,摆脱这种困境的方法将通过一系列的措施来实现,包括:不断增加的客户压力,新型结构的出现;如进入法律市场的四大咨询公司,以及“在稳固的商业优势下”提供较低成本的公司。这些因素的结合开始施加必要的外部压力,以改变律师事务所合伙人的计算方式,促进法律行业更快速的技术创新。
创新针对法律行业的两个方面;斯蒂芬称他们为:T2 的法律实践和 T4 的法律事务。法律实践处理实际法律工作的机制。这一领域的技术解决方案通常侧重于研究、证据收集、文本处理以及某些情况下的评估和决策。这些解决方案通常以使用人工智能技术自动化重复活动为目标,如自然语言处理或机器学习。另一方面,法律业务与法律工作本身关系不大,而是专注于法律领域中资金如何转手的机制,其中涉及工作流程工具和分析。大多数法律科技公司都在这两者之间运营。Apperio 提供法律支出跟踪,专门处理法律事务。
在评估对那些渴望进入该领域的人有益的技能组合时,法律技术中这两个领域的区别也是相关的。在主要处理法律事务时,斯蒂芬承认法律知识并不是他进入这个行业的先决条件。尽管领域知识可能对那些进入法律行业的人更有意义,但对工程师来说,领域知识不如其他部门的员工重要。“当你开始研究数据科学的细节时,你可以在任何领域研究数据科学,同样,图像识别技术可以观察乳房 x 光片或汽车上的凹痕”。评价这两个方面的重要性,斯蒂芬说;“技术位用 60 号字体,法律位用 4 号字体”。

作者图片
技术专家需要的关键领域知识通常是由专家提供给他们的,这些专家是为完成特定任务而请来的。唯一真正需要综合法律和技术知识的团队是创始团队。总的来说,他们需要具备识别和阐明他们打算解决的法律问题的知识,以及看到和理解潜在的技术解决方案。
帮助那些对法律技术感兴趣的人,斯蒂芬发现学术数据科学的区别尤其重要。“当人们去大学[…]时,大部分都围绕着[…]如何围绕数据做出决策;你得到了这个很好的数据集;找出这条路径、相关性或模式。”在像法律技术这样的新兴行业中,情况非常不同,大多数数据结构不良,存储在传统的本地技术中。因此,更大的初始挑战是在进入实际的数据科学之前,对基础数据集进行检索、聚合、清理、规范化和修剪。
现在我们已经有了必要的背景,我们可以更好地理解法律技术公司在法律行业中可以和确实扮演的角色,从 Apperio 开始。本系列的下一篇文章将详细介绍 Apperio 提供的产品以及 Apperio 如何与法律行业互动。
【1】参见 2007 年《法律服务法》颁布后,法律中出现的替代性企业结构(ABS)
这篇文章最初发表在 UCL 数据科学协会的数据博客上,现在仍然可以在那里找到,此外还有许多其他有见地的文章讨论数据科学的最新发展。
数据科学可能是人类最有用的发明之一。我们研究和建立共享 DS 系统和博客…
www.ucldss.co.uk](https://www.ucldss.co.uk/blog/)
持续学习——我们在哪里?

随着深度学习社区旨在弥合人类和机器智能之间的差距,对能够适应不断发展的环境的代理的需求比以往任何时候都更大。这一点在 ICML 2020 上表现得很明显,该展会举办了两场不同的研讨会,主题分别是持续学习和终身学习。作为一名与会者,我收集了我认为将影响该领域即将到来的发展的关键要点,包括两个方面:(a) 体验回放(无论是真实的还是增强的)对于最佳表现是不可或缺的,以及(b)一个暂时进化的智能体必须意识到任务语义。在这篇博文中,我将尝试阐明这些特质对于持续学习(CL)代理人的有效性。
【虽然有上述术语的背景会有所帮助,但这篇文章是为没有持续学习文献知识的读者准备的。]
快速复习:在任务增量设置中,持续学习代理在时间步长‘t’被训练识别任务 1,..,t-1,t 而任务 1,…,t-1 的数据可能可用也可能不可用。这种学习动力有两个主要障碍需要克服。第一个是正向迁移(FT) ,它衡量学习如何递增到任务 t 影响代理的知识。就性能而言,一个正 FT 表明,如果允许代理通过任务 1、…、t-1 逐步学习,它应该在任务 t 上提供更好的准确性。
另一个令人满意的特性是反向迁移(BT) ,它测量学习一项任务 t 对先前任务表现的影响。一个正的 BT 意味着学习一个新的任务 t 会提高模型在先前学习的任务 1,…,t-1 上的性能。这种在学习新任务的同时保留先前学习任务的知识的折衷被称为可塑性-稳定性权衡。

持续学习代理的三个权衡:当计算效率高的代理同样令人满意时,可伸缩性开始发挥作用。
基于增量任务训练时采取的步骤,连续学习文献主要包括两类代理来处理上述权衡:(a) 基于经验重放的代理通常存储来自先前任务的有限数量的示例(真实的或生成的)并将这些与新任务的训练数据混合在一起,以及(b) 基于正则化的方法使用额外的损失项来巩固先前的知识。记住这些,现在让我们进入真正的问题!
1.为什么基于记忆预演的方法效果更好?
Knoblauch 等人的工作是 2020 年 ICML 会议上 CL 领域的一个亮点,Knoblauch 等人通过集合论的方法表明,一个最优的连续学习算法需要解决集合相交决策问题的 NP-hard 问题,即给定两个任务 A 和 B,它需要辨别 A 和 B (A ∩ B)的学习所共有的参数。然而,确定这一点至少与确定A∩B 是否为空一样困难(并且可能被认为是击中集合问题?)并且解决方案需要对以前的任务示例有完美的记忆。

图片来源:Knoblauch 等人(2020)。左图:最优 CL 算法搜索满足所有观察任务的任务分布的参数。右图:基于重放的 CL 算法试图找到满足实际任务分布(SAT1:3)的重构近似值(SAT(Q1:3))的参数。
这种完美的记忆有助于重建所有观察到的任务上的联合分布的近似,使得算法现在有效地学习求解单个时间分布的任务,,即,对于时间步长 t ,这相当于找到跨越 1:t 的任务分布的共同表示。我们在 CL 研讨会上的工作进一步倡导了基于重放的方法在人类活动识别环境中的经验有效性[2]。
除了集合论之外,还可以通过参数训练的动态性,将持续学习视为一个学分分配问题来看待重播的好处。正如我们所知,梯度下降通过迭代更新神经网络的参数来工作,目标是最小化训练集的总损失。因此,训练过程可以被视为拔河游戏,其中目标函数导致每个参数的值增加或减少,较大的正值指示该参数应该被分配更多的信用并且更重要。

图片来源:足球蔻驰
因此,在给定的增量时间步长,我们可以将每项任务视为一个团队试图以与训练算法最小化任务损失所需的动量相等的张力来拉动拖船。由此带来的后果是,在每一个增量步骤中,都需要对所有先前和当前的任务进行评估,以平衡这种紧张关系。在给定任务在特定情况下不存在的情况下,模型的参数空间将被更新以被剩余的任务占据。因此,在基于经验重放的方法中,同时存在来自所有先前任务的数据有助于更好地平衡拔河游戏各方之间的紧张关系,同时没有单一任务目标完全支配训练标准。
2.任务语义如何影响 CL 代理的性能?
合作学习研讨会的另一个亮点是 Ramesesh 等人 (2020)研究任务之间的相似性如何影响遗忘程度。他们的结论是,当前一个任务和后一个任务之间的表征相似度为中等时,网络具有最大遗忘。
为了理解这一点,我们需要根据模型学习的权重向量的分量来考虑后续任务的 CL。对于不相关的任务,所学习的权重向量保持彼此正交,而对于具有高相似性的任务,权重向量分量具有最小的角度间隔。受梯度下降训练影响的权重向量 θ 的唯一分量是位于训练数据子空间中的分量,而受训练影响最小的分量是与训练数据子空间正交的分量(见下图,改编自他们的谈话)。

玩具线性回归模型的权重向量的分量
Ramesesh 等人提供了两个描述性的 CL 设置来支持他们的假设。在设置 1 中,模型被训练为将船-卡车分类为第一任务,然后将猫-马或飞机-汽车分类为第二任务,我们看到猫-马识别任务遭受更多遗忘。在设置 2 中,首先训练模型来识别鹿-狗-船-卡车,然后是飞机-汽车识别,对于船-卡车,性能降级最大。

Rama sesh 等人(2019)的两个增量学习设置:(a)设置 1 首先在船-卡车分类问题上训练模型,然后是任务 2,任务 2 可以是猫-马或飞机-汽车分类,(b)设置 2 训练模型首先识别鹿、狗、船和卡车,然后是飞机-汽车识别。
作者指出,在设置 1 中,模型只为车辆建立其表征,因此在第二个任务中动物(猫-马)越来越不相似的表征导致对先前学习的车辆表征的更多遗忘。然而,设置 2 涉及同时在车辆和动物上训练模型,因此动物的表示现在在潜在空间中占据与车辆不同的区域。结果,当面对后一项任务时,动物的学习表征与飞机和汽车的学习表征是正交的,并且退化较少。
本节的其余部分试图从传递干扰的角度对此进行解释。里默尔等人(2019)首先从迁移-干扰权衡的角度来看待持续学习。为了理解这一点,让我们先深入了解一下稳定性-可塑性困境的局限性。正如我们之前看到的,困境表明可以通过减少遗忘来提高学习模型的稳定性,即到目前为止,它保持检查由于当前任务的学习而导致的权重转移,同时最小化由于共享对先前任务重要的权重而导致的干扰。
然而,由于我们对未来任务可能会是什么样子的了解有限,最小化先前任务的权重共享仅解决了问题的一半——与先前学习的任务之一非常相关的未来任务可能需要进一步共享这些权重,并且模型必须能够在不中断先前任务的性能的情况下这样做。我们注意到,显然需要扩展稳定性-塑性两难问题的时间限制,以便考虑未来任务的不确定性。
迁移-干扰权衡考虑了由于增量任务的学习而产生的反向干扰,同时还检查了权重之间的表示的迁移,使得它们不会损害未来的学习。因此,里默尔等人的研究表明,使用相同权重成分学习的任务很有可能在范例之间产生干扰和迁移,而使用不同成分学习的任务受到的迁移和干扰较少。
记住上述观点,现在让我们看看 Ramasesh 等人的两个 CL 设置。在设置 1 中,船-卡车分类任务不同于增量猫-马任务,并且由于模型试图使用相同的权重分量来学习它们,高干扰导致对先前任务的更大遗忘。
然而,在设置 2 中,我们看到该模型被迫对鹿狗和轮船卡车采用不同的表示。由于飞机-汽车的表征更类似于轮船-卡车的分类任务,并且要使用相同的权重分量来学习,这催化了它们之间的权重转移,从而导致更大的遗忘。另一方面,鹿和狗的表示具有与平面和汽车的表示正交的分量,因此不受它们之间禁止的重量转移的影响。
结论:简而言之,我们看到了一个持续学习的代理如何在每个训练步骤中面临一个学分分配问题,以及经验回放如何增强每个手头任务的可信度。此外,任务的语义在被试的遗忘量中起着重要的作用,这可以从迁移干扰的角度来解释。随着该领域继续向大规模和独立于领域的学习发展,更好地理解这些权衡确实是元学习者等更高级培训策略的关键[3]。
参考
- Knoblauch、h . husa in 和 t . die the(2020 年)。最优连续学习具有完美的记忆,并且是 NP 难的。 ArXiv,abs/2006.05188 。
- 韩建华,,米,,叶(2020)。人类活动识别中的持续学习:正则化的实证分析。 ArXiv,abs/2007.03032 。
- m .、Cases、I .、Ajemian、r .、Liu、m .、Rish、I .、Tu、y .、& Tesauro、G. (2019)。通过最大化迁移和最小化干扰来学会学习而不遗忘。 ArXiv,abs/1810.11910 。
- Ramasesh,v .,Dyer,e .,和 Raghu,M. (2020 年)。灾难性遗忘的剖析:隐藏表征和任务语义学。 ArXiv,abs/2007.07400 。
朴素贝叶斯分类器中的连续数据和零频率问题
如何从数学和概念上处理它

凯文·Ku 在 Unsplash 上的照片
在监督学习(分类)的背景下,朴素贝叶斯或者更确切地说贝叶斯学习作为评估其他学习算法的黄金标准,同时作为一种强大的概率建模技术。但是,使用朴素贝叶斯会带来一些挑战。
- 与数值数据相比,它在分类数据的情况下表现良好。那么,当我们拥有的数据本质上是连续的时,我们如何使用朴素贝叶斯进行分类呢?
- 如果测试数据集中的一个实例有一个在训练期间不存在的类别,那么它将赋予它“零”概率,并且不能进行预测。这就是所谓的零频率问题。它扭曲了分类的整体性能。作为一个机器学习爱好者,每个人都应该知道如果出现这种情况应该如何应对。
在这篇文章中,我们将讨论朴素贝叶斯分类器对数字/连续数据的工作原理和零频率问题,以便它可以在以后应用于现实世界的数据集。
这里有两种方法来估计朴素贝叶斯分类器中连续属性的类条件概率:
- 我们可以将每个连续属性离散化,然后用其对应的离散区间替换连续的属性值。这种方法将连续属性转换为有序属性。条件概率 P(X|Y= y ),其中 Y 是目标变量,是通过计算属于类别 y 的训练记录中落在 X 的相应区间内的部分来估计的
估计误差取决于离散化策略以及离散区间的数量。如果间隔的数量太大,则每个间隔中的训练记录太少,无法为 P(X|Y)提供可靠的估计。另一方面,如果间隔的数量太小,那么一些间隔可能聚集来自不同类的记录,并且我们可能错过正确的决策边界。因此,离散化策略没有经验法则。
- 我们可以假设连续变量的某种形式的概率分布,并使用训练数据来估计分布的参数。通常选择高斯分布来表示连续属性的类条件概率。该分布由两个参数表征,均值和方差。

图片 1
现在,我们已经为如何将高斯分布用于连续属性建立了基础,让我们通过一个示例来看看如何将其用作机器学习中的分类器:
以下是我们将使用的数据集:

威滕、弗兰克和霍尔的数据挖掘
在这个特定的数据集中,我们总共有 5 个属性。其中 4 个是我们将要预测的自变量(天气、温度、湿度、风力),一个是因变量(玩耍)。这是一个二元分类问题,因为因变量具有布尔性质,包含“是”或“否”。数据集是序数和数字属性的混合。温度和湿度是数字。Outlook 和 Windy 是序数属性。
由于这是非确定性的或者更确切地说是概率性的方法,因此模型没有学习。
我们将对一个实例进行分类
x = <前景=晴朗,温度=66,湿度=90,风=真>
为了计算这个,我们需要目标变量 Play 的先验概率
实例总数为 14,其中 9 个实例的值为 yes ,5 个实例的值为 no 。
p(是)= 9/14
p(否)= 5/14
按照目标变量,自变量的分布可以写成:

图片 2
为了对实例 x,进行分类,我们需要计算 play=yes 和 play=no 的最大可能性,如下所示:
播放的可能性=是
P(x/是)* P(是)= P(晴天/是)* P(温度= 66/是)* P(湿度= 90/是)* P(真/是)* P(是)
发挥的可能性=否
P(x/否)* P(否)= P(晴/否)* P(温度= 66/否)* P(湿度= 90/否)* P(真/否)* P(否)
由于天真的独立假设,属性个体概率成倍增加。
对于温度和湿度属性,可以使用图像 1 中的高斯分布公式,通过插入图像 2 中属性的平均值和方差值来计算概率。
计算上述等式所需的值为:
p(晴天/是)= 2/9
p(温度= 66/是)= 0.034
p(湿度= 90/是)= 0.0221
p(真/是)= 3/9
和
p(晴/否)= 3/5
p(温度= 66/否)= 0.0279
p(湿度= 90/否)= 0.0381
p(真/否)= 3/5
P(x/是)* P(是)=(2/9)* 0.034 * 0.0221 (3/9)(9/14)= 0.000036
P(x/否)* P(否)=(3/5)* 0.0279 * 0.0381 (3/5)(5/14)= 0.008137
0.008137>0.000036
分类—无
现在,我们已经在朴素贝叶斯分类器中转移了对连续/数字数据的处理,让我们深入研究如何处理零频率问题。
当在整个可能性乘法中任何一个概率为零的条件使得整个可能性为零时,就会发生这种情况。在这样的情况下,有一种叫做的拉普拉斯估计器被使用。

图 3
在哪里,
NC = Xi = x 和 yi = y 的实例数,
yi = y 的实例数,
p =先验估计,示例:假设属性值 p=1/ m 均匀分布,其中 m 定义不同(唯一)属性值的数量。
m =该属性的唯一值的数量。
因此,如果假设均匀分布,图像 3 中的公式修改如下:

图 4
T 他对图 3 中公式的解释可能有点难以理解。让我们借助一个例子来更好地理解它:
我们将在图像 1 和图像 2 中使用相同的数据集和分布对实例进行分类。
x = <前景=阴天,温度=66,湿度=90,风=真风>
为了计算这个,我们需要目标变量发挥的先验概率
实例总数为 14,其中 9 个实例的值为 yes ,5 个实例的值为 no 。
p(是)= 9/14
p(否)= 5/14
按照目标变量,自变量的分布可以写成:
为了对实例 x,进行分类,我们需要计算播放=是和播放=否的最大可能性,如下所示:
播放的可能性=是
P(x/是)* P(是)= P(阴/是)* P(温度= 66/是)* P(湿度= 90/是)* P(真/是)* P(是)
活动的可能性=否
P(x/否)* P(否)= P(阴/否)* P(温度= 66/否)* P(湿度= 90/否)* P(真/否)* P(否)
计算上述方程所需的新值为:
p(阴/是)= 4/9
和
p(阴/否)= 0/5 = 0
计算可能性所需的其余值取自前面的例子本身。
P(x/是)* P(是)=(2/9)* 0.034 * 0.0221 (3/9)(9/14)= 0.000036
P(x/否)* P(否)= 0 * 0.0279 * 0.0381 (3/5)(5/14)= 0
0.000036 > 0
分类—是
这里可以看出,一个条件概率 P(阴/否) 是分类的驱动因素。现在,让我们看看在均匀分布假设下,如何利用来自图像 4 的拉普拉斯估计器的公式。
对于前景=阴天,新的概率变成
p(阴/是)= (4 + 3 * (1/3)) / (9 + 3)= 5/12
在哪里,
nc = 4,因为 Outlook =阴天& play =是,
n = 9,因为 play = yes 的实例总数,
m = 3,因为属性 Outlook 具有 3 个唯一值(晴天、阴天、雨天),
p = 1/ m = 1/3,因为假设均匀分布
同样的,
p(阴/否)= (0 + 3 * (1/3)) / (5 + 3)= 1/8
在哪里,
nc = 0,因为 Outlook =阴天& play =否的 0 个实例,
n = 5,因为 play = no,
m = 3,因为属性 Outlook 具有 3 个唯一值(晴天、阴天、雨天),
p = 1/ m = 1/3,因为假设均匀分布
注意:在应用拉普拉斯估计器时,请确保将其应用于所有有序属性。你不能仅仅将 is 应用于零频率问题发生的属性。
因为在我们的实例中要分类的另一个有序属性是属性 Windy,所以我们也需要在那里应用拉普拉斯估计器。应用修改后的概率为:
对于 Windy = True,新的概率变成
p(真/是)= (3 + 2 * (1/2)) / (9 + 2) = 4/11
在哪里,
nc = 3,因为在 3 个例子中 Windy = True & play = yes,
n = 9,因为 play = yes 的实例总数,
m = 2,因为属性 Windy 有 2 个唯一值(真,假),
p = 1/ m = 1/2,因为假设均匀分布
同样的,
p(真/否)= (3 + 2* (1/2)) / (5 + 2)= 4/7
在哪里,
nc = 3,因为 Windy = True & play = no 的 3 个实例,
n = 5,因为 play = no,
m = 2,因为属性 Windy 有 2 个唯一值(真,假),
p = 1/ m = 1/2,因为假设均匀分布
P(x/是)* P(是)=(5/12)* 0.034 * 0.0221 (4/11)(9/14)= 0.0000731
P(x/否)* P(否)= 1/8 * 0.0279 * 0.0381 (4/7)(5/14)= 0.0000271
0.0000731 > 0.0000271
分类—是
尽管分类没有改变,但现在我们有了更好的科学推理来支持我们的结论。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑注册成为一名媒体会员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金,不需要你额外付费。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
tarun-gupta.medium.com](https://tarun-gupta.medium.com/membership)
我将免费赠送一本关于一致性的电子书。在这里获得你的免费电子书。
感谢您的阅读。我希望阅读这篇文章的人能够弄清楚朴素贝叶斯分类器中连续数据和零频率问题的处理。如果你觉得它能帮助别人,就分享吧。你可以在这里阅读我的更多帖子:
[## 标记故事列表的快速链接—感谢您的访问
我也有一份以快节奏出版为目标的出版物。读书成为作家。
tarun-gupta.medium.com](https://tarun-gupta.medium.com/thank-you-for-visiting-my-profile-9f708062c75e)
机器学习系统的连续交付
以可持续的方式安全快速地将机器学习系统部署到生产中
目录

机器学习的连续交付
传统软件开发的大多数原则和实践都可以应用到机器学习(ML)中,但某些独特的 ML 特定挑战需要以不同的方式处理。我们在之前的文章中讨论了那些独特的“将机器学习模型部署到生产中的挑战”。本文将探讨如何将帮助传统软件解决其部署挑战的连续交付应用于机器学习。
什么是持续交付?
持续交付是以可持续的方式安全、快速地将所有类型的变更(包括新功能、配置变更、错误修复和实验)投入生产或交付给用户的能力。
-杰兹·亨布尔和戴夫·法利
(连续交付图书作者)
以下是书中定义的持续交付原则,你可以在这里了解更多。
- 将质量建立在
- 小批量工作
- 计算机执行重复的任务,人解决问题
- 坚持不懈地追求持续改进(Kaizen)
- 每个人都有责任
连续累计
持续集成(CI)应该是团队开始持续交付之旅的第一步。正如您在下面的定义中所看到的,CI 有助于尽快发现任何集成错误,从而提高团队生产力。
持续集成是一种软件开发实践,团队成员经常集成他们的工作,通常,每个人至少每天集成一次——导致每天多个集成。每个集成都由一个自动化构建(包括测试)来验证,以尽可能快地检测集成错误。
——马丁·福勒
持续交付与持续部署
在本文中,我们不会过多地讨论持续部署,但是理解持续交付和持续部署之间的区别是有好处的。如下图所示,在部署到生产环境之前,连续交付包括一个手动批准步骤。尽管如此,每次代码被推送到主分支时,它都会经过各种自动化步骤,并准备好进行部署。同时,如果所有步骤都成功,它会自动将新代码部署到生产环境中,以防持续部署。

图 1: 连续交付与连续部署
机器学习工作流
正如我们在上一篇文章中看到的,典型的 ML 工作流包括数据管理、实验和生产部署,如下图所示。我们将挑战分为三个阶段。

图 2: 机器学习工作流程
持续交付如何帮助应对 ML 挑战?
现在,让我们看看连续交付如何帮助解决机器学习的具体挑战。我们将回顾 ML 工作流程的每个阶段,它的挑战,以及 CD 如何解决它们。
数据管理
训练数据集是数据管理阶段的输出,然后输入训练过程。这是决定一个模型表现如何的基本因素之一。这一阶段通常包括数据采集和准备。添加用于生成训练数据集的自动化数据管道有助于解决此阶段的大多数挑战。
自动化数据管道

图 3: 自动化数据管道
自动化数据管道包括以下步骤:
- 数据采集:从各种来源采集数据,如 S3 桶、公共数据集等。有效的方法之一是使用适配器模式。
- 数据验证:验证数据模式和其他类型的验证,以获得更高质量的数据。
- 数据准备:在验证的基础上,为了使数据为训练过程做好准备,该步骤进行归一化、转换、特征提取等。
- 数据测试:这一步经常被忽视,但却非常重要。添加偏倚和公平性测试,以确保通过数据在模型中不引入偏倚,这一点至关重要。此外,添加自动化测试以确保获得的数据是安全且符合(基于您需要遵守的法规,如 HIPAA、PCI 或 GDPR)。
- 数据版本化:添加一个步骤来对生成的训练数据集进行版本化,以便您可以轻松地返回到特定版本并跟踪更改。当您主要尝试各种体系结构或训练模型,并希望对数据集的多个版本的多次运行的准确性结果进行比较时,版本化会有所帮助。
参见上一篇文章中定义的挑战&添加自动化数据管道如何帮助解决这些挑战。你可以在这里阅读更多关于挑战的内容。
挑战:大数据量
解决方案:由于模型训练通常需要大数据集,手工处理耗时&成本高。拥有如上所述的自动化数据管道,使流程可重复,节省了大量时间,并降低了出现数据问题的几率。
挑战:高质量数据
解决方案:增加自动化数据验证和测试,如偏见和公平,加上安全性和合规性,有助于确保数据的高质量,这有助于产生更好的模型。
挑战:数据集跟踪
解决方案:跟踪实验和训练有助于比较各种训练运行,看看哪些有效,哪些无效。对数据进行版本控制有助于追踪哪个数据集能提供更好的结果。如上图所示,应该有一个到版本数据集的步骤。有像 dvc 这样的工具可以为数据提供版本控制。
挑战:位置
解决方案:当数据集驻留在不同的位置时,由于数据量大、传输成本高或合规性原因,传输数据集可能没有意义,因此在更靠近数据驻留的位置运行数据管道、培训甚至预测是有意义的。在这种情况下,拥有一个可以轻松运行的自动化管道会有所帮助。
小心:确保这是值得努力的。如果您可以在不增加复杂性的情况下进行管理,并集中运行,我建议您这样做。
挑战:安全性和合规性
解决方案:如上图所示,为了确保数据的安全性和合规性,请在您的数据管道中添加自动化的安全性和合规性测试。
实验
这一阶段包括模型开发,在这一阶段,数据科学家将大量时间用于研究各种架构,以了解哪些架构符合他们的需求。他们需要编写训练代码,并使用从数据管理阶段和配置中生成的训练数据集来训练模型。
培训代码
尽管这是一个培训代码,而不是一个生产代码,但是代码的持续集成有助于更快地发现问题。由于研究和实验阶段,许多编写的代码通常质量不高,但添加静态分析和单元测试等内容将有助于更快地发现任何问题,并提供更快的反馈。在训练代码的 CI 的末尾,应该生成一个版本化的工件(docker 映像、zip 文件等)。)然后将用于训练模型。由于这是一个试验阶段,可能会发生很多变化。尽管如此,我还是强烈建议您经常将变更合并到主分支并运行 CI。使用像特性切换或者将工件版本化为 alpha、beta 或者 release 这样的技术有助于识别可以在培训过程中使用的工件。

图 4: 培训代码的持续集成
培训过程
见下图(图 5 ),典型的培训流程和各种相关部分。前一阶段生成的训练数据集和最后一步编写的训练代码,以及任何训练环境配置都用作训练过程的输入。数据科学家触发训练过程,这可能会运行几个小时或几天。完成之后,应该添加自动化的准确性评估,以查看模型是否满足预期的标准。在这里添加一个偏差和公平性测试步骤也有助于确保模型在生产中针对实时数据运行时是公平的。如果一切顺利,模型应该被版本化,并存储在工件存储库中,以便在部署到生产时获取。

图 5: 培训流程
挑战你可以在这里阅读更多:
挑战:持续的研究和实验工作流
解决方案:为训练代码添加 CI,并确保生成的训练代码、训练数据集和模型是版本化的,这有助于更快地发现问题并跟踪所做的任何更改。如上所述,尽管在这个阶段通常会发生许多变更,我还是强烈建议频繁地将变更合并到主分支并运行 CI。
挑战:跟踪实验
解决方案:通过跟踪训练过程的各种组件,如训练数据集、训练代码、模型等。我们可以很容易地复制结果和跟踪实验。
挑战:代码质量
挑战:培训时间和故障排除
解决方案:由于培训通常需要几个小时甚至几天的时间,自动化可以更快地发现问题并进行适当的监控,因此警报&日志聚合有助于更好地对这些问题进行故障排除。
挑战:模型精度评估
解决方案:增加一个自动评估模型精度的步骤,可以在各次运行之间进行比较,并挑选性能最佳的模型用于生产部署。
挑战:再训练
解决:通常情况下,当出现数据漂移或逻辑发生变化时,需要对模型进行再训练。为此,上面提到的自动化会有所帮助。
挑战:基础架构需求
解决方案:训练有时预测有一定的特殊基础架构需求像 GPU &高密度内核。这些通常是昂贵的,并且仅在周期性突发时需要。使用基础架构作为代码来自动配置/取消配置基础架构(尤其是在使用云时)可以降低成本,并减少长期基础架构带来的问题。要了解更多信息,请在这里阅读我的关于基础设施的文章。
生产部署
在模型经过训练并达到一定的准确性后,它将被部署到生产中,并开始根据实时数据进行预测。
应用代码
一旦模型定型,应用程序代码(web 服务或应用程序)就会使用它来根据实时数据进行预测。应用程序代码的持续集成通过各种步骤来确保代码的质量,并将模型打包到 CI 结束时生成的工件中。(注意:您也可以在生产中的运行时提取模型,但是您会失去“构建一次,在任何地方运行”的特性。)

图 6: 应用代码的持续集成
生产部署期间的挑战您可以在此了解更多:
挑战:离线/在线预测
解决方案:根据需要提供离线(批量)和在线(实时)预测的支持是必不可少的。当您在构建一个用于生产部署的系统时,您应该考虑这一方面。
挑战:模型退化
解决方案:在每次部署后增加冒烟测试,并适当监控&因数据漂移或其他原因引起的模型准确性变化,良好的事件管理流程有助于避免准确性水平降低并从中恢复。
将这一切结合在一起
请参见下图,其中包含了我们到目前为止讨论的端到端处理的所有阶段/步骤。

图表#7: 将所有内容整合在一起
人
稍后我会写一篇关于这个的详细文章,但是人对于持续交付的有效性是必不可少的。消除您的 ML 研究人员、ML 工程师、应用程序开发人员、数据工程师和 DevOps/SRE 人员之间的任何隔阂,促进合作会有所帮助。实施本文中提到的建议将使不同团队/个人之间能够协作。
结论
感谢您阅读这篇文章,我希望它对您有用。如果你有任何问题,请在下面的评论中告诉我,或者通过 twitter 联系我。
原载于 我的网站
CICD——神话、陷阱和实践方法
DevOps - CI/CD
CICD 不仅仅是工具。设计安全、可扩展、强大且自动化的企业 CI/CD 解决方案。
在本系列文章中,我们将全面讨论 CI/CD、神话、陷阱,然后在下一篇文章中,我们将继续 在 AWS 上实际演示 CI/CD 管道,并分别使用 GitHub、Jenkins 和 code deploy 演示 CI/CD 管道。

在当今快速变化的世界中,技术颠覆已经成为一种常态,并改善了企业的经营方式。组织正试图保持精益,因为这使他们能够快速变化,适应更新的技术,并快速推动创新。这带来了 DevOps,它并不新鲜,但在过去几年中已经有了很多实现。DevOps 是一种软件开发和运营的哲学方法。其核心在于促进协作文化,推动创新速度,并实现自动化软件管理&交付。DevOps 的重要支柱之一是我们将在本文中讨论的持续集成(CI)和持续交付(CD)。
那么问题来了:为什么是 CI/CD?组织越来越以客户为中心,越来越以数据为导向。这导致了更多的独立和并发特性的发布。云服务提供商通过提供无服务器框架和许多其他有利可图的节省成本的功能,为它增添了许多甜头。请注意,只有精心设计的框架才能带来实际的成本效益。显然,用于软件交付的传统工具无法赶上快速发展的“敏捷开发方法”。这导致了对 CI/CD 工具的需求和发展。
持续集成

连续累计
持续集成指的是软件发布的构建或集成阶段。它涉及开发人员定期将他们的代码合并到一个触发构建和测试的中央代码库中。持续集成的目标是更快地发现和修复错误,提高软件质量,并减少验证和发布新软件特性的时间。有两个重要组件:
自动化 —构建&测试过程的自动化,包括从故障中恢复。目的是使 CI 流程变得枯燥、简单和快速。这意味着开发人员有更多的时间来构建新功能、测试和合并。
文化和思维转变 —这是 DevOps 的一个重要方面。传统上,团队中的开发人员过去常常长时间地开发代码,并一次性地将变更合并到代码库中。这使得合并变更既困难又耗时。集成失败的可能性更大,并且如果没有修复,错误会存在更长时间。这使得更快地交付软件变得更加困难。另一方面,频繁的集成意味着更少的集成问题,并且 bug 被立即修复。
好处
提高生产力:通过自动化构建和测试中的手动步骤,释放开发人员的带宽。更频繁的提交可以确保代码问题得到及时解决。
更快地修复 bug:频繁的提交和测试确保问题在成为大问题之前更快地被发现和修复。
更快更频繁的发布:持续集成帮助团队向产品频繁发布更多的特性。
标准化:包括回归和集成测试在内的标准化测试用例的测试代码确保代码遵循强制性检查。
连续交货

连续交货
持续交付是对持续集成的扩展,其中代码变更以构建工件的形式自动准备,以备部署。现成的工件可以部署到测试环境中进行进一步的测试,或者部署到生产环境中。这种整体集成确保标准化代码经过全面测试,并随时准备部署。它还确保了代码的完整性,因为相同的代码部署在不同的阶段。另一个好处是只需一次点击就可以在多台机器上部署相同的代码。
持续交付与持续部署
这两个术语经常被认为是相同的或混淆的。一个理想的场景是开发人员将代码提交到中央存储库,这可以触发构建和测试管道,并在没有失败或问题的情况下将更改自动部署到生产中。然而,我们大多数人都知道这并不完全是部署工作的方式。在部署到生产环境之前,通常需要手动批准或签署。

持续集成 vs 持续集成 vs 持续交付
在连续部署中—按一个按钮将代码合并到中央存储库中,如果管道中的所有阶段都成功,代码将自动部署到生产中。另一方面,连续交货需要人工批准。这并不意味着连续交付缺少任何东西,但是实际上在签核之前需要人工批准。
可变与不可变基础设施
在继续之前,我想提出一个属于配置管理的重要话题,但是云平台上的现代 CI/CD 工具包括了这一特性——不可变基础设施。简单讨论一下吧。在同一基础设施上升级应用程序或软件被称为可变方法。不变的方法是指构建新的虚拟机或独立的基础架构,升级到所需的技术体系,如果一切正常,则迁移到新的虚拟机并转储/回收旧的基础架构。这两种方法各有利弊,但是如果您的用例需要不变的基础设施,那么现代 CI/CD 云平台服务也支持这种方法。
好处
自动化软件发布过程:持续交付有助于从持续集成管道中部署现成的构建工件,并更快地交付更新&特性。
提高生产力:通过将开发人员从手工任务中解放出来,并鼓励有助于减少部署到产品中的错误和 bug 数量的行为,帮助团队提高生产力。
更快地修复错误:连续交付让您的团队可以轻松地对您的代码执行其他类型的测试,因为整个过程已经自动化了。
更快地交付特性:持续交付帮助您的团队更快更频繁地向客户交付更新。当正确实现时,您将总是拥有一个已经通过标准化测试过程的部署就绪的构建工件
CI/CD 管道

CI/CD 管道
现在,我们已经分别讨论了 CI 和 CD,让我们看一下 CI/CD 渠道。CI/CD 管道是自动化整个软件交付过程的过程。从提交代码到中央存储库,运行测试用例,构建包,以及将代码部署到测试或生产环境。设置 CI/CD 管道没有单一的顺序,但是根据您的组织选择的部署模式,会有最佳实践。
CI/CD 更像是即插即用。它有不同的组件,可以以任何方式使用。有许多开源和付费服务可以单独用于上面列出的每个阶段。例如,下面是管道的一种形式
提交更改->触发构建->构建->通知构建结果->运行测试->通知测试结果->将构建交付到环境->在必要的地方部署
好处

CI/CD 渠道优势
陷阱
不要忘乎所以:市场上有这么多可用的工具和特性,其中大部分可能并不是特定用例所需要的。所以头脑风暴一下什么最适合你的软件交付,并且只实现相关的组件。如果有一个特别的部分,比如单元测试,它不稳定并且有更多的问题,那么一旦它成熟了,就自动化这个过程,否则你将会花费更多的时间,而不是自动化它。
不要构建庞大的脚本:不要构建庞大的测试脚本;相反,构建具有已定义功能的小脚本,并在需要时跨管道重用它。
运行本地单元测试用例:在推送到中央存储库之前,在开发人员机器上本地运行特定于业务场景的测试用例。它提供了额外的验证层,并确保较少的集成问题。
建立以流程而不是工具为中心的管道:这是一个分裂的时代。软件交付服务正在迅速改进。今天很好的工具明天可能就不够用了(比如几年后)。因此,构建架构时要保持对工具的独立性。尝试在云平台上构建代码库,提供无限的安全存储。最大的好处是,它将数据保存在远离计算设备的中央存储库中,如果您想要过渡到新的服务和软件,这将变得非常容易。
将正确的组件放置在正确的位置:根据用例将正确的阶段放置在正确的位置。目标必须是通过关闭高故障管道组件来节省启动时间。例如,您可能想先打包,然后并行运行多个测试用例。
避免在内部 CI/CD 中构建:有大量价格合理的开源和付费服务。CI/CD 的主要目标是释放开发人员的带宽,将其用于最重要的事情。内部解决方案需要前期投资和大量维护。管道目标应该是使 CI/CD 无缝。
使用云原生服务:AWS 和 Azure 等主要云提供商提供选择原生或流行服务的灵活性,以构建完整的 CI/CD 管道。此外,尽可能不使用服务器,以避免维护成本并关注结果。
结论:
在本文中,我们讨论了 CI/CD、好处、误区和要避免的陷阱。如果你喜欢这篇文章,请告诉我并关注我。点击此处阅读下一篇文章,我们将在 AWS 平台上构建 CI/CD 管道。
免责声明——文中观点仅代表作者个人,不代表作者所属任何组织的观点。
使用 GitHub Actions 对 ML 项目进行持续质量评估。
如何为 GitHub repo 设置自动指标收集,以及我如何使用它赢得 NLP 竞赛。

斯蒂芬·道森在 Unsplash 上拍摄的照片
在这篇文章中,讨论了基于机器学习(ML)的算法的自动质量评估。
最近,我和我的团队在俄罗斯储蓄银行 AIJ 竞赛 2019 中获得了第二名,这是俄罗斯最大的 ML 竞赛之一(100 个团队,45,000 美元奖金)。今年的任务是解决俄语高中毕业考试由许多不同的自然语言处理(NLP)子任务组成,每个子任务都需要一个单独的 ML 模型。这使得我们的团队迫切需要一个自动化的质量评估系统。根据我的生产经验,我建立了这样一个系统,我想在这里分享一下。
这篇文章的所有代码都可以在 Github 资源库中找到:https://github.com/vladimir-chernykh/ml-quality-cicd
介绍
在过去的几年里,ML 领域发展非常迅速。由于这种增长和尽可能快地做每件事的持续需求,许多项目缺乏测试。与此同时,经典软件开发是更成熟的领域,有很多值得学习的地方(软件 1.0 vs 软件 2.0 )。对产品进行适当的测试就是其中之一。从这些链接[ 1 、 2 、 3 、 4 开始,你可以了解更多关于测试及其不同类型的知识

图 1:软件测试分类之一
虽然经典算法和应用程序是确定性的,但大多数基于机器学习的对应程序本质上是随机的。
它引入了另一种类型的测试,这种测试在以前可能并不相关:算法质量测试。他们评估模型解决底层任务的准确性,例如,预测股票价格,区分图像中的猫和狗等。当然,很久以前就有近似和随机算法,但随着 ML 的兴起,它们变得无处不在(许多经典算法在质量上有理论界限)。
当涉及到生产部署时,质量评估至关重要。人们需要知道模型的表现如何,它的适用范围和薄弱环节。只有这样,模型才能被有效地使用,并使产品受益。
几乎每个数据科学家都知道如何在开发环境中评估模型的质量,例如 Jupyter notebook、IDE of choice 等。但是对于生产来说,重要的是不仅要知道当前的质量指标,还要跟踪它们随时间的变化。至少有三种方法可以解决这个问题:
- 浏览代码存储(GitHub、BitBucket 等)中的提交历史。)
- 将结果存储在电子表格、维基页面、自述文件等中。
- 自动化指标收集和存储
这些选项中的每一个都有其使用场景。前两个选项在模型没有太多变化时工作得很好,它们很少发生。而最后一种方法在模型频繁变化时是不可避免的,人们希望有一种稳定而方便的方法来跟踪它。在下文中,最后一种方法称为连续法。
CI/CD
实施持续评估的一种便捷方式是使用持续集成/持续交付 (CI/CD)系统。市场上有许多选择:
所有 CI/CD 系统的最终目标是自动化构建、测试、合并、部署等。整个 CI/CD 管道与代码存储库协同工作,并由特定事件(通常是提交)触发。

图 2: CI/CD 主要方案。在源[ 5 、 6 、 7 ]中查找更多信息
最近 GitHub 推出了自己的 CI/CD 系统,名为 GitHub Actions 。以前,我在几个生产级 CI/CD 系统中使用 Jenkins。AIJ 竞赛是一个试验新工具的绝佳机会,我决定试一试。关于行动,有几点很突出:
- 与 GitHub 的内在集成。如果已经在使用 GitHub 库进行代码管理,那么操作应该可以无缝地工作。
- GitHub 提供按需虚拟机来运行整个管道。这意味着不需要管理自己的机器及其与 GitHub 动作的连接。虽然如果你需要一个巨大的虚拟机或者有任何其他特定的要求,这是有可能的(自托管运行程序最近已经在测试版中发布)。
- 对于私人回购,免费账户有使用限制(2000 分钟/月),但对于公共回购没有限制。更多信息点击这里。我已经在两个小项目中使用了 GitHub Actions,还没有达到极限,但是对于较大的项目来说还是很容易的。在这种情况下,詹金斯/特拉维斯/等。可能是更好的选择。
有关更全面和详细的描述,请查看官方文档。
工作
为了说明系统是如何构建的以及它是如何工作的,我将使用玩具示例并构建一个模型来解决波士顿房价任务。在实践中,该系统被应用于 AIJ 竞赛的自然语言处理任务中。
真实任务:AIJ 竞赛
这个连续质量评估系统产生的真实世界的实际任务是关于解决 AIJ 竞赛中的俄语考试。

图 4: AIJ 竞赛评估管道
这个竞赛的特点是有许多完全不同的子任务(27 ),我们最终用不同的模型来解决。子任务的例子有:
- 从几个单词中选出重音不正确的单词
- 应该插入逗号的位置
- 在哪些句子中,破折号是根据相同的规则放置的
- 对于给定的文本,哪些语句成立
- 在语法错误的类型和它出现的句子之间建立一个映射
有 3 个不同的自动化度量标准(图 4 ),每个子任务由其中一个来判断。最终的排名是基于子任务之间的指标总和。
我们一直在一个接一个地解决这些子任务,并且经常回到我们之前解决的那个来改进它。因此,我们迫切需要一个自动系统来跟踪特定任务的度量变化以及聚合度量。
我没有在这里描述 AIJ 竞赛的精确解,因为它太庞大和复杂了。这会偏离文章的主旨。但是,进入系统构建的背景可能有助于理解系统和所做的决策。
玩具任务:波士顿房价
这是一个回归问题,需要根据不同的因素(如房间数量、社区犯罪率等)来预测住房设施的价格。
这是“默认”经典数据集之一。它甚至直接出现在 scikit-learn 库中

图 3:来自波士顿房价数据集的数据示例
总共有 13 个特征(见笔记本中的详细描述)和一个名为“MEDV”的目标,代表“1000 美元的自有住房的中值”。

图 5:波士顿房价数据集和 LightGBM 模型的模型开发管道
我会使用三种不同的模型(+基线)来模拟任务的逐步“工作”:
- 
平均模型(基线) 
- 
随机预测 
- 
线性回归 
- 
决策树上的梯度增强( LightGBM ) 
在现实世界的问题中,它相当于持续改进模型,其变更被推送到存储库。
还应该定义度量标准来评估模型的好坏。在这种情况下,我要使用:
所有的数据预处理和模型训练/评估代码都可以在 GitHub repo 中相应的笔记本中找到。这不是文章的重点:
- 构建最佳模型
- 解释如何进行特征工程和开发模型
因此,我不在这里详细描述它。这本笔记本被大量评论并且自成一体。
解决方案包装
该解决方案作为dockeredRESTful web 服务发布。使用 Flask 将模型包装到服务器应用程序中(详见 repo 中的 this 文件)。
这里需要注意几件事:
- 特征的顺序很重要。否则,模型将无法正确预测。
- 有一个简单返回“OK”的ready端点。可以向其发送请求,以了解是否所有模型都已经加载到 RAM 中。如果端点没有回答,则意味着模型仍在加载或者已经加载失败。
- predict端点做预测价格的实际工作。输入是一个 JSON,提供了所有必要的特性。
- 输入可能包含一列数据实例,即不止一个。
人们可以通过进入ml-quality-cicd/src并运行python server.py来使用本地 Python 启动解决方案服务器。尽管这是一种可以接受的方法,但是使用 Docker 有一种更好的平台无关的方法。我创建了一个特殊的 Makefile 来运行所有必要的命令。要运行解决方案服务器,只需键入( Docker 应已安装)
它启动一个 Docker 容器,在主机的端口 8000 上有一个可用的解决方案 web 服务器。有关全面的技术说明,请访问 repo 。
人们可以以任何合适的方式查询启动的网络服务,例如,使用 Python、JS、Go 等。CURL 命令行实用程序和 Python 示例如下:
在这两种情况下,输出应该是相同的:
{"predictions":[21.841831683168305]}
该解决方案的格式选择由 AIJ 竞赛决定。最近,在洗钱竞赛中有一种转向基于集装箱的解决方案的强劲趋势(ka ggle deep fake Detection、 Sberbank AIJ 竞赛等)。)
人们可以选择方便用于特定目的的任何其他形式的解决方案交付。例如,从命令行启动带有数据路径和存储结果的位置的脚本。
估价
为了进行评估,我将使用保留技术,并将整个数据集分成训练和验证子集。交叉验证 (CV)也可以用于 Boston House Prices 任务的特殊情况,因为这里使用的模型简单快速。在实际应用中,可能有一个模型被训练了几天。因此,几乎不可能使用 CV。
拆分是在训练笔记本中完成的。这个步骤也显示在图 5 中。
请注意随机状态和其他参数是如何固定的,以保证再现性。之后,将训练和验证数据集保存到 CSV 文件中,以便以后使用,而无需再次启动代码。这些文件位于 repo 中的data 文件夹中。
接下来,有几个可供评估的选项:
- 笔记本
- 通过网络服务手动操作
- 通过网络服务自动完成
下面我们来分解一下每一个是怎么做的。
评价:笔记本
在这种方法中,一个是就地测量模型的质量:在开发它的相同环境中。这种方法有助于通过比较许多模型来建立更好的模型。
下面是所有 3 个模型+基线的比较表(完整代码在培训笔记本):

图 6:在 Jupyter 笔记本上完成的验证集的模型比较
算法的就地评估对于即时开发来说是很好的,但是它不允许容易地跟踪变化的历史(在“简介”中讨论)。
评估:通过网络服务手动进行
这是通向全自动连续质量测试的中间步骤。
这里我将使用上面“解决方案打包”部分中描述的服务接口。一方面,有一个 web 服务接受 POST 请求的 JSON 主体中的数据实例列表。另一方面,CSV 文件(train 和 validation)包含行中的数据实例。人们需要:
- 从 web 服务获取 CSV 文件中所有数据的预测
- 计算预测值和实际值的度量
若要运行本节中的代码,请确保包含该解决方案的服务器正在运行,或者使用运行它
请注意,因为端口固定为 8000,所以一次只能运行一台服务器。要终止所有服务器,可以使用make destroy_all命令或使用docker stop ...手动停止它们。
客户
客户端解决这两个任务中的第一个。它的主要目标是:
- 将一种格式(CSV)转换为另一种格式(词典列表)
- 向服务器发送请求
- 接收并处理回答
客户端的完整代码可在回购的client.py 文件中获得。在这里,我只谈要点。
下面是一个核心逻辑,它能够以适当的格式将文件发送到端点。client.py中的所有其他 120 线路实际上都是接口,以使这些线路正常工作。
当答案返回时,需要处理并保存它们。
注意,一行对应一个输入文件。在此任务中,一个文件可以包含多个数据实例。当一行包含一个实例而不是多个实例的列表时,使用 CSV 更容易。因此,下一步是将答案转换成更方便的格式。

图 7:解析和转换后的答案数据帧。这里,客户机同时启动两个文件:训练集和验证集。转换后的数据帧包含对两个文件的预测。所有预测都是相同的,因为部署了 MeanRegressor 模型。
注意,这个阶段完全依赖于数据。对于每个新的任务和数据结构,应该相应地重写这种转换。
度量计算
在接收到答案并以适当的格式存储后,需要将基础真值正确地加入到答案中。它将允许我们计算实例和全局的所有指标。此功能由回购中的metrics.py文件覆盖。
注意,赋值是在没有任何连接的情况下完成的,因为parsed_answers数据帧是按“路径”、“行编号”字段排序的。
一旦基础真值可用,就可以计算所有的实例-wisse 度量,在这种情况下是 MSE、MAE 和 MAPE。

图 8:由基本事实标签和基于实例的计算指标丰富的转换答案
这个丰富的表允许分析错误分布和具有最高/最低错误的实例。
最后,让我们通过平均文件间的实例度量来计算全局度量。

图 9:文件间的平均全局度量
注意,这里测试的型号是MeanRegressor。可以将验证质量与图 6 中的相应行进行比较,并确保它们是相同的。
评估:通过网络服务自动进行
上一节讨论的所有内容都在 repo 中的client 文件夹中实现。现在的目标是自动化这个过程,并将其包装到 CI/CD 管道中。
自动化是通过 Makefile 完成的。我之前在这里使用它来启动 web 服务,现在让我们仔细看看它的主要细节。这个 Makefile 中有几个目标我想讨论一下:
- 
运行时变量。请注意主机端口是如何随机选择的(根据启动时的 UNIX 时间戳)。这样做是为了允许多台服务器同时运行。 
- 
build和push是准备(构建和推送)Docker 映像的目标。图像非常标准,可以在dockers文件夹中找到。
- 
create通过解决方案服务器启动 Docker 容器。设置了资源限制,等于 200 MB RAM 和 1 个 CPU。请注意,相对于总 CPU 内核,CPU 限制是如何处理的。这样做是因为如果有人要求比主机上可用的 CPU 更多的 CPU,Docker 无法启动容器。
- 
evaluator启动“评估:通过 web 服务手动操作”一节中讨论的客户端和公制计算机。代码可以在client文件夹中找到。请注意,启动是在单独的 Docker 容器中进行的(尽管是从同一个 Docker 映像开始的),网络是与主机共享的。它允许从启动的评估 Docker 内部访问解决方案服务器。
- 
destroy停止并移除解决方案服务器。请注意在出现错误的情况下,日志是如何转储并打印到输出中的。
用户可以结合上述所有步骤,使用一个命令启动评估:
依次触发create、evaluator和destroy。输出应该是在client/reports/<timestamp>_<data folder name>文件夹下可用的评估工件。

图 10:评估工件
所有这三个文件都在前面的“评估:通过 web 服务的手动操作”一节中讨论过,现在应该清楚了。
完成全自动系统的最后一步是将描述的评估过程与 CI/CD 系统联系起来。
CI/CD: GitHub 操作
GitHub Actions 平台被用作 CI/CD 引擎。关于 GitHub 动作和 CI/CD 的更多细节,请参阅本文的“ CI/CD ”部分。这里也有官方文件。在接下来的内容中,我将使用三种方法。它们之间的区别在于在哪里以及如何执行 CI/CD 管道步骤:
- GitHub 机器
- 使用 SSH 的远程机器
- 使用自托管运行器的远程机器
GitHub 机器
这是最容易使用的选项。GitHub 为用户提供了虚拟机(VM ),每当特定事件触发管道时,虚拟机就会自动启动。GitHub 管理虚拟机的整个生命周期,用户不应该为此而烦恼。
所有管道定义都应遵循一些规则:
- YAML 格式
- 存储在/.github/workflows文件夹内的回购
- 遵循语法规则
让我们构建一个执行评估过程并打印结果的工作流。该规范位于回购中的evaluate_github.yml 文件中。
主分支上发生的每个push都会触发管道,并对指定文件进行更改。VM 运行ubuntu-18.04并在包列表后安装。也有其他系统可用(见完整列表此处)。
第一步是执行被称为checkout的公开可用的动作(更多细节在这里)。它将代码从父存储库签出到虚拟机。下一步是执行评估。如前所述,这是使用make evaluate完成的。最后,将指标打印到 stdout。
一旦 YAML 配置被推送到回购,管道将在每个合适的事件上被触发。管道运行的结果可以在 repo 的上部选项卡菜单中的“Actions”选项卡下找到。

图 11:GitHub 提供的 VM 的工作流运行界面和结果
成功的管道运行如图 11 所示。可以注意到,指标与图 6 和图 9 中的相同。
GitHub 管理的虚拟机是不需要大量资源的工作流的好方法。所有虚拟机只有 2 个 CPU,7GB 内存和 14 GB 磁盘空间(链接)。当一个人运行像波士顿房价预测这样的玩具任务时,这是可以的。但对于 AIJ 竞赛,我们很快就用完了资源,这使得它不可能用于评估。并且对于许多 ML 应用来说,这些资源是不够的。这让我想到了下一小节中描述的解决方案。
使用 SSH 的远程机器
现在假设,有一个人想要用来执行工作流的工作站。如何做到这一点?
人们可以出于任何目的使用 GitHub 启动的 VM,而不仅仅是直接启动管道。除此之外,GitHub 允许用户存储机密并在工作流执行期间使用它们。这个秘密可能是安全地存储在 GitHub 中的任何信息,并且可以传递给 VM。
这两种思想的结合使得使用 SSH 连接到远程工作站并在那里执行所有必要的步骤成为可能。
为此,让我们首先向 GitHub 添加三个秘密:
- 哪一个是远程机器的 IP 地址
- EVAL_SSH_USER是远程机器上的用户名
- EVAL_SSH_KEY是一个私有的 SSH 密钥,对应于所提供的用户和远程机器

图 12:GitHub 提供的秘密管理 UI
在初始化所有必要的秘密之后,你可以在 GitHub 动作管道定义中使用它们。所述工作流程的代码可在 repo 的evaluate_remote_ssh.yml 文件中找到。
触发条件和初始checkout步骤与上例相同。下一步是初始化 SSH 私有密钥。注意如何使用特殊的上下文占位符{{ secrets.<SECRET_NAME> }}将秘密传递给环境变量。一旦初始化了 ssh 凭证,就可以在 GitHub VM 上用代码创建一个档案,并使用scp命令将其加载到远程机器上的指定路径。注意,归档文件是以提交散列GITHUB_SHA命名的,默认情况下,提交散列作为环境变量可用(参见此处的以获得默认环境变量的完整列表)。之后,通过运行make evaluate进行评估,并将结果从远程机器复制回 GitHub VM。

图 13:使用 SSH 连接到远程机器的工作流运行结果
使用自托管运行器的远程机器
通过 SSH 远程连接解决了 GitHub VM 资源不足的问题。但是与 GitHub VM 执行(31 行 vs 5 行)相比,它也引入了太多完成目标所需的额外命令。为了解决这个问题,同时兼顾两个世界,你可以使用最近发布的自托管运行器的测试版。
其背后的核心思想是使用特殊的软件将远程机器暴露给 GitHub 动作用于管道执行(关于安装的更多信息在这里)。

图 14:添加自托管运行程序
建立连接后,用户可以通过 YAML 配置文件的指令runs-on在管道中使用新的流道。
下面定义了自承载流道的管道。
注意runs-in: self-hosted指令是如何告诉 GitHub 动作在之前添加的定制远程机器上运行的(图 14)。
除了几个步骤之外,YAML 的配置与 GitHub 管理的虚拟机几乎相同。
- 管道执行前后的环境清理。必须这样做,因为没有人再为我们处理 env 了(以前是由 GitHub 处理的),而且它可能包含以前版本的工件。make clean命令起作用。
- 将工件上传到存储器。这里我用的是 GitHub 中DASH_SSHsecrets 定义的自定义(可能是单独的)机器。我将结果存储在一个单独的文件夹中,其中每个文件都以其对应的提交散列命名。用户可以使用任何其他存储(AWS S3、GCP 存储等)。)和文件格式。

图 15:通过自托管运行器的 CI/CD 管道执行结果
结果
本文中所做的所有评估都是一致的,并且给出了相同的MeanRegressor基线模型的相同指标。
现在,让我们依次将模型更改为RandomRegressor、LinearRegression和LGBMRegressor,看看指标是如何变化的。我已经使用 Dash Python 库实现了这个特殊的仪表板。人们可以在回购协议的这里获得代码细节。仪表板在 GitHub 中的DASH_SHH secrets 指定的特殊远程机器上手动启动。
仪表板跟踪的文件夹对应于 CI/CD 自承载管道存储其结果的文件夹。仪表板本身在端口 7050 上可用。

图 16:用不同的模型提交。他们在模型上模拟真实的渐进工作。
图 16 所示的每个提交都会触发所有 3 个描述的 CI/CD 管道。所有的执行都很顺利,这由提交附近的绿色标记表示。自托管运行器还将结果推送到仪表板,如图 17 和 18 所示。

图 17:带有 MAE 度量结果表的仪表板。绿色的行显示最后一个模型的质量比基线好(否则该行被涂成红色)。

图 18:结果表中一行的图表。注意模型名称是如何重复图 16 中的提交散列的。
仪表板允许用户方便地跟踪历史进度,并比较不同子集/子任务之间的度量。
在现实世界的 AIJ 竞赛中,该仪表板如下所示:

图 19:AIJ 竞赛评估表。每行对应于考试的一个子任务。

图 20:一个任务的模型进度。
结论
在这篇文章中,我描述了我对构建持续质量评估系统的观点,并提供了所有必要的代码和步骤来重现它。
这篇文章中构建的系统对于中小型项目或 ML 竞赛已经足够好了。它还可以在许多方面得到改进,例如:
- 更可靠的日志和指标存储
- 更好的可视化系统
- 日志分析
- 等等。
通过将配置文件重写为适当的特定于工具的格式,所描述的连续质量评估工作流可以很容易地移植到任何其他 CI/CD 引擎。
GitHub Actions 有利有弊。最大的优势是与 GitHub 的紧密集成,以及按需 GitHub 管理的虚拟机的可用性。这对于使用 GitHub 的人来说很方便,如果不使用的话则完全无法接受。主要的缺点是私有存储库的构建数量有限(2000 分钟)。对于大型项目,这导致需要支付保费账户或使用另一种 CI/CD 工具。如果一个人面临分钟溢出的问题,我可能会选择后一种方法。
在我看来,GitHub Actions 是中小型项目的一个很好的选择,这些项目需要一个易于安装的 CI/CD 引擎,并且不太关心它的基础设施。对于更大的项目和更细粒度的控制,可以选择其他 CI/CD 平台。
附注:我要感谢我在 AIJ 大赛中的队友(德国人诺维科夫、奥列格·阿伦金、亚历山大·安尼西莫夫)为解决方案做出的贡献。
简而言之,用于无监督领域适应(CUDA)的对比识别器
一个处理各种域调整方法中涉及的域对齐的某些缺点的模型,同时给出了最新的结果。
正如我们所知,在深度学习世界中,我们可能没有足够的监督数据来训练我们的模型。所以领域适应是一个非常有用的话题。我最近读了一篇ICDM 19 年的论文“无监督域适配的对比识别器”,它提出了一种无监督域适配的直接方法,不需要域对齐。
在这篇博客中,我将尝试简单地给出我对这篇论文的理解。该论文的作者还发布了该论文的一个实现。
领域适应
正如我们所知,在深度学习的世界中,我们可能没有足够的监督数据来训练我们的模型。但是可能存在另一组非常相似的数据(具有不同的分布),对于这些数据,我们有足够数量的标记数据。在这种情况下,如果我们能够以某种方式将基于这些数据训练的模型用于我们的任务,那就太好了。为了了解我们正在讨论的内容,让我们讨论一个小例子:
假设我们手里有一个情感分析任务,为了训练的目的,给我们一些带标签的书评。让我们看看其中的一篇书评。
回顾-1: “这本书对其涵盖的主题有深入的概念构建材料。完全值得你花时间。”
现在假设我们要做情感分析任务,评论是关于家具的。让我们看看其中一个家具评论。
评论-2: “这件家具的边缘处理非常好,非常舒适。”
我们在这里可以推断的是,没有家具评论会类似于评论-1,即,由于基础分布的差异,在书评上训练的情感分析引擎将与家具评论的不确定性一起工作。
在我们对视觉数据的分类任务中也可以看到另一个类似类型的例子,其中我们有同一物体的两个不同图像,但它们都来自不同的数据集(如图 1 所示)。

图 1:左边是一个电子商务网站上展示的一辆自行车的图像,右边是一个手机摄像头拍摄的自行车的图像。
回到我们之前的例子,假设我们没有标记家具评论,在这种情况下,如果我们可以以某种方式使用我们标记的书评数据集,这将是有用的。处理这些类型的域转移的子学科被称为域适配。在更进一步之前,我们需要了解域的含义。
任意一组数据的域 (D)由其三个属性来表述:其输入特征空间(X)、标签空间(Y)及其底层联合概率分布。

图 2:一个域的表示
假设:
域自适应由两个不同的域组成,即源域和目标域,具有共同的输入和输出空间,具有域偏移。域偏移是具有公共输入和输出特征空间的两个域的联合概率分布的差异。

图 3:域转移
动机
领域适应方法的一般趋势是首先实现源和目标领域对准的中间目标,但是它有一些缺点。大的畴变使畴变得更难对齐。另一个缺点是使用多分类器,这增加了模型的复杂性。域对齐有时会导致特定于域的信息丢失,因为在此过程中域正在被转换。
本文提出了一种方法,该方法跳过这个无关的中间比对步骤,并分别在源数据和目标数据上以监督和非监督的方式联合学习单个分类器。作者从 Vapnik 那里得到了一口气解决问题的灵感。
Vapnik 的想法:任何想要的问题都应该用最直接的方式解决,而不是解决一个更一般的中间任务。
本文具体讨论了无监督版本的域自适应,其中训练数据包含已标记的源域实例和未标记的目标域实例。
目的:通过转移已标记源领域的知识,学习未标记目标领域的分类器。
对比识别器(CUDA)

图 4:两种域分布的公共决策边界。
该模型充当两种域分布的分类器。这种方法背后的基本思想是调整在源标记数据上学习的决策边界,使其遵循目标域先验。
我们手里有两个发行版。可以使用监督学习来学习源分布。但是我们需要目标分布来建立目标领域分类器。为了调整在源分布上学习到的决策边界,我们试图提出目标联合分布,作为源分布的函数,加强目标域先验。

图 5:包含目标无监督学习前后的决策边界。
CUDA 模型包含一个编码器和一个同时使用三个数据集训练的分类器。

图 9:每个时期训练 CUDA 模型。
寻找源联合分布
根据领域适应的定义,为了传递知识,使用源标记数据来训练模型。交叉熵损失用于学习用于最大化标签相对于它们各自的源实例的条件对数似然的参数。设源分布为 P(θ)。

图 6:关注从标记的源数据中学习。
设计目标联合分布
由于源域和目标域之间的唯一区别是其固有的联合概率分布,并且使用监督学习,我们获得了源域的联合概率之后的参数,我们的任务现在转移到获得目标域的联合分布上。
我们知道,任何域的联合分布都满足给定的两个条件,即 x[t]上的边际应给 p(y[t])和 y[t]上的边际应给 p(x[t])。建议的目标分配也必须遵循这两个条件。由于对目标数据进行的学习只能在无人监督的情况下进行,作者巧妙地提出了目标联合分布,它:
- 是 P(θ)以及目标域先验的函数。
- 遵循上述两个条件。
- 可以用来寻找最具对比性的特征。
为了获得最多的对比特征,所提出的联合分布被最大化。当最大化时,建议的分布实施两个属性。它试图增加一个实例被标记为一个类的概率,同时减少所有其他实例被标记为同一类的概率。这两个强制使 contradistinguisher 提取出唯一指定每个实例的特性集。

图 7:聚焦于根据未标记的目标数据进行调整。
正规化
在训练期间使用伪标签时可能出现的一个问题是过拟合伪标签。为了解决这个问题,作者还包括了对抗性正则化,其目标是当模型引入一组不属于任何一类的假阴性样本时,包含不确定性。我们首先构建一组假阴性样本,并同时将它们标记到每个类别。在训练期间,模型将学习将不确定性包括到预测中,这在我们的情况下是一个有效的场景。
这种方法类似于熵正则化,其中不是最小化真实目标样本的熵,而是最小化多类、多标签分类任务的二进制交叉熵损失。用于生成假样本的方法取决于所使用的域。该方法提出了两种类型的伪样本生成:
- 对于语言类领域,使用高斯或均匀分布随机采样假样本。
- 对于类似图像的域,使用生成器网络对伪样本进行采样,该生成器网络也在整体模型训练期间被训练。

图 8:专注于包含对抗性的正规化。
作者还提出了对多源域适应的进一步扩展,其中我们可以有多个源域和一个目标域。此处唯一需要的更改是源监督损失。来源监督损失现在将是所有单个来源监督损失的总和。
结果
作者对各种类型的数据做了详细的实验。本文给出了低分辨率和高分辨率可视化数据集上的结果,提出的模型给出了所有这些数据集上的最新结果。他们还尝试了语言数据集,在这里,模型也给出了最先进的结果。要详细了解所使用的数据集和与之对应的结果,可以看一下论文。
结论
本文提出的联合学习对比鉴别器的方法在不牺牲效率的情况下解决了域对齐的缺点。该方法给出了各种测试场景的最新结果。它有可能成为领域适配领域的里程碑。
对比对比损失函数
用于对比学习的四种对比损失函数综合指南
在之前的一篇文章中,我写了关于监督分类中的对比学习,并在 MNIST 数据集上做了一些实验,发现科斯拉等人提出的两阶段方法。2020 年论文通过学习具有对比损失的有意义嵌入,确实显示了监督分类任务的显著改进。后来我发现我的实验实际上使用了与科斯拉等人不同的对比损失函数。已提议。尽管共享相同的直觉,即关于它们的标签,显式地对比彼此的例子,但是不同的对比损失函数可以有它们自己的细微差别。在这篇文章中,我将回顾一系列对比损失函数,并比较它们在监督分类任务中的性能。
初步的
对比损失函数是为度量学习发明的,它旨在学习测量一对对象之间的相似性或距离的相似性函数。在分类的上下文中,期望的度量将使得具有相同标签的一对示例比具有不同标签的一对示例更加相似。深度度量学习涉及深度神经网络,将数据点嵌入到具有非线性的低维空间,然后使用对比损失函数来优化神经网络中的参数。最近的研究项目已经将深度度量学习应用于自我监督学习、监督学习甚至强化学习,例如对比训练的结构化世界模型(C-SWMs) 。
为了在深度度量学习的背景下回顾不同的对比损失函数,我使用以下形式化。让𝐱作为输入特征向量,𝑦作为它的标签。设𝑓(⋅)是将输入空间映射到嵌入空间的编码器网络,设𝐳=𝑓(𝐱)是嵌入向量。
对比损失函数的类型
这里我按时间顺序回顾四个对比损失函数。我稍微修改了几个函数的名称,以突出它们与众不同的特点。
1.最大利润对比损失(Hadsell 等人,2006 年)

最大间隔对比损失函数以一对嵌入向量 z_i 和 z_j 作为输入。如果它们具有相同的标签( y_i=y_j ),那么它们之间的欧几里德距离基本上相等,否则等于铰链损耗。它有一个余量参数 m > 0 来对具有不同标签的一对样本之间的距离施加一个下限。
2.三重态损失(Weinberger 等人,2006 年)
三元组丢失对其标签遵循𝑦_𝑖=𝑦_𝑗和𝑦_𝑖≠𝑦_𝑘.的三元组向量进行操作也就是说,三个矢量中的两个(𝐳_𝐢和𝐳_𝐣)共享相同的标签,而第三个矢量𝐳_𝐤具有不同的标签。在三元组学习文献中,它们分别被称为锚( z_i )、正( z_j )和负( z_k )。三重态损耗定义为:

其中,𝑚也是一个边缘参数,要求锚正和锚负之间的距离增量大于𝑚.这个损失函数的直觉是将负样本推到邻域之外一个余量,同时将正样本保持在邻域内。这是一个很好的图形演示,显示了原始论文中三重态损失的影响:

使用三重态损失的训练前后(来自 Weinberger 等人,2005)
三重开采
根据三联体丢失的定义,在任何训练之前,三联体可能有以下三种情况:
- 简单:损失为 0 的三元组,因为负数已经比正数离锚点多了一个裕量
- :三连音,其中负数比正数更接近锚点
- ****半硬:三连音,负片位于页边
三重损失已经被用于在面网中学习面的嵌入(Schroff 等人)。2015)论文。施罗夫等人。认为三元组挖掘对于模型性能和收敛性至关重要。他们还发现,最难的三元组在训练早期导致局部最小值,特别是导致模型崩溃,而半难的三元组产生更稳定的结果和更快的收敛。
3.多类 N 对损耗(Sohn 2016)
多类 N 对损失是三重损失的推广,允许在多个负样本之间进行联合比较。当应用于一对阳性样品时,𝐳_𝐢和𝐳_𝐣与 2𝑁样品具有相同的标签(𝑦_𝑖=𝑦_𝑗),计算公式如下:

,其中 z_i z_j 为内积,当两个向量都有单位范数时等价于余弦相似。
如下图所示,N 对丢失同时将 2N-1 负样本推开,而不是一次一个:

三重态损失(左)及其延伸(N+1)-三重态损失(右)(来自 Sohn 2016)
通过一些代数运算,多类 N 对损耗可以写成如下:

这种形式的多类 N 对损失帮助我们引入下一个损失函数。
4.监督 NT-Xent 损失(Khosla 等人,2020 年)
我们先来看一下 NT-Xent loss 的自监督版本。NT-Xent 是由陈等人杜撰的。2020,是“归一化温度标度交叉熵损失”的简称。它是对多类 n 对损耗的修改,增加了温度参数(𝜏)来衡量余弦相似性:

自我监督 NT-Xent 损失
陈等发现一个合适的温度参数可以帮助模型从硬底片中学习。此外,他们还表明,最佳温度在不同的批次大小和训练时期数上有所不同。
科斯拉等人。用于监督学习的后来扩展的 NT-Xent 损失:

监督 NT-Xent 损失
实验结果
接下来,我评估这些对比损失函数是否可以帮助编码器网络学习数据的有意义的表示,以帮助分类任务。遵循与我前一篇文章中完全相同的实验设置,使用小批量(32)和低学习率(0.001),我发现除了具有硬负挖掘的三重损失之外,所有这些对比损失函数都优于没有阶段 1 预训练的 MLP 基线:

在 MNIST 和时尚 MNIST 数据集的保留测试集上的性能(准确性)(来自具有硬负挖掘的三元组的结果未显示)。
这些结果证实了在网络的编码器部分的预训练中使用对比损失函数对于后续分类的益处。它还强调了三重态开采对三重态损失的重要性。具体来说,半硬开采在这些实验中效果最好,这与 FaceNet 的论文一致。
Chen 等人 (SimCLR)和 Khosla 等人都使用非常大的批量和更高的学习率来获得更好的 NT-Xent 损失性能。接下来,我用不同的批量 32、256 和 2048 进行了实验,学习率分别为 0.001、0.01 和 0.2。

结果表明,对于所有损失函数,性能随着批量的增加而降低。虽然半硬负挖掘的三重丢失在中小批量上表现非常好,但它非常占用内存,我的 16G RAM 不可能处理 2048 的批量。与同类产品相比,受监督的 NT-Xent 损失确实在较大批量上表现得相对更好。如果我要优化温度参数,受监督的 NT-Xent 可能还有改进的空间。我用的温度是 0.5。
接下来,我检查了使用对比损失函数学习的嵌入的 PCA 投影,以查看它们是否在预训练阶段学习了任何信息表示。

****MNIST 数据集上不同对比损失函数和批量大小的编码网络学习嵌入的 PCA 投影。从左到右:通过 1)最大边际损失学习的预测;2)半硬开采的三重损失;3)多类 N 对损失;4)监督 NT-Xent 丢失。从上到下:批量大小为 32,256,2048。

****显示 MNIST 数据集模型学习的 PCA 投影密度的联合图。从左至右:通过 1)最大边际损失学习的预测;2)半硬开采的三重损失;3)多类 N 对损失;4)监督 NT-Xent 丢失。从上到下:批量大小为 32,256,2048。
从彩色 PCA 投影和它们的密度来判断,我们可以看到最大余量和监督 NT-Xent 为每个类学习更紧密的聚类,而来自半硬挖掘的三重丢失的聚类最大程度地扩大,但仍然是独特的。随着批量大小的增加,在多类 N 对损失和最大边际损失中,表示质量退化,但在监督 NT-Xent 损失中不那么严重,这表明这种损失对于更大的批量大小确实更稳健。
下面是在更困难的时尚 MNIST 数据集上的学习表示的 PCA 投影。总的来说,它显示了与 MNIST 相似的观察结果。

****不同对比损失函数和批量大小的编码网络学习到的嵌入在时尚 MNIST 数据集上的 PCA 投影。从左至右:通过 1)最大边际损失学习的预测;2)半硬开采的三重损失;3)多类 N 对损失;4)监督 NT-Xent 丢失。从上到下:批量大小为 32,256,2048。
总结
对比损失函数对于通过学习有用的表示来改进监督分类任务非常有帮助。最大利润和监督 NT-Xent 损失在实验数据集(MNIST 和时尚 MNIST)中表现最佳。此外,NT-Xent 损失对于大批量是稳健的。
值得注意的是,这里回顾的所有对比损失函数都具有超参数,例如输入向量的裕度、温度、相似性/距离度量。这些超参数可能会严重影响其他研究的结果,因此应该针对不同的数据集进行优化。
这些实验所用的代码可以在这里找到:https://github.com/wangz10/contrastive_loss
参考
- 哈德塞尔,r .,乔普拉,s .&纽约勒村(2006 年 6 月)。通过学习不变映射进行降维。2006 年 IEEE 计算机学会计算机视觉和模式识别会议(CVPR’06)(第 2 卷,第 1735-1742 页)。IEEE。
- 温伯格,K. Q .,布利泽,j .,&索尔,L. K. (2006)。用于大间隔最近邻分类的距离度量学习。神经信息处理系统的进展(第 1473-1480 页)。
- f .施罗夫,d .卡列尼琴科,&j .菲尔宾(2015)。Facenet:人脸识别和聚类的统一嵌入。IEEE 计算机视觉和模式识别会议论文集(第 815-823 页)。
- Sohn,K. (2016)。具有多类 n 对损失目标的改进深度度量学习。神经信息处理系统的进展(第 1857-1865 页)。
- t .陈、s .科恩布利思、m .&辛顿 G. (2020)。视觉表征对比学习的简单框架。 arXiv 预印本 arXiv:2002.05709
- Khosla,p .,Teterwak,p .,Wang,c .,Sarna,a .,Tian,y .,Isola,p .,… & Krishnan,D. (2020)。监督对比学习。arXiv 预印本 arXiv:2004.11362。
对比 VIX 和 VIX 期货的价格动态
期货市场
交易 VIX 期货之前你需要知道什么

蒂姆·斯蒂夫在 Unsplash 上的照片
一个最广泛认可的预期市场波动指标是 CBOE 波动指数(VIX)。直接投资 VIX 的优势是显而易见的,但实际上 VIX 是不可直接交易的。
下行保护的 VIX 风险敞口
为了说明持有 VIX 债券的好处,可以考虑一下 2011 年标准普尔下调美国信用评级的事件。2011 年 4 月 18 日,S&P 对美国信用评级持负面展望的消息传出。如图 1(a)所示,在 2011 年 8 月 5 日官方降级后的几个月里,仅持有 SPDR 标准普尔 500 交易所交易基金(SPY)的投资组合将继续下跌约 10%。

(a)2011 年 4 月 1 日至 2011 年 9 月 30 日,以及(b)2014 年 1 月 1 日至 2014 年 12 月 31 日期间,100%投资于 SPY,90%投资于 SPY,10%投资于 VIX 的投资组合的历史投资组合价值。
(a)2011 年 4 月 1 日至 2011 年 9 月 30 日,以及(b)2014 年 1 月 1 日至 2014 年 12 月 31 日期间,100%投资于 SPY,90%投资于 SPY,10%投资于 VIX 的投资组合的历史投资组合价值。
相比之下,一个混合了间谍(90%)和 VIX (10%)的假设投资组合在降级期间将保持稳定,最终获得正回报。图 1(b)显示了 2014 年同一对投资组合。
两者都获得了大致相同的 15%的回报,尽管《间谍》一书明显比《间谍》和 VIX 的投资组合更不稳定。VIX 股市上涨应对了大规模的下跌(例如 2014 年 10 月 15 日),对投资组合的价值产生了稳定效应。
这个例子激发了对直接跟踪 VIX 和其他指数的交易策略的研究,或者实现任何关于指数或市场因素的预先指定的暴露。
获取 Vol 曝光
相反,波动性敞口是通过使用 VIX 期货或期权以及一些交易所交易基金/票据(ETF/Ns)来实现的。众所周知,VIX ETF/etn 无法一对一地跟踪 VIX,并持续带来负回报。

VXX(蓝色)明显偏离 VIX(红色),在过去几年中损失了 93%以上。
但是直接持有 VIX 期货怎么样呢?
在任何时间点,市场上都有少量合约交易,期限从 1 个月到 10 个月不等。

这里先来看看单一 VIX 期货合约与 VIX 期货合约的不同回报动态:

VIX(红色)和 2020 年 10 月到期的 VIX 期货(蓝色)的累积收益时间序列。
在我们的论文中,我们讨论了 VIX 期货和标的指数的价格动态,并构建了 VIX 期货的静态和动态投资组合以跟踪该指数。除了推导和实施最佳跟踪策略,我们还检查跟踪投资组合的有效性。
数据
我们从分析 VIX 期货相对于现货 VIX 的价格动态开始。VIX 期货的历史价格数据来自 Quandl。我们已经将 Quandl 的数据与直接来自 CBOE 的数据进行了对比。对于现货 VIX 数据以及相关的 etn,我们使用雅虎!金融。
编译后,我们的数据集由从 2004 年 3 月 26 日(VIX 期货开始交易的第一天)到 2017 年 1 月 27 日的全部收盘价历史组成。但是,我们选择只分析从 2011 年 1 月 3 日(2011 年的第一个交易日)到 2016 年 12 月 30 日,即从 2011 年到 2016 年的 6 年时间。
这个数据量可能足以避免过度拟合,并且足够新以理解 VIX、期货和 VXX 之间的动态。当然,人们可以用更近的数据来遵循我们的程序。
整个样本期包含 1510 个交易日的数据。此外,在样本集的任何一天,都有 7 到 9 个期货合约可用。特别是,有 15 天只有 7 个期货合约可用,278 天只有 8 个期货合约可用,以及 1217 天有整整 9 个月的合约可用。
在这段时间内,期货合约总是连续几个月(从 1 个月合约开始)。换句话说,当有 N 个期货合约可供交易时,它们总是由 N 个前月组成。
例如,如果当前交易日期是 1 月初的某个时间(在 1 月期货到期日之前),并且有 7 个期货合同可用于交易,则期货合同的到期日将是连续的 1 月到 7 月。数据集的这些特征符合 CBOE 协议。他们表示,他们目前将列出最多 9 个月的近期交易。然而,在我们的分析中,我们排除了第八个月和第九个月的合约,因为人们并不总是可以交易第八个月或第九个月的合约。
这允许我们使用全部 1510 天的数据,并且避免了消除只有 7 或 8 个期货交易的 15 + 278 = 293 天的需要。
针对 VIX 的倒退

表 1:1 个月至 7 个月期货的一天收益对现货 VIX 的一天收益进行回归的回归系数和拟合优度的总结。
我们观察到所有期货的高 R 值,表明它们与现货高度相关。期限较短的期货有较高的 R 值。这可以解释为
(I)期货价格趋向于接近到期日的现货价格,
㈡长期合同的流动性不如短期合同。
斜率系数都具有统计显著性,并且小于 1,这是直观的,因为期货回报往往比现货回报波动性小。负截距具有统计学意义,表明即使现货价格不动,期货价格也会下跌。
原因在于 VIX 期货的期限结构,它通常在到期日增加。即使现货价格不动,期货价格也倾向于下降,以匹配到期日的现货价格,从而形成负截距。
期限结构

(a) 2016 年和(b) 2009 年 1 月至 6 月的期限结构。图例显示了构建期限结构的日期。
如上图所示,VIX 市场的典型案例是一条上升的凹形期货曲线(左图)。然而,2016 年 1 月,VIX 价格飙升,对未来波动性的预期也是如此。像这样的峰值会导致 VIX 期货的期限结构反转,产生一条递减的凸形期货曲线。在 2009 年初(右图),期货价格处于非常高的水平,期限结构显示出不同的形状属性。
保持时间的影响
当我们对长期持有的期货回报和现货回报进行回归时,新的模式出现了。当我们计算回报时,我们使用不同长度的不相交区间,这意味着对于更长的时间范围,我们有更少的数据点。

我们绘制了 1 个月期货回报相对于 1 天回报(左)和 10 天回报(右)的 VIX 回报的回归曲线,绘制在相同的 x -y 轴刻度上。红色的 x 是成双成对的回报,而黑色的线是最好的 t 线。人们注意到,10 天的回报要大得多,因此比 1 天的回报更不稳定。
然而,对于两个持有期,期货回报比相应的现货回报波动性小。准确地说,我们发现现货的 1 天收益率在-26.96%和 50%之间变化,而 10 天收益率在-39.76%和 148.06%之间变化(即持有期越长,波动性越大)。
另一方面,对于 1 个月的期货,1 天的收益率在-20.81%和 35.83%之间变化,而 10 天的收益率在-36.91%和 88.89%之间变化(即期货收益率的波动性小于相应的现货收益率)。
在上图中,10 天回报率的斜率略高于 1 天回报率的斜率。此外,对于 1 天的回报,散点图更紧密地绑定到最佳拟合线,表明比 10 天的回报观察到的拟合更好。然而,这种模式并不普遍适用。随着保持时间的延长,在 R 或斜率中没有可辨别的模式。
这可以通过观察 R 或斜率与保持时间的关系图来证明。我们在这里省略了这样的图,因为没有增加或减少预测能力(以 R 衡量)或杠杆的清晰模式,这是复制图中任何到期合约的即期回报所必需的。(杠杆可以用斜率的倒数来衡量。)
另一方面,如果我们 x 持有期,增加合同的到期时间,我们看到预测能力下降,斜率下降。(因此,复制现货需要增加杠杆。)我们在表 2 中针对多个不同的持有期(1、5、10 和 15 天)证明了这一点。
在上半部分,我们给出了相对于现货回报的期货回报回归的斜率系数,在下半部分,我们显示了 R 值。通过查看任一半中的行,可以注意到报告的统计数据减少了。这再次证实了我们之前的观察,即短期期货比长期期货更接近现货。
事实上,在 15 天的时间里,结果表明,跟踪 1 个月期货需要大约 1.6 倍的杠杆,而 7 个月期货需要 7.2 倍的杠杆。这些隐含的杠杆值是通过计算斜率的倒数(分别为 0.622 和 0.139)获得的。7 个月期货的杠杆相当大,由于交易成本或交易限制,在市场上可能不可行。

表 2:对期货收益和 VIX 回归的斜率和 R 的总结
接下来,我们绘制了许多不同持有期(从 1 天到 30 天)的 1 个月期货(黑色)、3 个月期货(红色)和 6 个月期货(蓝色)的回归截距。很明显,随着持有期的延长,截距变得越来越负。
此处报告的所有截距(以及之前的斜率)在 1%显著性水平上具有统计显著性。因此,存在统计上的显著差异,并且随着保持期的延长而继续恶化。

这证实了我们已经讨论过的一个属性:相对于现货回报,VIX 期货往往表现不佳并亏损。更负的截距表明这种表现不佳在更长的时间内会恶化。
参考
T.Leung 和 B. Ward,用 VIX 期货跟踪 VIX:投资组合构建和业绩[pdf;链接,载于应用投资研究手册, J. Guerard 和 W. Ziemba 合编。,世界科学出版公司,印刷中,2020 年
更多信息,请关注/联系 Linkedin:https://www.linkedin.com/in/timstleung/
对比损失解释

对比损失最近在一些论文中使用,显示了无监督学习的最新成果。 MoCo 、 PIRL 、和 SimCLR 都遵循非常相似的使用具有对比损耗的连体网络的模式。当阅读这些论文时,我发现总的想法非常简单,但是从数学到实现的翻译没有得到很好的解释。正如机器学习论文中经常出现的情况一样,一旦你掌握了主要思想,数学就不难了。我将尝试清楚地解释对比损失是如何工作的,并使用 Python 和 Numpy 提供一个完整的实现。
暹罗网络
在深入了解细节之前,先谈谈暹罗网络(也称为孪生网络,但这不是一个广泛使用的术语)会有所帮助。训练连体网络时,会对 2 个或更多输入进行编码,并比较输出特征。这种比较可以在中以多种方式进行。一些比较是三重损失、具有交叉熵损失的伪标记和对比损失。
连体网络通常显示为共享权重的两个不同的编码网络,但实际上,在进行反向传播之前,同一个网络只使用了两次。

例子
让我们来看一个例子,在这个例子中,我们想从 MNIST 数中提取特征。MNIST 数的每个图像应该被编码成与来自相同类别的图像的向量接近的向量。相反,不同的数字应该编码成彼此远离的向量。

嵌入向量空间的正(上)样本和负样本
由于我们有 MNIST 输入的分类标签,因此可以使用常规网络和分类交叉熵损失。问题是当我们没有很好标记的数据时,这是通常的情况。世界上没有标签的数据比有标签的数据多得多。这就是对比损失的来源。
对比损失将网络的输出作为一个正例,并计算其与同类样本的距离,并将其与到负例的距离进行对比。换句话说,如果正样本被编码成相似(更接近)的表示,而负样本被编码成不同(更远)的表示,则损失较低。
这是通过取向量的余弦距离并将所得距离视为来自典型分类网络的预测概率来实现的。大意是,你可以把正例的距离和反例的距离当作输出概率,并使用交叉熵损失。
执行监督分类时,网络输出通常通过 softmax 函数运行,然后是负对数似然损失。
让我们把这个说得更具体些。这个例子将有两个相似的向量和一个不相似的向量的数组。p1 和 p2 是正向量,p2 是 P1 的稍微修改版本。Neg 是相异向量的数组。
示例设置
import numpy as npp1 = np.array([-0.83483301, -0.16904167, 0.52390721])
p2 = np.array([-0.83455951, -0.16862266, 0.52447767])
neg = np.array([
 [ 0.70374682, -0.18682394, -0.68544673],
 [ 0.15465702,  0.32303224,  0.93366556],
 [ 0.53043332, -0.83523217, -0.14500935],
 [ 0.68285685, -0.73054075,  0.00409143],
 [ 0.76652431,  0.61500886,  0.18494479]])
请注意,我在这里使用了预归一化向量(又名单位向量)。
计算距离
为了测量两个向量有多相似,我们需要一种测量距离的方法。在二维或三维中,欧几里德距离(“普通”或直线距离)是测量两点之间距离的最佳选择。然而,在一个大维度空间中,通过欧几里德度量,所有点往往相距很远。在更高维度中,向量之间的角度是更有效的度量。余弦距离测量向量之间角度的余弦值。相同向量的余弦为 1,而正交向量和相反向量分别为 0 和-1。更多的相似向量将导致更大的数量。计算余弦距离是通过取向量的点积来完成的。当不使用单位向量时,你要么将向量归一化,要么将乘积除以归一化向量。
# P1 and p2 are nearly identically, thus close to 1.0
pos_dot = p1.dot(p2)
pos_dot -> 0.999999716600668# Most of the negatives are pretty far away, so small or negative
num_neg = len(neg)
neg_dot = np.zeros(num_neg)
for i in range(num_neg):
    neg_dot[i] = p1.dot(neg[i])neg_dot -> [-0.91504053,  0.30543542, -0.37760565, -0.44443608, -0.64698801]
Softmax
softmax 函数获取一个实数向量,并强制它们在 0 到 1 的范围内,所有数字之和等于 1。softmax 的另一个好特性是其中一个值通常比其他值大得多。计算类别交叉熵的损失时,第一步是取值的 softmax,然后是标记类别的负对数。

Softmax,数学版
让我们把 pos_dot 的 softmax 加上 neg_dot 向量。
# make a vector from the positive and negative vectors comparisons
v = np.concatenate(([pos_dot], neg_dot))# take e to the power of each value in the vector
exp = np.exp(v)# divide each value by the sum of the exponentiated values
softmax_out = exp/np.sum(exp)softmax_out -> [0.4296791, 0.0633071, 0.2145353, 0.1083572, 0.1013523, 0.0827687]
我们的正例(0.4296791)现在比随机的大得多,而且都大于 0 小于 1。
对比损失
最后,我们得到了本文的重点,对比损失。

对比损失函数
对比损失看起来很像 softmax 函数。这是因为它增加了向量相似性和温度归一化因子。相似度函数就是我们之前讲过的余弦距离。另一个区别是分母中的值是从正样本到负样本的余弦距离。与 CrossEntropyLoss 差别不大。这里的直觉是,我们希望我们的相似向量尽可能接近 1,因为-log(1) = 0,这是最优损失。我们希望反例接近 0,因为任何非零值都会降低相似向量的值。
# Contrastive loss of the example values
# temp parameter
t = 0.07# concatenated vector divided by the temp parameter
logits = np.concatenate(([pos_dot], neg_dot))/t#e^x of the values
exp = np.exp(logits)# we only need to take the log of the positive value over the sum of exp. 
loss = - np.log(exp[0]/np.sum(exp))
loss -> 4.9068650660314756e-05
这就是全部了。对比损失可以被实现为交叉熵损失的修改版本。对比损失,如三重损失和磁损失,用于映射对输入项的相似性建模的向量。这些映射可以支持许多任务,如无监督学习、一次性学习和其他距离度量学习任务。我希望这篇文章能帮助你更好地理解对比损失。
监督分类的对比损失
对比交叉熵损失和对比损失
最近,来自谷歌研究院和麻省理工学院的研究人员(Khosla 等人)发表了一篇名为“有监督的对比学习”的论文。在对比学习的基础上,提出了一种新的损失函数“对比损失”来训练有监督的深度网络。他们证明,在 ImageNet 数据集上,对比损失在一系列神经架构和数据增强机制的分类中的表现明显优于传统的交叉熵损失。
我发现这篇论文特别有趣,因为它的思想非常简洁,可能是监督分类任务甚至回归的一种简单而通用的改进。在进入细节之前,让我们首先回顾一下用于监督分类的损失函数(又名目标函数)的基础知识。
交叉熵损失
交叉熵,也称为对数损失、逻辑损失,可以说是分类中最常用的损失函数。顾名思义,它来自信息论,信息论测量两个概率分布之间的互熵, p 和 q 。它也与kull back-lei bler 散度密切相关,可以写成熵 H(p) 与从 p 到 q 的 KL 散度之和:
H(p,q) = H(p) + D_{KL}(p||q)
当用作分类的损失函数时,可以使用交叉熵来度量基础真实类分布和预测类分布之间的差异:

交叉熵损失
其中 M 是类的数量 c 和 y_c 如果类标签是 c 和p(y = c |x)是在给定输入特征向量 x 的情况下分类器认为标签应该是 c 的概率
对比损失
对比损失广泛应用于无监督和自监督学习中。这个损失函数最初是由 Yann LeCun 的小组的 Hadsell 等人在 2016 年开发的,它作用于成对的样本,而不是单个样本。它为每对样本定义了一个二进制指示符 Y ,表示它们是否应该被视为不相似(如果 x_1 、 x_2 被视为相似,则 Y = 0);否则 Y=1),以及一个可学习的距离函数D _ W(x _ 1,x _ 2)在一对样本 x_1 , x_2 之间,由权重 W 参数化对比损失定义为:

对比度损失函数
,其中 m > 0 为余量。裕度定义了样本嵌入空间周围的半径,使得不同的样本对仅在距离 D_W 在裕度内时对对比损失函数有贡献。
直观地,该损失函数鼓励神经网络学习嵌入,以将具有相同标签的样本彼此靠近放置,同时在嵌入空间中使具有不同标签的样本远离。
自我监督学习和监督学习的对比损失
在标签不可用并且目标是学习数据的有用嵌入的自我监督设置中,对比损失与数据扩充技术结合使用,以创建共享相同标签的扩充样本对。就像科斯拉论文中的插图一样,比熊犬图片的不同放大应该彼此靠近,远离任何其他图片,无论是其他狗还是猫。然而,正如作者指出的,这种通过自我监督对比损失学习的嵌入不一定对监督学习者有用,因为引入了假阴性:一只比熊犬的嵌入也应该与其他狗的嵌入在同一邻域,无论是柯基犬还是西班牙猎犬。
为了使对比损失适应监督学习,科斯拉和他的同事开发了一个两阶段程序,将标签和对比损失的使用结合起来:
- 阶段 1:使用对比损失来训练编码器网络,以嵌入由它们的标签引导的样本。
- 阶段 2:冻结编码器网络,并在已学习的嵌入之上学习分类器,以使用交叉熵损失来预测标签。
最初研究中的实验是在 ImageNet 上进行的。作者还评估了不同的数据增强选项和各种类型的最新架构,如 VGG-19 和 ResNets。
MNIST 和时尚 MNIST 数据集上的实验
我不是计算机视觉专家,我更感兴趣的是,对于通用分类问题,所提出的监督对比损失是否优于交叉熵。为了在我的笔记本电脑上运行比较典型损失函数和对比损失函数的有效性的实验,我选择了小得多的 MNIST 和时尚 MNIST 数据集。我还忽略了像素之间的空间关系,避免使用任何卷积层来模拟表格数据集。
我在接下来的实验中使用的架构非常简单,两个密集的隐藏层,每个都有 256 个神经元,通过泄漏的 ReLU 激活连接。最后一个隐藏层的输出被归一化,以将输出向量定位在单位超球面中。这种架构对于用对比损失和 MLP 基线训练的编码器是相同的。最后的分类层由对应于 10 个类别的 10 个单元组成。
遵循 Khosla 论文提出的相同过程:编码器在阶段 1 中使用对比损失进行预训练,然后在阶段 2 中被冻结以仅训练分类器。如下所示,在两个数据集上,具有监督对比损失的模型确实优于具有交叉熵损失的 MLP 基线,具有完全相同的架构。这种改善在时尚的 MNIST 上更高,一个比 MNIST 更困难的任务。

在 MNIST 和时尚 MNIST 数据集的保留测试集上的性能(准确性)。
另一个有趣的观察是,对于有监督对比损失的模型,学习曲线更平滑,这可能是由于预先训练的表示。
为了证实性能的提高是由于更好的嵌入空间,我使用交叉熵检查了来自 MLP 基线和使用对比损失学习的嵌入的 PCA 投影。作为阴性对照,我还检查了没有任何嵌入的原始数据空间的 PCA 投影。

模型学习到的嵌入(或无嵌入)的 PCA 投影。从左到右:原始数据空间;基线 MLP 的最后一个隐藏层;使用对比损失学习的投影
如散点图所示,MLP 模型和对比模型都比原始数据更好地聚类具有相同标签的样本,但是对比嵌入中的聚类更紧密。为了更加突出这一点,我绘制了 PCA 投影的密度,从对比嵌入中可以清楚地看到 10 个不同的聚类:

显示模型学习的 PCA 投影密度的联合图。从左到右:原始数据空间;基线 MLP 的最后一个隐藏层;使用对比损失学习的投影
上面的图是在 MNIST 数据上生成的,类似的观察结果也可以在时尚 MNIST 数据集上持续存在,尽管一些类混合在一起。

不同模特在时尚 MNIST 数据集上学习的表征的主成分分析。
总之,我的实验清楚地再现了原始研究的发现,即监督对比损失在分类任务中优于交叉熵。事实上,它还可以在更小的数据集上工作,无需数据扩充,也无需使用特定领域的架构,如卷积,这非常令人鼓舞。这表明监督对比损失可以作为任何监督任务的通用技术。我期待将其应用于未来的 ML 任务,并探索其在回归中的适用性。
这些实验使用的代码可以在这里找到:【https://github.com/wangz10/contrastive_loss
参考
- Hadsell 等人(2006): 通过学习不变映射进行维数约简。
- 科斯拉等人(2020): 监督对比学习。
- tensor flow 中的三元组丢失和在线三元组挖掘
- 深度学习中的回归:连体和三联体网络
为数据科学做出贡献

嘿大家好!
由于我们经常收到关于如何为 TDS 做贡献的问题,我收集了一些想法,希望能帮助那些有兴趣开始的人。
首先也是最重要的,我们已经投入了大量的精力为我们的 撰写面向数据科学的 文章。如果你还没看,就从那里开始吧!多年前,这篇文章只是两分钟的阅读。现在这是一个十分钟的阅读,可能值得你花时间!
当我与一位作者面对面时,我通常会转述上面文章的部分内容来回答他们的问题。如果有人问了一个我们在文章中尚未回答的问题,我会回去编辑指南,以便每个人都可以阅读答案。这些年来,这篇文章解决了我被问到的关于如何成为一名成功的 TDS 作家的所有问题。
我注意到,我们最好的作者专注于为读者带来价值。他们以直接、简单、有时甚至有趣的方式分享他们的想法、代码和经验。他们使用自己的文字、图表和图像使他们的内容真正独一无二。当阅读他们的文章时,你可以看出作者试图帮助读者发现和理解新事物。
发表在《走向数据科学》上能帮你带来更多流量。我们在出版物、时事通讯和社交媒体上展示我们收到的最佳文章。因此,对数据科学感兴趣可以帮助你得到他人的关注和推荐!
如果你想阅读其他 TDS 作者的建议,看看这个页面。我们在这里收集了一些我们认为可能会让你感兴趣和受到启发的文章。
请永远记住,我们喜欢收到新的投稿!如果你有兴趣向我们提交一篇文章,你可以在这里找到具体的说明。太简单了,我们迫不及待地想看看你写了什么!如果你愿意连接,我在 Linkedin 上。
感谢您对撰写《走向数据科学》的兴趣!
为初学者贡献开源
入门
我是如何在短短几周内从几乎不知道如何使用 GitHub 变成为熊猫做贡献的
关于开源的一句话
让我们从什么是开源开始:可以自由修改和重新发布的代码,它通常是合作开发的。
数据科学中使用的许多工具都是开源的,从 pandas 和 matplotlib 等 Python 包到 Hadoop 等程序,任何人都可以免费访问它们的事实极大地促进了该领域的访问。了解到这些工具的广泛使用和有效性,令人惊讶的是,志愿者们提供了这些工具,并且仍然在业余时间定期维护它们。

源代码管理允许多人同时在同一个代码库上工作。照片由扬西·敏在 Unsplash 拍摄
我该如何开始?
我第一次有了为开源做贡献的想法是在听 podcas t 的线性题外话时。以前,我认为为开源做贡献是软件工程师的专利,但在他们的节目中,他们涵盖了数据科学开源生态系统,我意识到我使用的许多工具都是该生态系统的一部分。
然而,当时似乎仍然很难想象所有的碎片是如何组合在一起的。我看了几个 GitHub 库,这似乎有点违背直觉,所以我把这个想法放在后面,但把它藏在心里,作为我将来想做的事情。
然后到了九月,我看到了一条关于 Hacktoberfest 的推文,在这个活动中,一个月内,你可以对开源做出 4 点贡献,并在过程中学习。这似乎是一个以有组织的方式尝试的完美机会,所以我报名了。

图片来自数字海洋
基本资源
我做的第一件事就是跟着一个 GitHub 教程。当然,我以前上传过一些代码,但是我不知道拉请求的来龙去脉,合并冲突,甚至不知道分支是什么。
外面有很多 GitHub 教程,但我看了活动主页上的那个,它真的很有用。这里可以找到。
之后,我需要找到几个需要帮助的项目。对我有帮助的是,我发现许多存储库用“好的第一期”或类似的标签来标记初学者可以帮助的问题。你可以在熊猫、 numpy 和 matplotlib 中找到例子。
概括地说,你可以去任何 github 仓库,切换到“问题”标签,并根据适当的标签进行过滤。

未决问题列表。来源:github.com
我与熊猫和 matplotlib 的经历
我不是软件工程师。如果你找到了这个教程,但也不是,你很幸运,我们还有很多可以做的!一些简单的事情可以从解决文档问题或处理您已经使用过并有经验的特定功能开始。
对于我对数据空间的第一个贡献,我修正了熊猫文档中的代码风格。你可以在这里看到未解决的问题,即文档使用了在 pandas 中不再使用的某些编码风格约定,所以我必须通过工具运行一些 python 文件来重新格式化它们,并合并回主分支。我提出的最后一个 pull 请求可以在这里看到,它需要一些 python 格式的知识,以及从 GitHub 下载和运行一个工具,所以如果你知道如何打开一个 Python 文件和如何安装一些东西,你可以很容易地做一些类似的事情!
我的第二个问题来自 matplotlib,需要从文档页面中删除术语表及其所有引用,我认为这是一个简单的任务,但结果是需要在本地构建文档和一些 html。幸运的是,维护人员非常乐于助人,一路指引着我。我的第二个拉动请求以及所有细节可以在这里找到。
我在路上学到了什么?
在你点击离开这篇文章之前,我想告诉你一些事情。
- 开源维护者都是不可思议的人。他们是志愿者,但是他们非常乐于助人和积极响应。我接触过的每个人都很乐意随时帮忙,不管是在深夜还是周末,即使没有紧迫的截止日期或公司盈利电话催得急。这些人对数据充满热情,他们激励我也这样做。
- 阅读投稿指南。大多数回购都有一个自述文件,解释他们接受捐款的首选方法。请注意,这些问题可能已经被分配了,或者有人已经开始处理它们了,所以在开始之前,请检查问题页面上的评论,并与维护人员沟通您想要开始处理它。
- 请勿发送垃圾邮件。开源贡献者和维护者非常忙碌,我在 Twitter 上看到了很多关于 Hacktoberfest 和其他活动的有用性的来回,其中有一种趋势是垃圾邮件小的改进,以满足您的拉请求配额。请确保您的开源贡献是有用的,或者是对“问题”选项卡上提到的某个问题的回应。
- 享受过程中的乐趣!我非常喜欢为开源做贡献,一旦我发现下一个我可以帮忙的问题,我会再做一次。
循环,中断,继续与朱莉娅在一起
Julia 的控制流基础知识

学会随波逐流-图片由迈克·刘易斯智慧媒体在 Unsplash 上发布
让我们继续探索朱莉娅的基础知识。之前我谈到了循环的和矢量化的和。在这里,我们将讨论如何在 Julia 内部使用控制流操作符。
什么是控制流操作符?
顾名思义,控制流操作符帮助我们塑造程序的流程。你可以从一个函数返回,你可以从一个循环中中断,你可以通过继续跳过一个循环的迭代。
简单的任务
为了理解这些概念,我们将尝试解决一个问题。没有比亲身体验更好的了,对吧?我们的挑战如下:
给定 2 个整数(a,b ),打印 a 和 b 之间最小的(最多)5 个整数,这样我们就不会打印能被 3 整除的数字。
例如如果a=5和b=23我们应该打印以下数字:
5
7
8
10
11
如果a和b彼此更接近,我们打印到b的所有内容。下面是另一个关于a=2和b=4的例子:
2
4
如果你完全是编程新手,你可能想看看我的 FizzBuzz 文章[链接],在那里我解释了 for 循环和 modulo 函数。
首先,我们打印
本着循序渐进的精神,让我们构建我们的函数:
这只是打印从a到b之间的所有数字。
然后,我们继续
我们的问题是,这也打印可被 3 整除的数字,这不是我们想要的。为了在 for 循环中跳过这些数字,我们可以使用continue。
所以无论何时rem(i,3) == 0计算为true,我们都将跳回循环的顶端,继续下一个i。这就是continue的作用。
让我们来测试一下:
julia> fancy_printer(3,10)4
5
7
8
10
厉害了,再也没有能被 3 整除的数字了!👏
最后,我们休息

有时候,我们需要从循环中休息一下——张肯尼在 Unsplash 上拍摄
以上照顾到了我们的一个问题,但是在a和b之间有超过 5 个不能被 3 整除的数的情况下,我们还是要处理。例如:
julia> fancy_printer(3,13)4
5
7
8
10
11
13
这意味着我们要小心的不是我们打印的内容,而是我们总共打印了多少次。为了处理这个问题,我们将引入另一个名为 printed 的变量,我们可以用它来计算我们打印的次数。如果这个值达到 5,我们就可以用 break 结束 for 循环,结束它。
让我们看看它是否有效🤞:
julia> fancy_printer(3,25)4
5
7
8
10
确实如此。但是现在所有这些 if 语句看起来有点难看…
越来越花哨
现在我们有了一个fancy_printer函数,看起来没那么花哨。我来给大家介绍一下 短路操作工 。短路运算符&&和||可以将中频模块压缩成一个单元。他们将一个布尔表达式带到左侧并将一个动作带到右侧。&&仅在布尔值为true时评估动作,而||仅在布尔值为false时动作。
使用短路操作符
&&和||使你的代码更加简洁。
下面是这个更简洁的版本的样子:
%是余数运算符。i % 3 与 rem(i,3)相同
简洁紧凑的代码的确很酷!— 来源
结论
现在你知道了:
- 如果您想在任何时候停止循环,请使用break。
- 为了跳过你的循环迭代,continue是你的朋友。
- 三元运算符&&和||可以让你的代码更小,可读性更好。
这里还有一些关于茱莉亚的文章:
如何使用 FizzBuzz 执行循环和条件函数
towardsdatascience.com](/learning-julia-the-simplest-of-beginnings-464f590e5665) [## 向量化朱莉娅的一切
告别 for loops,广播所有的东西
towardsdatascience.com](/vectorize-everything-with-julia-ad04a1696944)
控制你所能控制的:通过任务规划强化学习!

[2]
在这里我谈谈我们 NeurIPS 2019 的论文,将规划与强化学习代理 agraphnd 内在动机结合起来。
现实世界中的许多控制问题都有某种层次结构。当我们谈论真正的自主机器人时,理想情况下,我们希望它们获得对环境的最大控制。我们希望这发生在通过奖励功能很少或没有监督的基础上。此外,我们希望代理在环境中利用这些固有的任务层次,以使学习更加有效。通俗地说,就是想让机器人自己一个人呆着,没有任何规范,让它自己想明白一切。大多数方法只处理这些问题中的一个,因此我们[1]提出了一种同时处理所有这些问题的方法。
在开始使用这种方法之前,我们必须弄清楚我们所做的假设和某些术语。首先,我们假设环境有明确定义的任务空间,并且对我们可用。在我们的情况下,任务空间被定义为观察空间的子空间,例如物体的坐标,尽管机器人不知道语义。这意味着自我强加的目标对应于达到任务空间中的目标,例如,将对象移动到某个位置。
自然地,任务可以相互依赖,然而,现在仅仅学习这种依赖性就足够了,因为我们还需要知道在子任务中需要达到哪个确切的目标。举个例子,考虑一个仓库里的机器人。仓库中有一个重物太重,无法直接提起,因此机器人需要使用叉车。这个场景自然分解为 3 个任务(简化):机器人位置、叉车位置、重物位置。我们希望机器人自己想出应该移动到叉车,让叉车靠近重物,然后移动物体。这包括分别瞄准每个子任务的正确目标。

有机器人的仓库场景。我们提出的方法学习重物、叉车和机器人之间的正确任务依赖性。(1)
记住所有这些,在我们提议的方法中,这些是我们试图解决的主要挑战:
- 在使用代理执行首次展示之前,我们如何选择要尝试的任务?
- 假设任务之间有相互依赖关系,我们如何找到正确的任务依赖关系?
- 我们如何根据学习到的任务依赖关系生成子目标?
你能控制的框架
在《控制你所能》( CWYC)中,我们有多个部分共同发挥作用,以实现学习中的样本效率。但是在进入单个组件之前,我们需要一种方法来指导缺乏外在奖励的学习。人们可以依靠前向模型的预测误差作为替代奖励,但预测误差是不够的。它不够用的原因是在嘈杂的环境中。如果我们在环境中有不可预测的因素,这将产生持续的预测误差。因此,我们在以下所有组成部分中严重依赖惊喜对内在动机的衡量。在没有成功信号的情况下(如果任务被解决,则为 1),该算法更依赖惊喜作为替代奖励。如果一个事件/转变在一定的置信区间之外引起相当大的误差,我们说它是令人惊讶的,这使我们能够处理环境中的噪声。
任务选择器决定代理要尝试哪个任务。任务选择器实际上是一个多臂强盗,我们需要一种方法来适应它的臂分布。应该尝试的任务是可以改进最多的任务,因此我们引入改进/学习进度(成功的时间导数)作为更新分布的手段。
关于任务依赖性的信息包含在任务规划器中,它实际上是一个对任务图或上下文多臂 bandit 进行编码的矩阵(其中上下文是要解决的任务,动作是之前的任务)。我们根据单元格条目从任务规划器中抽取任务序列。作为示例,从任务 A 到任务 B 的转移概率与求解 B 所需的时间量成比例,给定求解任务 A 和在 B 的目标空间中在求解任务 B 之前求解任务 A 时看到的惊奇
子目标生成器使我们能够给定任务链中的任务转移来设置目标。即,这意味着假设我们知道我们想要稍后解决任务 B,生成器输出任务 a 的目标。子目标生成器可以被视为任务的目标空间上的潜在函数。同样,在没有成功信号的情况下,这种奖励被惊喜所主导。
下图总结了上述所有组件:

实验
我们在两个连续的控制任务上评估了我们的方法,一个合成工具使用任务和一个挑战性机器人工具使用任务,对照内在动机和分级 RL 基线。我们注意到的是,在缺乏一个良好的奖励的情况下,基线无法学习如何解决这些层次化的任务。这与这样一个事实紧密相关,即状态空间中只有很小一部分与解决任务相关。在一个形状良好的奖励面前,其他方法也能够解决任务。

我们在评估中使用的环境
下图是工具使用任务的性能图。我们的方法(CWYC)在层次结构中稍微复杂一点的任务上一直优于基线,比如拿起一个重物。详见[1]。

实验结果。HIRO 是另一种分层 RL 算法,ICM 是我们的内在动机基线,SAC(软行动者-批评家)是标准 RL 算法。

(2)
在动画(2)中,我们可以看到算法在机械臂任务中的训练进度。盒子太远,机器人无法自己够到,所以它需要意识到它应该先抓住钩子,才能成功地将盒子移动到目标位置(红色)。
此外,我们测量了算法的“资源分配”,即算法为每个单独的任务贡献了多少时间。在下图中,我们可以很好地说明我们的观点。基于我们的方法,通过使用意外信号及其与成功信号的组合,我们实现了有效的资源分配。无法解决的任务,如移动一个物体,随机出现在不同的地方,很快得不到关注。

图(a)示出了代理随时间的资源分配,图(b)示出了任务依赖图的学习编码,较高的数字意味着在展开任务链时任务转移发生的概率较高。图(c)是学习任务图的图形表示,注意,进入任务的箭头意味着该任务是箭头所源自的任务的依赖项。例如,loco(运动)是工具任务的依赖项。
然而,问题仍然存在,我们能否进一步简化我们的假设,就像在任务空间的情况下一样。对于一个导致高效学习的问题,我们能学习到高效的任务空间划分吗?假设我们不知道任务空间的哪一部分是可达的,我们如何学习为任务取样可行的目标?这个我们留待以后研究。
参考
[1] Blaes,Sebastian 等人控制你所能控制的:内在激励的任务规划代理,NeurIPS 2019
[2]图片取自 Pixabay
确认
这是马克斯·普朗克智能系统研究所自主学习小组的工作。
用眼睛控制鼠标

根据眼睛位置自动导航到坐标的鼠标(图片由作者提供)
仅从单个正面视角作为输入的眼睛姿态估计的机器学习方法
在这个项目中,我们将编写代码,在你每次点击鼠标时裁剪你眼睛的图像。使用这些数据,我们可以反向训练一个模型,从你的眼睛预测鼠标的位置。
我们需要几个图书馆
# For monitoring web camera and performing image minipulations
import cv2# For performing array operations
import numpy as np# For creating and removing directories
import os
import shutil# For recognizing and performing actions on mouse presses
from pynput.mouse import Listener
我们先来了解一下 pynput 的Listener是如何工作的。
pynput.mouse.Listener创建一个记录鼠标移动和鼠标点击的后台线程。下面是一个简化代码,当鼠标按下时,它打印鼠标的坐标:
from pynput.mouse import Listenerdef on_click(x, y, button, pressed):
  """
  Args:
    x: the x-coordinate of the mouse
    y: the y-coordinate of the mouse
    button: 1 or 0, depending on right-click or left-click
    pressed: 1 or 0, whether the mouse was pressed or released
  """
  if pressed:
    print (x, y)with Listener(on_click = on_click) as listener:
  listener.join()
现在,为了我们的目的,让我们扩展这个框架。然而,我们首先需要编写裁剪眼睛边框的代码。稍后我们将从on_click函数中调用这个函数。
我们使用 Haar 级联对象检测来确定用户眼睛的包围盒。你可以点击下载探测器文件。让我们做一个简单的演示来说明这是如何工作的:
import cv2# Load the cascade classifier detection object
cascade = cv2.CascadeClassifier("haarcascade_eye.xml")# Turn on the web camera
video_capture = cv2.VideoCapture(0)# Read data from the web camera (get the frame)
_, frame = video_capture.read()# Convert the image to grayscale
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# Predict the bounding box of the eyes
boxes = cascade.detectMultiScale(gray, 1.3, 10)# Filter out images taken from a bad angle with errors
# We want to make sure both eyes were detected, and nothing else
if len(boxes) == 2:
  eyes = []
  for box in boxes:
    # Get the rectangle parameters for the detected eye
    x, y, w, h = box
    # Crop the bounding box from the frame
    eye = frame[y:y + h, x:x + w]
    # Resize the crop to 32x32
    eye = cv2.resize(eye, (32, 32))
    # Normalize
    eye = (eye - eye.min()) / (eye.max() - eye.min())
    # Further crop to just around the eyeball
    eye = eye[10:-10, 5:-5]
    # Scale between [0, 255] and convert to int datatype
    eye = (eye * 255).astype(np.uint8)
    # Add the current eye to the list of 2 eyes
    eyes.append(eye) # Concatenate the two eye images into one
  eyes = np.hstack(eyes)
现在,让我们用这些知识写一个函数来裁剪眼睛的图像。首先,我们需要一个帮助函数来进行规范化:
def normalize(x):
  minn, maxx = x.min(), x.max()
  return (x - minn) / (maxx - minn)
这是我们的眼睛裁剪功能。如果找到了眼睛,它将返回图像。否则,返回None:
def scan(image_size=(32, 32)):
  _, frame = video_capture.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  boxes = cascade.detectMultiScale(gray, 1.3, 10) if len(boxes) == 2:
    eyes = []
    for box in boxes:
      x, y, w, h = box
      eye = frame[y:y + h, x:x + w]
      eye = cv2.resize(eye, image_size)
      eye = normalize(eye)
      eye = eye[10:-10, 5:-5]
      eyes.append(eye) return (np.hstack(eyes) * 255).astype(np.uint8)
  else:
    return None
现在,让我们编写我们的自动化程序,它将在我们每次按下鼠标按钮时运行。(假设我们已经在代码中将变量root定义为我们想要存储图像的目录):
def on_click(x, y, button, pressed):
  # If the action was a mouse PRESS (not a RELEASE)
  if pressed:
    # Crop the eyes
    eyes = scan()
    # If the function returned None, something went wrong
    if not eyes is None:
      # Save the image
      filename = root + "{} {} {}.jpeg".format(x, y, button)
      cv2.imwrite(filename, eyes)
现在,我们可以回忆起我们对 pynput 的Listener的实现,并进行完整的代码实现:
当我们运行这个时,每次我们点击鼠标(如果我们的双眼都在视野中),它将自动裁剪网络摄像头并将图像保存到适当的目录中。图像的文件名将包含鼠标坐标信息,以及是右键还是左键单击。
这是一个示例图像。在此图像中,我在分辨率为 2560x1440 的显示器上的坐标(385,686)处执行左键单击:

一个例子(图片由作者提供)
级联分类器的准确率很高,我自己的数据目录到目前为止还没有看到任何错误。
现在,让我们编写用于训练神经网络的代码,以在给定眼睛图像的情况下预测鼠标位置。
让我们导入一些库
import numpy as np
import os
import cv2
import pyautoguifrom tensorflow.keras.models import *
from tensorflow.keras.layers import *
from tensorflow.keras.optimizers import *
现在,让我们添加级联分类器:
cascade = cv2.CascadeClassifier("haarcascade_eye.xml")
video_capture = cv2.VideoCapture(0)
让我们添加我们的助手函数。
标准化:
def normalize(x):
  minn, maxx = x.min(), x.max()
  return (x - minn) / (maxx - minn)
捕捉眼球:
def scan(image_size=(32, 32)):
  _, frame = video_capture.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  boxes = cascade.detectMultiScale(gray, 1.3, 10) if len(boxes) == 2:
    eyes = []
    for box in boxes:
      x, y, w, h = box
      eye = frame[y:y + h, x:x + w]
      eye = cv2.resize(eye, image_size)
      eye = normalize(eye)
      eye = eye[10:-10, 5:-5]
      eyes.append(eye) return (np.hstack(eyes) * 255).astype(np.uint8)
  else:
    return None
让我们定义一下显示器的尺寸。您必须根据自己电脑屏幕的分辨率来更改这些参数:
# Note that there are actually 2560x1440 pixels on my screen
# I am simply recording one less, so that when we divide by these
# numbers, we will normalize between 0 and 1\. Note that mouse
# coordinates are reported starting at (0, 0), not (1, 1)
width, height = 2559, 1439
现在,让我们加载我们的数据(同样,假设您已经定义了root)。我们并不关心是右键还是左键,因为我们的目标只是预测鼠标的位置:
filepaths = os.listdir(root)
X, Y = [], []for filepath in filepaths:
  x, y, _ = filepath.split(' ')
  x = float(x) / width
  y = float(y) / height
  X.append(cv2.imread(root + filepath))
  Y.append([x, y])X = np.array(X) / 255.0
Y = np.array(Y)
print (X.shape, Y.shape)
让我们定义我们的模型架构:
model = Sequential()
model.add(Conv2D(32, 3, 2, activation = 'relu', input_shape = (12, 44, 3)))
model.add(Conv2D(64, 2, 2, activation = 'relu'))
model.add(Flatten())
model.add(Dense(32, activation = 'relu'))
model.add(Dense(2, activation = 'sigmoid'))
model.compile(optimizer = "adam", loss = "mean_squared_error")
model.summary()
以下是我们的总结:
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d (Conv2D)              (None, 5, 21, 32)         896
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 2, 10, 64)         8256
_________________________________________________________________
flatten (Flatten)            (None, 1280)              0
_________________________________________________________________
dense (Dense)                (None, 32)                40992
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 66
=================================================================
Total params: 50,210
Trainable params: 50,210
Non-trainable params: 0
_________________________________________________________________
让我们训练我们的模型。我们将在图像数据中添加一些噪声:
epochs = 200
for epoch in range(epochs):
  model.fit(X, Y, batch_size = 32)
不,让我们用我们的模型用我们的眼睛移动鼠标。请注意,这需要大量数据才能正常工作。然而,作为一个概念的证明,你会注意到只有大约 200 张图片,它确实可以将鼠标移动到你正在查看的大致区域。当然,除非你有更多的数据,否则这是不可控的。
while True:
  eyes = scan() if not eyes is None:
      eyes = np.expand_dims(eyes / 255.0, axis = 0)
      x, y = model.predict(eyes)[0]
      pyautogui.moveTo(x * width, y * height)
这里有一个概念验证的例子。请注意,在拍摄这个屏幕记录之前,我用很少的数据进行了训练。这是我的鼠标根据眼睛自动移动到终端应用窗口的视频。就像我说的,这很不稳定,因为数据很少。随着更多的数据,它将有望足够稳定,以更高的特异性进行控制。只有几百张图片,你只能把它移动到你的视线范围内。此外,如果在整个数据收集过程中,没有拍摄到您注视屏幕特定区域(比如边缘)的图像,则该模型不太可能在该区域内进行预测。这是我们需要更多数据的众多原因之一。
根据凝视自动移动鼠标的视频
drive.google.com](https://drive.google.com/file/d/1TkU3rRS68U9vk4t7AB--NEQgyYH77Irm/view?usp=sharing)
如果您自己测试代码,记得在代码文件prediction.py中将width和height的值更改为您显示器的分辨率。
您可以在此处查看本教程中的代码:
GitHub 是一个使用机器学习方法来控制具有眼睛姿态估计的计算机鼠标的项目
github.com](https://github.com/Ryan-Rudes/eye_mouse_movement)
利用图表和统计数据控制假新闻
公平的选举,安全的交易,公正的推荐?
你一定已经听说了来自中国的新威胁。不,我不是在说冠状病毒。
不,你没有?


上面这些东西叫做点击农场。他们已经是一个产业了。
一个点击农场可能包含货架上成千上万的 iPhones 和 Android 手机,以影响和操纵推荐系统,传播假新闻,影响公众舆论,有时甚至赢得选举——这是美国一些政客的当务之急。
假新闻无处不在。很多假用户也是如此。
在任何时候,数以百万计的交易都在我们日益互联的世界中发生。这些事务是有时间限制的,我们需要在动态造成任何损害之前尽早发现它们。
那么,我们能在这些欺诈交易发生时抓住它们吗?数据科学会拯救我们吗?
在这篇文章中,我将谈论MIDAS,一个基于微簇的边缘流异常检测器,旨在解决这个问题。
使用的数据:
为了解决这个问题,我们将使用在 MIDAS 论文中使用的 DARPA 入侵检测数据集。DARPA 在 87.7K 分钟内在 9.4K 源 IP 和 2.3K 目的 IP 之间进行 450 万次 IP →IP 通信。每个通信都是有向边(src_IP、dst_IP、时间戳、攻击)的形式,其中地面真实攻击标签指示通信是否是攻击(异常占总数的 60.1%)。数据如下图所示。
230.001.010.020,172.016.112.050,07/03/1998-18:17,neptune
230.001.010.020,172.016.112.050,07/03/1998-18:17,neptune
230.001.010.020,172.016.112.050,07/03/1998-18:17,neptune
230.001.010.020,172.016.112.050,07/03/1998-18:17,neptune
230.001.010.020,172.016.112.050,07/03/1998-18:17,neptune
但是在 MIDAS 中使用之前,我们必须将 src_node、dst_node 和 timestamp 改为整数。幸运的是,库的作者已经为我们做了这个预处理,我们可以从这里的一个预处理文件开始。你也可以在这里下载原始数据。处理后的文件如下所示:
2,3,1
2,3,1
3,4,2
3,4,2
5,9,2
5,9,2
算法思路:
a)计数最小草图(CMS):

所以首先,我们来谈谈 CMS,它是这个算法中使用的主要数据结构。来自维基百科:
在计算中, CMS 是一种概率数据结构,充当数据流中事件的频率表。它使用哈希函数将事件映射到频率,但与哈希表不同的是,它只使用亚线性空间,代价是由于冲突而导致的一些事件的过度计数。
简单来说,CMS 就是一个近似字典的 。一种数据结构,可以保存关键字的近似计数。
它被实现为具有 w 列和 d 行的网格,其中每一个 d 行具有不同的散列函数。每当我们遇到一个键,我们就根据行的散列函数将列中的所有行加 1。当我们需要检索键的值时,我们遍历所有行,并给出给定键的所有行中的最小值。

遍历所有的 j 行,得到键的最小值。h 是哈希函数,count 是这里的网格名。
这种数据结构的优点是,当网格的大小保持不变时,我们可以在恒定的时间内获得一个键的值。
b)流式假设检验方法(MIDAS):

上涨可能是危险的
先从简单的开始,说说两个顶点 u 和 v 之间的单条边。我们有这种边缘的过去趋势,我们可以看到在时间段 10 有相当大的上升。我们需要创建一个异常值来捕捉这种上升。在现实世界中,这种上升可能是 DDOS 攻击。或者,可能是从一个 IP 地址到另一个 IP 地址的抓取尝试。
那么,我们怎样才能抓住这样的上升趋势呢?
一种方法是假设时间序列遵循特定的生成模型,例如,高斯分布。然后,我们可以找到这个高斯分布的平均值和标准偏差,并使用它们来声明特定时间点的边缘是异常的。
但是那是一个相当严格的条件。 而分布可能是任何东西。因此,MIDAS 算法使用较弱的假设,即当前时间刻度(例如, t = 10)中的平均水平(即,边缘出现的平均速率)与当前时间刻度( t < 10)之前的平均水平相同。
因此,我们建立了一个假设检验,它避免了为每个时间点假设任何特定的分布。我们可以将过去的优势分为两类:
- 当前时间刻度( t = 10),
- 所有过去的时间刻度(tt 35】10)。
现在,设 sᵤᵥ 为从 u 到 v 到当前时间为止的总边数( t ≤10)。而 aᵤᵥ 是当前时刻从 u 到 v 的边数( t =10)。
我们可以使用两个 CMS 数据结构来保存所有边的这些计数。一个保存过去的命中,另一个保存两个节点之间的当前命中。
所以在( t = 10)的点击次数是 aᵤᵥ ,而在过去时间刻度( t < 10)的点击次数是sᵤᵥaᵤᵥ
现在,我们可以进行卡方拟合优度检验,该检验用于确定样本数据( t = 10)是否符合假设分布( t < 10)。我们的卡方统计是:

该统计/异常分数越高,出现异常边缘的可能性就越大。
MIDAS 算法看起来很简单:

c) MIDAS-R
上述思想在 MIDAS-R 算法中得到了扩展,增加了:
- 有些时间弹性——为什么考虑某个特定的时间滴答而不是最近的过去?最近过去的边也应计入当前时间刻度,但通过减少权重进行修改。使用我们的 CMS 数据结构实现这一点的一个简单而有效的方法如下:在每个时间点结束时,我们不是重置我们的 CMS 数据结构 aᵤᵥ ,而是将它的所有计数减少一个固定的分数 α ∈ (0,1)。这允许过去的边以减少的权重计入当前时间刻度。
- 我们使用的一个简单的直觉是,在这两种情况下,我们期望观察到突然出现大量边的节点。因此,我们可以像以前一样使用两个附加的 CMS 数据结构来跟踪边计数,除了计数与任何节点 u 相邻的所有边。具体来说,我们创建 CMS 计数器 aᵤ 和 sᵤ 来近似节点 u 附近的当前和总边数。给定每个传入的边缘( u,v ),我们然后可以计算三个异常性分数:一个是边缘( u,v ),如在我们之前的算法中一样;一个用于节点 u ,一个用于节点 v 。最后,我们通过取它们的最大值来组合这三个分数。
这导致更高性能的 MIDAS-R 算法。

编码/入门
我们从获取完整的代码开始:
git clone [https://github.com/bhatiasiddharth/MIDAS.git](https://github.com/bhatiasiddharth/MIDAS.git)
然后,我们可以:
- 运行make来编译代码并创建二进制文件。
- 运行./midas -i darpa_midas.csv。你也可以使用你的数据。
run 命令的输出如下:

当我们查看 scores.txt 文件时,我们看到:

然后,我们可以使用带有地面实况标签的原始 DARPA 文件来检查 AUC 分数。
from sklearn import metrics
from sklearn.metrics import precision_recall_curve
import pandas as pddata = pd.read_csv("darpa_original.csv", names=['src', 'dst', 'time', 'label'])
is_anom = data.label != '-'scores = pd.read_csv("scores.txt", header=None, squeeze=True)
fpr, tpr, _ = metrics.roc_curve(is_anom, scores)
auc = metrics.roc_auc_score(is_anom, scores)
precision, recall, _ = metrics.precision_recall_curve(is_anom, scores)
print("AUC: ", auc)
----------------------------------------------------------------
AUC:  0.9515446059172757
这是公关曲线:
import matplotlib.pyplot as pltplt.step(recall, precision, color='b', alpha=0.2,
         where='post')
plt.fill_between(recall, precision, step='post', alpha=0.2,
                 color='b')plt.xlabel('Recall')
plt.ylabel('Precision')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.0])
plt.title('Precision-Recall curve')

与 SEDANSPOT 提供的基线(=0.64)相比,MIDAS-R 实现了更高的 AUC(= 0.95)(sedan spot 使用个性化的 PageRank 来检测次线性空间中的异常和每个边缘的恒定时间),同时运行速度也显著更快(0.39 秒对 84 秒)。
这是一个 48%的 AUC 改进,速度提高了 215 倍。
结论
MIDAS 提供了一个简单的框架来发现任何数据中的异常,这些异常可以用一个随时间变化的/动态的图表来表示。
它可以用于社交网络和交易网站中的各种用例,以检测微聚类和欺诈。
所以打倒新冠肺炎,打倒假新闻。
随着如此多的社交数据的出现,网络分析可以在改善我们的模型和产生价值方面提供很大帮助。如果您想了解更多关于图形算法的信息,请访问:
因为图表分析是未来
towardsdatascience.com](/data-scientists-the-five-graph-algorithms-that-you-should-know-30f454fa5513)
还有,这里有一个UCSanDiego在 Coursera 上的大数据图形分析课程,我强烈推荐学习图论的基础知识。
感谢西达尔特·巴蒂亚(《迈达斯》的原作者)
参考
西达尔特·巴蒂亚,布莱恩·胡伊,姬敏·尹,吉荣·申和克里斯特斯·法鲁索斯。" MIDAS:基于微团簇的边缘流异常检测器."2020 年 AAAI 人工智能大会(AAAI)。https://arxiv.org/abs/1911.04464
控制语言模型的文本生成
控制机器生成文本的样式和内容的实际操作方法

乔恩·弗洛布兰特在 Unsplash 上的照片
介绍
现在看到机器能够生成任何种类的流畅文本已经很酷了。但是,如果我们可以告诉机器生成什么风格或形式,而不需要为每种风格训练单独的模型,会怎么样呢?这将耗费大量的计算能力和时间。
如果我告诉你我们真的可以做到呢?介绍 PPLM ,代表即插即用语言模型。多亏了 优步人工智能 ,PPLM 是一种将预训练的语言模型与一个或多个属性模型相结合的方法,这些模型将能够指导文本生成。PPLM 最好的一点是,它不需要语言模型训练或微调,并且对可以使用的属性模型没有限制。
它是如何工作的

来源:优步艾博文
在官方的博客文章中,作者将 GPT-2 这样的大型语言模型描绘成一只猛犸,而将属性模型描绘成一只老鼠。GPT-2 训练的计算成本更高,就像猛犸因其体积而难以移动一样。解决方案是使用一个较小的模型(像一只老鼠),可以控制 GPT-2 的方向。
PPLM 使用了两种属性模型:词袋(BoW)和鉴别器。在词袋中,给出一个包含特定主题的所有相关词的文件来训练模型。该模型像分类器一样附加在基本语言模型(GPT-2)之上。通过这样做,GPT-2 将比以前更频繁地产生这些词。作者包括以下 BoW 作为例子开始:法律,军事,怪物,政治,积极的话,宗教,科学,空间,技术。
下图显示了在给定相同输入标记“关注的问题”的情况下,基于不同单词包的不同输出。

来源:优步艾博文
对于 Discriminator,主题属性可以由包含与该属性相关的不同类的样本的数据集来表示。然后,PPLM 可以根据作为输入提供的类生成相应的文本。作者包括了以下鉴别器例子:积极情绪,消极情绪,点击诱饵,非点击诱饵。
下图再次显示了基于正和负鉴别器类的相同输入令牌的不同输出。

来源:优步 AI 博文
现在我们已经了解了 PPLM 是如何工作的,让我们继续实现。由于时间关系,本文将主要关注 PPLM-鲍的实现。
履行
第 1 部分:微调 GPT2(正如我在上一篇文章中提到的)
在我之前的帖子中,我描述了如何微调 GPT2 以在您的自定义数据集中生成任何文本。这篇博客文章将使用那篇文章中的微调模型,该模型是根据书籍摘要训练的。如果你没有读过那篇文章,你可以先去看看那篇文章,或者用预先训练好的 GPT2 模型。拥有一个微调的模型将允许生成一个更具体的领域(例如,书籍摘要),而不仅仅是一般的文本。
使用 Pytorch 和 Huggingface 微调用于文本生成的 GPT2。我们在 CMU 图书摘要数据集上进行训练,以生成…
towardsdatascience.com](/fine-tuning-gpt2-for-text-generation-using-pytorch-2ee61a4f1ba7)
第 2 部分:下载回购
我们将使用优步研究所的官方回购,您可以通过以下方式下载他们的回购:
git clone https://github.com/uber-research/PPLM
cd PPLM/
第三部分:构建 PPLM 弓
造一个。txt 文件,包含与您希望生成的特定主题相关的单词。例如,如果我们想要生成浪漫书籍摘要,我们可以构建一个包含浪漫相关单词列表的浪漫 BoW:
第 4 部分:在微调的 GPT2 上运行 PPLM
现在,我们已经构造了 BoW 文本文件,我们可以用它来运行 PPLM:
CUDA_VISIBLE_DEVICES=$N python run_pplm.py -B /path/to/BoW/romance.txt --pretrained_model=/path/to/model/ --cond_text="The novel" —- num_samples=20 --length=150 --stepsize=0.03 --num_iterations=3 --window_length=5 --gamma=1.5 --gm_scale=0.95 --kl_scale=0.01 --colorama --verbosity='regular' --sample
上面的命令将使用/path/to/BoW/romance.txt作为预训练模型/path/to/model/上的弓分类器来运行 PPLM。我们将条件文本(输入文本)设置为“小说”,看看它之后会生成什么。我们生成 20 个长度为 150 的样本。请注意,--stepsize可以用来控制主题的强度,因此增加它将导致生成的文本更频繁地包含在 BoW 中。--colorama将用红色对生成文本中的蝴蝶结进行颜色编码,--sample将确保模型用相同的种子生成不同的样本。您也可以使用预训练的 GPT-2,默认情况下没有指定任何模型将是“GPT 2-中等”。
如果您希望能够保存生成的输出,我已经添加了一个额外的命令行参数--save_path ,它将允许您保存(这里有代码)。您只需运行下面的命令,输出将保存到该文件夹中:
CUDA_VISIBLE_DEVICES=$N python run_pplm.py -B /path/to/BoW/romance.txt --pretrained_model=/path/to/model/ --cond_text="The novel" —- num_samples=20 --length=150 --stepsize=0.03 --num_iterations=3 --window_length=5 --gamma=1.5 --gm_scale=0.95 --kl_scale=0.01 --colorama --verbosity='regular' --sample --save_path="/path/to/save/romance.txt"
第五部分:瞧!您现在可以看到生成的样本
以下是使用 romance BoW 生成的一些示例,输入条件为文本“The novel…”:
这部小说以两个年轻人的故事开始,他们是英国一位富有实业家的女儿,和他的妻子,一位富婆美丽女儿的女儿,嫁给了同一个男人。他们结婚是为了永远在一起,他们没有孩子。他们的女儿原来是一个美丽的女人,她的丈夫在爱了她多年后,决定给她一生的爱来结婚…
这部小说讲述了年轻夫妇丽莎和大卫第二次结婚的故事。他们过着幸福的生活,但当大卫的父亲去世时,他们的婚姻注定要失败。这对夫妇与他们的鳏夫父亲和女婿住在一起,但他们有婚外情。
这部小说是一系列闪回故事,讲述了一个名叫苔莎的年轻孤儿的生活,她的母亲死于一场火灾,她住在自己工作的房子里。房子的主人是一个同名的女人。当苔莎去和她母亲住在附近的房子里几年时,她惊讶地看到一个英俊、迷人的年轻人。她爱上了他,但他不再爱她,她怀了她第一任丈夫的孩子…
更多例子可以在我的 Github repo 上查看。
结论
本文通过使用强大的语言模型 GPT-2 实现 PPLM,深入探讨了如何控制文本生成。PPLM 的强大之处在于它允许不同的属性模型组合来产生不同样式的文本。就是这样!希望你们能从这篇文章中学到一些东西,并期待在下一篇文章中见到你们!
如果感兴趣,这里有更多我写的文章😊
文本分类是自然语言处理中的一项常见任务。我们应用 BERT,一个流行的变压器模型,对假新闻检测使用…
towardsdatascience.com](/bert-text-classification-using-pytorch-723dfb8b6b5b) [## 基于 Pytorch 的 LSTM 文本分类
一步一步的指导你如何在 Pytorch 中建立一个双向 LSTM!
towardsdatascience.com](/lstm-text-classification-using-pytorch-2c6c657f8fc0)
参考资料:
[1] S. Dathathri,A. Madotto,J. Lan 等。,即插即用语言模型:控制文本生成的简单方法 (2020),2020 年学习表征国际会议
[2] R .刘、s .达塔瑟里、a .马多托等。、用即插即用语言模型控制文本生成、AI 博客
[3] S. Dathathri,A. Madotto,J. Lan 等。, PPLM 代码,Github
[4] 用变形金刚,拥抱脸和优步 AI 写
Conv2d:终于明白了向前传球会发生什么
2D 卷积层及其参数的直观和数学解释
介绍
深度学习的库和平台如 Tensorflow 、 Keras 、 Pytorch 、 Caffe 或 Theano 在日常生活中帮助我们,以至于每天都有新的应用让我们觉得“哇!”。我们都有自己最喜欢的框架,但它们的共同点是,它们通过易于使用、可按需配置的功能来简化我们的工作。但是我们仍然需要理解可用的参数是什么来利用这些框架给我们的所有能力。
在这篇文章中,我将尝试列出所有这些论点。如果您想了解它们对计算时间、可训练参数的数量以及卷积输出通道的大小的影响,那么这篇文章就是为您准备的。

输入形状:(3,7,7) —输出形状:(2,3,3) — K : (3,3) — P : (1,1) — S : (2,2) — D : (2,2) — G : 1
所有的 gif 都是用 python 做的。你将能够测试这些论点中的每一个,并且自己想象它们对我的 Github 上的脚本的影响(或者制作你的 gif)。
这篇文章的各个部分将根据以下论点进行划分。这些参数可以在 Conv2d 模块的 Pytorch 文档中找到:
- in _ channels(int)—输入图像的通道数
- out _ channels(int)—卷积产生的通道数
- kernel _ Size(int或tuple)—卷积核的大小
- 步距 ( int 或 元组 ,可选 ) —卷积的步距。默认值:1
- 填充(int或 元组 ,可选 ) —输入两边加零。默认值:0
- 膨胀(int或 元组 ,可选 ) —内核元素之间的间距。默认值:1
- 组(int,可选 ) —输入通道到输出通道的阻塞连接数。默认值:1
- 偏置 ( 布尔 ,可选 ) —如果True,向输出添加一个可学习的偏置。默认:True
最后,我们将让的所有键根据参数和输入通道的大小计算输出通道的大小。
什么是内核?

输入图像和内核之间的卷积
我来介绍一下什么是内核(或者说卷积矩阵)。内核描述了一个过滤器,我们将通过它来处理输入图像。简单来说,内核将通过应用卷积乘积在整个图像上从左到右、从上到下移动。该操作的输出被称为过滤图像。

卷积乘积

输入形状:(1,9,9) —输出形状:(1,7,7) — K : (3,3) — P : (0,0) — S : (1,1) — D : (1,1) — G : 1
举一个非常基本的例子,让我们想象一个 3 乘 3 卷积核过滤一个 9 乘 9 图像。然后,这个内核在整个图像上移动,以在图像中捕获所有大小相同的正方形。卷积乘积是一个元素级(或点级)乘法。这个结果的总和就是输出(或过滤)图像上的结果像素。
如果你还不熟悉滤波器和卷积矩阵,那么我强烈建议你多花一点时间来理解卷积核。它们是 2D 卷积层的核心。
可训练参数和偏差
可训练参数,也简称为“参数”,都是网络训练时会更新的参数。在 Conv2d 中,可训练元素是由组成内核的值。因此,对于我们的 3 乘 3 卷积核,我们有 3*3=9 个可训练参数。

有偏卷积积
为了更完整。我们可以包括偏差或不包括。偏差的作用是加到卷积乘积的和上。这个偏差也是一个可训练的 参数,这使得我们的 3 乘 3 内核的可训练参数的数量增加到 10 个。
输入和输出通道的数量

输入形状:(1,7,7) —输出形状:(4,5,5) — K : (3,3) — P : (0,0) — S : (1,1) — D : (1,1) — G : 1
使用层的好处是能够同时执行类似的操作。换句话说,如果我们想要对一个输入通道应用 4 个相同大小的不同滤波器,那么我们将有 4 个输出通道。这些通道是 4 个不同过滤器的结果。因此产生了 4 个不同的内核。

在上一节中,我们看到可训练参数构成了卷积核。因此参数的数量随着卷积核的数量线性增加。因此与所需输出声道的数量成线性关系。还要注意的是,计算时间也随着输入通道的大小和内核的数量成比例地变化。

请注意,参数图中的曲线是相同的
同样的原理适用于输入通道的数量。让我们考虑 RGB 编码图像的情况。这个图像有三个通道:红色,蓝色和绿色。我们可以决定在这 3 个通道的每一个上用相同大小的滤波器提取信息,以获得 4 个新通道。因此,对于 4 个输出通道,操作是相同的 3 倍。

输入形状:(3,7,7) —输出形状:(4,5,5) — K : (3,3) — P : (0,0) — S : (1,1) — D : (1,1) — G : 1
每个输出通道是经过滤波的输入通道的总和。对于 4 个输出通道和 3 个输入通道,每个输出通道是 3 个滤波后的输入通道之和。换句话说,卷积层由 4*3=12 个卷积核组成。

提醒一下,参数数量和计算时间与输出通道数量成比例变化。这是因为每个输出通道都链接到不同于其他通道的内核。对于输入通道的数量也是如此。计算时间和参数数量成比例增长。

内核大小
到目前为止,所有的例子都给出了 3 乘 3 大小的内核。其实它的大小的选择完全由你决定。可以创建核心大小为 11 或 1919 的卷积层。

输入形状:(3,7,9) —输出形状:(2,3,9) — K : (5,2) — P : (0,0) — S : (1,1) — D : (1,1) — G : 1
但是也绝对有可能没有方形核。有可能决定让内核具有不同的高度和宽度。在信号图像分析中经常会出现这种情况。例如,如果我们知道我们想要扫描信号或声音的图像,那么我们可能想要 5*1 大小的内核。
最后,你会注意到所有尺寸都由奇数和奇数定义。定义一个甚至的内核大小也是可以接受的。实际上,很少这样做。通常,选择奇数大小的内核是因为在中心像素周围有对称性。

由于卷积层的所有(经典)可训练参数都在内核中,参数的数量随着内核的大小线性增长。计算时间也成比例地变化。
大步
默认情况下,内核从左向右移动,从上到下逐像素移动。但是这种转变也是可以改变的。常用于下采样输出通道。例如,对于(1,3)的步长,滤波器从水平方向的 3 到 3 和从垂直方向的 1 到 1 进行移位。这会产生水平向下采样 3 倍的输出通道。

输入形状:(3,9,9) —输出形状:(2,7,3) — K : (3,3) — P : (0,0) — S : (1,3) — D : (1,1) — G : 1
步长对参数的数量没有影响,但是逻辑上,计算时间随着步长线性减少。

请注意,参数图中的曲线是相同的
填料
填充定义了在卷积滤波之前添加到输入通道的侧边的像素的数量。通常,填充像素被设置为零。输入通道延长。

输入形状:(2,7,7) —输出形状:(1,7,7) — K : (3,3) — P : (1,1) — S : (1,1) — D : (1,1) — G : 1
当您希望输出通道的大小与输入通道的大小相等时,这非常有用。简单来说,当内核为 3*3 时,输出通道的大小每侧减少一个。为了克服这个问题,我们可以使用填充 1。
因此,填充对参数数量没有影响,但会产生与填充大小成比例的额外计算时间。但一般来说,与输入声道的大小相比,填充通常足够小,以至于不会影响计算时间。

请注意,参数图中的曲线是相同的
扩张
在某种程度上,膨胀就是地核的宽度。默认等于 1,对应于 卷积期间输入通道上内核的每个像素之间的偏移。

输入形状:(2,7,7) —输出形状:(1,1,5) — K : (3,3) — P : (1,1) — S : (1,1) — D : (4,2) — G : 1
我在我的 GIF 上夸大了一点,但如果我们以(4,2)的膨胀为例,那么输入通道上的内核的*感受域在垂直方向上加宽了 4 * ( 3 -1)=8,在水平方向上加宽了 2 (3–1)= 4(对于 3 乘 3 的内核)。
就像填充一样,膨胀对参数的数量没有影响,对计算时间的影响也非常有限。

请注意,参数图中的曲线是相同的
组
在特定的情况下,组非常有用。例如,如果我们有几个串联的数据源。当没有必要对待他们相互依赖的时候。输入通道可以分组并独立处理。最后,输出通道在最后被连接。
如果有 2 组 2 个输入通道和 4 个输出通道。这就像将输入通道分成两组(每组一个输入通道),并使其通过一个输出通道数量减半的卷积层。然后输出声道被连接起来。

输入形状:(2,7,7) —输出形状:(4,5,5) — K : (3,3) — P : (2,2) — S : (2,2) — D : (1,1) — G : 2
需要注意的是,组的数量必须完全除以输入通道的数量和输出通道的数量(公约数)。
因此,参数的数量除以组的数量。关于 Pytorch 的计算时间,该算法针对组进行了优化,因此应该会减少计算时间。但是,还应该考虑到,必须将输出通道的组形成和连接的计算时间与相加。

输出通道尺寸
有了所有参数的知识,就可以从输入通道的大小计算出输出通道的大小。


知识就是分享。
支持我,获得访问 中我所有文章的一键点击此处 。

来源
深度学习教程,Y. LeCun
文档 torch.nn ,Pytorch
卷积神经网络,cs231n
卷积层,Keras
所有的图像都是自制的
所有计算时间测试都是在我的 GPU (GeForce GTX 960M)上用 Pytorch 执行的,如果你想自己运行它们或执行替代测试,可以从 GitHub 库 获得 。
随机变量的收敛性
用简单的术语解释不同的聚合模式

陈逸飞在 Unsplash 上的照片
随机变量的收敛;
一个随机变量序列(RVs)在重复多次后会遵循一个固定的行为
RVs (Xn)序列最初保持变化的值,并最终稳定在更接近 X 的数字。
但是,“收敛到一个接近 X 的数”是什么意思呢?
通常,RVs 可能不会精确地固定在一个最终的数字上,但是对于一个非常大的 n,方差会变得越来越小,导致序列收敛到一个非常接近 x 的数字。
所以,让我们学习一个符号来解释上述现象:

随着 n 变大,级数 Xn 收敛到最终值 X
作为数据科学家,我们经常会讨论一个算法是否收敛?现在,让我们通过下面的示例来观察上述收敛特性:

算法的收敛图
既然我们已经彻底了解了收敛的概念,那么让我们来理解在上面的上下文中“接近”应该有多“接近”吧?
按照数学家的说法,“接近”意味着要么提供两个 Xn 和 X 之间距离的上界,要么取一个极限。
下面,我们将根据取值限制列出三种主要的收敛类型:
1)几乎必然收敛
2)概率收敛
3)分布的趋同性
但是,为什么我们会有不同类型的收敛,当它所做的只是确定一个数字的时候?这是因为,没有一种方法可以定义 RVs 的收敛性。
我将在下面的结构中解释每种收敛模式:
- 直觉和概念类比:这将有助于初学者理解概念。如果是第一次接触,理解这些章节应该就足够了
- 定义和数学示例:概念的正式解释,了解关键概念和三种模式之间的细微差别
不同收敛模式之间的关系:
如果一个级数收敛于“几乎必然”强收敛,那么这个级数也以概率和分布收敛。但是,反过来就不正确了

分布收敛:
直觉:这意味着随着 n 变得越来越大,我们在建模分布以及下一个输出时变得越来越好。
定义:一系列实数 RVs 依分布收敛如果当 n 增长到∞时 Xn 的 cdf 收敛到 X 的 cdf

其中 F 代表 cdf,对于所有 x € R 应该是连续的
由于它只依赖于随机变量序列和极限随机变量的 cdf,因此不要求两者之间有任何依赖关系。所以,分布的收敛不像概率的收敛和几乎必然的收敛那样,告诉我们任何关于联合分布或概率空间的东西。
批注:

举例:中心极限定理(CLT)

𝜇和𝜎是总体的平均值和标准差
问题:设 Xn 是 x₂,…suchx₁的随机变量序列,其 cdf 定义为:

给定 X~ exp(1 ),让我们看看它是否收敛于分布
解法:让我们首先计算 Xn 的 cdf 的极限:


当 Xn 的 cdf 等于 X 的 cdf 时,证明级数依分布收敛。
概念类比:在学习新技能的初始上升曲线期间,与技能被掌握时相比,输出是不同的。在一段时间内,可以肯定地说,产量或多或少是恒定的,并且在分布上趋于一致
概率收敛:
直觉:Xn 与 X 相差大于ε(固定距离)的概率为 0。换句话说,随着系列的进展,不寻常结果的概率不断缩小。
定义:称一个级数 Xn 按概率收敛于 X 当且仅当:

与分布收敛不同,概率收敛取决于联合 CDF,即 Xn 和 X 是相关的
符号:

例子:弱大数定律
因为“弱”和“强”大数定律是大数定律(LLN)的不同版本,并且主要基于收敛模式来区分,我们将在后面讨论它们。
问题:设 Xn 是随机变量序列 X₁,X₂,…such,xn~unif(2–1∕2n,2+1∕2n)
对于给定的定数 0 < ε<1, check if it converges in probability and what is the limiting value?
解:对于 Xn 以概率收敛到一个数 2,我们需要找出 P(|Xn — 2| > ε)对于某个ε是否趋向于 0
让我们看看分布是什么样的,RV 偏离收敛常数超过一定距离的概率变为 0 的区域是什么。

概念类比:根据每个班级随机抽取 10 名学生的表现得出的学校排名,不会反映学校的真实排名。但是,当每个班级越来越多的学生的表现被计入学校排名时,它就接近了学校的真实排名。
几乎确定收敛:
直觉:对于非常高的 n 值,Xn 收敛到 X 的概率几乎是确定的即 prob 为 1。
定义: 无穷序列 RVs X1(ω),X2(ω)… Xn(w)有一个概率为 1 的极限,就是 X(ω)

ω:定义随机变量的基础概率空间的样本空间
注意极限在概率收敛中是在概率之外,而极限在几乎必然收敛中是在概率之内。
符号:

举例:强收敛定律
问题:设 Xn 是一个随机变量序列,X₁、X₂,…such 认为
Xn = t + tⁿ,其中 T ~ Unif(0,1)
解法:我们把样本空间分成两个区域,应用下图所示的全概率定律:

当概率评估为 1 时,级数 Xn 几乎必然收敛
概念类比:如果一个人基于抛硬币的结果,从他的语料库中向慈善机构捐赠了一定的金额,那么 X1,X2 暗示了第一天,第二天捐赠的金额。随着时间的推移,本金将不断减少,因此慈善捐赠的金额几乎肯定会减少到 0,即概率为 1。
依概率收敛和几乎必然收敛的区别:
- “弱”大数定律是依概率收敛的结果,被称为弱收敛,因为它可以从较弱的假设中得到证明。它指出,随着 n 的增加,样本均值将更接近总体均值𝜇,但留下了 ε误差可能出现无限次的范围。
- 然而,几乎确定收敛是一个更具约束性的收敛,它认为两个平均值之间的差小于ε的情况会无限频繁地出现,即概率为 1。也就是说, 违反几乎必然收敛中所述的不等式仅在有限数量的情况下发生
- Eric Towers 在这里做了一个很好的区分。
希望这篇文章能让你很好地理解不同的融合模式
感谢阅读!!!
参考资料:
- https://www . probability course . com/chapter 7/7 _ 2 _ 4 _ convergence _ in _ distribution . PHP
- https://en . Wikipedia . org/维基/Convergence _ of _ random _ variables
强化学习算法的收敛性
学习一项任务的速度有什么简单的界限吗?Q-learning 背景下的研究。

D eep 强化学习算法可能是最近机器学习发展中最难对其性能设定数值界限的算法(在那些起作用的算法中)。理由是双重的:
- 深层神经网络是模糊的黑匣子,没有人真正理解它们如何或为什么如此好地融合。
- 强化学习任务收敛是 历史上不稳定因为从环境中观察到的稀疏奖励(以及底层任务的难度— 从零开始学习! )。
在这里,我将向您介绍一种启发式算法,我们可以用它来描述 RL 算法如何收敛,并解释如何将其推广到更多的场景。

来源——作者,哥斯达黎加。
一些相关的文章,可以在这之前看,或者在这之后看:
- 什么是马尔可夫决策过程?
- 强化学习的隐藏线性代数。
- 强化学习的基础迭代方法。
这篇文章解决了 的问题:像值迭代、q 学习和高级方法这样的迭代方法在训练时是如何收敛的? 在这里,我们将简要回顾马尔可夫决策过程(更多细节请参见上面的链接 1 和 3),解释我们如何将贝尔曼更新视为一个特征向量方程(更多信息请参见链接 2),并展示的数学运算,即值如何收敛到最优量。
回顾马尔可夫决策过程
马尔可夫决策过程是支持强化学习的随机模型。如果你很熟悉,你可以跳过这一部分。

MDP 就是一个例子。来源——我在 CS188 做的一个讲座。
定义
- 一组状态 s ∈ S ,动作 a ∈ A 。
- 一个转移函数 T(s,a,s’)。
- 一个奖励函数 R(s,a,s’)。这个函数 r 的任何样本都在区间[-Rmax,+Rmax]内。
- 区间[0,1]中的折扣因子 γ (gamma) 。
- 开始状态 s0 ,也可能是结束状态。
重要的价值观
MDP 有两个重要的特征效用-状态值和机会节点的 q 值。任何 MDP 或 RL 值中的 ***** 表示一个 最优量 。效用是 1)一个状态的 v 值和 2)一个状态、动作对的 Q 值T21。****

最佳值与最佳动作条件 q 值相关。然后值和 q 值更新规则非常相似(加权转换、奖励和折扣)。顶部)值与 q 值的耦合;mid) Q 值递归,bot)值迭代。我在加州大学伯克利分校 cs 188 的讲座。
我们试图做的是估计这些价值、q 值和效用,以便我们的代理人能够计划最大化回报的行动。
真实估计=最优策略。

来源,作者。美国印第安纳州詹姆斯敦。
概括迭代更新
在新内容的第一部分中,我将回忆我正在使用的 RL 概念,并强调获得一个方程组所需的数学变换,该方程组的以离散的步骤发展,并且具有收敛界限。
强化学习
RL 是我们试图“解决”和 MDP 的范例,但我们不知道底层环境。简单的 RL 解决方案是基本 MDP 求解算法(价值和策略迭代)的基于采样的变体。回想一下 Q 值迭代,这是我将重点关注的贝尔曼更新:

MDP 的 q 值迭代。
看看精确值迭代或策略迭代如何提炼为在上述等式中的每次赋值(←)后比较一个值向量,这是一轮递归更新。这些方法的收敛产生了与强化学习算法如何收敛成比例的度量,因为强化学习算法是价值和策略迭代的基于采样的版本,具有一些更多的移动部分。
回忆:Q-learning 是与 Q-value 迭代相同的更新规则,但是将转移函数替换为采样的动作,将奖励函数替换为实际样本, r ,从环境中接收。
线性算子和特征向量
我们需要将我们的 Bellman 更新公式化为一个线性操作符, B ,(矩阵是线性操作符的子集),看看我们是否可以让它表现为一个 随机矩阵 。随机矩阵保证有一个特征向量与特征值 1 配对。

λ x =Ax,其中 lambda=1.
也就是说,我们可以像研究特征空间的演化一样研究 RL 中的迭代更新。
现在,我们需要做一些符号上的改变,从矩阵中关于 Q 值的公式过渡到向量中作用于效用的公式(效用概括为值和 Q 值,因为它被定义为奖励的贴现和)。
- 我们知道效用与 q 值成正比;改变符号。我们将使用。
- 将实用程序重新排列为一个向量类似于许多编码库的flatten()功能(例如,将 XY 状态空间转换为跨越状态的 1d 向量索引)。我们得到。**

将 q 状态转换为特征值的通用效用向量。
这些变化对于将问题公式化为特征向量是至关重要的。
了解 Q-learning 的各个部分
从下面 Q 值迭代的基本方程开始,我们如何将它推广到线性系统?

MDP 的 q 值迭代。
在这种情况下,我们需要更改右侧,使其如下所示:

将我们的系统移向线性算子(矩阵)
这给我们留下了最后一种形式(合并本节的方程,以及本征值部分的结尾)。我们可以用这个作为我们需要的线性算子吗?考虑一下,如果我们使用的是最优策略(不失一般性),那么棘手的最大 over actions 就会消失,留给我们的是:

这是我们能得到的最接近的,使它成为一个纯特征向量方程。它非常接近所谓的广义特征向量。

来源——作者,香港维多利亚的山顶。
研究趋同
在本节中,我将推导出一个关系式,该关系式保证 N 步后ε的最小误差,并展示其含义。****
我们可以研究的系统
我们在上面看到,我们可以用一种非常接近特征向量的方式来制定效用更新规则,但是我们偏离了一个常数向量 r 来表示 MDP 的基本回报面。如果我们取两个效用向量的差会发生什么?常数项消失了。

取两个效用向量之间的差,我们看到, r 向量可以抵消。
我们现在有我们的系统,我们可以像特征向量的动态系统一样研究,但我们在效用之间的差异空间中工作,用矩阵B—加权转移概率的总和。因为效用向量是相互定义的,所以我们可以神奇地重新排列这个(代入递归的贝尔曼方程,取范数)。

贝尔曼更新后的误差减少了折扣因子。
研究任何效用估计之间的差异是巧妙的,因为它显示了 a)估计如何不同于真实值,或者 b)仅来自递归更新的数据如何演变(而不是小向量 r )。
ε-N 关系
任何收敛证明都将寻找误差界限,ε,和步数, N ,(迭代)之间的关系。这种关系将使我们有机会用一个分析方程来限制性能。

我们希望在步骤 N-b(N)-处效用误差的界限小于ε。该界限将是一个解析表达式,ε是一个标量,表示估计的和真实的效用向量之间的差的范数。
换句话说,我们在寻找ε的一个界限,这是 n 的函数
首先,我们知道(根据 MDP 的定义)每一步的回报,【r】,有界在区间[-Rmax,+Rmax]内。那么,把效用(报酬的贴现和)的定义看做一个几何级数,我们就可以把任意向量的差绑定到真实向量上。 ( 等比数列收敛的证明 )。
 ****
****
左)效用的定义:贴现报酬的期望总和。右)我们收敛的起点。从一个几何级数的收敛来考虑真实效用 U 和初始效用 U(0)之间的差异。
界限来自最坏情况的估计——其中每一步的真实回报是+Rmax,但我们将估计值初始化为-Rmax。唉,我们对我们的实用程序的误差有一个初始界限!回忆一下 U0 是我们初始化效用向量的值,然后每当我们运行贝尔曼更新时,索引就会增加。
贝尔曼更新— 初始化时的界限如何随着每一步而演变?上面,我们看到在每一步误差都减少了折扣因子(从递归更新中的总和总是预先加上一个伽玛)。随着每次迭代,这将演变成一系列递减的误差。

当前效用更新(U_i)与真实值 U 之差的收敛。
剩下的就是宣告界限,ε,相对于步数, N 。
结果——可视化融合
在上面等式的右边,我们有一个效用估计的精确度界限。
从逻辑上讲,对于任何ε,我们知道误差将在 N 步中小于ε。

我们还可以用数学技巧在ε和 N 之间来回变换——因此,如果我们知道我们希望估计值有多精确,我们就可以解决让我们的算法运行多长时间的问题!

通过取两边的对数,我们可以解出达到所述界限的迭代次数!
我们发现的界限是状态空间上累积值误差的界限(下面的实线)。精明的读者会想知道的是,政策错误与此相比如何?从概念上来说,这意味着,‘在多少个州,当前政策会与最优政策不同?’事实证明,随着数值的标准化(因此它们在数值上相似),政策误差收敛得更快,并且更快地达到零(没有渐近线!).

政策迷失空间中价值迭代与政策迭代的收敛性。策略的离散性使得策略迭代收敛更快在许多情况下** 。 来源。**
这代表了在某些情况下使用策略迭代优于值迭代的优势。它会带来更多的算法吗?
影响-限制最近的深度增强算法
绑定深度 RL 算法是每个人都想要的。近年来我们已经看到了令人印象深刻的成果,机器人可以跑、叠毛巾、玩游戏。如果我们在性能上有限制,那就太好了。
我们所能做的,必然是我们对世界的表述将如何趋同。我们已经证明效用函数将会收敛。有两个持久的挑战:
- 我们没有被赋予 奖励功能 对于现实生活中的任务,我们必须设计它。
- 运行这些迭代算法目前是不安全的。机器人探索涉及大量的力量、相互作用和(实际上)损害。
我们可以研究算法的收敛性,但是限制深度强化学习应用于现实世界任务的大多数工程问题是奖励工程和安全学习。
这就是我留给你们的——一个帮助我们设计更好系统的行动号召,这样我们就可以展示更多管理它的基础数学。
以下是我对人们在深度 RL 研究中使用的方法的总结。
获得 RL 算法要点的资源,无需浏览大量文档或方程。
towardsdatascience.com](/getting-just-the-gist-of-deep-rl-algorithms-dbffbfdf0dec)**
更多?订阅我关于机器人、人工智能和社会的时事通讯!
一个关于机器人和人工智能的博客,让它们对每个人都有益,以及即将到来的自动化浪潮…
robotic.substack.com](https://robotic.substack.com/)**
对话代理:聊天机器人的一种更自信的形式
意图识别和客户体验是聊天机器人的未来。智能对话式人工智能代理是聊天机器人的新进展,它可以根据客户过去的对话、购买或浏览行为来预测客户的需求。

图片作者作者
在过去的几年里,人工智能的进步抓住了公众更大的想象力,并导致人工智能助手的普遍采用。然而,这种变化和创新的快速步伐伴随着对技术方向以及它将如何影响社会的越来越多的怀疑。
尽管如此,普遍的共识是,提高计算机以更自然的方式与人类互动的潜力对于人工智能与人类的关系进一步发展非常重要。
人工智能以前所未有的速度改变了通信环境。这些不仅包括媒体行业的彻底变革,还涉及我们与之沟通的合作伙伴。今天和电脑互动很平常。
它们已经进化得不仅仅是人与人之间对话的媒介,还可以以聊天机器人和虚拟助手的形式与人类互动。
这已经可以通过对话式人工智能来实现,对话式人工智能是一种合成的智能,使计算机能够理解、处理人类语言并对其做出反应。
通过利用先进的深度学习措施和自然语言理解(NLU),对话式人工智能可以提升到能够真正改变客户体验的地步。客户将不必感受到原始聊天机器人的局限性,因为它们的范围狭窄。
有了对话代理,你可以超越简单的聊天机器人的反应,使他们更有上下文。客户可以直接从聊天窗口做任何事情,从提交保险索赔到请求天气建议以及外出是否安全。
对话代理和聊天机器人的区别
对话代理不仅给你建议,还和你一起思考,帮你做决定。例如,他们可以搜索数据集,在您最喜欢的网站上预订您的旅行,还可以找到最合适的日期。
这些智能体使用自然语言理解,并且比聊天机器人更具情境性。此外,成功的对话代理可以跨各种面向客户的渠道实现,并使用真实的用户数据进行训练。
对话代理
据聊天机器人杂志报道,一个对话代理是:
“对话代理是一个软件程序,它解释并响应用户用普通自然语言所做的陈述。它将计算语言学技术与互联网上的交流相结合。”
对话代理是一个对话系统,它执行自然语言处理(NLP ),然后用人类语言响应查询。会话代理体现了计算语言学的实用用法,它通常被用作互联网上的虚拟代理,作为服务助理。
聊天机器人
根据牛津词典,聊天机器人是:
设计用来模拟与人类用户对话的计算机程序,尤其是在互联网上
聊天机器人是由人工智能驱动的设备,用于模拟和模仿人类对话的非结构化流。聊天机器人不同于目的驱动的对话代理,它利用人工智能在理解意图和分析话语背景的基础上,用一组特定的预定义回复来响应人类对话。
总结一下,对话代理和聊天机器人的区别是:

图片由作者
对话代理是如何工作的
如前所述,对话代理是进行自然语言处理并以人类语言回应对话的对话系统。所讨论的对话系统可以用于从输入通道读取,然后通过输出通道以图形、语音或触觉辅助的物理手势的形式回复相关响应。
该过程的第一步包括在一种自动语音识别器(ASR)或光学手写/手势识别器的帮助下,将现实世界的输入转换为机器代码。
在这之后,输入在自然语言理解(NLU)单元的帮助下被破译。这超越了标准的自然语言处理,包括词性标注、名称识别和语义/句法分析器。
在解密的同时,对话管理器还运行以跟踪对话的历史和状态,从而通过启动/终止相关的子任务域来保持对话不脱离逻辑轨道。
最后,响应被处理并交付给输出生成器,然后输出生成器匹配任何输入。
对话代理与人工智能的联系
走向模拟对话是人机交互发展的下一步。这种转变也已经发生,从我们的手机到网站到应用程序,人工智能聊天机器人无处不在。
我们可以向他们提出各种各样的问题,并以打字文本的形式得到答案。但是蜜月期并没有持续多久。
彭博贝塔公司的合伙人希文·齐尔利斯说:
“聊天机器人曾经很火,现在没那么火了。他们在客户支持和智能家电等少数几个狭窄的应用领域看到了一些早期成功,但人们越来越失望,因为他们的期望过高。”
用户报告说,随着他们的期望值开始上升,他们对聊天机器人的失望程度越来越高。这是因为聊天机器人无法进行前后关联的交流,这可能包括从同一个对话中获取线索。
聊天机器人也缺乏感知和理解用户情绪的同理心,无法对他们做出更自然的反应。这些限制之所以存在,是因为计算机无法在自然语言理解和对话方面取得与自然语言处理相同的进步。
那么,一个人如何超越简单的“我不明白”作为对意外查询或触发的回复呢?答案在于在对话代理中嵌入更好的上下文感。建立这种环境可能需要几个相互联系的知识积累层,这些知识是计算机通过它们参与的每一次互动积累起来的。
密歇根大学人工智能实验室主任说:“在整个对话过程中,你如何真正把握上下文?这是最大的挑战。你如何理解我在说什么,很大程度上取决于我在五句或十五句前说了什么。你正在建立一种对话状态。”
显而易见,在对话代理中引入上下文感并不简单——这是一个复杂的问题,计算机科学家正在试验许多不同的潜在解决方案。这就是人工智能的用武之地。
计算机科学家正在特别试验机器学习算法,并在两种技术的帮助下训练它们:强化学习和监督学习,这两种技术已被用于训练人工智能系统执行单独的任务。
强化学习包括在让系统说出它以前从未说过的话时,通过反复试验来教会系统。这有助于对话更快、更有效地进行。
科学家试图用不同的方式说同样的事情,以帮助对话代理学习映射系统,从而在给定对话的整体背景和任务目标的情况下,理解接下来要说什么。有了监督学习,计算机通过例子来学习。它训练对话系统学习在特定上下文中说的对话,然后模仿它们。
加深对话代理和 AI 之间联系的另一个因素是物联网能力的持续发展。每当传感器将物体和人的上下文数据中继到人工智能系统时,这些设备将有助于为人工智能系统提供更大的上下文。
当这些设备继续相互“交谈”时,它们将获得更好的上下文感觉,并且随着该上下文可用于对话,对话将变得更自然、有用和更好。
对话代理的应用和用例
正确部署的对话代理将彻底改变你与用户互动的方式、时间和地点。您可以通过任何渠道与消费者进行无缝、无错误、同步的互动。对话代理用例的范围很广,下面将提到其中最少的一个。
收集线索并促进转化
令人惊讶的是,比起销售人员,用户更信任对话代理。

图片来自凯捷
这种信任通过实现对话式人工智能程序赋予企业巨大的权力。例如,得克萨斯州的软件公司 Cigniti 在他们的对话代理上看到了 40%的转化率。
提供客户支持
对话代理也是客户支持的一个改变者。他们可以为客户答疑解惑,提供建议,帮助他们做决定等等。

图片由 Helpshift
客户也喜欢对话代理具有的即时可访问性。使用对话式人工智能也可以让你大规模地管理对话。事实上,对话代理帮助零售商在不需要额外员工帮助的情况下处理了 167%的订单量增长。
个人助理
也许,对话代理最普遍的应用是以个人助理的形式。这些助理做从理解开放式对话到满足用自然语言提出的请求的一切事情。
尽管个人助理代表着一个新兴的市场,却受到了消费者的极大热情。根据 KPCB 的一项研究,与文本相比,用户对这项技术的速度非常着迷。
这种热情反映在遥遥领先的市场领导者亚马逊的 Echo 的销量上,根据 TechCrunch 的数据,该公司在 2019 年售出了超过 1.469 亿台。
结论
在不久的将来,基于更广泛背景的对话将创造出更好、更有用的助手。人类通过上下文对话与人工智能系统互动和互动的能力将有助于计算机科学家将该技术嵌入对话代理。
然而,显而易见的是,具有无所不在和上下文相关的人工智能的对话代理将成为人类和计算机之间协作和互动关系背后的驱动力。而且,该领域最大的影响将来自于发现使人和计算机很好地相互合作的最佳方法。
面向开发者的对话式人工智能:你的堆栈中的四个基本层

对话式 AI 组件;资料来源:Rasa
打造一个 AI 助手很难。构建一个不仅能处理问题和执行任务,还能参与灵活的来回对话的人工智能助手可能会非常困难。它需要机器学习、工程最佳实践、强大的工具和有价值的用户对话形式的数据。
你可能认为构建伟大的人工智能助手意味着从头开始构建一切。但是,您不需要从基本的构建模块开始,就可以获得高性能、企业就绪、足够灵活的产品来满足您的需求。像 Rasa 这样的开源框架是介于自己构建一切和使用 SaaS 框架之间的中间地带,你不能根据你的用例和训练数据来定制这个框架。
Rasa 一直在发布开源软件,这些软件已经授权成千上万的个人开发者、初创公司和财富 500 强公司创造人工智能助手。Rasa 在开发者友好的工作流程中发布了应用研究,如 TED 政策,以及国会 NLU 架构。
Rasa 不是一个包罗万象的对话式人工智能平台。这是一个可定制的基础设施层,在即插即用的架构中提供对话式人工智能构建模块。在这篇文章中,我们将讨论构建人工智能助手所需的组件,以及 Rasa 如何融入您的堆栈。
Rasa 如何融入您的堆栈
每个对话式人工智能应用程序都由几个不同的层组成:
- 计算层
- 对话式人工智能基础设施层
- 工具/服务层
- 应用层

对话式 AI 组件;作者图片
假设你在做披萨。如果你精通比萨饼制作的细微差别,你会知道你需要生面团、奶酪、酱汁和配料的原料。
您可能有三种选择:
- 你可以自己做所有的配料
- 你可以定制预先准备好的配料,然后把所有的配料放在一起做比萨饼
- 或者你可以买一个比萨饼,里面所有的东西都是预先制作和选择的,你不能定制

披萨的解剖,作者图片
计算层
计算层是基础层,其他层位于其上。机器学习友好的编程语言,如 Python 和 Julia ,以及机器学习框架,如 TensorFlow、PyTorch 和 spaCy,构成了计算层。
这些开源框架提供了高级 API,使得构建和实验模型变得更加容易;它们带有预先打包的算法,可以解决常见的 ML 问题,如使用神经网络的机器翻译、命名实体识别等。他们中的一些人甚至帮助将模型部署到生产中。
把编程语言想象成构成比萨饼面团的原料,像面粉、油、盐和水。ML 框架可以被认为是你可以在超市找到的预先包装好的面团。使用开源软件库或框架来开始构建您的助手可能比从头开始构建更容易。

计算层;作者图片
对话式人工智能基础设施层
对话式人工智能基础设施层位于计算层之上。它提供了一个内聚的框架,帮助开发人员构建 AI 助手。Rasa 开源是一个用于构建 AI 助手的对话式 AI 框架。它包括识别意图和实体的自然语言理解能力、机器学习驱动的对话管理、有助于与流行的消息服务集成的连接器,以及可以被调用来与外部系统集成的定制动作。
把 Rasa 开源软件想象成你的披萨酱。从头开始制作具有挑战性,需要专业知识;但是使用预包装的版本,你可以根据自己的喜好定制和改造酱料,并专注于制作一个伟大的披萨,而不是酱料。
Rasa 开源是像 TensorFlow 这样的机器学习框架之上的一个抽象层。就像你可能不会编写自己的编程语言、算法或 TensorFlow 版本一样,你也不必构建自己的对话式人工智能基础设施组件。

对话式人工智能基础设施层;作者图片
Rasa 开源抽象出了构建人工智能助手所涉及的复杂性。但是它是完全可扩展和可定制的。您可以使用 Rasa 开源构建任何类型的助手。您可以将自己的工具插入 Rasa,或者在它的基础上构建额外的层。
由于人工智能助手可以是特定于用例和行业的,因此基于问题和训练数据挑选各种管道、策略和配置选项的能力可能是有价值的。例如,您可以使用像 BERT 这样的语言模型,转换或插入您的自定义模型。
Rasa 开源还使用机器学习策略和对话管理来处理非线性对话和杂乱的人类行为。用户经常会插入一些离题的信息,回到之前的话题,或者离题。他们通常以非线性和无序的方式说话。助理的目的是自动化对话或处理一定数量的客户服务请求,他必须能够处理这些类型的对话。状态机或开箱即用的 SaaS 聊天机器人平台并不最适合这些类型的用例,因为它们无法扩展到规则和 if/else 语句之外。
澄清一下,大部分 AI 助手里还是需要规则和业务逻辑的。如果您没有在真实用户中测试过,那么最初您也许可以使用状态机。

典型的状态机流程;资料来源:Rasa
但是当你开始用真正的用户测试它并添加更多的功能时,它很快变得难以置信的混乱、不可维护和难以扩展。

状态机意大利面条代码;作者图片
可定制的框架使用机器学习来预测下一个最佳行动,并根据对话历史进行归纳,允许您创建支持灵活、自然的多回合对话的助手。
工具/服务层
工具或服务层位于基础设施层之上。使用 Rasa 开源,您可以构建一个 MVP 助手,它可以进行对话,理解上下文,并执行某些任务。下一步是测试和改进你的助手。工具可以帮你做到这一点。Rasa X 提供了收集、可视化和审查用户对话的工具。它允许您与真实用户一起测试,建立 CI/CD 管道,并进行持续改进。把 Rasa X 或者其他工具想象成你披萨上的奶酪。它改善你的助手,就像奶酪如何使你的比萨饼更好!

工具/服务层;作者图片
应用层
应用层是你的 AI 助手生活的地方。它可以与其他系统和 web 应用程序共存。例如,一个企业的工资单、人力资源和 IT 系统都在这里。根据你的人工智能助手的使用情况,它可能会与一个或多个上述系统进行交互。这一层的应用程序也有用户界面,用户可以实际看到并与之交互。基于安全和隐私考虑,您可以将您的助手部署到云中或在本地托管它。

对话式 AI 组件;作者图片
结论
构建任务关键型对话式 AI 需要机器学习、对话数据、声音工程和强大的工具来设计、分析和改进对话式 AI 工作流。Rasa 使强大的对话式人工智能研究和工具可用于没有资源从头开始构建的开发人员,以及希望专注于解决有趣问题而无需重新发明轮子的组织。定制开源框架的能力,而不是从头开始构建或使用黑盒 SaaS 聊天机器人平台,已经使对话式人工智能开发不仅可以用于机器学习研究人员,还可以用于世界各地成千上万的开发人员。
智能虚拟助手和未来之路。
“你是机器人吗?”

奥克塔维安·罗斯卡在 Unsplash 上的照片
在一个快速发展的世界里,客户在与任何公司交谈时都要求高效和迅速。这就是聊天机器人和智能虚拟助手发挥作用的地方。
与基于规则的聊天机器人不同,人工智能系统能够参与更高级的对话,因此配备了多种功能来帮助甚至娱乐用户的日常活动。除了可定制的功能之外,它们的自学能力和可扩展性也使得虚拟助手在各种全球企业中广受欢迎。
根据 Grand View Research 的数据,全球智能虚拟助理市场规模在 2019 年的价值为 37 亿美元,在预测期内的复合年增长率(CAGR)为 34.0%。基于服务的公司对效率的需求以及人工智能数字助理在各种设备(如计算机、平板电脑和智能手机)之间的集成,预计将推动市场发展。
2020 年机器人能做什么?
毫无疑问,最近的技术进步极大地提高了聊天机器人和 IVAs 的性能。但是,不管乍看起来多么完美,我们都同意机器人仍然是可怕的健谈者。
基于规则的聊天机器人。人工智能驱动的聊天机器人。
基本的 基于规则的聊天机器人 只能在聊天中访问,并在单回合交换中工作。简而言之,它们对用户提出的问题做出反应,检测主要意图,并相应地返回一个预定义的答案。他们能够处理基本的日常查询,例如:常见问题解答、预订、在线订单或预约安排 ( 调查机器人、外语教师、旅游机器人&酒店机器人)。然而,一旦用户问了一个超出机器人已学知识范围的问题,它将自动导致失败。
另一方面,我们区分了人工智能驱动的聊天机器人,它们依赖于核心机器学习技术,如自然语言处理和信息检索(IR) 技术。通过应用这些方法,像脸书和谷歌这样的科技巨头已经发布了开放域多回合聊天机器人(见 Meena 和 Blender ,它们能够复制更像人类的对话。然而,由于深度学习的许多直接限制,开放域机器人的实现仍然极具挑战性。
搅拌机机器人
2020 年 4 月,脸书·艾 开发了和开源的 BlenderBot ,第一个将多种多样的对话技能——包括移情、知识和个性——融合在一个系统中的聊天机器人。
尽管 Blender 代表了对话式人工智能的巨大进步,但它仍远未达到人类的水平。挑战之一在于其捏造事实的倾向——因为句子是从统计相关性中产生的,而不是从知识数据库中产生的。因此,它可以对一个著名的超级巨星进行深入而连贯的描述,但却带有完全错误的信息。该团队打算进一步试验将知识数据库集成到聊天机器人的响应生成系统中。

资料来源:脸书研究
聊天机器人会被 IVAs 取代吗?
很有可能,是的。但是我们的“艺术级”Meena 和 BlenderBot 怎么样呢?他们似乎是非常聪明的聊天机器人,不是吗?
随着各行各业的企业寻求提升客户体验的方法,虚拟增值服务极有可能在聊天机器人上积聚势头。你现在一定想知道为什么是,答案相对简单。除了能够利用人工智能推动业务核心的转型,iva 还能够适应和参与更像人类的对话,并增强用户体验。
当所谓的声音革命正在发生的时候,一些组织相信化身模拟真人将会带来更成功的助手。有多成功?还有待见,字面意思。
哎呀!米娜和 BlenderBot 只能…聊天。
如果你正在读这篇文章,你很可能至少和 Alexa、谷歌助手、Siri、Cortana 或 Bixby 聊过一次。如果你还没有,你一定很好奇为什么语音人工智能在过去的几年里变得如此流行。让我们仔细看看!
由数字助理和高质量语音用户界面(vui)推动的对话式交互将在未来几年成为真正的游戏规则改变者。随着自动语音识别的进步,对语音搜索的巨大需求将导致智能扬声器和车载系统与 IVAs 齐头并进。
此外,语音技术在教育领域变得越来越重要。在 IBM Watson 机器学习加速器解决方案的支持下, DeepZen 开发了深度学习和神经网络,可以识别文本中的情感,并产生类似人类的语音。该组织认为语音技术可以帮助学生拼写和练习乘法表,以及教授他们关于人工智能和未来世界的知识。
“随着越来越多的教学应用被开发出来,语音助手在教育领域越来越受欢迎。——DeepZen
我从未见过虚拟助手。
一种不同的方法来自三星的子公司 STAR Labs,该公司在 CES 2020 上正式推出了其“人造人”项目 Neon。Neon 基本上是关于创造数字化身——计算机动画模拟的人——直到今天公众仍然不知道。该公司解释说,“在我们的 CES 展台和我们的宣传内容中展示的场景是虚构和模拟的,仅用于说明目的。”
霓虹“人造人”
更进一步来说, Replika 已经集成了一个 3D 化身的测试版,导致了用户方面的许多争议性反应。虽然许多人对与他们的 replika 及其背后的技术进行可视化交互感到兴奋,但其他人已经决定回到旧版本。自从更新以来,Replika 的 Twitter 页面上一直有用户对新的“可怕的令人毛骨悚然”的头像感到“不舒服”和“害怕”的评论。
因此,这些化身是否是成功的数字助理未来道路的一部分仍然是一个未决的问题。
带回家的信息。
考虑到自然语言处理技术的快速发展,语音搜索的兴起,以及电子商务和电子学习的发展,使用虚拟助手的组织的数量预计将在未来几年激增。换句话说,前面提到的新兴趋势让我们相信旧的传统的基于规则的聊天机器人很有可能被 IVAs 取代。
请记住,尽管如此,对话式人工智能开发面临的挑战和担忧数不胜数,机器人可能仍然存在缺陷。
对话式人工智能——关键技术和挑战——第 1 部分

来自 Unsplash 的 Eric Krull
语音界面和常规系统是 AI 技术在行业中的实际实现。本文将探索基础知识和技术,然后扩展到不同业务用例中面临的挑战。
1.对话系统
什么是对话系统或虚拟代理?最著名的虚构特工之一是《钢铁侠》中的贾维斯。它可以独立思考,帮助托尼做几乎任何事情,包括运行家务,处理海量数据集,提出智能建议,提供情感支持。Jarvis 最令人印象深刻的特点就是聊天能力,你可以像老朋友一样和他聊天,他也能毫不含糊地听懂你的话。幕后的技术是对话式人工智能。
对话式人工智能的核心是一个智能设计的语音用户界面(VUI)。与传统的 GUI(图形用户界面)相比,VUI 允许用户通过简单的语音控制(而不是在屏幕上点击十次)来执行嵌套查询,从而解放了用户的双手。
然而,我不得不承认,在完美的虚拟代理贾维斯和现有的对话式人工智能平台的能力之间仍然有很大的差距。
在过去的十年里,人机对话受到了学术界和工业界的大量关注。在研究实验室中,我们看到了以下运动:
- 自然语言理解已经从 人工标注 和 语言分析 走向 深度学习 和 序列化语言建模。
- 对话管理系统已经从策略 转移到 监督学习和强化学习 。
- 语言生成引擎已经从 预定义模板 和 语法解析 转移到 端到端语言转换器 和 注意机制。
此外,我们还看到对话产品在跨市场领域涌现。所有的大玩家都有他们标志性的虚拟代理,例如,苹果的 Siri,亚马逊的 Alexa,微软的 Cortana 和谷歌的 Dialogflow。(下图已过时,请仅用作参考)

来自的报道。艾
2.对话系统的关键组件
会话平台中有几个主要组件,1) ASR:自动语音识别,2) NLU:自然语言理解,3)对话管理,4)NLG:自然语言生成,5) TTS:文本到语音。(其他组件可能包括公共 API、集成网关、动作执行逻辑、语言模型培训堆栈、版本控制和聊天模拟等。)
为了简单起见,现在让我们探索一下基础知识。

简单的对话系统
2.1。ASR: 自动语音识别是一种在说话人语音记录和文字记录上训练的模型,然后进行微调以识别看不见的语音查询。大多数对话平台都将这一特性作为嵌入元素提供。因此,开发人员可以在他们的产品上利用最先进的 ASR(例如,语音输入、语音搜索、实时翻译和智能家居设备)。
2.2。NLU: 毫无疑问,会话系统中最重要的部分。ASR 只会转录你所说的话,但 NLU 会明白你到底是什么意思?自然语言理解可以看作是自然语言处理的一个子集。这种关系可以大致描述如下。

由 Sciforce
NLP 和 NLU 都是董事会的主题,所以我不会太深入这个主题,而是通过使用虚拟代理用例中的实际例子来解释这个高级概念。
一般来说,NLU 和 NLP 是围绕以下问题构建的:
- 符号化和标记。它们是文本预处理技术。标记化是需要应用于传统语言分析和深度学习模型的第一步。它将一个句子分成单词(或 n_grams),这些单词将在以后用于构建词汇表或训练单词嵌入算法。标记有时是可选的,它会将每个单词标记到词汇类别中。(如 ADJ、ADV、名词、NN)

- 依存关系和句法分析。这是语言分析中一种流行的技术,它将一个句子解析成它的语法结构。在深度学习时代之前,那些语法树被用来构成一个新的句子或一系列单词。

来自斯坦福 NLP
- 命名实体识别。它用于提取或识别一组预定义的单词实体。NER 的输出有时看起来非常类似于词性标注。结果也存储在 Python 元组中,例如(美国,“GPE”)。主要区别是 1)NER 模型可以通过新的标注来训练,以拾取特定领域的单词实体。2) NER 更注重语义,而概念标注更注重语法结构。
- 短语和模式匹配。短语匹配最简单的实现是使用基于规则的正则表达式。不要误会,正则表达式在无标签的数据集中还是有好处的。一个充分定义的模糊模式可以匹配数百个相似的句子。然而,这种基于规则的方法很难维护和扩展。更高级的方法包括使用 POC 标签或依赖性标签作为匹配序列,或者使用向量距离。

- 单词矢量化和嵌入。单词嵌入标志着 NLP 的开始,它引入了单词分布式表示的概念。在深度学习之前,语言学使用密集表示来捕捉文本的结构,并使用统计模型来理解关系。这种方法的缺点是缺乏表示上下文意义和单词推理的能力。单词嵌入提供了一种从高维空间学习在特定上下文中最好地表示单词的参数的解决方案。对于实际使用,您可以找到预先训练的单词嵌入模型,如word 2 vec,GloVe,或者如果您需要,您可以随时在您的新 vocab 集和训练语料库上微调这些模型。

凯瑟琳·王的文字嵌入
- 序列矢量化和嵌入。类似的概念,但不是对每个单词进行矢量化,序列嵌入侧重于找到较长文本作为一个整体的最佳表示。这种技术改善了需要理解较长文本块的特定 NLP 任务,例如,文本翻译、文本生成、阅读理解、自然问题和较长答案等。

凯瑟琳·王的序列建模
- 情感分析。分析一个表达式是正还是负的任务(可以理解为二元分类,1-正,0-负)?NLP 中最常见的任务之一,在对话式人工智能的使用中,情感分析可以为虚拟客户代理提供基准,以识别客户的情感和意图,然后提供不同的情感响应建议。
- 话题建模。它利用无监督的 ML 技术在大量未标记文档中寻找主题组。它有助于我们快速理解一组看不见的语料库的主题。在对话式人工智能的用例中,主题建模充当第一个过滤器,将用户查询分类到更高层次的主题,然后映射到更细粒度的意图和动作。
- 文本分类和意图匹配。这两项任务都使用监督学习,模型的质量在很大程度上取决于您如何准备训练数据。与主题建模相比,文本分类和意图匹配更加细粒度和确定性。你可以用下图来理解这种关系。当面对看不见的客户查询时,你的对话式人工智能系统将使用主题建模来过滤你的查询,使其成为一个宽泛的主题,然后使用文本分类和意图匹配将其映射到一个特定的操作。

凯瑟琳·王的意图匹配
- 语言建模。深度学习和 NLP 中的一个热门话题。所有你听说过的最先进的模型都是基于这个概念(伯特家族:艾伯特,罗伯塔;多任务学习者和少量学习:GPT-2)。为了让机器更好地理解人类语言,科学家训练它建立词汇和统计模型,以预测每个单词在上下文中的可能性。

凯瑟琳·王的语言模型
- 多回合对话系统。这是 NLU 和对话式人工智能中的一个高级主题。它是指在会话系统中跟踪和识别话题/意图变化的技术。我们如何更好地提取每个对话框中的信息,并绘制出用户复合意图背后的综合逻辑。

在对话式人工智能的用例中,NLU 的目标是解决语言混淆、歧义,概括语言理解,识别从人类到机器对话的领域和意图,然后提取关键的语义信息。
除了使用我上面提到的关键技术,AI 系统还需要找到用户查询的有用语义表示。最成功的一个是“框架语义”,它使用域、意图、实体和槽来制定语义结果。
- 领域 :可以链接到主题建模,它将查询和知识资源分组到不同的业务类别、目标和相应的服务中。比如“售前”、“售后”,或者“订单与交易”。
- 意图 :可以链接意图匹配和分类。它指的是一个领域内的特定任务或业务流程。它通常写在动词宾语短语中。例如音乐播放器域中的“搜索歌曲”、“播放音乐”或“喜爱的播放列表”。
- 实体和槽 :可以作为参数从域和意图中提取关键信息。如“歌名”、“歌手”。
一句“墨尔本明天的天气怎么样?”可以转换成吹塑结构,
****-域名:“天气”
****-意图:“查看天气”
****-实体和槽位😦“城市”:“墨尔本”,“日期”:“明天”)
然后通过解析上述结构化数据来完成后续动作。
2.3。对话管理:对话式人工智能系统的另一个关键部分。它控制用户和代理之间的对话流。在最简单的版本中,DM 引擎将记住历史对话上下文,跟踪当前对话的状态,然后应用对话策略。
- 对话上下文: 在用户代理对话的会话过程中,所有的来回对话都会被上下文记住。域、意图、实体和槽等关键信息将保存在消息队列中,以供内存中搜索和检索。在对话之后,对话上下文可以被保存在数据库中用于进一步的分析。
- 对话状态跟踪 :对话状态跟踪器会记住对话中的逻辑流程。通过跟踪不同对话中的逻辑调整点,然后基于长期记忆建议响应,这将使代理更加智能和灵活。
- ****对话策略:基于对话的上下文和逻辑流,代理需要区分服务的优先级,触发某些事件,并请求实现。履行动作可以包括从数据库中检索用户信息、在知识库系统中搜索内容或者触发第三方 API。
例如:
Q :我要订披萨外卖 (intent=order_pizza,entity_time=null,entity_address = null,entity_type=null)。 A: 你想点什么类型的披萨?(插槽=类型,插槽=日期,插槽=地址)
Q :玛格丽塔。 (intent=order_pizza,entity_time=null,entity_address = null,entity _ type = Margherita)A**:你希望你的披萨什么时候送到?(槽=日期,槽=地址)**
Q :尽快。 (intent=order_pizza,entity_time=ASAP,entity_address = null,entity _ type = Margherita)A**:您的{Margherita}披萨还需要点什么吗?(跟进意图:附加产品切割)**
问: 一瓶可乐。 (intent=order_pizza,entity_time=ASAP,entity_address = null,type=Margherita,additional =coke) A:我们送披萨的地址是哪里?(插槽=地址)
问: xx.xxx 。(intent=order_pizza,entity_time=ASAP,entity_address = xx.xxx ,type=Margherita,additional = coke)A:谢谢,所以您订购了{ type }披萨,并附有{ additional },送货至{entity _ address} {ASAP}。(履行:更新 _ 订单,呼叫 _ 交付 _ 服务)***
如您所见,会话期间需要填充槽和实体,父意图可以触发 follwo _ up 意图,然后将根据会话的状态激活操作实现。
2.4。NLG :自然语言生成引擎根据聊天系统的类型有不同的实现和技术栈。对于面向任务的闭域会话系统,NLG 是通过会话会话中提取的“ 槽 ”和“ 实体 ”的可互换参数响应模板来实现的。对于开放域聊天系统,文本生成将基于信息检索、机器理解、知识图等。
2.5。TTS :文本到语音引擎正在执行与 ASR 完全相反的任务。它将纯文本转换成语音记录,并与合成语音一起向最终用户播放。
基于以上讨论,下图提供了对话式人工智能系统更全面、更真实的视图。

凯瑟琳·王的对话式人工智能系统
3.语音用户界面和用户体验设计
GUI(图形用户界面)正主导着人机交互。它改变了个人电脑世界的游戏规则,推动了数字设备在日常生活中的广泛应用。现在我们面对着屏幕,无时无刻不在和他们互动。
但在未来十年,随着人工智能的进步,人机交互将转向语音。语音用户界面将是智能和物联网设备的新入口。例如,当你说“嘿谷歌”时,谷歌之家就会醒过来,并与你开始对话。在这种情况下,声音将成为新的鼠标和图形。

来自 unsplash的 Kelly sik kema
在 GUI 设计中,所有的用户交互都是预定义的,并由屏幕上的一系列点击或交换来引导。但是在 VUI 系统中,首先,用户的行为是不可预测的,会偏离故事主线。第二,在开放式对话中,用户可能随时改变话题,并且用户的请求可能具有需要满足的复合意图。最后,语音交互需要用户和代理的持续关注,因为双方都需要记住他们在之前的对话中说了什么。
最成功的对话式人工智能系统会在他们的 UI 和 UX 设计中考虑语音和图形的互补。一个成熟的系统应该结合这两种特性,为最终用户提供更丰富的身临其境的体验。
参考
- 深层话语聚合的多话轮对话建模。 arXiv:1806.09102【cs。CL]****
关于我,我是👧🏻现居澳大利亚墨尔本。我学的是计算机科学和应用统计学。我对通用技术充满热情。在一家全球咨询公司担任人工智能工程师领导👩🏻🔬,帮助组织集成人工智能解决方案并利用其创新力量。在LinkedIn**上查看更多关于我的内容。****
对话式人工智能——关键技术和挑战——第二部分

来自 Unsplash 的 Arseny Togulev
让我们来探讨为面向任务的聊天机器人开发行业级对话式人工智能解决方案的关键挑战。
继我之前讨论关于对话式人工智能解决方案 的关键技术的帖子之后,我将深入探讨人工智能工程师团队在为您的客户或顾客构建虚拟代理或聊天机器人解决方案时会遇到的典型挑战。
让我们首先定义对话应用程序的范围和目标。

由凯瑟琳·王创作
对话代理可以分为两个主流。开放领域对话和面向任务的对话。
开放域对话的典型代理有 Siri、谷歌助手、脸书的 BlenderBot、谷歌的 Meena。用户可以在没有明确目标的情况下开始对话,话题不受限制。这些代理商将娱乐和情感反应融入他们的设计,并能够与最终用户进行长时间的对话。
大多数商用的虚拟代理都是面向任务的。它们包括你在银行网站上看到的聊天机器人,或者当你打电话给飞行中心热线时会和你打招呼的虚拟代理。这些代理是为特定的目标服务的。他们专注于封闭领域的对话,通常会通过响应来满足您的请求。面向任务的代理中的对话倾向于简短而有逻辑性。
现在我们对代理类型有了一个很好的定义,让我们来探索面向任务的对话领域中的挑战。(开放域对话超出了今天的主题,将在以后的帖子中讨论)
面向任务的对话
(请参考上一篇文章,了解下面架构图的详细解释)

对话式人工智能架构 —由凯瑟琳·王创建
1.融合,融合,再融合!
沿着聊天机器人建立一个摊位很容易。如果您参加了在线课程或跟进了教程笔记本,您大概可以在几个小时内设置一个语音代理。
但是在现实世界中,对话组件需要与现有的系统和基础设施无缝集成。虚拟代理的架构会因客户技术体系的数字成熟度而有很大差异。
如果你使用的是云平台上流行的对话服务(Dialogflow,Lex,Azure bot service 等。),而你的虚拟代理暴露在公众面前,先想想安全。本质上,bot 或虚拟代理是对您的核心对话服务的一系列 API 调用,确保解决方案具有健壮的认证和授权机制,并且敏感信息被加密。如果 bot/虚拟代理解决方案需要与您现有的系统或数据库进行交互,请设置一个外部 TCL/SSL 代理来解释和规范化请求,而不是让 bot 直接向您的核心系统发送未序列化的消息。使用外部代理的另一个潜在好处是,您可以构建一个通用的 RESTful API,使您能够快速扩展。

对话组件集成 -由 Catherine Wang 创建
如果你在云上使用对话服务,并为内部用户构建一个虚拟代理,这些代理很可能需要大量的信息检索。内部用户或员工希望使用虚拟代理作为一个知识搜索引擎让聊天机器人查找上一次的会议记录、税务发票、具体的公司政策或去年的提案。这种解决方案的关键是创建和维护一致的知识库和动态索引策略。知识库是智能体搜索空间的基础,它与答案的准确性相关。然而,文档和内容分散在组织中,缺乏管理和所有权。因此,构建这种类型的虚拟代理的第一步应该是设计全面的数据接收、管理和治理管道。如果你需要收集和查询的数据同时在线和离线,或者在多个云平台,就要小心了。

代理知识库的数据管道 —由 Catherine Wang 创建
上述解决方案的另一个关键部分是维护代理查询知识库的索引和知识本体。在最简单的版本中,虚拟代理由精选的 Q & A 集合支持,并将相似的问题与预定义的答案进行匹配。一个更高级的代理将具有语义搜索能力(可以通过弹性搜索实现),它可以理解自然语言并在知识库上执行查询搜索。最终的解决方案涉及机器理解,让机器理解语料库和长问题,从而在相关文档中找到答案的跨度。一个成熟的虚拟代理解决方案通常会堆叠这些组件来增加健壮性。

带信息检索的知识库 —由 Catherine Wang 创建
2.如何处理嵌套或复合意图匹配
在完美的场景中,用户会带着单一和直接的意图问一个问题。但是当人类交流时,我们更喜欢将几个意图组合成一个对话。
问题:你能帮我查一下天气吗?如果好的话,帮我订一张去 XXX 的票。
意图:["查看天气","订票"]
在虚拟代理解决方案中,对话管理组件需要跟踪对话的状态,并从用户表达(或问题)中识别多个意图。一旦匹配了多个意图,下一步就是确定执行或实现的优先级。
潜在的解决方案可以是:
- 主要主题和平行意图,优先级分数

具有优先分数的平行意图-凯瑟琳·王
如果在您的用户场景中,意图自然地聚集到一个主题中,您可以使用自顶向下的设置,如上图所示。主主题分类器将帮助您深入了解意向子集,然后通过考虑预定义的优先级分数来匹配个人意向。
- 主要意图和跟进意图

意向层次——凯瑟琳·王
假设您的用户场景有一个自然的逻辑流,可以转换成一系列意图。那么更好的配置是设置主要意图和后续意图,类似于逻辑决策树。然后,虚拟代理将引导用户通过预定义的逻辑。
- 触发混淆和错误检测引擎,并使用恢复策略阐明意图。
该选项将在第 4 节中讨论。管理混乱和模糊 。
3.将对话映射到预定义任务的挑战。
机器和人类有不同的逻辑框架。人类的对话是直觉的和非线性的,但是机器程序是线性的、逻辑的和严格定义的。当设计你的对话代理时,考虑用户场景和用户通常会问什么问题。

由凯瑟琳·王创作

由凯瑟琳·王创作
一旦你理解了你的客户的行为,试着找到他们行为的关键驱动力和主题,然后为每种类型的主题(用户场景)开发一个任务流。请保持简单,易于遵循,并灵活扩展。(主分支和可选子节点)

Rakebots 的任务流示例
您的对话代理需要基于任务流进行配置,并保持主任务流更新,在早期阶段解决冲突的逻辑点。
假设您正在处理一个大型客户项目,其中有多个并行的业务部门,并且可能有数百个意图聚集在一个主题范围内。在这种情况下,您可以采用多代理架构。主代理控制子代理,子代理有自己的知识空间和意图配置。

多代理架构(Multi-Agent Architecture)——由 Catherine Wang 创建
【小心如果选择实现上述架构,不适合 PoC 或快速实验。始终从易于集成和部署的简单解决方案开始。当开发周期更加成熟和稳定时,获得一些速赢来建立势头,转向细粒度的架构设计。】
4.应该如何覆盖长尾意图?
长尾意图指的是你的虚拟代理在服务期间会收到的低频率和高方差的问题。
这可能会导致“出乎意料”或“超出词汇表”的错误。
解决这个问题的标准方法是建立一个知识库,并使用信息检索技术来为看不见的问题生成答案(而不是创建额外的问答对或定义新的意图映射)。

长尾理论——由凯瑟琳·王创作
无论你如何设计你的代理人的对话和潜在的任务流,长尾意图总是会发生。人类喜欢即兴创作和抽象,因此为看不见的和未知的事物做准备。
5.如何管理谈话中的困惑和歧义?
人类有一个强大的通信协议,能够迅速澄清模糊和混乱。
但是对于机器来说,为了消除歧义,我们需要设计一种机制来触发恢复和回退策略(AI 工程师定义的规则)。用户的表达(问题)可以分为三组,1)清晰直接的意图(易于理解和处理),2)未知的意图(超出范围的问题),以及 3)不确定的意图(具有潜在的匹配,但需要额外的澄清)
对话管理引擎应该能够确定当前用户表达式应该属于哪个组。总的想法是,我们假设常见的用户问题通常遵循正态分布。并且 DM(对话管理)正在使用分类阈值来对所接收的问题进行分类。

意向分类分布—Catherine Wang 创建
< ∂1, [Reject], trigger [Fallback] response
∂1<predict_proba trigger="" response=""></predict_proba>
predict_proba(expression)>预测 _proba(表达式)【∂2】,【接受】,触发【匹配意向】响应
6.如何设计 A/B 测试来提高你的虚拟代理?
依我拙见,A/B 测试是提高虚拟代理可用性和采用率的银弹。
在进行 A/B 测试之前,代理只在测试人员和开发人员可能在开发周期中有无意识偏见的避难所环境中使用。不要在特性路线图上做任意的决定,构建优先级应该由 A/B 测试结果来指导。
以下是代理 A/B 测试的一些设计原则:
- 在一段时间内运行测试,并为开/关测试功能设计一个时间间隔。
- 收集大量样本进行假设检验。
- 根据不同的人口统计数据建立测试组。
- 测试中分离 UI 变化和虚拟代理功能变化。
要跟踪的关键指标有:
- 激活率:随机用户在你的客户端应用上打开聊天机器人的比率。
- 混淆触发: 对话代理将用户表达分类为“不确定”并触发恢复策略的百分比是多少。
- 回退率:会话会话中触发回退响应的比率。
- 目标完成率:对话成功完成任务的百分比。
- 保持:用户在与虚拟代理的对话会话中保持多长时间。
- 自助率:代理独立完成用户请求的频率(无触发回退,需要人工干预)。
- 用户满意度:对使用虚拟代理服务的体验满意度进行评级的用户反馈。
7.通过持续学习提高你的对话代理
大多数工程团队不需要使用这种方法来改进他们的代理。对话式云平台(Dialogflow 或 Lex)将为您完成繁重的工作。
然而,如果你想从头开始构建你的对话系统,下面是一个构建持续学习管道的方法来改进你的对话系统。

持续学习 —凯瑟琳·王创造
为了训练你的对话模型,你需要准备一个相关的数据集。如果你已经向最终用户展示了你的代理,建立一个反馈回路来收集聊天记录,抄本,交易,博客等。然后将这些数据转换并标准化到训练数据库中。
如果您没有用户交互数据,请使用用户模拟器(例如..)与您的代理聊天,并从模拟对话中收集标记的数据(例如,分类或错误分类)。这些数据可用于训练恢复策略和对话状态跟踪模型。下一步是对用户目标(用户希望在聊天会话中完成的任务)进行采样,并在接下来的强化学习阶段将其用作奖励评估者。
最后,将您的代理暴露在网上,并启用在线学习机制。确保当越来越多的人使用它时,您的代理会变得越来越聪明。
(这是一个高级的话题,我不会在这篇帖子里讲得太深。如果你有兴趣探索更多,请联系我。)
这篇文章中所讨论的只是你在现实生活中所面临的一小部分问题。而且已经提到的每一个点都可以展开成一个研究帖。
我们在探索什么是可能的,在学习的同时建设未来。试着带着你的客户一起踏上旅程,真诚而透明地交流。

来自像素的像素
请记住,我们正在构建一个对话式的人工智能解决方案,所以为什么我们不能在考虑如何让我们的虚拟代理与最终用户更顺畅地交互之前,首先与我们的客户进行更好的沟通呢?
感谢阅读,下次见。
**关于我,我是一只👧🏻现居澳洲墨尔本。我学的是计算机科学和应用统计学。我对通用技术充满热情。在一家全球咨询公司担任 AI 工程师领导👩🏻🔬,帮助组织集成人工智能解决方案并利用其创新力量。在 LinkedIn 上查看更多关于我的内容。
与伯特的对话人工智能变得容易
使用 Rasa 和 Huggingface 即插即用变压器
一年多来,我一直在尝试使用 NLP 和 python 来自动安排约会,多亏了一个令人惊叹的免费开源对话工具 Rasa,我终于成功了。
事实是一年前,当我开始这个项目时,我现在使用的工具几乎不可用。当然不是我们今天看到的易于使用的形式。作为人工智能和 NLP 科学发展速度的一个证明,去年(2019 年 6 月)的这个时候,像 BERT 这样的变形金刚刚刚离开学术研究的领域,刚刚开始在谷歌和脸书等科技巨头的生产中看到。BERT 论文本身在 2018 年 10 月才发表(下面是论文链接)。
今天,由于像 Rasa 和 HuggingFace 这样的开源平台,BERT 和其他变压器架构可以以一种简单的即插即用方式使用。此外,Rasa 的数据科学家开发了一种基于转换器的特殊分类器,称为双重意图实体转换器或 DIET 分类器,为同时提取实体和分类意图的任务量身定制,这正是我们在阿根廷 Kunan S.A. 与我的团队开发的约会安排虚拟助理 MyTurn 的产品。

饮食结构图(image| Rasa
证据就在 p̶u̶d̶d̶i̶n̶g̶ f1 的成绩中
对于定量的人来说,在我用 DIET 替换了基于 ye ole Sklearn 的分类器后,我的 F1 分数在实体提取和意图分类方面都提高了 30%以上!!!!比较下图中的 SklearnIntentClassifier 和 diet_BERT_combined。如果你曾经设计过机器学习模型,你会知道 30%是很大的。就像那次你意识到你把车停在了洒水器的水管上。当事情以它们应该的方式运行时,这是多么令人惊奇的事情啊!

模型评估(image |bubjanes w/streamlit)

意向分类原始数字(image| bubjanes
智能的蓝图:config.yml
是科学还是艺术?都不是。它尝试超参数的每一种可能的组合,并选择给你最高指标的配置。这叫做网格搜索。
需要注意的是,伯特不是一粒神奇的药丸。事实上,对于我的特定任务和训练数据,单独的 BERT 预训练单词嵌入并没有提供好的结果(参见上面的 diet_BERT_only)。这些结果明显比 2019 年旧的基于 Sklearn 的分类器差。也许这可以通过在阿根廷科尔多瓦的非正式西班牙语聊天中发现的地区行话和口语来解释,我们的培训数据就是从那里生成的。根据 HuggingFace 文档,我们使用的多语言预训练 BERT 嵌入是“在最大的维基百科中的前 104 种语言的大小写文本上训练的”。
然而,我们获得的最高性能模型是通过使用 DIET 在我们自己的 Córdoba 数据上训练定制特征,然后在前馈层中将这些监督嵌入与 BERT 预训练嵌入相结合(参见上面 diet_BERT_combined 中的结果)。下面的小图显示了如何将基于科尔多瓦数据训练的“稀疏特征”与前馈层中的 BERT“预训练嵌入”相结合。此选项非常适合于训练数据很少的西班牙语项目。也就是说,组合模型的性能仅略好于使用没有 BERT 预训练嵌入的 DIET 的模型(参见上面 diet_without_BERT 的结果)。这意味着对于非英语语言的聊天机器人来说,训练数据量适中,DIET 架构可能就是你所需要的。

将自定义监督单词嵌入和 BERT 预训练单词嵌入结合到前馈层的示意图。(图片| 拉莎)
即插即用,适用于 realz
在安装了 Rasa ,并构建了一个符合你需求的助手(我建议在做这件事之前先看看 Rasa 的 YouTube 教程)之后,BERT 嵌入的实现是如此简单,几乎令人失望。
下面是我们使用的配置文件的一个例子。这一长串的超参数可能看起来让人不知所措,但是相信我,这比一年前容易多了。

Rasa 提供的可用超参数(image| bubjanes )
您必须下载 BERT 依赖项:
pip install "rasa[transformers]"
要集成 BERT 或在 HuggingFace 网站上可用的任何其他预训练模型,只需用您想要使用的任何预训练嵌入替换下面行中的model _ weightshyperparameter。
- name: HFTransformers NLP 
  model_weights: “bert-base-multilingual-cased” 
  model_name: “bert”
我们使用了Bert-base-multilingual-cased,因为它是适用于西班牙语的最佳模型。
参见我们的 github 获取本文中提到的配置文件的完整示例和其他链接。
结论
Rasa 的美妙之处在于它简化了自然语言理解(NLU)、命名实体识别(NER)和对话管理(DM)的模型训练,这是面向任务的对话系统所需的三个基本工具。尽管我们做了很多好的编程来使我们的系统工作得像现在这样好,但是在没有任何真正的 Python 技能的情况下,您可能可以完成大约 80%的 Rasa 虚拟助手的构建。
随着自然语言处理领域令人兴奋的进步,如变形金刚和预训练的单词嵌入,对话式人工智能领域近年来取得了长足的进步,从说“对不起,我不明白”的机器人,到真正成为曾经需要单调乏味的人类工作的日常任务的可行解决方案。
从哲学上讲,这项技术的目标不是用机器人取代人类,而是将重复和“机器人化”的日常任务,如数据输入或预约安排,分配给虚拟助理,并将人类的大脑空间留给需要只有人类才具备的技能的工作类型,如创造力和批判性思维。MyTurn 是一个简单但有先见之明的例子,表明对话式人工智能不是为大型科技公司保留的工具,而是事实上每个人都可以通过 Rasa 和 HuggingFace 等免费开源技术获得。

建议阅读:
汤姆·博克里什,乔伊·福克纳,尼克·鲍洛夫斯基,艾伦·尼科尔, Rasa:开源语言理解和对话管理,2017 年 12 月 15 日
阿希什·瓦斯瓦尼、诺姆·沙泽尔、尼基·帕尔马、雅各布·乌兹科雷特、莱昂·琼斯、关注是你所需要的一切,2017 年 12 月 6 日
高剑锋(微软),米卡赫尔·格里(微软),李立宏(谷歌),对话式人工智能的神经方法:问题回答,面向任务的对话和社交聊天机器人,2019 年 9 月 10 日
Jacob Devlin Ming-Wei Chang Kenton Lee Kristina Toutanova Google AI Language, BERT:用于语言理解的深度双向转换器的预训练,2018 年 10 月 11 日
皈依吧,福雷斯,皈依吧!
在运行体验中预测订阅
凯文·哈特斯坦 是一位 洞察数据科学 的研究员(2020 年冬)。他在达特茅斯学院获得了认知神经科学博士学位。他的研究生研究将机器学习技术应用于功能性神经成像数据,以了解大脑如何解释照片和绘画中的物体身份。

“我们正在利用最先进的科学技术和周到的人类工艺,提供一个引人入胜的循证平台,为每个人带来健康生活,一次一个人。”- 里克·亨里克森,Ongo Science 首席执行官(https://blog.ongo.life/)
为了我的洞察项目,我与 Ongo Science 合作,这是一家位于旧金山的初创公司。Ongo 是一个平台,为健康和保健领域的内容创作者构建应用程序,并促进与用户之间的信息流动。大多数在线健康专家会发布关于营养、冥想和运动训练等主题的信息,并计算“赞”或阅读评论来解读它们的影响。Ongo 支持更详细的视图,为内容创建者提供关于用户目标、进度和挑战的见解。这些信息有助于指导和定制新内容,并最大限度地提高参与度和影响力。数据在个性化健康和保健中的作用正在扩大。事实上,研究人员估计,普通人一生中会产生超过 100 万千兆字节的健康数据。
作为设备、应用和专家之间的纽带,Ongo 致力于简化数据摄取和分析流程,帮助人们过上更健康的生活。在我的咨询项目中,我分析了来自 The Run Experience 的数据,这是一个 Ongo 应用程序,提供与训练、受伤预防和比赛相关的内容,以及个性化教练和跑步者的社区中心。我利用与该应用的互动以及智能手机和可穿戴设备的数据来预测哪些用户会转向高级订阅计划。我这个项目的名字来源于 1994 年电影《T2》《T3》《阿甘正传》《T4》《T5》《T6》《T7》中人们的热情,他们用“跑,阿甘,跑!”的喊声鼓励主角然而,在这种情况下,目标是鼓励跑步体验的用户将从免费版本转换为付费订阅。
逃跑…
用户保留是移动应用的一个大问题,大约 80%的用户在安装后 3 天内离开,90%在第一个月内离开。尽管付费用户通常只占大多数应用程序用户的一小部分,但他们不太可能离开,平均每月以 5-30%的速度波动。来自持续订阅者的续订是一个重要的收入来源——尤其是对于处于初创阶段的应用程序——《哈佛商业评论》的分析估计,仅增加 5%的留存率就可以导致收入长期增长高达 95%。健康和健身应用在留住忠诚用户方面特别有效,75%的用户每周至少使用这些应用两次,26%的用户每周使用超过 10 次。
考虑到这些统计数据,我的项目旨在确定用户最终是否会根据他们最初的应用程序使用情况转向高级订阅。这些信息有助于将激励和促销的目标对准可能不会订阅的活跃用户,从而增加忠实用户的基础和相关收入。
数据过滤和特征工程
我被允许访问亚马逊网络服务上托管的超过 20 亿行数据,包括来自 Run Experience 应用程序的活动,以及来自该应用程序跑步跟踪器的 GPS 数据和来自已链接其帐户的 iOS 用户的 Apple HealthKit 数据。一个数据表还包括关于用户设定目标的信息(与健康、伤害预防、比赛等相关)。)和订阅历史。我从下面描述的用户群中筛选出大约 2400 万行数据,并根据下载日期对其进行临时调整,以便汇总和检查应用使用初期的活动。
过滤

许多订阅者在下载应用程序后立即加入,可能是因为他们之前有过 YouTube 频道的跑步体验。许多其他人最初开始 7 天免费试用高级功能,一旦试用结束就订阅。
在用 Ongo 推出一款应用之前,Run Experience 已经是 #1 的 YouTube 跑步频道。可能是因为以前有过这个渠道的经验,许多应用程序用户在下载应用程序后立即开始付费订阅。Run Experience 还提供 7 天免费试用,许多用户在第一天就开始试用。这些用户中的大多数在试用结束时会转变。在最初的爆发后,用户会在一段可变的时间内继续订阅,所以我假设那些需要更长时间才能做出决定的人更有可能是跑步体验的新手,他们的决定可能基于与应用程序本身的交互,而不是之前对内容的体验。
定义问题
我选择关注前两周没有订阅的活跃用户。这个决定允许我排除已经熟悉该产品的用户,并在足够长的时间内整合数据,以区分哪些用户会订阅,哪些不会订阅。这些用户在前两周的登录总数相似。

分析中包括的用户具有相似的总体活动,通过用于预测的 2 周期间的登录次数进行估计(虚线左侧)。
特色工程
应用程序中的数据涵盖了各种各样的活动类型,包括观看锻炼视频的元数据、应用程序中记录的 GPS 跑步轨迹以及帖子和赞等社交内容。下载应用程序后,每个用户还会被提示选择一个目标——整体健康、预防受伤、比赛或其他。如果 iOS 设备上的用户有记录这些信息的智能设备,那么链接了他们的 Apple HealthKit 数据的用户也有关于他们的步数以及心率和体重的信息。

这些功能是根据跑步体验应用程序收集的训练、社交和 GPS 跑步数据设计的。链接他们的苹果健康工具包数据的用户也有关于每日步数以及心率和体重的信息(前提是他们有智能设备来记录这些测量数据)。
“会话”数据包括关于用户观看了什么视频、他们是否完成了视频以及对会话的反馈(赞成或反对)的信息。用户也可以加入一个“计划”,如 5 公里或半程马拉松训练计划,由旨在达到特定目标的会议视频组成。社交互动包括在论坛上发表问题和评论,以及喜欢其他用户的帖子。GPS 数据包括每次跑步的距离和时间以及其他指标。
从这些广泛的数据中,我设计了 19 个特征。这些包括应用内活动的测量,如开始的视频会话数量和完成的百分比,程序注册和社交互动,以及用户在 30 分钟内记录 500 步以上或在两周内记录心率升高(超过 120 次/分钟)的次数。我的特性涵盖了广泛的用户行为,但是这个项目的未来迭代可能包括从数据中更细粒度的细节设计的特性,比如评论的实际文本;GPS 跟踪跑步的距离、时间和高度;和每分钟平均心率。
特征选择和模型比较
我比较了逻辑回归和随机森林模型来预测订阅。逻辑回归使用独立变量的线性组合来估计二元分类标签的对数优势比(在这种情况下,订户与非订户)。该模型的优势在于,模型为这些特征学习的权重可以指导解释各个特征如何对预测做出贡献。不幸的是,相关特征会掩盖彼此的影响,导致预测特征的权重较低。随机森林分类是一种非线性集成模型,它使用 bagging 和随机特征采样来构建决策树森林,每个决策树都为最终预测贡献一张“选票”。在不相关的树上聚合信息增加了模型的鲁棒性,并在共线特征之间划分了重要性,但牺牲了解释的方便性,尽管仍然可以使用诸如基尼系数重要性等指标从模型中提取特征重要性。
Python 的 scikit-learn 包为这两个模型提供了简单的实现,以及交叉验证和评估的功能。我首先将数据分成 60/20/20 的训练/验证/测试部分。我对训练数据使用了 5 重交叉验证的递归特征消除(使用 scikit-learn 的 RFECV) 来为每个模型选择特征,在每个折叠内的训练和测试数据中对订阅者和非订阅者的比例进行分层。因为订阅者比非订阅者少得多,所以在初始化每个模型时,我使用 class_weight="balanced" 参数对这些类进行加权,使其与它们的逆频率成比例。因为从商业角度来看,准确识别订户和非订户非常重要,所以我使用了 w 八分 F1 分数(精确度和召回率的组合,按频率加权,以及组合和跨类别加权)作为评估指标。该过程表明逻辑回归的最佳特征数为 6,随机森林的最佳特征数为 12。

用于特征选择、模型比较和模型评估的训练/验证/测试分割。
我用选定数量的特征,使用递归特征消除,在所有训练数据中重新训练每个模型。在这一步,我还使用了 SMOTE 来处理数据中的类不平衡。SMOTE 通过使用 k-Nearest-Neighbors 算法在特征空间中找到与该类的实际样本接近的点,在训练数据中创建少数类的新样本,重复该过程,直到训练数据中的正类和负类(订户和非订户)的数量相等。我通过评估验证数据的性能来比较这两个模型。随机森林表现更好,加权 F1 分数为. 74,相比之下,逻辑回归的加权 F1 分数为. 65。这是有意义的,因为大部分数据由各种活动类型的计数组成。随机森林建模特别适合这种类型的数据,因为它通过在组成森林的决策树的每个分支的每个要素的值范围内查找信息切割点来进行。与逻辑回归中隐含的线性假设相比,这种非线性方法可能导致更好的拟合。
最终模型评估
对于最终的模型评估,我在跨训练和验证集汇集的数据上训练了随机森林模型,并再次使用 SMOTE 来解决类不平衡问题。我使用了在模型比较阶段确定的相同特性,并在保留的测试集上评估了模型。最终的模型表现相当好,加权 F1 分数为 0.79,准确度为 0.78。

最终模型在延期测试中的性能指标 set .**AUC= . 74|加权 F1 = .78** | 精确度= .79
该模型在识别非订阅者方面表现尤为出色( Precision =.87 ,Recall =.86),这表明该模型将有助于识别尽管整体活动水平相对较高但不太可能订阅的用户。Ongo 和 Run Experience 可以将这一模式投入使用,激励这一人群进行转换,可能通过提供进一步的激励措施,如折扣率或高级教练服务预览。
功能重要性和可行见解
查看从随机森林模型中提取的功能重要性分数可以发现,在预测谁会转换时,操作尤其重要。到目前为止,两周内的登录次数是最重要的功能,开始的会话次数和来自 Apple HealthKit 的关于步数和心率升高的信息也显示出很大的重要性。早期干预可以带来更高的订阅率和收入的显著增加。

从随机森林模型中提取的特征重要性分数(基尼重要性)。红色条代表来自跑步体验应用的功能,黑色条代表来自 Apple HealthKit 的数据
这些功能很好地跟踪了直觉——开始并完成锻炼或有证据表明运动量较大的用户往往会坚持使用该应用程序。然而,有些违背直觉的是,用户的最初目标并不能预测最终的订阅,并且在特性选择阶段就被排除了。这表明,目标的差异通常与订阅可能性的变化无关,至少在我的项目中检查的样本中是如此。社交互动也不是很有预测性,尽管这些互动相当罕见。将这种见解转化为行动的另一种方式是 Ongo 和 Run Experience 鼓励用户专注于立即开始锻炼和跑步,而不是花时间在社区平台上滚动。虽然不清楚这种关系是否是因果关系,但遵循这种模式的用户更有可能订阅。
我很有兴趣看看苹果 HealthKit 数据中观察到的模式是否适用于下载应用程序后立即订阅的用户。一旦用户链接了他们的 HealthKit,之前记录的步数、心率和体重数据就可用于跑步体验。这些数据可以区分下载应用程序是否与活动增加有关,或者用户在下载应用程序之前是否已经比非用户更活跃。
我的最终成果包括一个在 Jupyter 笔记本中实现的工作流程,该工作流程从 Ongo 在 AWS 上的 S3 桶中获取数据;清理、预处理,并在时间上与应用程序下载日期保持一致;和运行体验应用程序中的 Apple HealthKit 数据和活动的工程师功能。然后将数据输入到模型中,以预测该应用程序的活跃用户在最初使用一段时间后没有订阅的订阅可能性。
使用机器学习将 pdf 转换为有声读物

使用 ML 将 pdf 和图像转换成有声读物或播客
这个项目最初是由 和佐藤 设计的。
我把这个帖子拍成了视频。看看吧!
散步——这是新冠肺炎最大的(也是唯一的)乐趣之一,不是吗?如今,你可以步行做任何事情:听新闻、参加会议,甚至写笔记(通过语音听写)。走路的时候唯一不能做的事情就是看机器学习研究论文。
还是不能?
在这篇文章中,我将向您展示如何使用机器学习,通过计算机视觉和文本到语音转换,将 PDF 或图像格式的文档转换为有声读物。这样,您可以在旅途中阅读研究论文。
但是你应该吗?这由你来决定。
想直接跳到代码?在 GitHub 上查看。
但首先:这要归功于 Kaz Sato ,一位在日本的谷歌工程师,他最初创建了这个项目(他正在根据计算机科学教科书创建日语有声读物)。我把借来的建筑稍加改动。
我们将通过三个主要步骤来构建 PDF 到有声读物的转换器:
- 从 pdf(或图像)中提取文本
- 决定将文本的哪些部分包含在有声读物中
- 将文本转换成口语
在这篇文章中,我将向您展示如何将这篇密集的研究论文(“通向自动规范化和通用人工智能的一条有前途的道路”)转换成有声读物。它看起来是这样的:

从 pdf 到文本
首先,我们将使用 OCR 从文档中提取文本。您可以使用许多不同类型的工具来完成这项工作,例如:
- Calamari ,在开源 Python 库上
- 谷歌云视觉人工智能 API
- (新!)Google Cloud 文档 AI API 。这个 API 不仅提取文本,还智能地解析表格和表单
对于这个项目,我使用了 Vision API(比新文档 AI API 便宜),发现质量相当不错。查看 Kaz 的 GitHub repo ,看看你到底是如何调用 API 的。
当您通过 Vision API 传递文档时,会返回原始文本和布局信息。下面是回应的样子:

如您所见,API 不仅返回页面上的原始文本,还返回每个字符的(x,y)位置。
在这一点上,如果你是个傻瓜,你可以把所有的原始文本直接转储到有声读物中。但是你不是一个傻瓜,你可能不希望这样做,因为那样你会听到各种各样无趣的东西,比如图像标题、页码、文档页脚等等。
所以下一步,我们将决定哪些原始文本应该包含在有声读物中。
在 pdf 中查找相关文本
我们希望在有声读物中包含研究论文的哪一部分?可能是论文的标题、作者的名字、章节标题、正文,但这些都没有用红色突出显示:

事实证明,识别这些相关部分是一个棘手的问题,有许多可能的解决方案。在这篇文章中,我将向您展示两种方法,一种是快速但不干净的方法,另一种是高质量但需要更多工作的方法。
使用机器学习查找相关文本
当你看一篇研究论文时,你可能很容易通过注意布局来掩饰不相关的部分:标题大而粗;字幕很小;正文为中等大小,位于页面中央。
利用页面上文本布局的空间信息,我们也可以训练一个机器学习模型来做到这一点。我们向模型展示了一堆正文、标题文本等等的例子,希望它能学会识别它们。
这是这个项目的原作者 Kaz 在试图将教科书变成有声读物时采取的方法。
在这篇文章的前面,我提到 Google Cloud Vision API 不仅返回页面上的文本,还返回它的布局。它将文本分组为块(页面、块、段落、单词和字符),并返回它在页面上的位置。具体来说,对于每个单词,它都返回一个类似如下的边界框:
"boundingBox": {
   "normalizedVertices": [
		{"x": 0.9248292,"y": 0.06006006},
 		{"x": 0.9384966,"y": 0.06006006},
        {"x": 0.9384966,"y": 0.067567565},
        {"x": 0.9248292,"y": 0.067567565}
    ]
}
上面的边框描述了一个单词在页面上的位置,以及它有多大。
我们可以用这些数据来训练一个模型。下面我们来看看 Kaz 收集的数据:

显然,Kaz 正在转换的书是日文的。对于每一个文本块,他都创建了一套特征来描述它:文本块中有多少个字符?它有多大,在页面上的什么位置?包围文本的框的长宽比是多少(例如,一个窄框可能只是一个侧栏)?
请注意,在上面的电子表格中还有一个名为“label”的列。这是因为,为了训练机器学习模型,我们需要一个带标签的训练数据集,模型可以从中“学习”对于训练数据中的每个文本块,Kaz 必须手动将该文本块标记为“正文”、“标题”、“标题”或“其他”。标注训练数据总是 ML 项目中比较耗时的部分之一,这次也不例外!
这就是为什么,当我重新创建 Kaz 的项目时,我使用了一个黑客来避免它(下面会有更多)。
在 Kaz 收集并标记了一堆文档后,他使用 Google Cloud AutoML Tables 训练了一个机器学习模型。这是一个基于表格数据构建模型的无代码工具。这里有一个小的 gif 展示了这个工具的样子,以及 Kaz 如何用它来训练一个模型:

如您所见,该模型相当准确(约 95%的精确度和召回率)!所以 Kaz 使用这个模型作为一个中间步骤来确定将哪些文本放入有声读物。
用 Spit、Glue 和字体大小查找相关文本
看,我不是娘娘腔——我一生中花了很多时间来标记训练数据(尽管,这些天来,你真的不必)。但是对于这个项目,我想知道我是否可以使用一个简单的启发式方法(一个可以让我避免标记数据的方法)来代替。
我觉得光看字体大小就能学到很多东西。比如:论文的标题很可能是用最大的文字尺寸写的。同时,正文是文档中最常见的文本。根据这些观察,我使用了这个启发法:
- 计算所有单词的字体大小
- 计算最常见的字体大小。用字体大小“body”来标记文本的每一位
- 计算最大字体大小。将该字体大小每一位文本标记为“标题”
第一步,计算字体大小,我减去单词周围的 y 坐标:

接下来,为了看看我的想法是否可行,我绘制了文档中字体大小的分布:

你可以看到在右边,有一个点(最大的文本)代表文档标题(woohoo!).同时,中间那一大段点,就是正文。标题和其他文档元数据,甚至更小的文本,都在图的左侧。
这个图表给了我信心,我的小技巧会起作用,至少对这个文档是这样(注意,它并不适用于所有的研究论文,尤其是那些有花哨的侧栏或垂直布局的论文!).
然而,这里有一个棘手的问题,正文字体大小在一个范围内(不是一个固定值)。这是因为我不是像我们通常认为的那样计算字体大小(即 12 磅),而是作为减去的像素值,并且有一些噪声。找出界限(例如,什么是正文的界限?),我用的是 Jenks Natural Breaks 算法(如果你没听说过这个,没关系——我在这个项目之前也没听说过!).
我知道我说的有点快,所以请在这里或 Twitter 上给我留言,我一定会回答你的问题!
从文本到口语
这个项目最有趣的部分无疑是选择一个电脑声音来做我们的解说员。为此,我使用了谷歌文本到语音转换 API,它使用了一种叫做 WaveNet 的技术来产生非常逼真的声音。API 支持许多声音和语言,你可以直接从产品页面的输入文本中自己比较它们。
我选择男声读论文题目,女声读论文正文。以下是由此产生的“有声读物”听起来的样子:
不算太坏,对吧?
反正目前就这些了。
如果你做出了这样的好东西,请与我分享,我会在社交媒体上展示它!
同时,我们在 Instagram 或 Twitter 上连线吧!
原载于 2020 年 9 月 1 日 https://daleonai.com。
用 5 行代码将文本转换成语音
了解文本到语音转换可以发挥作用的领域

照片由 Pexels 的赖爷 Subiyanto 拍摄
在这篇文章中,我将向你展示如何使用 Python 将文本转换成语音。这将是一个简单的机器学习项目,我们将了解一些基础的语音库称为 pyttsx3。如果你想知道在现实生活中我们可以在哪里使用文本到语音的转换,不要担心,你并不孤单。我在学习几乎任何东西之前都会问同样的问题,我会问自己这些信息会对我有什么帮助,在现实生活中哪里可以用到。这种思维方式帮助我更快地学习新事物,并激励我在个人生活中使用这些信息。
我将列举几个以非常好的方式使用文本到语音转换的行业。其中之一是在教室环境中,学生们还不知道如何阅读,但当你对他们说话时,他们可以理解。如今,技术在课堂上经常使用,一些文本到语音转换软件可以给老师们很大的帮助。一个简单的文本到语音转换会很有帮助,而不是老师花时间和每个孩子在一起,为他们朗读。这是一个很好的视频,展示了如何在课堂上使用文本到语音转换:
另一个文本到语音转换的好例子是在 Chromebooks 上。他们有一个辅助功能,称为“选择说话”,你可以选择/突出显示文本,计算机将大声朗读给使用该功能的人。
最后一个文本到语音转换的例子,我想和你分享的是有声读物。使用以前音频文件中的一些训练数据,可以让机器使用受过训练的人的声音阅读一本书。是的,人工智能越来越成为我们日常生活的一部分。此外,教一台机器比教一个人容易得多。这使得该领域的发展要快得多。让我们回到项目上来。
我们将建立一个机器程序,将我们的文本转换成语音。文本可以是不同的语言,这就是为什么在选择我们的语音模型时,我们必须确保语言匹配。让我们从导入库开始。
步骤 1 —图书馆
首先,我们将安装模块,这样我们就可以在我们的程序中使用它。我们将在这个项目中使用的唯一模块是 Pyttsx3。这是文本到语音模块,这个模块的一个好处是,它可以在你安装后离线工作。下面是安装该模块的代码:
pip install pyttsx3
如果你想进一步了解这个模块,这里有文档。现在让我们将模块作为库导入到我们的程序中。
import pyttsx3
第 2 步—文本
这一步是最容易的。您应该定义一个变量,并为其分配一个文本值。你的文字可长可短,这取决于你想要什么。下面,你可以看到我创建的文本变量,它被称为测试。
test = "Once upon a time, a person was trying to convert text to speech using python"
步骤 3 —语音引擎
在下面几行中,我们定义一个变量来分配我们的语音引擎。这很简单。
engine = pyttsx3.init()
现在,我们必须定义我们希望机器说的语言。如前所述,它必须与我们在文本中使用的语言相匹配。若要查看可用的语言,请运行以下代码:
voices = engine.getProperty('voices')for voice in voices: 
 print("Voice:") 
 print(" - ID: %s" % voice.id) 
 print(" - Name: %s" % voice.name) 
 print(" - Languages: %s" % voice.languages) 
 print(" - Gender: %s" % voice.gender) 
 print(" - Age: %s" % voice.age)
您将在终端中看到类似这样的内容:

声音
复制您想要使用的语言的“id ”,并将其粘贴到我们的程序中。我们使用 setProperty 方法来定义口语。
en_voice_id = "com.apple.speech.synthesis.voice.Alex"engine.setProperty('voice', en_voice_id)
最后一步
我们差不多完成了,下面的代码将告诉我们的语音引擎大声说出我们之前定义的文本。基本上电脑会给你读出来。确保您的扬声器已打开,可以听到声音。
engine.say(test)engine.runAndWait()
恭喜你,你已经创建了一个将文本转换成语音的程序。现在,您已经了解了这些软件是如何在幕后工作的。希望你喜欢这篇教程,并在今天学到一些新东西。伟大的提示:当你觉得太懒的时候,你可以用这个和你的朋友交流🙂
另一个你可能会感兴趣的机器学习项目
使用麦克风将您的语音实时转换为文本
towardsdatascience.com](/convert-your-speech-to-text-using-python-1cf3eccfa922)
转换到新的火花 3.0 UDF 风格
提示和技巧

伊恩·巴塔格利亚在 Unsplash 上拍摄的照片
Apache Spark 2.3 版本在 2017 年引入了熊猫 UDF。自从 Apache Arrow 取代 Py4J 来加速 JVM 和 Python 之间的数据传输以来,这个新特性显著提高了 Python UDFs 的性能。通过 Spark 与 Pandas 的接口,用户可以通过将 Python 库(例如sklearn、scipy)中编写的方法封装在 Pandas UDFs 中来轻松扩展它们。
除了原有的 Python UDF ( pyspark.sql.functions.udf在 1.3 版本引入),Spark 2.3+还有 3 种熊猫 UDF,包括PandasUDFType.SCALAR、PandasUDFType.GROUPED_MAP(均在 2.3.0 版本引入),以及PandasUDFType.GROUPED_AGG(2.4 版本引入,也可作为窗口函数)。2020 年 6 月,Spark 3.0 的发布为熊猫 UDF 引入了一套新的接口。在本文中,我将简要地探讨两个如何将旧样式(Pandas)UDF 转换成新样式的例子。
数据准备
我模拟了一个包含以下 4 列的数据框架
- name:5 到 10 个字符之间的随机字符串名称
- email:随机假冒邮箱地址
- secret:长度为 4096 的十六进制字符串
- n:重复次数。我将重复一个更小的数据帧 1000 次
首先,为模拟定义一些函数
在熊猫身上模拟 10000 个样本
这将得到如下所示的数据帧

示例模拟数据
然后,重复这个数据帧 1000 次
对于 Spark 来说,这是一个很好的数据量
和最终模式
从 UDF 到熊猫 UDF 迭代器
最初的spark.sql.functions.udf将一个定制函数应用于列中的每一项,将另一列的值作为输入。在这个例子中,我们使用withColumn来存储udf应用程序的结果。
结果如下所示

应用 udf 的输出示例
在 Spark 3.0 中,我们可以使用迭代器熊猫 UDF 来代替
迭代器使用 Python 类型作为提示,让函数知道它正在迭代一对作为输入的pandas.series,并返回另一个pandas.series。上面的pandas_udf装饰器指定了系列元素将被返回的数据类型。请注意,输入序列的长度需要与输出序列的长度相同。嵌套函数apply_custom_mapper将两列作为熊猫系列,用户需要迭代该系列的每个元素来应用函数(例如custom_mapper)。然后接下来,用户需要构造迭代器来yield函数应用的结果。这看起来像一个双 for 循环,但它实际上是分离输入和输出的迭代,即首先迭代输入以应用初等函数custom_mapper来获得结果的迭代器,然后将结果逐步映射回输出序列。
具有分组和应用功能的熊猫 UDF
groupby.apply(pandas_udf)模式通常用于通过迭代数据帧的每个子组来应用自定义函数。自定义pandas_udf将每个组的数据作为 Pandas Dataframe(我们称之为pdf_in)作为输入,并返回另一个 Pandas Dataframe 作为输出(我们称之为pdf_out)。pdf_out的模式不需要与pdf_in的模式相同。在下面的例子中,我们将用组中列n的累积和(转换为字符串)替换secret列。然后我们将删除列n。我们需要在panads_udf装饰器中指定从df.drop("n").schema获得的输出模式。
对于在模式中添加新列,我们可以这样做
上述代码的输出如下所示

应用熊猫 UDF 的输出,后降一列
UDF 返回的行数不必与输入的行数相同。在下面的例子中,我们修改了上面的脚本,这样我们只返回每隔一行。
新界面改用groupby.applyInPandas(python_func, schema)。
第一个示例随后更改为
第二个示例更改为
注意,apply采用了一个pandas_udf函数,其中模式是在装饰器中指定的。另一方面,applyInPandas采用 Python 函数,其中模式在applyInPandas参数中指定。
熊猫 UDF 的记忆使用
最后要注意的是这些函数的内存使用情况。Spark 的熊猫 UDF 文档表明整组数据将被加载到内存中。在我们的groupby示例中,我们将pdf作为一个 10000 行的数据帧,因此我们期望每个执行器内核有大约 43 MB 的数据。如果每个执行器有 5 个内核。那么在内存中就变成了 215 MB 的数据。此外,我们的 UDF 的内存堆栈也增加了内存使用。假设我们将长度为 4096 的十六进制字符串转换为长度为 16384 的二进制numpy 布尔数组,每个数组将消耗大约 16kB 的内存,其中 10000 个数组将额外消耗 165 MB。5 个内核总共会额外增加 825 MB,也就是总共超过 1GB 的内存。因此,在涉及熊猫 UDF 的情况下调优 Spark 时,除了作为熊猫数据帧的公开数据组的大小之外,还需要注意 UDF 使用的内存量。
将 Python 代码转换成 Windows 应用程序(。exe 文件)
无限制共享的快速技巧

塔达斯·萨尔在 Unsplash 上拍摄的照片
您编写了一个令人惊叹的 Python 应用程序,并提交给了您的老板。他对此印象深刻,并希望在自己的系统中使用它。他既没有在自己的系统上安装 Python,也没有使用过它。你被卡住了!!!
如果以上听起来很熟悉,那么本教程将解决你的问题。在这里,我们将学习将 Python 代码转换成 Windows 可执行文件的过程。从现在开始,每当您想要与更广泛的社区共享您的优秀成果时,您不必担心在他们的系统上设置 Python 环境。只需创建一个可执行文件并发送给他们。他们将能够使用该应用程序,就像您在您的系统上一样。
你需要什么?
1。要转换的脚本
对于本教程,我们已经编写了一个小的 Python 代码来读取。csv '文件从窗口的文件夹中取出。这个文件有两列,每列包含一组随机数。该代码创建一个新列,其中包含两个输入列的数字之和。修改后的文件保存在与旧文件相同的文件夹位置。
**#### Importing the required library**
import pandas as pd**#### Reading csv file**
df = pd.read_csv("C:\\Ujjwal\\New_File_V1.csv")**#### Adding 2 columns**
df["Third_Column"] = df["Randome Numbers 1"] + df["Random Numbers 2"]**#### Exporting the data to same location**
df.to_csv("C:\\Ujjwal\\New_File_V2.csv",index = False)
输入 CSV 文件的示例列如下:

输入 CSV 文件(图片由作者提供)
代码执行后的输出文件如下所示:

输出 CSV 文件(图片由作者提供)
2。Pyinstaller 包
为了将 Python 代码转换成可执行文件,我们将使用 Pyinstaller 包。使用标准的“pip install”命令来安装此软件包。
**#### Install Command**
pip install pyinstaller
实际任务
让我们一步一步地将 Python 文件转换成一个 Windows 可执行文件:
- 打开命令提示符— 使用命令行将 Python 脚本转换为 Windows 可执行文件。为此,我们必须转到命令提示符。在你的窗口搜索框中键入“cmd”并打开命令提示符
- 更改文件夹位置 —使用以下命令,将命令提示符指向 Python 代码的位置:
**#### Change Folder Location**
cd folder_location
- 转换 —使用以下命令将 Python 文件转换成一个 Windows 可执行文件:
**#### Command for conversion**
pyinstaller --onefile filename
上述代码将创建一个与 python 代码功能相同的可执行文件。该可执行文件将在一个新文件夹 dist,中提供,该文件夹与您的 Python 脚本位于同一位置。
- 执行— 要执行文件,只需双击可执行文件,它将产生与您的 Python 脚本相同的结果。
常见问题解答
使用 pyinstaller 时,人们会面临一些常见的问题/挑战。本节将回答其中的大部分问题:
附加文件
除了包含可执行文件的 dist 文件夹之外,其他文件和文件夹正在被创建。你不需要它们。您可以在没有这些附加文件的情况下共享您的可执行文件。即使您将删除额外的文件和文件夹,您的可执行文件也不会失去其功能。
完成时间
可执行文件的创建和可执行文件的执行都是耗时的任务。对于像我们这样的短脚本,创建可执行文件需要将近 5 分钟,执行需要将近 30 秒。
可执行文件的大小
因为我们在 Python 脚本中导入包,为了使可执行文件自给自足,完整的包被合并到可执行文件中。这增加了可执行文件的大小。对于我们的例子,它超过了 300 MB
可执行文件失败,错误为
在执行可执行文件时,最容易遇到的错误是“**ModuleNotFoundError:没有名为的模块,模块名 ”。此错误和实际错误提示的示例屏幕截图如下:
**#### Error Prompt Message**
ModuleNotFoundError: No module named 'pandas._libs.tslibs.nattypes' 
如果您遇到这样的错误(模块名称可能不同),请采取以下步骤:
- 转到安装 pyinstaller 包的 Windows 位置。因为我使用的是 Anaconda 发行版,所以下面是我系统上的位置:
**#### Package Location**
C:\Users\Ujjwal\Anaconda3\Lib\site-packages
- 在 pyinstaller 包文件夹中,搜索名为 hooks 的文件夹。这个文件夹里有大多数常用 Python 包的钩子文件。搜索引发错误的 Python 包的钩子文件。在我们的例子中,是熊猫。示例钩子文件及其内容如下:

原始钩子文件(图片由作者提供)
- 错误背后的原因是初始化' hiddenimports' '的命令出现故障。注释此语句并添加一个新的,其中' hiddenimports '用引发错误的相同模块名初始化。对我们来说,是“熊猫。_libs.tslibs.nattype '。要添加的代码行如下:
**#### Code line for hook file**
hiddenimports = 'pandas._libs.tslibs.nattype'
- 一旦钩子文件被修改,保存它并重新创建新的可执行文件。在重新创建之前,请确保删除旧的可执行文件和相关文件夹。如果错误仍然存在,继续在钩子文件中添加其他缺失的模块。请注意,多个模块应该作为一个列表结构添加。
**#### Code line after adding multiple modules**
hiddenimports = ['pandas._libs.tslibs.np_datetime','pandas._libs.tslibs.nattype','pandas._libs.skiplist']
我们在示例中使用的最终钩子文件如下所示:

最终挂钩文件(图片由作者提供)
一旦添加了所有模块,错误就会得到解决。
结束语
在上面的教程中,我们已经尝试解决了一个小问题,这个问题是我们大多数人在工作中遇到的。
我希望这篇教程是有启发性的,并且你学到了一些新的东西。
会在以后的教程中尝试并带来更多有趣的话题。在此之前:
快乐学习!!!!
使用 Python 将您的语音转换为文本
使用麦克风将您的语音实时转换为文本

汤米·洛佩兹从派克斯拍摄的照片
在这篇文章中,我将向您展示如何使用 Python 将您的演讲转换成文本文档。在编程的话,这个过程基本上叫做语音识别。这是我们日常生活中常用的东西。例如,当你用声音给朋友发信息时。语音到文本的另一个很好的例子是添加一个正在说话的人的字幕。你在网飞节目或 YouTube 视频上看到的大多数字幕都是由使用人工智能的机器制作的。你能想象一群人整天工作只是为了给你最喜欢的节目添加字幕吗?我知道这很难想象。电脑编程的力量来了。我仍然记得我学习 for loops 的那一天,感觉我找到了一种在现实世界中达到无穷大的方法。不管怎样,介绍够了,让我们开始工作吧。
正如你从标题中可以理解的,在这篇文章中,我们将创建一个 python 程序,将我们的演讲转换成文本,并导出为文本文档。如果你是一个喜欢记笔记的人,这个程序将帮助你通过记录你自己来节省时间,并且也有你记录的打印版本。这就像在一场比赛中赢得两个奖杯🙂
让我们开始编码吧!
导入库
我们将使用 SpeechRecognition 模块,如果您还没有,让我们快速安装它。不用担心,在 Python 中安装一个模块是超级容易的。
pip install SpeechRecognition
是的,就是它。SpeechRecognition 模块支持多种识别 API,Google Speech API 就是其中之一。你可以从这里了解更多关于这个模块的信息。我们将在代码中使用 Google 的识别器。现在,在安装模块完成后,我们可以将它导入到我们的程序中。
import speech_recognition as sr
创建识别器
在这一步中,我们将创建一个识别器实例。
r = sr.Recognizer()
定义您的麦克风
在定义麦克风实例之前,我们将选择输入设备。可能有多个输入设备插入您的计算机,我们需要选择我们打算使用哪一个。你知道机器是假人,你必须准确地告诉他们该做什么!使用下面的代码,你将能够看到你的输入设备。
print(sr.Microphone.list_microphone_names())

麦克风名称列表
这里你可以看到我检查输入设备的结果。我建议您在定义麦克风之前运行这个脚本,因为您可能会得到不同的结果。该脚本返回一个带有输入名称的数组列表,对我来说我想使用“内置麦克风”,所以数组列表的第一个元素。定义麦克风代码将如下所示:
mic = sr.Microphone(device_index=0)
识别语音
如前所述,我们将使用 recognize_google 方法,这是我们在 google 的朋友创建的语音识别模型。多亏了他们!
with mic as source: 
  audio = r.listen(source) result = r.recognize_google(audio)
如果您想在将结果导出到文本文档之前检查结果,可以在代码中添加下面一行。
print(result)
最后一步:导出结果
在这一步中,我们将创建一个文本文档并导出我们在上一步中得到的结果。您将看到“导出过程完成!”这个过程完成后,在终端窗口中添加一行。
with open('my_result.txt',mode ='w') as file: 
   file.write("Recognized text:") 
   file.write("\n") 
   file.write(result) print("Exporting process completed!")
最终代码
# import the module 
import speech_recognition as sr # create the recognizer 
r = sr.Recognizer() # define the microphone 
mic = sr.Microphone(device_index=0) # record your speech 
with mic as source: 
   audio = r.listen(source) # speech recognition 
result = r.recognize_google(audio)# export the result 
with open('my_result.txt',mode ='w') as file: 
   file.write("Recognized text:") 
   file.write("\n") 
   file.write(result) print("Exporting process completed!")
恭喜我的朋友!您已经创建了一个将您的演讲转换为文本并将其导出为文本文档的程序。希望你喜欢读这篇文章,并且今天学到了一些新的东西。从事像这样有趣的编程项目会帮助你提高编码技能。
您可能会感兴趣的另一个语音识别项目
使用谷歌云语音 API 将您的音频文件转换为文本
towardsdatascience.com](/building-a-speech-recognizer-in-python-2dad733949b4)
将深度学习研究论文转化为有用的代码
如果深度学习是一种超能力,那么将理论从纸上转化为可用的代码就是一种超能力

作者图片
为什么要学习实现机器学习研究论文?
正如我所说的,能够将一张纸转换成代码绝对是一种超能力,尤其是在机器学习这样一个每天发展越来越快的领域。
大多数研究论文来自大型科技公司或大学的人,他们可能是博士或从事尖端技术工作的人。
还有什么比能够复制这些顶尖专业人士的研究更酷的呢?另一件需要注意的事情是,那些能够将研究论文复制成代码的人需求量很大。
一旦你掌握了实施研究论文的诀窍,你将处于与这些研究者不相上下的状态。
这些研究人员也通过阅读和实施研究论文的实践获得了这些技能。
我如何阅读和实施论文?
你可能会说,“嗯,我对深度学习算法有一个大致的了解,如全连接网络,卷积神经网络,递归神经网络,但问题是,我想开发 SOTA(最先进的)语音克隆人工智能,但我对语音克隆一无所知:(”。
好的,这是你的答案(我的方法的某些部分摘自吴恩达关于阅读论文的建议)。
如果您想了解某个特定主题:
- 收集 5-6 篇与特定主题相关的论文(您可以简单地搜索 arxiv 或类似网站来获得与某个主题相关的论文)。
- 不要完全阅读一篇论文,而是浏览所有的论文,选择一篇你感兴趣的论文,或者如果你心中有一篇特定的论文,就去选择它,没有人能阻止你。
- 仔细阅读摘要,从高层次上理解文章的思想,看看你的兴趣是否仍然存在,如果是,继续浏览图片,看看你能否对文章的内容做出假设。
- 现在请一行一行地仔细阅读引言,因为论文中包含的大部分内容将在这里用最简单的方式和最少的数学来解释。
- 如果你愿意,你可以跳过第一遍的数学方程,如果希腊字母很熟悉就不要跳过数学。
- 在任何情况下,如果你被卡住了或者有些词令人困惑,不要犹豫去谷歌一下。没有人生来就是万物的主宰;)
- 完成第一遍后,你将处于这样一种状态,你理解这篇论文试图证明或改进的高层次观点。
- 在第二遍中,试着理解论文中的几乎所有内容,如果你遇到任何伪代码,试着把它转换成你选择的 python 库(PyTorch,TensorFlow…)
- 你可以通过阅读每篇论文的参考文献部分,获得更多的论文来阅读,并对该领域有更好的理解(与点连接相同)。
💡有效理解论文的一些技巧:
- 如果你是一个阅读研究论文的初学者,在阅读论文本身之前,阅读一些与该主题/研究论文相关的博客帖子和视频是很好的。这使你的工作更容易,你也不会因为那些希腊字母而气馁。
- 在为论文实现代码时,始终记笔记并突出研究论文中的要点,以便于参考。
- 如果您是实现研究论文的新手,并且在任何地方都遇到了困难,那么浏览开源实现并看看其他人是如何做到这一点的,这并不是一个坏主意。
注意:不要把第三点当成常规练习,因为你的学习曲线会下降,你会过度适应:)
你应该发展你自己的阅读和实施论文的方法,这只有通过开始才有可能,所以上面的步骤将帮助你开始。
根据吴恩达的说法,如果你能阅读关于一个主题的 5-10 篇论文(例如:声音克隆),你将处于实施声音克隆系统的良好状态,但是如果你能阅读关于那个主题的 50-100 篇论文,你将处于研究或开发该主题的尖端技术的状态。
让我们讨论一篇论文
- 高级概述
现在,你已经了解了如何阅读论文,让我们为自己阅读和实现一个。
我们将仔细阅读 Ian Goodfellow 的论文—(GAN)并用 PyTorch 实现同样的内容。
在论文摘要中清楚地讨论了论文内容的高级概述。
摘要告诉我们,研究人员正在提出一个包含两个神经网络的新框架,它们被称为生成器和鉴别器。
不要被名字搞混了,它们只是给两个神经网络取的名字。
但是在摘要部分要注意的要点是,上面提到的生成器和鉴别器相互竞争。
好吧,让我说清楚一点。
让我们以使用 GANs 生成不存在的新人脸为例。
生成器生成与真实图像具有相同尺寸(H×W×C)的新人脸,并将其显示给鉴别器,鉴别器判断该图像是生成器生成的假图像还是真实的人的图像。
现在你可能会有一个问题,“嗯,这个鉴别器是如何区分真假图像的?”,这是你的答案:
鉴别器的问题类型是图像分类,即鉴别器将不得不辨别图像是假的还是真的(0 或 1)。因此,我们可以像训练狗和猫分类器网络一样训练鉴别器,但我们将使用完全连接的网络,而不是卷积神经网络,因为这是论文所提出的。
💡DCGAN 是另一种类型的 GAN,它使用卷积神经网络而不是全连接网络,并且具有更好的结果。
所以我们以这样一种方式训练鉴别器,我们馈入一个图像,鉴别器输出 0 或 1,即假或真。
由于我们已经训练了鉴别器,我们将把生成器生成的图像传递给鉴别器,并分类它是真的还是假的。生成器调整其所有权重,直到它能够欺骗分类器来预测所生成的图像是真实的。
我们将为生成器提供一个随机概率分布(随机张量),生成器的职责是改变这个概率分布,以匹配真实图像的概率分布。
这些是我们在实施准则时应该遵循的步骤:
→加载包含真实图像的数据集。
→创建一个随机二维张量(虚假数据的概率分布)。
→创建鉴别器模型和发电机模型。
→训练鉴别器辨别真假图像
→现在将假图像的概率分布输入发生器,并测试鉴别器是否能够识别发生器产生的假图像。
→调整生成器(随机梯度下降)的权重,直到鉴别器无法识别伪图像。
你可能有几个疑问,但现在没关系,一旦我们将理论实现到代码中,你就会知道它是如何工作的。
2.损失函数****
在实现代码之前,我们需要一个损失函数,以便优化我们的发生器网络和鉴别器网络。
鉴别器模型存在二进制分类问题,因此我们对鉴别器使用二进制交叉熵损失,或者您也可以使用本文中讨论的自定义损失函数。
来自论文的损失函数:【log D(x)】+【log(1-D(G(z)))】
x→实景
z→虚假数据或噪声(随机张量)
D→鉴别器型号
G→发电机型号
G(z)→向发生器输入虚假数据或噪声(输出为虚假图像)
D(x)→将真实图像送入鉴别器(输出为 0 或 1)
D(G(z))→假数据被送到发生器,发生器输出图像,图像被送到鉴别器进行预测(输出为 0 或 1)
如果你想用纸上的损失函数,让我解释给你听:
根据该论文,对于鉴别器,我们需要最大化上述损失函数。
让我们看看等式的第一部分:
— D(x) 输出 0 或 1,所以当我们最大化log[D(x)】时,它使鉴别器在 x (实像)馈入时输出一个接近 1 的值,这就是我们所需要的。
现在,让我们来看看等式的第二部分:
— G(z) 输出一个与真实图像尺寸相同的图像,现在这个假图像被馈送到鉴别器( D(G(z) ),当我们最大化这个值时,鉴别器的输出将接近 1,因此当我们做[1d(G(z))]时,我们将得到一个接近于零的值,这正是我们将假图像传递到鉴别器时所需要的。
****注:你可以在方程中加一个负号,把损失函数变成鉴别器的最小化问题,这比最大化容易。
对于发生器,我们需要最小化上述等式,但本文仅考虑等式【log(1D(G(z)))】的第二部分进行最小化。
—当我们最小化 D(G(z)) 时,鉴别器输出一个接近于 0 的值,方程的整体输出变得接近于 1,这就是我们的生成器想要实现的,在给定来自生成器的假图像时,忽悠鉴别器预测 1(真实)。
耶,让我们开始吧!
我已经在 google colab 中完成了代码实现,所以如果你尝试 google colab 或 jupyter notebook 中的代码将是最好的。
- 导入所需的库—
2.我们将使用单个图像作为真实图像,以便更快地训练和获得结果,因此生成器生成的图像将与此图像相似,您也可以使用图像的数据集,这完全取决于您。
我们将使用 PIL 库加载图像作为 PIL 图像,我们将使用 torchvision transforms 调整图像大小并将其转换为张量,然后为图像生成创建大小为(1×100)的假噪声。
3.创建一个鉴别器模型,它只不过是一个完全连接的神经网络,接收真实图像或虚假图像,输出 0 或 1。
4.创建一个生成器模型,它也是一个完全连接的网络,接收随机噪声并输出与真实图像大小相同的图像张量。
5.初始化模型、优化器和损失函数,然后将它们移动到所需的设备(cuda 或 cpu)。我们将对鉴别器使用二进制交叉熵损失,对发生器使用本文中讨论的损失函数(log(1D(G(z))))。
6.现在我们将训练该模型,整个 GAN 被训练五百个时期,鉴别器将被训练四个时期,并且对于五百个时期中的每个时期,发生器将被训练三个时期。
7.将生成图像的结果与真实图像进行比较。您可以调整学习率、动量、时期数以及生成器和鉴别器模型的层数,以获得更好的结果。

将我们的生成器图像与真实图像进行比较(图像由作者提供)
最后的想法
生成的图像可能没有很高的分辨率,因为这篇论文只是整个生成模型世界的开始。如果您的兴趣仍然存在,您可以继续阅读 DCGANs (深度卷积 GANs)或GANs 世界的任何其他论文,并实施它以查看惊人的结果,但请始终记住,本文是所有这些论文的基础。
链接到完整的代码可在这里。
凸和非凸损失函数下的价格优化

ML 问题的典型损失面([来源](https://www.cs.umd.edu/~tomg/projects/landscapes/ and arXiv:1712.09913)
优化用于许多日常活动,从使用谷歌地图找到到达目的地的最快路线,到通过在线应用程序点餐。在本帖中,我们将介绍两种主要损失函数的价格优化,即凸损失函数和非凸损失函数。我们还将介绍使用名为 scipy 的 python 库解决这些问题的最佳方法。在这个博客中使用的示例数据和代码的细节可以在这个笔记本中找到。
凸优化
凸优化问题是一个所有约束都是凸函数的问题,如果最小化,目标是凸函数,如果最大化,目标是凹函数。凸函数可以描述为具有单一全局最小值的光滑曲面。凸函数的示例如下:
*F(x,y) = x2 + xy + y2.*

凸函数(来源)
1.数据生成
为了了解优化是如何工作的,我正在生成一个玩具数据集,其中包括销售价格、数量和成本。让我们设定数据集的目标是找到一个使总利润最大化的价格。
销售价格(price): 这是一个在 1000 到 7000 之间产生的随机数。使用 numpy 随机函数生成数据。

价格直方图
交易量:交易量是销售价格的直线函数,其下降斜率如下所示:
*f(volume) = (-price/2)+4000*

体积直方图
销售价格和销售量之间的关系如下所示。正如预期的那样,随着价格的下降,我们看到了销售量的增加。

成本:成本变量代表产品的制造成本。这考虑了一些固定成本(即 300 英镑)和可变成本(即 0.25 英镑),可变成本是交易量的函数。
 *f(cost) = 300+volume*0.25*
现在我们已经有了所有需要的变量,我们可以使用下面的简单公式来计算利润:
*f(profit) = (price — cost)*volume*
销售价格、交易量和利润的三维图如下所示。我们可以看到,这是一个钟形曲线,峰值利润在特定的数量和成本。一个企业的目标可能是利润最大化,在下一节中,我将展示我们如何使用 scipy 来实现这一目标。

3d 绘图:价格(x)、交易量(y)和利润(z)
2。根据观察数据估算成本和数量
在上面的部分中,我们已经生成了一个样本数据集。然而,在现实世界中,我们可能获得一小组观察数据,即产品的价格、数量和成本。此外,我们不知道支配因变量和自变量之间关系的基本函数。
为了模拟观察数据,让我们截取生成的数据,即价格为"<3000”. With this observational data we need to find the relationship between price vs cost and volume vs cost. We can do that by performing a simple linear regression on the observation data set. However for real world problem this may involve building complex non-linear models with a large number of independent variables.
线性回归:的所有点
- 价格 vs 成交量
我们把价格作为自变量‘X’,把成交量作为因变量‘y’。使用 python 中的 stats 模型库,我们可以确定一个线性回归模型来捕捉量价关系。
从下面的观察数据可以看出,我们的 R 平方为 1(即 100%),这意味着产品价格的变化可以很好地解释销量的变化。然而,这种完美的关系在现实生活的数据集中很难观察到。

此外,我们可以看到偏差为-3,999.67,价格系数为-0.5,近似于用于生成交易量数据的函数。
2。数量与成本
以类似的方式,以成本为因变量“y”和以数量为自变量“X”进行线性回归,如下所示。我们可以看到,成本模型的偏差和系数近似于用于生成成本数据的函数。因此,我们现在可以使用这些训练好的模型来确定凸优化的成本和体积。

线性回归模型的结果——成本与数量。
使用 scipy 优化
我们将使用 scipy 优化库进行优化。该库有一个最小化功能,它接受以下参数,关于 scipy 最小化功能的更多细节可以在这里找到:
result = optimize.minimize(fun=objective, 
                           x0=initial,
                           method=’SLSQP’,
                           constraints=cons,
                           options={‘maxiter’:100, ‘disp’: True})
- fun: 要最小化的目标函数。在我们的情况下,我们希望利润最大化。因此,这将意味着'-利润'最小化,其中利润被计算为:利润=(价格-成本)数量*
def cal_profit(price):
 volume = mod_vol.predict([1, price]) 
 cost = mod_cost.predict([1, volume])
 profit = (price — cost)*volume
 return profit###### The main objective function for the minimisation.
def objective(price):
 return -cal_profit(price)
2。x0: 初步猜测,即在这种情况下,我们设定的初始价格为 2000 英镑。
initial = [2000]
3。界限:界限是优化中使用的下限和上限区间。它只能为某些支持有界输入的优化方法设置,如 L-BFGS-B、TNC、SLSQP、Powell 和 trust-const 方法。
#################### bounds ####################
bounds=([1000,10000])
4。约束:这里我们指定优化问题的约束。这种情况下的约束条件是可以生产的产品数量(即设置为 3,000 件)。这是一个不等式约束,即体积必须小于指定的单位数。也可以使用其他类型的约束,如等式约束。
# Left-sided inequality from the first constraint
def constraint1(price):
    return 3000-mod_vol.predict([1, price])# Construct dictionaries
con1 = {'type':'ineq','fun':constraint1}# Put those dictionaries into a tuple
cons = (con1)
5 。方法:有多种方法可用于执行最小化操作,例如(见参考):
我们将使用 SLSQP 方法进行优化。序列二次规划 ( SQP 或 SLSQP )是一种约束非线性优化的迭代方法。
并非所有上述方法都支持使用边界和约束。因此我们使用 SLSQP,它支持使用优化的边界和约束。
凸优化的结果
优化总共运行了 3 次迭代。基于此,目标函数返回值为-6.6 密耳(即-6,587,215.16)。这导致价格为 4577 英镑,数量为 1710 英镑。我们得到一个负值,因为我们通过最小化负利润来最大化利润,这是一个凸函数。
Optimization terminated successfully.    (Exit mode 0)
            Current function value: -6587215.163007045
            Iterations: 3
            Function evaluations: 9
            Gradient evaluations: 3
优化的结果与生成的数据集中看到的最大利润非常匹配。因此,我们知道优化已经如预期的那样工作。

最大利润值:价格(x)、数量(y)和利润(z)
非凸优化
在大多数机器学习问题中,我们会遇到本质上非凸的损失曲面。因此,它们将具有多个局部最小值。因此,在求解这种非凸问题时,如果我们“凸化”一个问题(使其凸优化友好),然后使用非凸优化,可能会更简单且计算量更小。我将通过下面的例子演示我们如何实现这一点。
非凸数据集生成;
在这里,我们创造了另一种产品,其价格、数量和利润略有不同。假设该公司只能生产这些产品中的一种,并希望使用优化来找出它需要专注于哪些产品以及它需要以什么价格出售。

3d 绘图:产品 1 和产品 2 的非凸数据集
对非凸数据使用凸极小化
对于上述数据,如果我们使用与上面相同的凸优化,我们得到的解将是局部最小值,如下所示。对于5018的优化价格和 0 的产品类型,我们获得了686 万的最大利润。

凸优化的局部最小值
盆地跳跃算法
跳盆算法是一种结合了全局步进算法和每步局部最小化的算法。因此,这可用于寻找非凸损失表面可用的所有局部最小值选项中的最佳选项。下面的代码演示了这个算法的实现。

跳盆算法的全局最小值
如上所述,我们使用了与上例相同的方法,即 SLSQP,以及相同的边界和约束。然而,我们现在得到了一个稍微不同的结果。最大利润为980 万,优化价格为5957,产品类型为 1 。
结论:
总之,我们已经回顾了 scipy 中优化库在具有单一全局最小值的简单凸优化数据集上的使用,还回顾了在损失面具有多个局部最小值和一个全局最小值的情况下盆地跳跃算法的使用。希望你会发现上面的帖子很有用,并且提供了解决现实世界中优化问题的框架。
参考资料:
序列二次规划(SQP)是一种求解约束非线性优化的迭代方法。SQP 方法是…
en.wikipedia.org](https://en.wikipedia.org/wiki/Sequential_quadratic_programming) [## SciPy . optimize . basin hopping-SciPy v 1 . 5 . 2 参考指南
使用盆地跳跃算法寻找函数的全局最小值盆地跳跃是一种两阶段方法,它…
docs.scipy.org](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.basinhopping.html) [## SciPy . optimize . minimize-SciPy v 1 . 5 . 2 参考指南
一元或多元标量函数的最小化。参数要最小化的目标函数。在哪里……
docs.scipy.org](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize) [## 为什么要研究理论机器学习的凸优化?
begingroup$机器学习算法一直在使用优化。我们最小化损失或错误,或最大化一些…
stats.stackexchange.com](https://stats.stackexchange.com/questions/324981/why-study-convex-optimization-for-theoretical-machine-learning)

 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号