熊猫中方法链接的不合理有效性
方法链接如何提高代码的可读性,用 lambda 函数编写自定义管道以实现最大的灵活性,并以代码格式化技巧结束。
Image by Texturex
介绍
方法链接是一种编程风格,依次调用多个方法调用,每个调用在同一个对象上执行一个动作并返回它。它消除了在每个中间步骤命名变量的认知负担。 Fluent 接口,一种创建面向对象 API 的方法依赖于方法级联(又名方法链)。这类似于 Unix 系统中的管道。
方法链接大大增加了代码的可读性。如果你不相信我,我们去问问杰克和吉尔。
让我们尝试使用嵌套函数调用和方法链接来讲述杰克和吉尔的故事,
嵌套调用:
方法链接:
方法链接的一个明显的优点是,它是一种自顶向下的方法,参数放在函数旁边,不像嵌套调用,在嵌套调用中,跟踪各个函数调用到其参数是很费力的。
Pandas 为方法链接提供了几个函数,
向数据帧assign
添加新列,重命名列rename
,过滤数据帧query
等。让我们来看看教育学的wine
数据集。它包含 178 种葡萄酒的化学成分。
Wine dataset
下面的代码从重命名color intensity
开始,因为它的缩写是ci
。然后,它基于hue
和ci
上的值创建一个新列color filter
。然后,它过滤酒精含量超过 14 的葡萄酒,并通过彩色过滤器。最后,它根据酒精含量对数据框进行排序,并显示我们感兴趣的列。如果在没有方法链接的情况下重复相同的步骤,则必须在每个步骤创建一个新的数据帧。
方法链接的好处和坏处:
Image by Sally Dasouki
与 Python 相比,R 的一个优势是它的tidyverse
包具有丰富的方法链接功能。再加上margrittr
,你经常可以找到一种方法来做你想在管道中做的事情。另一方面,Pandas 没有一个可理解的方法列表用于方法链接。但是为了弥补它,熊猫从版本 0.16.2 开始引入了管道功能。管道在方法链中启用用户定义的方法。
随着 pipe 的引入,你几乎可以在一个方法链中写任何东西,这引发了一个问题,多少链是太多了?。这是一个完全主观的问题,必须由程序员来决定。大多数人发现在一个单独的链中有 7 或 8 个方法是最合适的。我对一个链中的方法数量没有任何硬性限制。相反,我试图用一个方法链来表示一个连贯的思想。
一些方法链的坚定批评者指责它增加了代码的可读性,但却增加了调试的难度,这是事实。想象一下,一个月之后,你正在调试一个十个方法长的链。从那时起,数据帧结构或列名已经改变,现在您的链开始抛出错误。尽管您可以很容易地找到哪个方法调用破坏了代码,但是现在不可能在整个链中进行调试,并在您沿着链移动时看到它对数据帧所做的更改。在生产或笔记本中开始使用长方法链之前,需要解决这个问题。
组合管道和 lambda 函数:
Image by Grofers
每当数据框架的形状改变时,我经常会遇到方法链接的问题。如果你能沿着链跟踪形状,调试就容易多了。让我们定义一个自定义管道函数。编写管道函数的关键是它应该接收数据帧并返回数据帧。
这个函数中需要注意的两件事是可以接受 lambda 函数的fn
参数和display
函数调用。Lambda 函数提供了灵活性,display 函数调用使 Jupyter 实验室或笔记本电脑环境中的数据框和绘图显示更加美观。
在这个例子中,我们可以看到我们从 13 列开始,assign
将列数增加到 14,随后的query
将行数减少到 22。末尾的管道从数据帧中随机打印出 5 行。这可以很容易地更改为 head 或 tail 函数。由于自变量是一个 lambda 函数,它给出了无数种可能性。当你开始写一个链时,在末尾添加一个带有csnap
函数的管道有助于你看到链上的变化。完成后,您可以删除管道或注释掉该行。这是拆除管道的一种幼稚的方法。相反,如果要将代码转移到生产环境中,可以使用 logger 对象并将其写入外部文件。
Example of logging to a file
日志记录为我们提供了不删除管道语句的灵活性,而是将日志记录级别更改为 INFO,以避免在生产过程中获得调试信息。
让我们看看其他自定义管道函数。setcols
用于设置链中的列名。通常,当我们从外部源读取数据时,列名将包含大小写以及空格和特殊字符。这些问题可以这样解决,
Iris data set before and after column rename
与 **csnap**
函数不同 **setcols**
函数创建数据帧的副本,这使得函数调用代价很高。但这是必要的,以确保我们没有在数据帧的全局副本上写入。大多数 pandas 函数的工作方式类似,都有一个就地标志,用于返回新的数据框或重新分配顶级引用。
杰夫·雷巴克说,
不能保证就地操作确实更快。通常,它们是对副本进行的相同操作,但是顶级引用被重新分配。
让我们用最后一个例子来结束这一部分。r 有一个多功能的选择函数,可以选择/取消选择宽数据框中的列,而不是列出所有内容。cfilter
使用 lambda 函数帮助我们实现同样的多功能性。
代码格式:
当多人使用不同的 IDE 在同一个代码库上工作时,代码格式的一致性是一个令人头疼的问题。在多行函数调用的情况下,方法链接使这个过程更加复杂。
输入黑色:
Image from Black
摘自他们的 GitHub 描述,
布莱克是不折不扣的 Python 代码格式化程序。通过使用它,您同意放弃对手写格式的细节的控制。作为回报, Black 为您提供了速度、确定性以及摆脱 pycodestyle 对格式的困扰。你将节省时间和精力去做更重要的事情。
不管你在读什么项目,黑化的代码看起来都一样。一段时间后,格式变得透明,您可以专注于内容。
黑色通过产生尽可能小的差异,使代码审查更快。
使用 black 的一个主要优点是它理解 fluent 接口并相应地自动格式化函数调用,不像任何其他 IDE 的默认格式化程序。
为 Pycharm 设置黑色:
第一步是pip install black
。在 pycharm 中,您可以将它集成为一个文件监视器,这样每次保存文件时,都会自动完成格式化;也可以将它集成为一个预提交挂钩,这样每次提交时,都会对代码进行格式化,从而保持整个项目的格式化完整性。详细的设置说明可在这里找到。
为 Jupyter 笔记本/实验室设置黑色:
第一步是pip install nb_black
。下一步是根据环境加载适当的扩展。如果是笔记本,那么使用%load_ext nb_black
其他实验室用%load_ext lab_black
。
一个快速演示布莱克的行动,
Before and after of Black Formatting
参考:
1)https://tomaugspurger.github.io/method-chaining
3)https://github.com/ambv/black
未言明的数据科学软技能
A basic neural map of some essential Data-Science skills
对于初学者来说,数据科学的世界可能令人望而生畏,因为在许多方面,数据科学是许多不同操作之间的桥梁。不仅有硬技能:编程、计算、统计,当然还有更多,而且作为一名数据科学家还有许多更小的软技能。这些技能是数据科学所必需的,并且在数据科学的世界中每天都非常依赖。
阅读和写作
每个数据科学家需要掌握的最不言而喻也是最重要的软技能之一是熟练的阅读和写作。如果你真的很擅长计算机,但英语课不及格,数据科学可能不适合你。对任何软件工程师来说,最重要的事情之一就是能够轻松快速地解读文档以及错误输出。当然,考虑到这一点,数据科学的世界一直在发生变化。作为一名数据科学家,要求你每天都要学习。大量的资源将无法在视频或有声读物中获得,而是必须阅读。理解所使用的词汇对于理解软件包如何工作,以及如何将它们合并到您的软件中,或者在您的数据上使用它们也是绝对重要的。
写作也是绝对重要的,但是对你写作能力的依赖也取决于你的工作。关于数据科学的一个有趣的事情是,许多工作在需要证明的技能和掌握程度方面非常不同。例如,在一份工作中,你可能大部分是工程包,并为你的团队编写函数。而在另一份工作中,你可能正在创建复杂的模型,或者用机器学习解决复杂的问题。有些工作可能需要更像软件工程师的数据科学家,而其他工作可能需要擅长写备忘录的数据科学家。无论如何,写作肯定是一项非常重要的技能。
商业
虽然一般来说,拥有业务相关技能的想法不被视为软技能,而是硬技能,但对于数据科学家来说,这往往最终会成为他们主要工作流程的副产品。然而,对于从事商业分析、预测建模和统计实验广告的数据科学家来说,这项技能绝对至关重要。
捍卫并能够验证你的模型和假设,是企业会发现数据科学家非常有价值的事情。此外,基本的演示技巧对于展示模型、统计数据和可视化来展示你的作品是绝对重要的。
开发运营、网络开发和软件工程
同样,开发运营、Web 开发和软件工程不是软技能。但对于数据科学家来说,尽管这些技能不一定与工作描述相符,但其中许多技能符合你作为数据科学家的日常职责。许多数据科学工作要求你能够设计软件,为全栈 web 开发人员推送数据,甚至编写自己的应用程序和模块来处理数据。
许多数据科学家都很熟悉 Docker,因为它是一个虚拟机工具,并且经常在他们的业务环境中使用它。Docker 传统上并不被认为是一个数据科学工具,而是一个开发运营工具。通常,像任何软件工程师一样,数据科学家需要熟悉他们的终端,以及虚拟操作系统终端,如 Debian、Ubuntu 和 Fedora。
领域知识
有时,领域知识可能是数据处理的关键。了解你的公司是做什么的,以及关于你的公司产品的事情,对于了解你的生态系统内的目标是绝对重要的。
一个很好的例子是人口普查数据,有时人口普查的回答并不完全一致。您可能会遇到这样一个数据集,根据您所拥有的领域知识,您可能会发现某些观察结果是无效的,否则会完全破坏您的模型。
最后
这些只是数据科学家在其环境中取得成功所需的一些较大技能。有时候,对普通人来说,记住所有的事情有点太多了。请注意,所有这些都是常规的数据科学知识。
数据科学是一个快速发展的领域,每天都在扩展和增长,通过对未来十年的预测,肯定可以为我们的生活奠定基础。数据科学最大的特点是个人发展。作为一名数据科学家,世界永远不会停止,因此,你也不能。您必须不断地学习,以跟上数据科学计算领域当前的发展。
现代软件开发的无名英雄
每个人都应该知道的开源软件基础
开源软件是人人受益的开发模式。这就是今天许多重要软件的构建方式。
支持这一发展的开源基金会起着至关重要的作用。然而,他们的工作往往不为人知。直到我研究了这篇文章,我才知道有这么多项目受益于这些基金会。
在本文中,我们将了解软件开发人员和数据科学家每天使用的许多工具的幕后推手。
Exploring the landscape of open source foundations
为什么要开源基金会?
开源基金会的出现是为了帮助维持和管理开源项目。这些基金会为与开源软件(OSS)项目有利害关系的公司和个人提供了走到一起的空间。它们作为独立的非营利实体的地位为相互竞争的公司合作提供了中立的基础。
Foundations support the things that get build on top of them
开源基金会通常为软件开发项目提供广泛的业务和运营支持。在基金会的庇护下,该项目通常可以获得财政支持和后台协助。基金会可以通过以下具体方式提供帮助:
- 谈判并签署供应商合同
- 雇用员工或承包商
- 缓冲法律风险
- 提供法律框架(许多基金会已经创建了自己的许可证)
- 提供代码签名证书等技术服务
- 访问银行账户等金融产品
- 提供营销服务,如新闻发布
- 提供监督和治理
- 管理项目成员资格
正如你所想象的,这些都是项目领导需要协助的无价角色。这些基础帮助项目成员专注于修复错误、编写文档和开发新功能。点击阅读更多关于开源基金会的角色。
开源基金会领导人
我将重点介绍六个开源基金会,它们是许多重要项目的关键。对于每个基金会,我都会简单介绍一下,提供截至 2019 年初得到支持的项目数量,并重点介绍一些知名项目。请注意,这些团体属于 IRS 对慈善和贸易组织的各种分类,并非所有团体都是基金会。
阿帕奇软件基金会
Apache 软件基金会已有 20 年的历史,是最大的基金会之一。截至 2019 年初,它有超过 350 项开源计划。
ASF 为知识产权和财务贡献提供了一个既定的框架,同时限制了我们的项目提交人的潜在法律风险。通过 ASF 被称为“Apache 方式”的精英管理过程,730 多个个人成员和 7000 名提交者成功地合作开发了免费提供的企业级软件,使全球数百万用户受益:数千个软件解决方案在 Apache 许可下分发;社区积极参与 ASF 邮件列表、指导计划和 ApacheCon,即基金会的官方用户会议、培训和展览。
许多 Apache 项目都大量使用 Java。受欢迎的项目包括:Apache HTTP Server、Hadoop、Tomcat 和 Arrow。
Linux 基金会
Linux 基金会是 Linux 操作系统和许多相关项目的所在地。它的其他 100 多个项目包括 NodeJS 和 RethinkDB。
Linux 基金会通过提供资金和智力资源、基础设施、服务、活动和培训来支持创建可持续的开源生态系统。Linux 基金会和它的项目一起工作,形成了在创造共享技术方面最雄心勃勃和最成功的投资。
www.linuxfoundation.org
Linux 基金会成立于 2000 年,由另外两个团体合并而成。它目前有超过 1000 名成员,包括所有常见的大牌技术公司。
所有托管项目都获得治理结构和后端资源。一些项目也获得资助。Linux 基金会也提供培训和会议。
自由软件基金会
成立于 1983 年的自由软件基金会维护着构成 GNU Linux 生态系统的项目。其他流行的项目包括 Bash、Emacs、Gawk、Make 和 r。
自由软件基金会(FSF)是一个非营利组织,其全球使命是促进计算机用户的自由。我们捍卫所有软件用户的权利。
【https://www.fsf.org/
自由软件基金会有 5,000 多名成员和大约 400 个开放源码软件项目。
软件自由保护协会
软件自由保护协会成立于 2006 年。它有超过 45 个项目,包括像 Busybox、Git、Homebrew、Inkscape、phpMYAdmin、PyPy 和 Selenium 这样受欢迎的项目。
软件自由保护协会是一个非营利慈善机构,帮助促进、改进、开发和维护自由、自由和开源软件(FLOSS)项目。保护协会为 FLOSS 项目提供了一个非盈利的家园和基础设施。这使得 FLOSS 开发人员可以专注于他们最擅长的事情——为公众编写和改进 FLOSS——而 Conservancy 则负责那些与软件开发和文档不直接相关的项目需求。
软件自由保护协会有超过 500 个赞助商,包括谷歌和其他一些大公司。
符合公众利益的软件
公益软件成立于 1997 年。它的 39 个项目包括 haskell、PostgrSQL、Jenkins、Arch Linux 和 Debian。
公益软件是一个非盈利组织,旨在帮助组织开发和分发开放硬件和软件。我们的使命是通过处理他们的非技术性管理任务来帮助真正的、实质性的、重要的自由和开源软件项目,这样他们就不需要运营他们自己的法律实体。
【https://www.spi-inc.org/ 号
云本地计算基金会(CNCF)
云本地计算基金会是这个街区的新生事物。成立于 2015 年,支持围绕 Kubernetes 容器化云微服务的开源项目。
CNCF 是一个开源软件基金会,致力于使云本地计算普及和可持续发展。云原生计算使用开源软件堆栈将应用部署为微服务,将每个部分打包到自己的容器中,并动态编排这些容器以优化资源利用率。云原生技术使软件开发人员能够更快地构建优秀的产品。
CNCF 成员包括技术名人录:AWS、阿里巴巴云、戴尔、英特尔、甲骨文、微软 Azure、IBM Cloud 和谷歌云。
截至 2019 年初,已有 4 个项目毕业,16 个在孵。受欢迎的相关项目包括毕业于 CNCF 的 Kubernetes。
有趣的是,CNCF 得到了 Linux 基金会的支持。
数字焦点
NumFOCUS 是许多流行的数据科学开源项目的所在地。它成立于 2012 年,其 25 个受欢迎的项目包括 NumPy、Matplotlib、Pandas、Jupyter、Julia 和 Bokeh。NumFOCUS 还将许多其他开源项目作为附属项目进行推广。
NumFOCUS 提供了许多程序来支持我们的使命,即促进可持续的高级编程语言、开放代码开发和可重复的科学研究。
NumFOCUS 在世界各地举办 PyData 会议。披露:我在 PyData DC 做志愿者,过得很愉快。强烈推荐志愿!😃
这是六个需要了解的开源基金会。他们为许多推动世界发展的开源软件项目提供了宝贵的支持。请注意,我没有包括那些支持少数项目或具有更广泛宣传重点的基金会。
以下是概要:
- 阿帕奇软件基金会
- Linux 基金会
- 自由软件基金会
- 软件自由保护协会
- 符合公众利益的软件
- 云本地计算基金会(CNCF)
- 数字焦点
如果你知道为其他 OSS 项目提供支持的其他组织,请在评论或 Twitter @discdiver 上告诉我。
要了解更多关于开源基金会的信息,请参见这项调查研究。
包装
现在,您知道当时机到来时,您的开源项目将转向何处。你也知道一些人,他们让开源项目有效地成长。
我希望这篇文章对你有所启发。如果你有,请在社交媒体上分享,以便其他人可以找到它。
我写关于机器学习、数据科学、深度学习、云计算、Docker、开源软件和其他有趣的东西。点击这里查看我的文章,如果其中有你感兴趣的,请关注我。
非常感谢所有为开源做出贡献和支持开源的人!👍
用户代理——支撑一系列分析的疯狂字符串
令人惊讶的是,我们竟然如此依赖它
Only Innocent Browsers Here (Photo: Randy Au)
在网络分析领域工作一段时间,甚至不到一周,我敢打赌,你就会听说这个叫做“用户代理”的东西。我们从用户那里得到的就是这个字符串,它应该告诉我们关于他们的各种事情。如果我们应该用这个东西来统计和分析用户,我们需要更多地了解它。
目前在 2019 年,它最重要的用途是弄清楚客户正在使用什么设备(桌面、手机等)来将正确的页面设计发送给客户。这不是进行设备检测的唯一方法,但它形成了一个非常重要的基础。甚至 Javascript 设备检测库经常只是请求和解析用户代理。
用户代理还提供了一个数据点,用于在不使用 cookies 的情况下对用户进行指纹识别。它包含在每个 HTTP 请求中,在某些不常见的情况下,它可能很长,并且对用户来说是唯一的。
但是这一串是关于什么的,为什么这些作品有点疯狂?
那么什么是用户代理字符串呢?
用户代理(UA)是 HTTP 报头中的一个字段,客户端“应该”(在技术上 RFC 2219 意识到如果不这样做,它们可能是技术上的分支)将该字段包含在对服务器的请求中。这在 RFC 1945-HTTP/1.0 和当前 RFC 7231-HTTP/1.1 规范中都有规定(具体章节见下图)。RFC 7540-HTTP/2 关注的是 HTTP 消息本身,而不是 UA 所在的报头,所以 7231 也适用于此。
让我们看看在 RFC 中是如何定义 UA 的。
TL;灾难恢复如下:用户代理是…
- HTTP 请求标头中的字段
- 这应该包括在每一项请求中
- 应采用 US-ASCII 格式(与整个标题一样)
- 由一系列“产品令牌”组成,即带有可选版本号的产品名称字符串,以“/”分隔
- 产品令牌之间用空格隔开。
- 允许用括号将注释括起来
- 产品令牌应排在最重要的前面
- 一个产品不应该复制另一个产品的令牌来声明兼容性
- 没有广告,没有过于精细的细节
RFC 1945—1996 年 5 月发布的 HTTP/1.0
rfc1945 — https://tools.ietf.org/html/rfc1945 — HTTP 1.0
虽然这不是必需的,但用户代理应该在请求中包含此字段。— RFC 1945
RFC 1945 早于 SHOULD 的RFC 2119语言,所以读起来更随意。然而,最重要的几点是:
- 不是必需的,但 UA 应包含在请求中
- 用于统计目的,跟踪违反协议的情况,根据用户代理限制定制响应
- 字段可以包含多个“产品令牌”和注释
- 产品令牌按照识别应用程序的重要性顺序列出
有趣的是,即使在这个 RFC 中,它也已经警告了异常情况,例如一些代理将数据附加到 UA,这使得解释不明确,以及一些客户端不遵循产品令牌语法。
什么是产品令牌?
RFC 1945 — HTTP/1.0
产品令牌只是采用了product/version
的格式,其中版本是可选的。令牌由空格分隔,所以产品不应该有空格。历史上,ISO 8859-1(Latin-1)和 RFC 2047 (Base64 和可打印引号)编码是允许的。如今,规范要求使用 US-ASCII,任何八位字节> 127 都被视为“不透明数据”
因此,理论上,你可以将表情符号或 base64 短文推入 UA,但所有的赌注都取决于服务器如何决定是否以及如何处理它。
Encoding to be used in HTTP header fields — RFC 7230 https://tools.ietf.org/html/rfc7230#section-3.2.4
2014 年 6 月发布的 RFC 7231-HTTP/1.1(当前标准)
rfc 7231 https://tools.ietf.org/html/rfc7231 — HTTP 1.1
HTTP RFCs 的后续版本有了更标准化的语言,但一般来说,事情保持不变。它仍然是由空格分隔的产品/版本条目,注释在括号中。值得注意的是增加了非常具体的禁令,禁止在 UA 中放置非必要的细粒度信息或广告。过长的 UAs 是专门针对潜在的指纹用户而言的(我们稍后会谈到这一点)。
还特别说明了不鼓励 产品使用其他产品的产品令牌来声明兼容性。我们将进入下一步。
有趣的是,在 1997 年 1 月的原始 HTTP/1.1 规范或 1999 年 6 月的 RFC 2616 中,这些新增内容并不存在。在互联网使用激增的 5 年间,他们似乎看到了一些他们想要阻止的滥用行为。
历史用户代理使用
虽然了解 RFC 对用户代理字符串的描述有助于了解它所采用的格式,但它并不能告诉你它在实践中是如何使用的。
正如在“浏览器用户代理字符串的历史”中非常有趣地陈述的那样,UAs 今天的现实是,几乎所有东西都首先伪装成 Mozilla。这是因为在 20 世纪 90 年代,当浏览器功能迅速变化时,Mozilla 浏览器具有某些功能,如最初在竞争浏览器中不可用的框架,所以 web 设计师根据用户代理发送不同版本的页面。
但是当其他浏览器在功能上赶上 Mozilla 时,网页设计者更新他们的服务规则很慢(或者懒得更新)。因此,可以支持高级功能的浏览器不会提供具有这些功能的页面。为了解决这个问题,其他浏览器只是声明自己是 Mozilla 来获取 Mozilla 版本。
很快,几乎每个主流浏览器都决定将 Mozilla 作为第一个产品字符串,同时在评论或后续产品字符串中添加实际的浏览器。这很可能是促使最新的 RFC 明确表示不鼓励使用另一个产品的字符串(例如 Mozilla)的原因。直到今天,浏览器仍然这么做,没有任何改变的迹象。
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36
-- A Chrome User-AgentMozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
-- A Firefox User-AgentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362
-- A Microsoft Edge User-AgentMozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
-- An Internet Explorer 11 User-Agent"Links (2.16; FreeBSD 12.0-RELEASE-p10 amd64; LLVM/Clang 6.0.1; text)"
-- A Links2 User-Agent
如果你愿意,你可以用资源在线浏览大量的用户代理,比如这个。
你还会注意到,每个浏览器都有自己独特的产品令牌序列和类型。通过足够的研究,你可以相当可靠地知道一个请求来自什么设备和浏览器。IE11 使用注释 Trident/7.0 来表示其渲染引擎,使用“rv:11.0”来表示 IE 版本,Chrome(以及 Edge,在后端转换为 chromium )是完整的产品版本令牌,但也会声称 Safari 兼容,因为它们都使用 AppleWebKit。这是一张纠结的网。
机器人呢?
各不相同:有些人比其他人表现更好。一些人声称该机器人是顶级产品,一些人还声称与 Mozilla 兼容,或者声称是在移动设备上。有些库提供了一个默认的用户代理,开发者可以覆盖它,但是忘记了。
W3C_Validator/1.3 [http://validator.w3.org/services](http://validator.w3.org/services) - W3c's validatorGooglebot/2.1 (+[http://www.google.com/bot.html](http://www.google.com/bot.html)) - A GooglebotMozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +[http://www.google.com/bot.html](http://www.google.com/bot.html)) - A Googlebot, but AndroidMozilla/5.0 (compatible; bingbot/2.0; +[http://www.bing.com/bingbot.htm](http://www.bing.com/bingbot.htm)) - one of the BingbotsPython/3.6 aiohttp/3.5.4 - Python's AIOHTTP librarypython-requests/2.20.1 - Python's Request library
机器人和脚本是如此的明显,但是你有时不得不眯着眼睛看。他们更有可能在文本中有 URL(作为允许人们识别和报告行为不端的机器人的一种做法),但这并不能保证,你必须逐个案例地解析它。
欺骗是完全允许的
出于各种原因,无论是出于测试、兼容性还是隐私原因,用户可能希望让他们的浏览器给出不同于默认的用户代理字符串。RFC 明确指出,如果一个客户端发送一个伪装的 UA,那么它应该被解释为客户端故意想要那个版本,即使它可能无法正确呈现。
如果一个用户代理伪装成一个不同的用户代理,接收者可以假定用户有意地希望看到为那个被识别的用户代理定制的响应,即使它们对于正在使用的实际用户代理可能不那么有效。— RFC 7231,HTTP/1.1
浏览器通常为用户提供欺骗他们的 UAs 的功能。也就是说,很少会遇到遇到麻烦的用户,因为从用户的角度来看,浏览器功能之间的差异已经变得微不足道。
好的,我们知道无人机是什么样子,它们是如何使用的?
关于用户代理,需要记住的一点是,它是一项非常古老的技术,历史上它被用来做的许多事情(如基本分析)已经被其他可以提供更多细节和解决方案的技术所取代。
为不同版本的网站服务
这种用法是 UA 字符串的最初目的,它使服务器能够根据客户机的处理能力提供不同版本的网页。Internet Explorer 6 可能是网站设计者需要了解浏览器版本的最惊人的例子,因为它对现代网络标准的处理非常糟糕。
有趣的一面是,IE6 在 2000 年代末/2010 年代初是一个大问题,因为它可怕的标准支持需要开发者进行大量定制,但使用人数居高不下。痛苦催生了努力 等作为这些去杀死它。
基本分析
早在 2000 年代中期/早期,无人机系统是分析用户使用情况的重要工具。使用 Javascript 获取浏览器信息仍处于初级阶段,所以用户代理是你所拥有的最有信息量的东西。由此,您可以看到使用 IE6 和 Firefox 的用户比例(以及您是否应该停止使用某些 HTML 特性)。你还可以看到搜索引擎爬虫访问的频率,以及你是否有真正的人类访客。以现代标准来看很原始,但总比没有好。
机器人检测
对于机器人、网络爬虫和类似的自动化程序来说,通过用户代理字符串识别服务器被认为是一种良好的实践和方式。通常情况下,UA 会包含字符串“bot”“crawler”“spider”,有时还带有 bot 所有者的 URL 作为注释。但普遍获得的具体实施将因 bot 而异。这是因为最终由人类开发人员决定“自我识别”的含义,没有标准的格式。
通过 robots.txt 进行基本的机器人控制
基本的robots . txt文件用于告诉网络爬虫和其他机器人在给定的网络服务器上应该或不应该访问什么。通常,它不会区分不同类型的客户端,但是实际上有一个工具可以指定在某些地方允许哪个用户代理字符串。
显然,这假设机器人 1)阅读并遵循 robots.txt 文件中的指令,2)在用户代理中正确地标识自己(并再次遵循指令)。
原始用户指纹
用户代理字符串可以非常简单,也可以非常长,将许多产品令牌链接在一起。最令人震惊的是安装了许多粗略工具栏的浏览器,每个工具栏通常会将自己的产品令牌附加到 UA 上。
UA 越长,就越有可能偏离浏览器提供的基本默认 UA,并且 UA 越有可能成为特定安装的唯一 UA。在这种假设下,人们可以使用 UAs 作为一种跨时间和 IP 地址伪识别用户的方法。我见过安装了多达 6 个工具栏的浏览器,浏览器+ IP 组合在请求的海洋中几乎是独一无二的,足以在互联网上跟踪某人。
实际上,这种方法只对一小部分人有效,他们安装了足够多的浏览器扩展,或者伪造了足够多的用户应用程序,从一大堆默认字符串中脱颖而出。但即便如此,UA 仍然在涉及使用 Javascript 和 HTML5 方法的更大的用户指纹框架中提供了一些熵。
我将不得不改天进入设备指纹,这是一个巨大的话题。现在,如果你感兴趣的话,你可以看看这个,了解一下当你使用所有可能的技术,从请求头到 Javascript、Flash 和 HTML5 Canvas,会发生什么。
设备和平台检测
如今,许多高级浏览器功能都是通过 Javascript 直接检测到的,这可能是 UA 字符串的主要现代用途,用于确定客户端运行的是什么设备/平台。
2000 年代末,随着智能手机和平板电脑的爆炸,无人机系统获得了新生。虽然 UAs 过去会告诉你 Windows 或 Mac 上的 IE、Firefox 或 Chrome 用户群的百分比,但我们突然开始看到令人兴奋的新字符串,如 iPad、iPhone 和 Android。不是每个人都在使用台式机/笔记本电脑,但我们可以看到他们在使用什么,并进行调整!
这些新设备具有不同的物理屏幕尺寸和物理功能。不再仅仅是“哦,这个浏览器没有 Javascript/HTML5/CSS 支持”。这些信息与设计人员高度相关,特别是在 2010 年初/中期的旧设备上,这些设备没有全高清 1080p 或更高的屏幕分辨率。(2011 年的 iPhone 4S 拥有 960×640 像素的屏幕。)为移动设备设计的网站需要有不同的用户界面,以便更好地用于触摸屏,并且要更小,以便用于 2G、3G 无线网络。
但是,因为用户代理是完全自由形式的,它可能以非常独特的方式对制造商/设备/操作系统/浏览器的每一个排列都有潜在的不同。开发人员应该如何处理这种情况?
当然是通过苦心分析,建立字符串的大型数据库!
幸运的是!有一些开源项目正在解决这类问题(以及进行用户代理字符串分析的付费 API 服务)。一个是 UA-parser 项目,其核心是一个超过 1100 个正则表达式定义的巨大列表***,该列表搜索 UA 中的唯一模式,并将其与制造商和特定设备相关联。这是许多人坚持不懈的努力。*****
ua-parser 有 18 个可用的存储库。在 GitHub 上关注他们的代码。
github.com](https://github.com/ua-parser/)
在单个字符串上运行(多达)一千次 regex 搜索显然是非常耗费资源的,但这是确保尽可能完整地识别一个 UA 字符串的唯一方法,除非手动读取它。
在实践中,您可以通过哈希和缓存来大幅提高速度,因为大多数用户都有相同的默认设置,所以您只需对新字符串运行 regex。这是因为 UA 字符串有一个非常密集的完全匹配的值集群,然后是一个庞大的深奥值长尾。
使用无人机系统会出什么问题?
无人机不必完全遵循 RFC 格式
RFC 在任何方面都没有得到强有力的执行,所以完全有可能遇到不一致的用户代理字符串。我个人见过字符串中放入了像换行符(\n)和 ASCII 空符(\0)这样的无意义字符。Unicode 在技术上也是允许的(因为系统将它视为不透明的字节),但通常是意外的和不常见的。大多数 UA 处理程序仍然假设 UA 是 US-ASCII(如最新 RFC 中所规定的)
通常,这些东西来自于编写机器人的人,他们不熟悉 RFC。或者他们想成为混蛋,试图破坏系统。无论哪种方式,当您大规模处理 UA 字符串时,您的代码将由于格式错误的字符串而中断大量数据,因此要准备好编写异常处理程序来捕获错误的字符串。
无人机不是用户,它们甚至不是唯一的浏览器
用户代理无处不在,所以很多人对它们知之甚少。当这些人试图解释它们并得出错误的结论时,问题就来了。
人们有时认为用户代理以某种方式为他们提供了用户数量。这通常是十年前的过时思维的混合,当时人们通常只有一台联网计算机,或者他们听说过浏览器指纹识别,但不知道对浏览器进行指纹识别需要多少数据点。
显然,这在今天是不正确的。如今,大多数用户全天使用多种设备/浏览器,指纹识别方法非常复杂。唯一 UAs 的数量确实给出了发出请求的唯一类型浏览器的大致数量。但是,即使与 IP 地址相结合,许多人也可以使用多个设备,这些设备都使用完全相同的 UA,并且位于 NAT 之后,共享一个 IP。
我个人不得不使用 UAs 对浏览一个网站的浏览器的最小数量进行粗略的限制,原始点击数是上限。这是因为我们确实没有其他可用的数据。但是我必须非常非常清楚我们测量的是什么。
无人机可能是谎言(欺骗),但通常大多数不是
当人们听说无人机很容易被欺骗时,他们通常担心他们被欺骗了,他们的指标会被关闭。他们经常担心得想太多,去寻找技术上更困难的解决方案。我经常告诉人们不要担心恶搞。
主要原因是绝大多数用户没有动力去摆弄他们的 UA 字符串。他们只是想使用互联网。唯一想操纵他们的用户应用的人通常是极少数编写某种机器人/爬虫的用户。然后,它被分解成几个主要案例:
- UA 是由一个人来做的,而且是人类规模的流量。在每天使用你的网站的成百上千甚至上百万人中,他们最终是微不足道的,因为他们只代表了流量的一小部分。
- UA 是一个小型机器人——这些是小型的学生 scraper 项目,随机的程序员在尝试一些东西。只要它们通常表现良好并且数量很少,它们就不会扭曲你的指标,所以你可以像上面那样忽略它。
- 这是一个垃圾邮件非常多的机器人— 你需要以某种方式解释这种情况。这些通常来自最终用户通常不使用的一小组 IP(如 AWS IP 块或数据中心),您通常可以基于试探法过滤掉这些 IP。当你面对这些类型的机器人时,通常会有问题的迹象(比如你的系统超载,就像是 DDoS 攻击)。
很少需要担心欺骗,除非你运行的东西机器人制造商有滥用的动机(如果这是你的问题,UA 不太可能是解决方案)。我认为有一些边缘情况:比如如果你几乎没有流量,那么机器人将占主导地位,但你也没有理由在这一点上做很多分析。
我们能发现假冒的无人机吗?
太好了。在一些非常特殊的情况下,你可以在谎言中抓住 UA。但不要指望这是一个经常性的事情。
最简单的情况是当有人在欺骗他们的 UA 字符串时出错。他们的 UA 非常显眼,因为没有其他人使用它。它可以是一些简单的事情,比如多加一个空格或标点符号。
类似地,有时欺骗者对用户代理字符串理解不够,将不可能的产品字符串组合在一起。他们也会因超级独特而脱颖而出。
另一种常见的情况是通过 JavaScript 访问浏览器的数据。如果你看到一部 iPhone 的屏幕尺寸与手机本身的物理像素尺寸大相径庭,那么它很可能在谎称自己是一部手机。
在这些有限的方法之后,整个想法开始进入 bot 和欺诈检测技术的领域,这有点超出了这里的范围。*****
消失梯度问题
问题、原因、意义及其解决方案
Title Image // Source
问题:
随着使用某些激活函数的更多层被添加到神经网络,损失函数的梯度接近零,使得网络难以训练。
为什么:
某些激活函数,如 sigmoid 函数,将大的输入空间压缩成 0 到 1 之间的小输入空间。因此,sigmoid 函数输入的大变化会导致输出的小变化。因此,导数变小。
Image 1: The sigmoid function and its derivative // Source
例如,图 1 是 sigmoid 函数及其导数。请注意,当 sigmoid 函数的输入变大或变小时(当|x|变大时),导数变得接近于零。
意义何在:
对于只有几层使用这些激活的浅层网络,这不是一个大问题。然而,当使用更多的层时,它会导致梯度太小而不能有效地进行训练。
使用反向传播找到神经网络的梯度。简单地说,反向传播通过从最后一层一层地移动到初始层来找到网络的导数。根据链式法则,每层的导数沿网络向下相乘(从最后一层到初始层),以计算初始层的导数。
然而,当 n 个隐藏层使用类似 sigmoid 函数的激活时, n 个小导数相乘在一起。因此,当我们向下传播到初始层时,梯度呈指数下降。
小的梯度意味着初始层的权重和偏差不会随着每次训练而有效地更新。由于这些初始层通常对识别输入数据的核心元素至关重要,因此会导致整个网络的整体不准确。
解决方案:
最简单的解决方法是使用其他激活函数,比如 ReLU,它不会引起一个小的导数。
剩余网络是另一种解决方案,因为它们提供直接到早期层的剩余连接。如图 2 所示,残差连接直接将块开头的值 x 加到块的结尾(F(x)+x)。这种剩余连接不经过激活函数,激活函数“挤压”导数,导致块的整体导数更高。
Image 2: A residual block
最后,批量规范化图层也可以解决这个问题。如前所述,当一个大的输入空间被映射到一个小的输入空间,导致导数消失时,问题就出现了。在图 1 中,这在|x|较大时最为明显。批量标准化通过简单地标准化输入来减少这个问题,这样|x|就不会到达 sigmoid 函数的外部边缘。如图 3 所示,它对输入进行了归一化处理,使大部分输入落在绿色区域,这里的导数不会太小。
Image 3: Sigmoid function with restricted inputs
如果您有任何问题或建议,请在下面留下您的评论:)
阅读这些文章了解更多信息:
- https://www . quora . com/什么是消失的渐变问题
- https://en.wikipedia.org/wiki/Vanishing_gradient_problem
- https://towards data science . com/intuit-and-implement-batch-normalization-c 05480333 C5 b
视频搜索引擎——我的计算机视觉之旅
制作视频很容易,但是谁有时间看完呢?我提出一个视频搜索引擎来找到相关的时刻(原型包括在内)。
创作视频内容是我一生的爱好。我记得在中学制作停止动画电影,毕业到 30 分钟。高中和大学的短片。由于我的孩子们,我的“电影”现在更加面向家庭,但我总是在思考新的项目。
当我反思我喜欢做的项目时,我不断回到同一个问题:记录镜头远比理解它容易。想想你手机的相机胶卷。它可能充满了数百个,如果不是数千个,未经编辑的视频,太长,不值得一看。
创作和消费视频之间的不平衡是“典型的现代问题”的一部分,这是由廉价的录制设备甚至更便宜的数字存储设备造成的。快速总结一下,问题是我们可以把 15 个摄像头从 15 个不同的角度对准同一个体育赛事两个小时,产生 30 个小时的未剪辑视频。但是没有人有 30 个小时来观看这些内容。
我们需要的是一种从视频内容中提取有趣瞬间的方法。
让我们考虑一个更实际的例子。你当地的零售商可能每个月会录制数千小时的视频来识别商店扒手。然而,尽管有明显的商业价值,这个视频是未经编辑的,冗长的,非常不值得一看。需要的是一个视频的“搜索引擎”。以图像或视频“关键时刻”作为搜索词输入,输出相关视频片段作为搜索结果的系统。
这个问题让我想起了 2004 年夏天在我们国家的一个顶级 R&D 实验室——IBM t . j .沃森研究中心的实习。在那里,我看到了计算机视觉项目的早期应用,例如在进出停车场的汽车上绘制边界框。
Bounding boxes identifying cars and persons
我在 IBM 工作已经 15 年了,但我从未忘记那种神奇的感觉——看着屏幕上闪烁着汽车和人周围的盒子。当时,这样的输出是机器学习和计算能力的突破。我现在可以探索是否可以建立通用的视频搜索引擎。
探索:视频搜索引擎
我的目标是建立一个上述视频搜索引擎的原型。该系统将视频记录乒乓球比赛,当球在比赛时提取视频剪辑,并只向用户显示相关的剪辑。第一个关键结果是使用便携式设备视频记录乒乓球比赛,并将视频发送到云存储进行分析。第二个关键结果是训练一个目标检测模型,该模型发现一个乒乓球在运动。第三个关键结果是将提取的视频剪辑输出到 web UI。
我对这个项目施加了各种限制,让它保持在晚上和周末的范围内。例如,我不打算在 Raspberry Pi 上构建一个完全包含的对象检测系统。相反,我将使用 AWS 来检索存储的视频剪辑,通过对象检测器处理它们,并将结果返回给 web UI。视频的实时处理也超出了这个项目的范围。也就是说,这些限制为这个项目带来了令人兴奋的未来机遇。
在便携式设备上录制视频
到目前为止,我已经完成了第一个关键结果的 70%,通过使用 Raspberry Pi 录制视频内容,并将视频发送到 AWS S3 以供未来分析。
从一开始,我就想象树莓 Pi(带有 Pi 相机模块)将是探索便携式视频捕捉的理想选择。我了解到有很多选择,包括 IP 摄像头、网络摄像头等等。但是事后来看,我很高兴我选择了 Raspberry Pi,因为它的外形、记录良好的代码和热情的社区。
一旦我启动了 Raspberry Pi,我就配置了一个 SSH 环境,这样我就可以从我的笔记本电脑上执行代码来捕捉图像和视频。
然后我不得不把视频发给 S3 AWS。我用 Python 做了一个简单的设计:(1)从 Pi 摄像机打开一个视频流,(2)每两秒钟向 S3 发送一帧。
import imutils
from imutils.video import VideoStream
from imutils.video import FPS
import boto3
import cv2
import datetime
import time
from decimal import Decimal
import uuid
import json
import pytz
from pytz import timezone
import os# init interface to AWS S3 via Python
s3_client = boto3.client('s3',
aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"])
# init where to send the captured frames
s3_bucket = "img-from-raspi-for-web"
s3_key_frames_root = "frames/"# init video stream from Raspberry Pi Camera
print("[INFO] starting video stream...")
vs = VideoStream(src=0).start()
# vs = VideoStream(usePiCamera=True).start()
time.sleep(2.0) # warm up the sensordef convert_ts(ts, selected_timezone):
*# Converts a timestamp to the configured timezone. Returns a localized datetime object
* tz = timezone(selected_timezone)
utc = pytz.utc
utc_dt = utc.localize(datetime.datetime.utcfromtimestamp(ts))
localized_dt = utc_dt.astimezone(tz)
return localized_dt
# loop over frames from the video file stream
while True:
# grab the frame from the video stream and resize it
frame = vs.read()
approx_capture_ts = time.time()
if frame is None:
continue # useful to skip blank frames from sensor
frame = imutils.resize(frame, width=500)
ret, jpeg = cv2.imencode('.jpg', frame)
photo = jpeg.tobytes()
# bytes are used in future section to call Amazon Rekognition API
now_ts = time.time()
now = convert_ts(now_ts, "US/Eastern")
year = now.strftime("%Y")
mon = now.strftime("%m")
day = now.strftime("%d")
hour = now.strftime("%H")
# build s3_key using UUID and other unique identifiers
s3_key = (s3_key_frames_root + '{}/{}/{}/{}/{}.jpg').format(year, mon, day, hour, frame_id)
# Store frame image in S3
s3_client.put_object(
Bucket=s3_bucket,
Key=s3_key,
Body=photo
)
time.sleep(2.0) # essentially, a frame rate of 0.5
图像开始出现在我的 S3 桶中,所以我开始为这个项目设计数据库。
我的设计将每幅图像、其时间戳和每幅图像的预测结果存储在 NoSQL 表中。稍后,我将查询该数据库中的预测,并获取相应的时间戳,以将视频剪辑成相关的剪辑。
现在,我为预测设置了一个存根,依靠 AWS Rekognition API 来检测对象。下面是我如何将数据保存到 DynamoDB 表中的:
def detect_labels_local_file(photo):
response = rekog_client.detect_labels(Image={'Bytes': photo},
MaxLabels=5,
MinConfidence=60.0)
for label in response['Labels']:
print (label['Name'] + ' : ' + str(label['Confidence']))
return response### In the while loop ###
rekog_response = detect_labels_local_file(photo) # Persist frame data in dynamodb
ddb_item = {
'frame_id': frame_id,
'processed_timestamp': now_ts, # beware of putting floats into DDB. Had to use json.loads as a workaround
'approx_capture_timestamp': approx_capture_ts,
'rekog_labels': rekog_response['Labels'],
'rekog_orientation_correction':
rekog_response['OrientationCorrection']
if 'OrientationCorrection' in rekog_response else 'ROTATE_0',
'processed_year_month': year + mon, # To be used as a Hash Key for DynamoDB GSI
's3_bucket': s3_bucket,
's3_key': s3_key
}
ddb_data = json.loads(json.dumps(ddb_item), parse_float=Decimal)
ddb_table.put_item(Item=ddb_data)
成功!我有一个 NoSQL 表,它引用了 S3 图像、时间戳及其相应的预测:
Items in DynamoDB table, showing Rekognition API results for captured frames from Pi Camera
构建乒乓球检测的机器学习模型
在摄像头对准我的情况下,AWS Rekognition 以 99.13%的置信度检测到了一个‘人’。但是,Rekognition 能否检测到正在运动的乒乓球来帮助我实现第二个关键结果呢?
遗憾的是,没有。在测试了许多乒乓球图像后,我发现 Rekognition 在检测场景方面的表现令人钦佩——例如标记属于“乒乓球”的图像。但是在寻找乒乓球方面,Rekognition 表现不佳。在大多数情况下,它根本没有将球区分为可识别的物体。当它找到球时,它把它标为“月亮”😎
Rekognition results for an image of a ping pong match
使用 Rekognition API 很方便,但是对于我的项目来说有所限制。幸运的是,如果你想定制自己的模型,亚马逊提供了 SageMaker 对象检测 API 。
我从一场乒乓球比赛的视频开始。以下是一些示例框架:
准备和标记数据
我的第一个任务是用乒乓球标记视频帧,以建立一个训练、验证和测试数据集。 FFmpeg 库有助于将视频转换成我可以标记的图像:
# from Terminal after installing the ffmpeg library# Get one frame to identify the area I care about
ffmpeg -i tt-video-1080p.mp4 -ss 00:00:59.000 -vframes 1 thumb.jpg
# I found an area I care about in a fixed-position video feed using a photo-editing program: a 512x512 image with a top-left corner at x:710, y:183y
ffmpeg -i tt-video-1080p.mp4 -filter:v "crop=512:512:710:183” cropped.mp4# output the video as a series of jpg images @ 10 frames per second
ffmpeg -i cropped.mp4 -vf fps=10 thumb%04d.jpg -hide_banner
上面的片段在我的机器上生成了成千上万的图像。下一步是给乒乓球添加“边界框”。
有许多服务为你执行这项艰巨的任务,但我选择亲自标记这些图像,以便更深入地理解和欣赏计算机视觉。我求助于 RectLabel,这是一个图像注释工具,用于标记图像以进行边界框对象检测和分割:
我在这项任务上花了大约四个小时,平均每分钟 8.3 个标签,得到了 2000 个带标签的图像。这是令人麻木的工作。
在我的标记工作进行到一半的时候,我想知道在给定 JPEG 压缩伪像和乒乓球上的运动模糊的情况下,紧密还是松散的边界框会更好地平衡模型精度和模型泛化。在打电话给一个朋友、保罗·布兰克利和咨询网络之后,我了解到“边界框通常紧紧围绕着图像中的每个[对象]”,因为:
如果没有精确绘制的边界框,整个算法都会受到影响,从而导致无法准确识别[对象]。这就是为什么质量检查和确保高度重视每个边界框的准确性,从而产生一个强大的人工智能引擎。
如果我不得不再次做这个项目,我会使用无损图像格式(*。png)并绘制更紧密的边界框来改善我的训练数据。然而我认识到这种优化不是免费的。当我开始用更紧的边界框标记图像时,我的平均标记速度下降了大约 50%。
一旦我完成了对图像的标记,RectLabel 就将数据输出到一个 JSON 文件中,该文件适合计算机视觉任务。以下是输出示例:
{"images":[
{"id":1,"file_name":"thumb0462.png","width":0,"height":0},
{"id":2,"file_name":"thumb0463.png","width":0,"height":0},
# ...
{"id":4582,"file_name":"thumb6492.png","width":0,"height":0}],"annotations":[
{"area":198,"iscrowd":0,"id":1,"image_id":1,"category_id":1,"segmentation":[[59,152,76,152,76,142,59,142]],"bbox":[59,142,18,11]},
{"area":221,"iscrowd":0,"id":2,"image_id":2,"category_id":1,"segmentation":[[83,155,99,155,99,143,83,143]],"bbox":[83,143,17,13]},
# ... {"area":361,"iscrowd":0,"id":4,"image_id":4582,"category_id":1,"segmentation":[[132,123,150,123,150,105,132,105]],"bbox":[132,105,19,19]},"categories":[{"name":"pp_ball","id":1}]
}
然后,我创建了一个函数,按照 Amazon SageMaker 输入通道的预期,将注释分成训练和验证文件夹。如果你正在遵循我的准则,请注意来自 Ryo Kawamura 的重要提示:
虽然 COCO JSON 文件中的“category_id”从 1 开始,但 Amazon SageMaker JSON 文件中的“class_id”从 0 开始。
import json
import osdef fixCategoryId(category_id):
return category_id - 1;with open(file_name) as f:
js = json.load(f)
images = js['images']
categories = js['categories']
annotations = js['annotations']
for i in images:
jsonFile = i['file_name']
jsonFile = jsonFile.split('.')[0] + '.json'line = {}
line['file'] = i['file_name']
line['image_size'] = [{
'width': int(i['width']),
'height': int(i['height']),
'depth': 3
}]
line['annotations'] = []
line['categories'] = []
for j in annotations:
if j['image_id'] == i['id'] and len(j['bbox']) > 0:
line['annotations'].append({
'class_id': fixCategoryId(int(j['category_id'])),
'top': int(j['bbox'][1]),
'left': int(j['bbox'][0]),
'width': int(j['bbox'][2]),
'height': int(j['bbox'][3])
})
class_name = ''
for k in categories:
if int(j['category_id']) == k['id']:
class_name = str(k['name'])
assert class_name is not ''
line['categories'].append({
'class_id': fixCategoryId(int(j['category_id'])),
'name': class_name
})
if line['annotations']:
with open(os.path.join('generated', jsonFile), 'w') as p:
json.dump(line, p)jsons = os.listdir('generated')
print ('There are {} images that have annotation files'.format(len(jsons)))
接下来,我按照 SageMaker 端点:/train、/validation、/train_annotation 和/validation_annotation 的要求,将文件移动到一个包含四个文件夹的亚马逊 S3 桶中。我在训练和验证文件上使用了 70%的分割,并混洗了数据:
import shutil
import randomnum_annotated_files = len(jsons)
train_split_pct = 0.70
num_train_jsons = int(num_annotated_files * train_split_pct)
random.shuffle(jsons) # randomize/shuffle the JSONs to reduce reliance on *sequenced* frames
train_jsons = jsons[:num_train_jsons]
val_jsons = jsons[num_train_jsons:]
#Moving training files to the training folders
for i in train_jsons:
image_file = './images/'+i.split('.')[0]+'.png'
shutil.move(image_file, './train/')
shutil.move('./generated/'+i, './train_annotation/')
#Moving validation files to the validation folders
for i in val_jsons:
image_file = './images/'+i.split('.')[0]+'.png'
shutil.move(image_file, './validation/')
shutil.move('./generated/'+i, './validation_annotation/')
### Upload to S3
import sagemaker
from sagemaker import get_execution_role
role = sagemaker.get_execution_role()
sess = sagemaker.Session()
from sagemaker.amazon.amazon_estimator import get_image_uri
training_image = get_image_uri(sess.boto_region_name, 'object-detection', repo_version="latest")
bucket = 'pp-object-detection' # custom bucket name.
# bucket = sess.default_bucket()
prefix = 'rect-label-test'
train_channel = prefix + '/train'
validation_channel = prefix + '/validation'
train_annotation_channel = prefix + '/train_annotation'
validation_annotation_channel = prefix + '/validation_annotation'
sess.upload_data(path='train', bucket=bucket, key_prefix=train_channel)
sess.upload_data(path='validation', bucket=bucket, key_prefix=validation_channel)
sess.upload_data(path='train_annotation', bucket=bucket, key_prefix=train_annotation_channel)
sess.upload_data(path='validation_annotation', bucket=bucket, key_prefix=validation_annotation_channel)
s3_train_data = 's3://{}/{}'.format(bucket, train_channel)
s3_validation_data = 's3://{}/{}'.format(bucket, validation_channel)
s3_train_annotation = 's3://{}/{}'.format(bucket, train_annotation_channel)
s3_validation_annotation = 's3://{}/{}'.format(bucket, validation_annotation_channel)
训练模型
在下一步中,我创建了一个具有某些超参数的 SageMaker 对象检测器,例如使用“resnet-50”算法的和一个类(我的乒乓球)以及大小为 512x512 像素的图像。
s3_output_location = 's3://{}/{}/output'.format(bucket, prefix)od_model = sagemaker.estimator.Estimator(training_image, role, train_instance_count=1, train_instance_type='ml.p3.2xlarge', train_volume_size = 50, train_max_run = 360000, input_mode = 'File', output_path=s3_output_location, sagemaker_session=sess)od_model.set_hyperparameters(base_network='resnet-50',
use_pretrained_model=0,
num_classes=1,
mini_batch_size=15,
epochs=30,
learning_rate=0.001,
lr_scheduler_step='10',
lr_scheduler_factor=0.1,
optimizer='sgd',
momentum=0.9,
weight_decay=0.0005,
overlap_threshold=0.5,
nms_threshold=0.45,
image_shape=512,
label_width=600,
num_training_samples=num_train_jsons)
然后,我为对象检测器设置训练/验证位置,称为。拟合函数,并将模型部署到一个端点:
train_data = sagemaker.session.s3_input(s3_train_data, distribution='FullyReplicated', content_type='image/png', s3_data_type='S3Prefix')validation_data = sagemaker.session.s3_input(s3_validation_data, distribution='FullyReplicated', content_type='image/png', s3_data_type='S3Prefix')train_annotation = sagemaker.session.s3_input(s3_train_annotation, distribution='FullyReplicated', content_type='image/png', s3_data_type='S3Prefix')validation_annotation = sagemaker.session.s3_input(s3_validation_annotation, distribution='FullyReplicated', content_type='image/png', s3_data_type='S3Prefix')data_channels = {'train': train_data, 'validation': validation_data, 'train_annotation': train_annotation, 'validation_annotation':validation_annotation}od_model.fit(inputs=data_channels, logs=True)object_detector = od_model.deploy(initial_instance_count = 1,
instance_type = 'ml.m4.xlarge')
鉴于我只有 2000 张图片,我的亚马逊盒子(ml.p3.2xlarge)花了大约 10 分钟来训练模型。部署端点通常需要更长的时间,并且测试模型的预期是令人痛苦的!
终于,真相大白的时刻到了。我通过传递一个它从未见过的 PNG 文件来调用我的模型:
file_with_path = 'test/thumb0695.png'
with open(file_with_path, 'rb') as image:
f = image.read()
b = bytearray(f)
ne = open('n.txt', 'wb')
ne.write(b)results = object_detector.predict(b)
detections = json.loads(results)
print(detections)
我得到了这样的输出:
[1.0, 0.469, 0.566, 0.537, 0.605, 0.595]
下面是如何根据 AWS SageMaker 解释这个输出:
这些对象数组中的每一个都由六个数字组成。第一个数字是预测的类标签。第二个数字是检测的相关置信度得分。最后四个数字代表边界框坐标[xmin,ymin,xmax,ymax]。这些输出边界框角索引由整体图像尺寸归一化。请注意,这种编码不同于输入使用的编码。json 格式。例如,在检测结果的第一个条目中,0.3088374733924866 是边界框的左坐标(左上角的 x 坐标)作为整体图像宽度的比率,0.07030484080314636 是边界框的上坐标(左上角的 y 坐标)作为整体图像高度的比率,0.7110607028007507 是右坐标(右坐标
凉爽的😏
可视化结果
坦白地说,我需要一些更实际的东西来欣赏结果。所以我用这个函数来可视化每个预测:
def visualize_detection(img_file, dets, classes=[], thresh=0.6):
import random
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img = mpimg.imread(img_file)
plt.imshow(img)
height = img.shape[0]
width = img.shape[1]
colors = dict()
for det in dets:
(klass, score, x0, y0, x1, y1) = det
if score < thresh:
continue
cls_id = int(klass)
if cls_id not in colors:
colors[cls_id] = (random.random(), random.random(), random.random())
xmin = int(x0 * width)
ymin = int(y0 * height)
xmax = int(x1 * width)
ymax = int(y1 * height)
rect = plt.Rectangle((xmin, ymin), xmax - xmin,
ymax - ymin, fill=False,
edgecolor=colors[cls_id],
linewidth=3.5)
plt.gca().add_patch(rect)
class_name = str(cls_id)
if classes and len(classes) > cls_id:
class_name = classes[cls_id]
plt.gca().text(xmin, ymin - 2,
'{:s} {:.3f}'.format(class_name, score),
bbox=dict(facecolor=colors[cls_id], alpha=0.5),fontsize=12, color='white')
plt.show()# then I used the function like this:
object_categories = ['pp_ball']
threshold = 0.40
visualize_detection(file_name_of_image, detections['prediction'], object_categories, threshold)
当我看到这个输出时,我兴奋而欣慰地发抖:
我花了一个小时用它从未见过的各种测试图像敲打模型。有些是伟大的预言。其他的则很傻,比如这个模型把球员制服上的白点当成了乒乓球🙄
幸运的是,我能够通过将置信度阈值提高到 0.40 来消除大多数误报。
未来方向
我对目前的结果很满意,但是未来的工作需要评估和优化我的模型。例如,我打算计算平均精度(mAP) 作为性能指标。那个地图度量将帮助我评估不同的优化,例如添加更多的训练图像,尝试迁移学习,以及尝试其他深度学习拓扑。我将把这些任务留给我的 2020 年路线图(和未来的帖子)。
我也很高兴能够在 2020 年实现我的第三个关键成果——通过网络用户界面向用户展示相关的视频剪辑。当关键结果完成后,我将在真实环境中测试整个设置:
- 用我的树莓皮视频记录一场现场乒乓球比赛
- 将视频导出到 S3 的图像帧中
- 使用对象检测器模型来识别球何时在比赛中
- 当球在比赛时存储时间戳
- 向用户提供网络用户界面
- 允许用户将视频过滤到球正在运动的瞬间
请继续关注这个方向的更多学习和发展。
结束语
数据科学家普遍认为
算法便宜;
数据为王。
这个项目让我深深体会到这一真理。事实上,在 AWS SageMaker 上,改变深度学习拓扑结构的能力是微不足道的。然而结果并没有明显的变化。我还以最小的努力在另一个模型中利用了迁移学习。同样,结果也好不到哪里去。然后,我想起了为我的项目收集和标记图像的艰苦工作…
当我将与模型相关的工作和与数据相关的工作在努力程度和跨项目适用性方面进行比较时,我感到困惑。例如,在深度学习拓扑结构之间切换相对容易,许多项目可以在各种计算机视觉任务中利用这些拓扑结构。相比之下,我的图像标签工作需要大量的努力,可能只会对我的项目有益。
面对这一现实,我对通用视频搜索引擎的可行性感到有点悲观,这种搜索引擎可以从用户任意输入的(1)视频和(2)图像作为搜索词中产生结果。诚然,一个特定用途的视频搜索引擎指日可待。但重要的、不平凡的工作还在后面,以探索一个模型如何能够根据几个图像示例进行归纳,以检测用户想要找到的任何内容。
在这方面,这是一个有趣的学习年!
非常感谢 Sandeep Arneja 、 Paul Blankley 、 Ryo Kawamura 和 Thea Zimnicki 对这个项目的反馈和贡献。
AlexNet、VGGNet、ResNet 和 Inception 之间的区别
在本教程中,我将快速浏览四个著名的 CNN 架构的细节,并通过解释它们的 W3H(何时、为什么、什么和如何)来说明它们之间的区别
AlexNet
什么时候?
- 艾伦·图灵年
- 人人享有可持续能源年
- 伦敦奥运会
为什么? AlexNet 的诞生源于提高 ImageNet 挑战赛成绩的需要。这是首批 深度 卷积网络之一,在 2012 ImageNet LSVRC-2012 挑战赛中获得了相当高的准确率,准确率为 84.7%,相比之下,第二名的准确率为 73.8%。使用卷积层和感受野探索了图像帧中空间相关性的思想。
**什么?**网络由 5 个卷积(CONV)层和 3 个全连接(FC)层组成。使用的激活是整流线性单元(ReLU)。网络中每一层的结构细节可以在下表中找到。
Alexnet Block Diagram (source:oreilly.com)
该网络共有 6200 万个可训练变量
**如何?**网络的输入是一批大小为 227×227×3 的 RGB 图像,并输出 1000×1 的概率向量,每个向量对应一个类别。
- 执行数据扩充以减少过拟合。这种数据扩充包括镜像和裁剪图像,以增加训练数据集中的变化。网络在第一、第二和第五个 CONV 图层之后使用重叠的 max-pooling 图层。重叠的最大池层只是跨度小于窗口大小的最大池层。使用 3×3 最大池层,步幅为 2,因此产生重叠感受野。这种重叠将前 1 名和前 5 名误差分别提高了 0.4%和 0.3%。
- 在 AlexNet 之前,最常用的激活函数是 sigmoid 和 *tanh。*由于这些函数的饱和性质,它们遭受消失梯度(VG)问题,并使网络难以训练。AlexNet 使用的是 ReLU 激活函数,不会遇到 VG 问题。原始论文显示,具有 ReLU 的网络实现 25%的错误率比具有 tanh 非线性的相同网络快大约 6 倍。
- 尽管 ReLU 有助于解决梯度消失的问题,但由于其不受限制的性质,学习到的变量可能会变得不必要的高。为了防止这种情况,AlexNet 引入了本地响应标准化(LRN)。LRN 背后的想法是在放大受激神经元的像素邻域中执行归一化,同时抑制周围的神经元。
- AlexNet 还通过使用丢弃层来解决过拟合问题,其中在训练期间以 p=0.5 的概率丢弃连接。虽然这通过帮助网络摆脱坏的局部最小值而避免了网络的过度拟合,但是收敛所需的迭代次数也加倍了。
VGGNet:
什么时候?
- 国际家庭农业和结晶学年
- 首次机器人登陆彗星
- 罗宾·威廉姆斯去世的那一年
为什么? VGGNet 的诞生是为了减少 CONV 层中的参数数量和提高训练时间。
**什么?**VGGNet 有多个变种(VGG16,VGG19 等。)不同之处仅在于网络的总层数。VGG16 网络的结构细节如下所示。
VGG16 Block Diagram (source: neurohive.io)
VGG16 共有 1.38 亿个参数。这里需要注意的重要一点是,所有 conv 内核的大小都是 3x3,maxpool 内核的大小是 2x2,步长为 2。
**如何?**固定大小内核背后的想法是,Alexnet (11x11,5x5,3x3)中使用的所有可变大小卷积内核都可以通过使用多个 3x3 内核作为构建块来复制。这种复制是根据核仁覆盖的感受野进行的。
让我们考虑下面的例子。假设我们有一个大小为 5x5x1 的输入层。实施内核大小为 5x5 且跨距为 1 的 conv 图层将产生 1x1 的输出要素地图。如下所示,通过使用跨距为 1 的两个 3x3 conv 图层,可以获得相同的输出要素地图
现在让我们看看需要训练的变量的数量。对于 5×5 conv 层过滤器,变量的数量是 25。另一方面,内核大小为 3x3 的两个 conv 层总共有 3x3x2=18 个变量(减少了 28%)。
同样,一个 7x7 (11x11) conv 层的效果可以通过以一个步长实施三(五)个 3x3 conv 层来实现。这将可训练变量的数量减少了 44.9% (62.8%)。可训练变量数量的减少意味着更快的学习和更强的抗过拟合能力。
雷斯内特
什么时候?
- 引力波的发现
- 国际土壤和光基技术年
- 火星电影
**为什么?**神经网络臭名昭著,因为当它存在时,无法找到更简单的映射。
- 例如,假设我们有一个完全连接的多层感知器网络,我们想在输入等于输出的数据集上训练它。这个问题最简单的解决方案是让所有的权重等于 1,所有的隐藏层的偏差为零。但是当使用反向传播来训练这样的网络时,学习了相当复杂的映射,其中权重和偏差具有大范围的值。
- 另一个例子是给现有的神经网络增加更多的层。假设我们有一个网络***【f(x),它在数据集上达到了 n% 的精度。现在将更多层添加到这个网络 g(f(x)) 应该至少具有 n% 的精度,即在最坏的情况下 g(。) 应该是产生与【f(x)***相同精度的相同映射,如果不是更多的话。但不幸的是,事实并非如此。实验表明,网络层数越多,精度越低。
- 上述问题的发生是因为消失梯度问题。随着 CNN 越做越深,当反向传播到初始层时,导数的值几乎变得微不足道。
ResNet 通过引入两种类型的“快捷连接”来处理这个网络:身份快捷方式和投影快捷方式。
**什么?**resnet XX 架构有多个版本,其中“XX”表示层数。最常用的是 ResNet50 和 ResNet101。自从消失梯度问题被解决后(在如何解决的部分有更多的介绍),CNN 开始越陷越深。下面我们介绍 ResNet18 的结构细节
Resnet18 有大约 1100 万个可训练参数。它由 CONV 层和大小为 3x3 的滤镜组成(就像 VGGNet 一样)。在整个网络中只使用两个池层,一个在网络的起点,另一个在网络的终点。每两个 CONV 层之间都有身份联系。实线箭头表示输入和输出维度相同的标识快捷方式,而虚线箭头表示维度不同的投影连接。
**如何?**如前所述,ResNet 架构利用快捷连接解决渐变消失问题。ResNet 的基本构造块是在整个网络中重复的剩余块。
Residual Block — Image is taken from the original paper
网络不是从 x →F(x)学习映射,而是从 x → F(x)+G(x)学习映射。当输入 x 和输出 F(x)的维数相同时,函数 G(x) = x 是恒等函数,这种快捷连接称为恒等连接。通过在训练期间将中间层中的权重归零来学习相同的映射,因为将权重归零比将它们推到 1 更容易。
对于 F(x)的维数不同于 x 的情况(由于在它们之间的 CONV 层中步长> 1),实现投影连接而不是单位连接。函数 G(x)将输入 x 的维数改变为输出 F(x)的维数。在原始论文中考虑了两种映射。
- **不可训练的映射(填充)😗*输入 x 简单地用零填充,以使维数匹配 F(x)的维数
- 可训练映射(Conv 层) : 1x1 Conv 层用于映射 x 到 G(x)。从上表可以看出,在整个网络中,空间维度保持相同或减半,深度保持相同或加倍,并且每个 conv 层之后的宽度和深度的乘积保持相同,即 3584。通过分别使用步长 2 和多个这样的滤波器,1×1 conv 层被用于空间维度的一半和深度的两倍。1x1 conv 层的数量等于 F(x)的深度。
初始状态:
什么时候?
- 国际家庭农业和结晶学年
- 首次机器人登陆彗星
- 罗宾·威廉姆斯去世的那一年
**为什么?**在图像分类任务中,显著特征的大小可以在图像帧内显著变化。因此,决定一个固定的内核大小是相当困难的。对于分布在图像的大面积上的更全局的特征,较大的核是优选的,另一方面,较小的核在检测分布在图像帧上的区域特定的特征方面提供良好的结果。为了有效地识别这种可变大小的特征,我们需要不同大小的核。这就是《盗梦空间》所做的。它不是简单地在层数上更深入,而是走得更宽。不同大小的多个内核在同一层中实现。
**什么?**初始网络架构由以下结构的若干初始模块组成
Inception Module (source: original paper)
每个初始模块由四个并行的操作组成
- 1x1 conv 层
- 3x3 conv 层
- 5x5 conv 层
- 最大池化
黄色显示的 1x1 conv 块用于深度缩减。四个并行操作的结果随后在深度方向上连接在一起,形成滤波器连接模块(绿色)。《盗梦空间》有多个版本,最简单的一个是 GoogLeNet。
如何? Inception 增加了网络空间,通过训练可以从中选择最佳网络。每个 inception 模块都可以捕获不同层次的显著特征。全局要素由 5x5 conv 层捕获,而 3x3 conv 层易于捕获分布式要素。max-pooling 操作负责捕捉邻域中突出的低级要素。在给定的级别上,所有这些特征都被提取并连接起来,然后再传送到下一层。我们让网络/训练来决定哪些特征具有最大的价值和权重。假设如果数据集中的图像富含全局特征而没有太多低级特征,那么与 5×5 conv 内核相比,训练的初始网络将具有非常小的对应于 3×3 conv 内核的权重。
摘要
在下表中,这四个 CNN 根据其在 Imagenet 数据集上的前 5 名准确度进行排序。还可以看到向前传递所需的可训练参数和浮点运算(FLOP)的数量。
可以进行几种比较:
- AlexNet 和 ResNet-152 都有大约 60M 的参数,但在它们的前 5 名精度方面有大约 10%的差异。但是训练 ResNet-152 需要大量的计算(大约是 AlexNet 的 10 倍),这意味着需要更多的训练时间和精力。
- 与 ResNet-152 相比,VGGNet 不仅参数和 FLOP 数量更多,而且精度也有所下降。训练精度降低的 VGGNet 需要更多的时间。
- 训练一个 AlexNet 和训练 Inception 花的时间差不多。内存需求减少了 1/10,精确度提高了约 9%
奖金:
可以在下面的链接中找到这个主题和机器学习中许多其他重要主题的紧凑备忘单
ML 面试的视觉备忘单(www.cheatsheets.aqeel-anwar.com)
medium.com](https://medium.com/swlh/cheat-sheets-for-machine-learning-interview-topics-51c2bc2bab4f)
如果这篇文章对你有帮助,欢迎鼓掌、分享和回复。如果想了解更多关于机器学习和数据科学的知识,请关注我@Aqeel an war或者在LinkedIn上与我联系。
实时数据的警告:当每一秒都很重要
一级和二级金融市场数据馈送介绍
汇总的一分钟价格和交易量信息提供了有价值的信息。虽然对于摇摆操作和某些日内策略/方法来说,这已经足够了,但是额外的一级和二级数据可以改善执行和退出决策,特别是在日内操作中。
我用这些信息进行自主交易,结果很有希望。我还开始研究处理和解析数据的复杂性(比常规的 OHLC 蜡烛复杂一到两个数量级)。我这样做是出于量化的考虑。
这让我对不同的实时数据馈送规范有了更好的、结构合理的理解,我在这篇文章中分享了这些信息。
我简要解释了不同交易所和市场数据供应商提供的不同级别的实时数据的含义,包括对它们的用途的快速分析以及它们在标准交易平台和定制量化软件中的使用示例。对一级和二级都进行了分析,还包括了对三级的介绍。
什么是一级
第一级增加了操作员提供最新买卖报价和已执行分笔成交点交易的可用信息。这在业内通常被称为*“账面净值”——为最新的买卖报价——以及“磁带”或“时间&销售”——为已执行订单——。*这两种信息结合在一起,可以洞察短期看涨或看跌压力,并构成构建交易量概况和订单流信息的基本构件。
可以获得洞察力,因为价格需要填充订单的买方以启动看涨趋势,或者,它需要填充订单的买方以启动看跌趋势。当订单的一方出现明显的趋势或压力时,它可能会被解读为供应超过需求或相反的情况。
“Time and sales” is the most iconic view of Level I Data. Traded orders can be seen including the information on where they were traded (either at the Bid or Ask side). Continuous trading at one of the sides will often lead price to initiate a bullish or bearish course of action.
虽然一些作者将这种压力称为“购买量”或“T2”【销售量】,但我更倾向于避免使用这样的术语,因为每一个买家都有一个卖家,而且不存在“T4”的买家比卖家多的情况,因此也就不存在“*购买量”的情况。这些术语对新来者具有误导性,因为比你想象的更多的人并不完全理解在受监管的市场中订单实际上是如何完成的,通过使用这些术语,他们一直认为交易量可以分为购买和销售,但事实并非如此。这些作者想要通过这些“购买/销售数量”术语来说明的是,已完成的订单正在挤压供应或需求,并且在失去平衡和另一次初始价格运动发生之前,一方可能会赢得短期战斗。其实不同的是“愿意卖”的人数和“愿意买”的人数。*在这种情况下,两个数字明显不同,因为订单未被执行,强烈的不对称分布可能意味着价格可能反转,或者至少在某一价格水平存在强大的阻力或支撑(未执行订单的信息是二级数据馈送的一部分)。理解这些概念很重要,因为它们是可以从一级(和二级)数据中提取的关键知识。
作为对一级数据的最后说明,我列举了潜在的用例:
- 测试平均持有时间更短的日内策略——刷单或简单的非常短期的日内策略,
- 为进场和出场寻找更好的交易执行时机,
- 过滤/确认由其他方法触发的交易,
- 启用订单流分析。
什么是二级
第二级用数据丰富了第一级,包括订单簿中一定深度的数据。这意味着您需要考虑关于已提交但尚未执行的未决订单的信息。这些信息是实时更新的,有助于发现高流动性区域,这些区域可能被解读为短期支撑或阻力位。
与级别 I 相比,级别 II 大大增加了解析需求,因为需要实时更新和解析特定范围的报价点(称为书的深度),而不是仅处理一个报价点价格级别。
Level II provides the ability to visualise in real-time pending orders in the book. Most software packages usually aggregate a certain amount of historical volume at each tick providing information on high liquidity ticks/areas. Imbalance and balance, as defined by Market Profile theory can be therefore be identified.
这些术语的命名和范围因来源不同而有所不同,但通常都是如此处描述的那样。无论如何,重要的一点是理解底层的概念。
为什么一级和二级信息对小型操作者的日内操作有价值
这些信息允许识别大型交易(可能是机构交易),并且如上所述,对短期熊市/牛市压力有一个估计。这些信息对你的赢/赔率有多大的影响是值得怀疑的。我的直觉是,如果掌握得当,这一比例可以在 10%到 20%之间,但我缺乏实际数字——它们很难量化。这个论断来自于我对实际交易者的观察,当他们成功地将订单流整合到他们现有的方法中时,他们有所改进。
我还认为,这只对真正的短期势头有效,这一信息需要得到其他操作标准的支持。作为独立来源的信息更有可能表现为噪音,而不是有价值的数据。同样,这来自我作为全权交易者非常有限的经验,而不是来自任何定量分析研究。我确信,有些操作者专门利用这些信息赚钱,因为在交易中,我一直知道,每一条信息/技术都有专门的参与者,绝对的陈述——这行得通,这行不通——通常是错误的。
三级:保留给机构公司
第三级包括直接来自交易所的所有原始信息,因此它包含关于给定交易所的每一条信息。处理这种数据馈送意味着处理真正高的吞吐量和与交换机的直接连接。某些交易所——比如法兰克福——只将这种联系作为批准程序的一部分保留给机构投资者,而其他交易所——比如芝加哥——不施加任何限制,只要你有钱*(我想是德国人对美国人的心态)*。无论如何,开发三级数据所需的相关成本、资本和基础设施,让它变得毫无用处,除非你是 HFT 的机构投资者,或者正与一家机构合作。
使用的协议是专有的,通常在不同的交换机之间共享。主要有 GLOBEX(CME 使用)和 T7(EUREX 使用)。使用的协议分别是 MDP3.0 和 EMDI/EOBI/RDI/ETI(最新的是 T7 的接口,而不是实际的协议)。所有交换 APIs 协议通常都利用 FIX 协议,这是高端实时金融数据的全球标准。
Level III data requires a direct connection with the Exchange. In the photo the European T7 EUREX System architecture.
这些系统还被出售给世界各地其他规模较小的交易所,这些交易所没有坚实的商业案例来建立自己的电子交易基础设施。其他较小的交易所使用它们自己的电子交易系统,虽然它们之间不兼容,但它们通常都共享相同的原则和底层协议。这些系统和协议仅在机构基础设施的环境中有用,较小的参与者可以通过访问聚合数据服务来以低得多的成本成功运营,该服务将提供更容易消费的 API 访问。
对于小玩家来说,一级可能是最有用的工具
如前所述,虽然有些人把深度书描述为日内交易和刷单操作的有价值的工具,但不断提交和删除订单的算法的存在经常使这些信息误导,如果不是完全欺骗的话。同样,如果要将这些信息纳入我们的交易策略中,理解适当的背景和附加的交易操作标准是必不可少的,这一点很重要。
对于小型全权操盘者来说,更可能有用的信息是与时间&销售相对应的信息,即已执行的交易,因为这些交易是已确认的交易,并暗示有人实际上在以给定的价格承诺资金。
虽然仍有可能成为算法欺骗策略的一部分,通过诱捕其他参与者来创造流动性,但已执行的订单可能比未执行的订单更不具欺骗性。声明“我正在以这个价格购买 100 份 CME ES 合约”与实际购买它们是完全不同的(在撰写本文时,这样的合约量的名义价值约为 1500 万美元)。然而,这还不是一种规范;最大的运营商拥有无限的资本,他们的轮换区域需要使用广泛的价格范围来满足他们所有的购买/销售需求。
就持有时间而言,你越是做短线,信息延迟和费用对操作者的影响就越大,因此这个领域就越是留给专业交易行业。零售和小型运营商需要部署将利润/损失和风险与延迟和经纪/市场费用充分分离的策略。
游击方式:你不是猎人
对二级数据值的这种解释需要非常谨慎。我们不能忘记,所有市场交易的 80%以上来自算法——可能更高。
毫无疑问:这些算法是由世界上最强大、最足智多谋、最熟练、资金充足、最聪明和最有经验的团队设计的。
他们指望无限的资本来运作,他们旨在欺骗其他玩家。他们标记订单,识别可能的来源(人工操作员、慢速算法、并置 HFT)并相应操作,他们实时关联不同的市场和交易所,以利用跨市场的差异(期货、现金、期权、场外交易、暗池)。由于搭配,基于 FPGA 的定制硬件使用,有时甚至是交易所本身向大型参与者提供的明显优势——称之为漏洞——它们的运营也比市场中的其他参与者具有时间优势。
基本上,他们在各方面都胜过你,重要的是永远不要忘记这一点,所以你所有的作战程序都使用游击战方法来专注于那些正规军无法充分对抗的优势。
最重要的是,机器学习和人工智能的兴起可能会继续以更快的速度增加算法的相关性。我甚至怀疑人工操作在未来是否可能实现。
虽然当前的技术允许小经营者做过去保留给大型机构公司的事情,但同样的技术将机构公司和做市商推向前所未有的知识和权力水平。当前的市场已经是一场大屠杀,而且只会变得更糟。
Small operators and retails operating intraday constitute actual market snipers fighting a regular and much stronger army. They are part of an heterogeneous, uncoordinated, irregular guerilla waiting for the right market conditions to appear. In the photo: detail of a hit-and-run scalping trade on E-Mini NASDAQ CME future contract.
小运营商的希望是,由于资本如此有限,有可能在数百毫秒内分配和解除分配资本,而不会对市场产生任何影响,因此有可能在非常短的时间内加入机构运动,并仍能获得不错的利润。
流动性从来不是问题,除非在新闻、特朗普的推文和其他意外事件等异常市场条件下——这只适用于零售和大多数普通公众,因为没有意外事件这种事情;在某个地方,总会有人在事件发生之前就已经知道并做好了充分的准备。
在这种情况下,滑点可能会让你在失败的交易中损失两倍或三倍,但这是游戏的一部分,任何有经验的交易者都可以成功避免其中的大部分。对于真正意想不到的风险,它们将被视为整体 P&L 中的异常值,风险管理更高的保护层将避免对我们的运营产生任何严重影响——是的,你需要更多的风险管理层,而不仅仅是止损。
通过将操作限制在恰到好处的时机,小参与者可以利用这种可用的流动性,在做市商的某些活动中表现出微不足道的回报/烦恼。我并不是说这很容易(因为这并不容易),但如果利用了正确的时机和环境,这绝对是可能的。这里的要点是,一级和二级数据有助于实现这个目标,特别是日内操作。
在定量分析中处理分笔成交点数据
就定量数据分析而言,分笔成交点数据是另一回事,尤其是当需要整合图书深度信息时。我估计从分钟 OHLC 棒线到分笔成交点数据需要一到两个数量级的数据处理工作。
Wolfpack Flow™ is my latest designed tool to operate Order Flow. Among its key functionalities: Big Trade analysis, Volume Clusters, Volume Profile and Order Flow. The software has been designed from scratch and it is fully optimised to operate intraday Order Flow and Volume. Source: www.wolfpackflow.com
原因在于,对于 OHLC 蜡烛线,每单位时间的信息量总是具有预定义的大小限制。即使移动到一秒钟蜡烛线(不使用订单流但需要分析短期策略时的常见选择)也会增加处理需求,但与一分钟棒线相比不会改变任何事情。对于分笔成交点数据,不仅数据量大得多,而且每时间单位的数据大小也不规则。这种额外的复杂性进一步限制了数据的处理方式,因为需要更复杂的存储和处理后端。
由于每秒钟订单簿的变化数量可能非常大,尤其是在波动性高峰期间,存储深度簿数据会带来更具挑战性的性能需求。对于这些环境,定制开发几乎是一个硬性要求。
摘要
在本帖中,我们对一级和二级市场数据进行了简要回顾。还简要介绍了第三级。
在没有涵盖任何实际策略的情况下,我们讨论了一级和二级数据如何对自主日内交易做出贡献。并对处理数据馈送中这一级别的细节所面临的挑战提出了一些意见。
总的来说,在我们的交易程序中包含至少一级和订单流数据可以显著提高我们的盈亏比,从而降低风险和支出。额外的信息也有助于减少与日内交易相关的压力,因为可以确定更清晰的进场点和出场点。
如何压缩你的时间序列
我们将看看什么是通货膨胀调整,以及为什么你应该紧缩你的时间序列。在这个过程中,我们将了解消费者价格指数(CPI) 以及它是如何计算的。
通货膨胀调整或通货紧缩是从数据中去除价格通胀影响的过程。只调整以这种方式表示的货币数据是有意义的。这类数据的例子有每周工资、存款利率或西雅图一袋 5 磅蛇果苹果的价格。如果你正在处理一个以货币命名的时间序列,缩小它将会消除它的上下波动的一部分,这是一般通货膨胀压力的结果。
在我们进入通货膨胀调整的“如何”之前,让我们看看通货膨胀调整可能产生的效果。
通货膨胀调整的影响
下面的时间序列代表了 1997-2017 年美国所有工薪阶层的平均年薪。数据显示,年同比平均增幅约为 3%。
Source: Wages and salaries by Occupation: Total wage and salary earners (series id: CXU900000LB1203M). U.S. Bureau of Labor Statistics (Image by Author)
当你根据通货膨胀调整这些数据时,图表明显变得起伏不定:
Wages after adjusting for inflation (Image by Author)
我们做了什么来得到第二张图?我们所做的是采用消费者价格指数,特别是美国劳工统计局发布的CPI-城市工薪阶层和文职人员:所有项目 1982=100 ,我们将年薪数据除以当年的 CPI 值,然后将结果乘以 100。以 1997 年为例,我们将 43615 美元的工资除以 1997 年的消费物价指数 157.6,再乘以 100,得出经通货膨胀调整的工资(以 1982 年美元计算)为 43617/157.6 * 100 = 27674 美元。然后每年重复这一计算,得到如上所示的第二个图。
这是另一个说明通货膨胀调整效果的图表。
(Image by Author)
图中的蓝线表示通货紧缩前每年工资的百分比变化,橙线表示通货紧缩后“实际工资”的百分比变化,灰线表示每年的通货膨胀。
在很大程度上,经通货膨胀调整的工资的变化与未经调整的工资的变化是一致的。但在与前几年相比通货膨胀率较低的年份(如 2009 年和 2015 年),经通货膨胀调整的工资大幅上升,而在与前一年相比通货膨胀率大幅上升的年份(如 2000 年、2008 年和 2011 年),经通货膨胀调整的工资大幅下降。
现在我们已经看到了几个通胀调整数据的例子,让我们来看看通胀调整是如何工作的具体细节。
通货膨胀调整公式
正如我们所看到的,你可以通过将数据除以适当的消费价格指数,然后将结果乘以 100 来调整通货膨胀。
Inflation Adjusted Value (Image by Author)
这是一个重要的公式。让我们把它标为等式 I。我们很快会再次用到它。
使用此公式时,您应该知道两件事:
- 在这个公式的分母中,使用正确的价格指数很重要——通常有几个价格指数可供选择,并且
- 知道如何解释通货膨胀调整值是很重要的。你如何解释这个值取决于你有什么数据和你用什么价格指数来缩小它。
让我们详细检查每一点。
我应该使用哪个通货膨胀指数?
通常有几种可用的 CPI,您应该针对您的数据类别使用正确的 CPI。例如,美国劳工统计局(BLS)发布了大量的价格指数。以下是一组示例:
Data source: U.S. Bureau of Labor Statistics (Image by Author)
您使用哪个指数取决于您希望压缩什么数据以及您希望测量数据的什么属性。让我用两个例子来说明这一点:
示例 1: 假设您有一个美国所有城市地区苹果年平均价格的时间序列。如果你希望得到美国城市消费者经历的苹果价格的核心增长,在扣除城市消费者经历的整体通货膨胀的影响后,你应该使用CPI-所有城市消费者:美国所有商品,1982–84 = 100或CPI-所有城市消费者:美国所有商品,1967=100 来降低你的苹果价格如果你错误地使用非常流行的指数CPI——城市工薪阶层和文职人员 你会得到不正确的结果,因为这个指数只衡量城市工薪阶层经历的物价上涨,而不是所有的城市消费者。
例子 2: 假设你希望在抵消城市整体食品通货膨胀的影响后,得出苹果价格的核心增长,你应该使用指数CPI-所有城市消费者:美国城市平均食品和饮料,所有城市消费者,不经季节调整 来压缩你的数据。这将会给你一个衡量苹果相对于其他食物变得更贵或更便宜的尺度,而且只对城市消费者而言。稍后我将更详细地描述 CPI 的这种特殊用途。
现在,我们已经了解了使用正确指数的重要性以及如何使用它来获得调整值,让我们更深入地了解调整值告诉我们什么。为此,我们首先需要理解 CPI 的概念。
如何解读通货膨胀调整值?
要知道如何解释紧缩值,必须了解什么是消费物价指数,以及它是如何计算的。CPI 的技术定义听起来很无聊,但事实就是这样:
消费者价格指数衡量家庭消费的一篮子商品和服务的价格变化。
要真正理解什么是 CPI,我们必须知道如何计算它,这个计算最好用一个例子来说明。因此,让我们启动一个迷你项目来创建一个闪亮的新指数。我们称之为高谭市的虚拟 CPI 所有项目,不经季节调整。
我们的第一个任务是计算一个在哥谭市的虚构家庭的年支出。
Image credit: Pexels from Pixabay
我们还将做一个相当大胆的假设:我们将假设我们的虚拟家庭的消费完美地代表了哥谭市所有家庭的消费。有了这个假设,让我们开始计算家庭开支。
下表包含连续两年测量的家庭年度支出在 8 个类别中的细分:
The market basket of our fictional household (Image by Author)
在这个例子中,市场篮子是我们虚构的家庭每年消费的商品和服务的集合。对于哥谭市的虚构家庭,市场篮子的价格在 2018 年是 42000 美元,在 2019 年是 43260 美元,比 2018 年增长了 3%,我们将归因于通货膨胀。
请注意,购物篮分布在八个类别中。这些也是美国劳工标准局在计算美国各种 CPI 时使用的一些类别。
现在,让我们回忆一下,我们想要得出的是一个指数,而不是一篮子货币的绝对美元成本。一个指数需要一个基础,所有的未来值都可以很容易地与之进行比较。所以我们会任意假设 2018 年是我们的基准年,我们会把 2018 年的指数值设为 100 点。让我们把它重新命名为:
哥谭市虚构的 CPI 所有项目,不经季节调整, 2018=100 。
我们现在可以计算 2019 年 CPI 虚构的值如下:
(Image by Author)
因此,2019 年的指数值= 43260 美元/42000 美元* 100 = 103。
CPI 值 103 告诉我们的是,对于哥谭市的所有家庭来说,市场篮子在 2019 年变得更贵了 103/100 = 1.03 倍。
总的来说:
A simplified version of the formula for calculating the value of the index (Image by Author)
对于时间段(或简称为期间),我们通常是指特定的一天、一周、一个月、一个季度或一年。
假设在 2020 年至 2023 年的连续 4 年中测量市场篮子的价格,发现 2020 年为
44400 美元,2021 年为
46200 美元,2022 年为
43800 美元,2023 年为
45240 美元。
那么,在这些年里,我们的虚构指数取值如下:
在 2020 年,指数值= 44400 美元/42000 美元* 100 = 105.7143
在 2021 年,指数值= 46200 美元/42000 美元* 100 = 110
在 2022 年,指数值= 43800 美元/42000 美元* 100 = 104.2857
在 2023 年,指数值= 45240 美元/42000 美元* 100 = 107.7143
以下是结果值表:
CPI Fictitious in Gotham City — All Items, not seasonally adjusted, 2018=100 (Image by Author)
一旦有了 CPI 数据,计算逐年通胀就非常容易了。公式如下:
Formula for rate of inflation (Image by Author)
使用这个公式,我们可以看到哥谭市家庭在 2019 年至 2023 年经历了以下通货膨胀。
Percentage Inflation Year over Year in Gotham City (Image by Author)
现在让我们看看通货膨胀对购物篮中的一件商品的影响——比如一碗意大利面汤。
在 2018 年至 2023 年期间,假设 mulligatawny 汤呈现以下价格趋势:
Price of soup in Gotham from 2018 through 2023 (Image by Author)
很明显,汤的价格上涨是因为那些年哥谭市经济的普遍通胀压力。我们看到哥谭市在 2019 年经历了 3%的整体通胀。我们将假设 mulligatawny 汤的成本在 2019 年也必须至少增加这么多。
设$X 为 2018 年汤的成本。如果在 3%的通胀率后,2019 年的成本变为 3.26 美元,那么 X * 103/100 = 3.26 美元。因此,2018 年的汤的成本应该是:
$3.26*100/103 = $3.16505
因此,3.16505 美元是汤在 2018 年应该拥有的成本,因为在价格上涨 3%后,它在 2019 年的成本为 3.26 美元。对 3.16505 美元价值的另一种解释是,这是 2019 年的汤的价格,但在 2018 年是美元。
我们从表中知道,3.08 美元是 2018 年以 2018 年美元计算的汤的价格。所以现在我们可以对两个成本进行比较:3.08 美元和 3.16505 美元,因为这两个成本都是以 2018 年的美元表示的。
我们现在可以计算出 2019 年哥谭市汤成本的内在通胀对 2018 年至 2019 年的整体通胀进行贴现后如下:
Inflation in price of soup after adjusting for overall inflation from 2018 to 2019 (Image by Author)
同样,2020 年的汤的价格可以通过除以哥谭市 2018 年至 2020 年经历的通货膨胀量来调整为 2018 年的美元。
为了计算这个膨胀量,回忆一下等式(III):膨胀的公式。我们将在下面复制它:
(Image by Author)
我们可以重新使用这个公式,找出当前时间段与基准年(2018 年)相比的通货膨胀率。让我们算一算:
Calculating the cost of soup in 2018 dollars (Image by Author)
还记得我们说过我们会重温方程(I)吗?又来了:
(Image by Author)
我们可以通过将等式(IV)的分母表示为如下,用等式(I)来表示等式(IV):
(Image by Author)
现在回到等式 I,2020 年的 CPI 是指数值,2018 年的 CPI 总是 100,因为它是基年。因此,
(Image by Author)
这和我们之前用不同但等价的公式得到的一样。
无论如何,我们现在已经将 2019 年和 2020 年的汤的成本调整到相同的基数,即 2018 年的美元。因此,像我们之前所做的一样,让我们在扣除整体价格通胀的影响后,比较从 2019 年到 2020 年汤类价格的实际百分比变化。价格的“核心”变化是:
(Image by Author)
我们现在可以陈述计算一个项目的价格相对于感兴趣的 CPI 的真实(内在)变化的一般公式:
Formula for intrinsic inflation in an item (Image by Author)
下表显示了 2019 年至 2023 年哥谭市在调整整体通胀之前和之后的汤价通胀。
(Image by Author)
这是一剂有益的数学良药。让我们休息一下,喝点汤,然后再继续工作。
Image credit: GeoTrinity CC BY-SA 3.0
我们虚构的 CPI 例子对我们很有帮助。到目前为止,它向我们展示了如何计算指数,如何计算指数中的通货膨胀,如何解释通货膨胀调整值,以及如何根据适当的指数计算一个项目价格的内在增长。
但是现在,冒着降低我们所有蝙蝠侠粉丝的兴趣(和自尊心)的风险,我们必须回到现实。
现实的快速剂量
每年,美国 BLS(或其他国家的相应政府机构)都会采访成千上万不同金融阶层(低、中、高收入)和不同职业的家庭,以收集他们的消费习惯数据。这项工作的结果是计算了几十个更小、更集中的指数。然后,使用一个权重系统将这些指数汇总起来,形成几个顶级消费物价指数。精确的计算可能会很复杂,但概念很简单。你想找出“普通”家庭在商品和服务上的花费,然后将这个数字指数化到某个任意的基准年。然后每个月、每个季度、每年重复这一计算,以了解与前一时期相比,生活成本变得贵了(或便宜了)多少。
如果您正在训练货币计价数据的模型…
如果你正在训练一个模型,如果你不紧缩你的货币计价数据,你的训练不会失败。与此同时,你应该考虑缩减它,因为这样做将从你的数据中移除那部分“信号”,那是由于普遍的通货膨胀压力。除了通货紧缩,您还应该考虑一个或多个其他转换,如对数转换(使趋势呈线性)、季节调整和差异。所有这些操作都将从数据中删除信号的相应部分。剩下的就是你的训练算法现在可以关注的残差趋势。此外,当然还会有噪声——你的算法必须学会忽略它!
快乐放气!
我写关于数据科学的主题,特别关注时间序列分析和预测。
如果你喜欢这篇文章,请在Sachin Date关注我,获取关于时间序列分析、建模和预测的技巧、操作方法和编程建议。
桑基图的内容、原因和方式
智能显示资源流动
知道什么
桑基图显示了资源的流动。他们交流所代表的资源、材料或成本的来源和用途。
阅读和解释桑基图的关键是记住宽度与所表示的数量成正比。在下面的例子中,观众很快就会看到,除了水文循环的其他特征之外,水的最大目的地是陆地蒸发。
Source: The Energy Sustainability Challenge
红旗
当呈现桑基图时,记住唯一的规则(好的,主要规则)是线条和箭头的宽度代表资源的数量或体积。如果箭头不具有代表性,这可能意味着构造者犯了一个错误,不理解工具的用途,或者试图隐藏一个难以忽视的事实。如果出现了什么问题,一定要问问题,以确保你理解了可视化。
为什么/什么时候它们很棒
- Sankey 图允许您直观地显示复杂的流程,重点关注您想要突出显示的单个方面或资源。如果你的团队正在做一个关于精力、时间或金钱的决定,那么这是一个考虑桑基图的好时机。
- Sankeys 提供了支持多种观看级别的额外好处。观众可以获得高层次的观点,看到具体的细节,或产生互动的看法。如果你有一个喜欢向下钻取的队友,许多工具会让你共享该功能,而不需要创建者做任何额外的工作。您还可以预先确定最适合您的目的的深度级别。
- 桑基图使主要贡献者或消费者脱颖而出,并帮助您的受众看到相对数量和/或具有最大机会的领域。
当他们不好的时候
This Sankey diagram from Data to Viz has a lot going on
有时,桑基图并不是适合您情况的合适工具:
- 它们可能显得过于复杂,让你的观众难以消化。
- 制作拙劣的桑基图,隐藏而不是突出可操作的洞察力。
- 由于不是每个人都熟悉这种可视化类型,复杂的桑基图可能需要花费更多的时间和精力来解释
- 桑基图很难区分和比较具有相似值(宽度)的流量。如果这些比较对你的目的是必要的,考虑一个(堆积)条形图。
如何制作桑基图
第一步:设计
首先,巩固你的目的和对你的观众最重要的收获。为了避免浪费时间重新构建图表或构建无效的桑基图,我建议在开始之前问自己以下几个问题:
- 您正在使用此 Sankey 进行探索性数据分析吗?
- 你是用它来讲述一个故事,促进一个特定的行动,改变想法吗?
- 谁是你的观众?
- 您的受众对数据可视化的体验水平如何?
- 你的受众在寻找什么并被什么说服——投资回报率、效率、有效性、盈利能力、地区或城市的比较?
从这里开始,在开始编码之前,从你想要的可视化的轮廓开始是一个好主意。绘制草图时,请考虑以下几点:
- 交流观点的替代方式
- 用空间和/或颜色对相关的输入或输出进行分组
- 用颜色表示从一种状态到另一种状态的转换
- 使用色彩饱和度或强度、位置、长度、角度、方向、形状来强调你的观众的主要观点。(除了宽度以外的任何东西!)
- 削减微小的流量或将它们归入“其他”类别以减少混乱
第二步:编码
from matplotlib.sankey import Sankey
from matplotlib import pyplot as pltfig = plt.figure(figsize=(15,10))
ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[],
title="Flow Refugees from the Syrian Civil War")
sankey = Sankey(ax=ax,
scale=0.0000001,
offset= 0.1,
format = '%d')
sankey.add(flows=[6918000, -3600000, -950000, -670000, -250000,
-130000, -1300000, -18000],
labels = ['Syria', 'Turkey', 'Lebanon', 'Jordan', 'Iraq',
'Egypt', 'Europe', 'USA'],
orientations=[0, 0, 1, 1, 1, 1, -1, -1],#arrow directions
edgecolor = '#027368',
facecolor = '#027368')
sankey.finish();
其他有用的论据
- Pathlength 使用此参数可以调整箭头的长度,一旦它们通过一个浮动列表从主流中分离出来。
- Trunklength 使用此参数调整输入和输出之间的空间长度
一句警告
最初,我得到了这个不太像桑基的可视化。我完全不知道哪里出了问题——我看到了一些数字、标签和宽度,但肯定不是我所期望的。
深入研究文档,我决定调整树干长度,这有助于我的桑基开始从它的几何艺术品茧中出现。正如我丈夫指出的,它从波洛克到了大理。
然后我找到了——比例因子。原来比例因子是处理大值的关键!经过一点试验,我让桑基看起来好多了。
看起来默认值对于百分比值很有效,但是要准备好缩放任何其他数据量。
添加中间或附加步骤
fig = plt.figure(figsize = (15,8))
ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[],
title="Household Budget")sankey = Sankey(ax=ax, scale=.1, offset=1, unit='%')sankey.add(flows=[100, -50, -30, -20],
labels=['household budget', 'necessities', 'fun',
'saving'],
orientations=[0, 0, 1, -1],
trunklength = 10,
edgecolor = '#027368',
facecolor = '#027368')sankey.add(flows=[50, -30, -10, -10],
labels=['','rent', 'groceries', 'other'],
trunklength = 2,
pathlengths = [3,3,3,3],
orientations=[0, 1, 0, -1],
prior=0, #which sankey are you connecting to (0-indexed)
connect=(1, 0), #flow number to connect: (prior, this)
edgecolor = '#58A4B0',
facecolor = '#58A4B0')diagrams = sankey.finish()
for diagram in diagrams:
for text in diagram.texts:
text.set_fontsize(16);
Matplotlib 的 sankey 包似乎不能完成您希望 Sankey 图所做的一切。例如,它似乎没有使用颜色来指示源或第三个属性来跟踪跨节点的流。如果你想制作更复杂的桑基图,特别是带有颜色功能的,我推荐你使用其他工具,比如[f](https://github.com/ricklupton/floweaver)loweaver
[ 如何在这里使用 ]。
Sankey diagram from floWeaver’s quick start guide showing the flows of apples and bananas
他们选择的主题——苹果和香蕉从农场到性别化消费者的转移——感觉有点做作。我很难相信没有女人吃农场 2 的苹果,只有女人吃农场 3 的苹果。同时,我认为这个工具是一个不错的选择,这取决于你的需求和风格偏好。
桑基图并不是每种情况下的完美工具。它们绝对不是创建或理解起来最快捷、最简单的可视化方式。但如果做得好,它们会成为强有力的话题引子。只要确保你正在使用它们,因为它们是传达你的信息的最佳方式,而不仅仅是炫耀你的可视化技能。
整个数据科学世界掌握在您手中
在不同的语言和工具上测试 MatrixDS 功能。如果你处理数据,你必须检查这个。
Image by Héizel Vázquez
多年来,我一直在寻找一个平台,在这个平台上,我可以运行我的数据科学项目,而不会有安装的痛苦,也不会让我的计算机充满许多不同的工具和环境。
幸运的是,我发现 MatrixDS 拥有所有这些以及更多免费的东西!在本文中,我将测试他们拥有的几乎所有工具,因此您不必这样做。
该项目在平台中是公开的,您可以在这里看到它:
[## MatrixDS |数据项目工作台
MatrixDS 是一个构建、共享和管理任何规模的数据项目的地方。
community.platform.matrixds.com](https://community.platform.matrixds.com/community/project/5cb72e2478de19f638a914b9)
如果你想测试它,你必须做的就是铲车,就是这样。
还有一个 GitHub 回购:
[## FavioVazquez/matrix _ languages _ tools
测试 MatrixDS 的不同工具和语言。通过…为 FavioVazquez/matrix _ languages _ tools 开发做出贡献
github.com](https://github.com/FavioVazquez/matrix_languages_tools)
测试 Python 的东西
Jupyter 笔记本
我目前最喜欢的编程语言是 Python。有很多很棒的工具和特性可以帮助你使用这种语言。其中最受欢迎的是 Jupyter 笔记本。要在 MatrixDS 中启动笔记本,请执行以下操作:
- 转到平台中的工具选项卡。
- 单击右侧的(+)按钮:
3.选择 Python 3(或 2)与 Jupyter 笔记本:
4.为工具选择一个名称,并设置内核和 RAM 的数量:
5.创建并启动笔记本后,只需打开它:
6.开心编程;)
在笔记本里面,你可以自由地做任何你想做的事情。我创建了一个简单的 Python 笔记本来测试 PySnooper ,所以你可以试试。
这是该笔记本的要点,您可以在 MatrixDS 项目中找到:
朱庇特实验室
JupyterLab 是 Jupyter 项目的下一代基于网络的用户界面。这就像类固醇上的朱庇特笔记本。
要在 MatrixDS 中启动笔记本,请执行以下操作:
- 转到平台中的工具选项卡。
- 单击右侧的(+)按钮:
3.选择 Python 3 和 JupyterLab:
4.为工具选择一个名称,并设置内核和 RAM 的数量:
5.创建并启动工具后,只需打开它:
6.玩得更开心:)
我在 JupyterLab 实例中创建了一个简单的 Python 笔记本来进行测试,所以您可以尝试一下。
如果你一直在关注我,这就是你应该看到的:
哦,顺便说一下,如果你想知道如何使用 git 和 MatrixDS,请查看这篇文章:
[## 擎天柱的数据科学。第 2 部分:设置您的数据操作环境。
用 Python、Spark 和 Optimus 分解数据科学。今天:数据科学的数据操作。…*第 1 部分在此…
towardsdatascience.com](/data-science-with-optimus-part-2-setting-your-dataops-environment-248b0bd3bce3)
我创建的测试笔记本测试功能性机器学习的新库 fklearn 。这是该笔记本的要点,您可以在 MatrixDS 项目中找到:
测试 R 事物
https://www.computerworld.com/video/series/8563/do-more-with-r
我在 r 上开始了我的数据科学生涯。它是一个非常棒的工具,可以进行数据分析、数据清理、绘图等等。我认为现在机器学习部分用 Python 更好,但要成为一名成功的数据科学家,你需要了解这两者。
要在 MatrixDS 中启动 RStudio,请执行以下操作:
- 转到平台中的工具选项卡。
- 单击右侧的(+)按钮:
3.使用 RStudio 选择 R 3.5:
4.为工具选择一个名称,并设置内核和 RAM 的数量:
5.创建并启动工具后,只需打开它:
6.玩得开心点:)
我创建的 test R 环境正在测试名为 g2r 的新库,该库使用 g2 为交互式可视化创建图形。
Btw!在运行 g2r 之前,我必须这样做:
sudo su
apt-get install libv8-dev
所以,通常这就是你用 ggplot2 得到一个图的方法:
library(ggplot2)ggplot(iris, aes(Petal.Length, Petal.Width, color = Species)) +
geom_point() +
facet_wrap(.~Species)
您将获得:
不算太坏,但是给它带来交互性怎么样??使用 g2r 非常简单。这是实现这一点的代码:
library(g2r)g2(iris, asp(Petal.Length, Petal.Width, color = Species)) %>%
fig_point() %>%
plane_wrap(planes(Species))
您将获得:
在您更改的代码中:
aes -> asp
geom_point() -> fig_point()
facet_wrap(.~Species) -> plane_wrap(planes(Species))
我仍然想知道为什么他们不使用相同的 API,但这是一个非常酷的项目。你可以在这里找到更多的例子:
启动并运行 g2r,发现与 ggplot2 的相似之处,并通过几个示例指出正确的方向…
g2r.dev](https://g2r.dev/articles/)
这是所有的代码:
测试朱莉娅的东西
当我在攻读物理学硕士学位时(大约两年前),我真的认为 Julia 将会彻底改变科学编程世界。不要误解我的意思,它做了一件了不起的工作,但是我认为 Python 的新进步已经让这个项目在很多方面都处于第二位。
为了测试 MatrixDS 的 Julia 功能,我想看看这种语言的数据库。你可以在下面看到。
要在 MatrixDS 中启动 Julia 笔记本,请执行以下操作:
- 转到平台中的工具选项卡。
- 单击右侧的(+)按钮:
3.选择 JupyterLab 的 Julia 1.1.0:
4.为工具选择一个名称,并设置内核和 RAM 的数量:
5.创建并启动工具后,只需打开它:
6.让我们朱莉娅:)(听起来很奇怪)
启动它时,您可以打开或创建任何 Python 或 Julia 笔记本:
这是我为测试 Julia 的数据科学能力而创建的笔记本:
在这里,我测试了一些库,如 DataFrames、牛虻、Queryverse、Vega 等用于绘图的库:)
如你所见,对我来说,这是在云中进行数据科学的更完整的平台。你需要最小的配置,你甚至可以用 docker 安装你自己的工具。
关于这个平台,还有更多的内容和事情要做,我将在其他文章中介绍。如果你想和我联系,请点击这里:
Favio Vázquez 的最新推文(@FavioVaz)。数据科学家。物理学家和计算工程师。我有一个…
twitter.com](https://twitter.com/faviovaz)
硅谷之狼
数据科学是新的投资银行
Photo by Rebecca Zisser on Axios
毫无疑问,物理学中最怪异的概念是爱因斯坦所谓的“幽灵般的超距作用”。显然,在宇宙一边的一个粒子可以在另一边有一个心灵感应的双胞胎——你扭动第一个,第二个也会抽搐。事实证明,这种现象也适用于人类。我们每个硅谷的数据科学家在华尔街都有一个替身。
没有人在成长过程中渴望每天 12 个小时左右洗牌,只有其中的 8 个小时你会得到报酬。大多数人长大后都渴望成为科学家。有些人研究社会科学,因为他们想为我们所有人消除贫困。高盛很快说服他们首先为自己清除病毒。其他人倾向于真正的科学,因为他们想知道是什么让宇宙运转。他们最终来到山景城,发现是什么让顾客点击。
不管你最终在海岸的哪一边,你都会花时间盯着一张反复无常的图表,看价格或损失函数值的变化。你存在的整个完形将被浓缩在这该死的一行字里。只有一条路,你会成为大人物的。另一方面,你最好从顶楼的窗户跳出去。或者万一你发现自己在谷歌上,头朝下滑下地铁滑道,让你的大脑撞到下面彩色的地板上。
为了掌控他们的图表,神童们如此积极地将信息强行输入他们的大脑,以至于他们不可避免地会癫痫发作并流口水。在资金管理中,一个人必须至少有两个屏幕——他的一双眼睛各有一个——调到美国消费者新闻与商业频道和彭博的假新闻。在数据科学中,总有另一篇 arXiv 文章可以阅读,总有另一篇 OpenAI shitpost 可以阅读。
然而,不管你读了多少,总有更多的首字母缩略词需要吸收。大多数人会记住字母组合,更大胆的人会去查完整的形式,但是绝对没有人理解其中隐含的概念。在金融领域,有 CDO 和 CDs,过去对它们的误解导致了社会动荡。在机器学习中,你有 DNNs、CNN 和 LSTMs,对它们的误解导致了目前的社会动荡,因为它们将有色人种贴上大猩猩的标签,将 T2·里奇·格威斯贴上极权主义者的标签。
即使你不完全理解这些技术,你仍然被要求去做上帝的工作来部署它们。在街上,这相当于为光缆挖沟,为你的股票交易节省 5 毫秒。在硅谷,你将永远刷新英特尔荒凉的支持论坛,因为你他妈的神经计算棒是开箱即用的。
如果所有其他方法都失败了,你总有可能通过欺骗达到顶峰。许多最著名的金融家——从伯尼·麦道夫(Bernie Madoff)到三个最初的雷曼兄弟(Lehman brothers)的门徒——都信誓旦旦地说,他们采用了久经考验的策略,骗取退休人员的养老基金。我们数据科学家也是如此,他们把老人的工作冒充成我们自己的来欺负老人。事实上,戈登“贪婪是好的”盖柯在机器学习领域的死对头理查德萨顿分享了他的口头禅:“任何贪婪的政策……都是最优政策。”
现在,所有这些忙碌和忙碌肯定会让你神经过敏。为了缓解压力,银行家们历来求助于可卡因和妓女。SQL script kiddies 从他们异常的酮水平中获得快感——这是剥夺他们身体在 Joylent 中找不到的必要营养的结果。至于性的自我表达,他们宁愿等待性爱娃娃变成人工智能。
神盾局。
成本函数的世界——包容性、多数主义和寡头政治
The curve fitting problem (Image credits)
大多数机器学习问题归结为将一组观察值(点)拟合到某条数学曲线(在机器学习的说法中简单地称为“模型”)。这就产生了一个压缩的表示形式,可以用于包括预测和模式识别在内的应用。
拟合过程通常包括最小化所谓的成本函数,该成本函数代表所选曲线相对于其试图拟合的观察样本的误差。在本文中,我们将更深入地研究成本函数的有趣世界,它们的选择如何在模型的选择中发挥重要作用,并最终看到一些令人难以置信的与现实世界治理模型的更深层次的联系(我确信这将是有趣的,因为选举即将到来!)!在理解这些成本函数的过程中,我们将使用最简单的曲线拟合问题——线性回归。
线性回归
Predicting the price of a house
假设你有一个特定问题的一堆特性(假设 n = 100)。你知道你试图预测的输出是这些特征的线性组合。大多数教程谈到的经典用例是根据房屋的各种特征(xᵢ)预测房屋的价格,其中特征的范围从位置到屋顶类型到面积等。价格(y)的预测由函数 h(x)表示,如下所示:
其中 fᵢ = featureᵢ,Wᵢ=为特征值,ᵢ和 W₀=为常数
成本函数
假设我有一个 m=2000 个数据点的训练数据。我们需要找到 W 的值,使得预测的价格尽可能接近实际价格。
换句话说,W 应该取什么值才能使下面的成本最小。
请注意,我们试图测量每个样本的预测绝对误差(如图所示),并取平均值。
Simple Linear regression: Y with respect to one feature variable X
然而,大多数文献(实际上也是)中使用的最流行的成本函数如下:
其中取每个样本中误差平方的平均值。注意,两个成本函数之间的差异在于绝对误差的幂。因此,平方误差函数被称为 L₂-norm,而前一个函数被称为 L₁范数。
成本最小化
现在是一个百万美元的问题:为什么 L₂-norm 比 L₁常模更受欢迎?大多数文献都指出了计算机出现之前的计算方法,如下所述。
对于成本最小化问题,任何微积分的初级教科书都会告诉我们,在最小点的一阶导数为零。简单地说,问题的解决方案将位于满足以下标准的点上。
Typical cost function whose first order derivative yields the minima
事实证明,当成本函数是绝对误差时,而不是当成本函数是平方误差的和时,封闭形式的解决方案——用简单的话来说是简单的公式——是不可能的。原因是成本函数在点 Cost = 0 处不可微,如下图所示。代表导数的紫色线在点 Cost = 0 处不连续
First order derivative (violet) of the absolute error cost function (red)
使用平方误差的成本函数是完全可微的,如下图所示。
First order derivative (violet) of the absolute error cost function (red)
事实上,封闭形式的解是下面的方程
其中 x 是特征值矩阵,Xᵢ,ⱼ代表 sampleᵢ.featureⱼ的值
然而,在计算机时代,可以应用像梯度下降这样的算法,并迭代地导出解决方案。我不打算深入研究梯度下降的细节,但我想指出,只要成本函数在零处的微分为零,梯度下降仍可用于 L₁-norm。或者,也可以使用诸如次梯度的方法。那么,还有理由使用 L₂标准吗?它值得如此受欢迎吗?我们一会儿就会明白。
Lp 范数
现在,我们已经研究了两个成本函数——l₁范数和 L₂范数,让我们尝试将其推广到 Lp 范数,其中 p 可以是正实数。
那么 L₀-norm 意味着什么呢?
这意味着如果特定点的绝对偏差为零,则误差为零,否则误差为 1。想象一下在一个连续的空间中发生这种情况的可能性。
如果在线性回归的情况下使用 L₀-norm,成本将是不在拟合线上的点的总数。这意味着使用 L₀-norm 将试图找到线,正是通过最大数量的点!
通过示例了解成本函数
所以我举了一个例子来更好的理解三个代价函数。假设我想对下图所示的点进行线性回归。
我知道这不是一个应用线性回归的好例子,因为这些点似乎分散在 3 个区块中。在顶部有一条线,在中间有一小组共线的点,在最右边的块中有一堆随机分布在零周围的点。然而,我将使用线性回归来说明成本函数的行为。另外,请假设这些点都不是噪声或异常值。
此时,同样重要的是指出梯度下降算法不能应用于 L₀-norm,因为函数在零处的不连续性质(其值为零时的脉冲下降!)以及其他地方的平坦性质(高原),如下图所示。
L₀-norm discontinuous at zero!
然而,有一些算法,如匹配追踪也允许我们求解 L₀-norm 成本函数。
成本函数统治着我们的世界!
现在让我们看看由三个成本函数拟合的曲线。
可以看出,L1 范数穿过最上面的点,而 L₀-norm 穿过中间的一小组共线点,L2 范数穿过图形的中间。我认为这很有趣,我们将在接下来的段落中看到原因。
L2 norm — attempts to be inclusive!
L2-诺姆试图迎合每一个观察到的样本——虽然乍一看它可能看起来过度拟合,但成本函数实际上试图“包含”和“适应”,并将线放置在尽可能接近每一点的位置。
Majoritarianism of L1-norm (Image credits)
另一方面,L1 范数通过围绕一条线采样的看似点的主要块。L1-诺姆成本函数实际上忽略了中间和最右边的块。本质上 L1 范数更像是一个多数派函数迎合了观察样本集中的最大块。
L₀-norm: Oligarchy at play?
L₀-norm 穿过一组较小的共线点。它是一个成本函数,几乎是“我的(我们的)道路或高速公路”的一个体现。它不能容忍哪怕是最轻微的偏差——所以它几乎忽略了所有的样本,只寻找样本中真正线性的点。这几乎是一种“T10”寡头政治在起作用,其中一小组完全一致的观察决定了模型。请想一想,在一个真正连续的空间中,如果是线性关系但不是完全线性的,三个点共线的概率几乎为零,所选的线可能只通过两个点,这几乎就像两个单独的点在表演一样!
获胜者
因此,推广它,在 Lp-范数成本函数中,随着 p 的减少,我们可以看到模型转变为多数主义,最终寡头政治!然而,Lp 范数的高阶成本函数促进了包容性!我认为这解释了选择 L₂范数函数背后的流行。成本函数的选择取决于场景,但对于大多数数据集,公平地说,L₂范数函数是可行的。
在下一篇文章中,我将讨论过度拟合的主题,这是通过一种称为正则化的技术来解决的。
(首次发表于 《印度分析》杂志 )
十四大灾难
为什么“稳赚不赔”变成了一场史诗般的金融灾难
我一直对 VIX 很感兴趣。对于金融领域以外的人来说,VIX 代表 CBOE(芝加哥期权交易所)波动指数。在新闻中,你可能会听到它被称为“恐惧指数”。
在 2018 年 2 月之前,VIX 因两个主要原因而闻名:
- 在严重的市场抛售期间,它往往会飙升,因此有了“恐惧指数”的绰号。
- 它为做空者创造了大量财富(做空是押注投资价格会下跌——因此,如果你做空 VIX,当 VIX 下跌时你会赚钱,当它上涨时你会赔钱)。做空 VIX 变得如此流行,以至于有了自己的子市场。
然后 2018 年 2 月到来,这种情况在短短几个交易日内就发生在 XIV、SVXY 和其他做空 VIX 基金的投资者身上(XIV 是最受欢迎的 ETF,或交易所交易基金,做空 VIX 期货)——GG。
Ouch! (Source: Yahoo Finance)
短短几天内,XIV(被迫清算)、SVXY 和其他做空 VIX 投资基金的投资者损失惨重(与此同时,跟踪标准普尔 500 的基金 SPY 仅损失了 5.5%)。在今天的帖子中,我们将探讨 XIV 这样的基金为何如此引人注目,以及它如何为不要将所有鸡蛋放在一个策略上提供了一个警示故事(特别是如果你不了解所涉及的所有风险)。
什么是 VIX?还有为什么大家都做空?
看看下面的图表。深蓝色的线是 SVXY,一个做空 VIX 期货的 ETF(不像 XIV,它今天仍然活跃)。浅蓝色的线(看起来几乎是水平的)是标准普尔 500。
从 2013 年初到 2017 年底,标准普尔 500 获得了 86%的可观回报,几乎让你的钱翻了一倍。在同一时间段内,通过 SVXY 做空 VIX 获得了令人难以置信的 565%的回报,几乎是你的钱的 7 倍(从 1 到 7)!这些在相对较短的时间内获得的荒谬回报是人们持续光顾 VIX 赌场的原因。
Source: Yahoo Finance
我一直在说的这个 VIX 是什么?VIX 的技术定义是标准普尔 500 期权的隐含波动率。但它是如何计算的远没有它代表什么重要。
简而言之,VIX 衡量股票市场的恐惧和投资者的不确定性。
当事情进展顺利时,VIX 会越来越低。当市场崩溃时,VIX 飙升。
做多 VIX 相当于购买了应对市场崩盘的保险,而做空 VIX 相当于卖出崩盘保险。
那么,市场恐惧指数是如何变成这样一座金矿的呢?
两个因素推动了做空 VIX 者的回报:
- 随着时间的推移,市场趋向于上涨——随着市场上涨,VIX 逐渐下降(如果 VIX 下跌,VIX 做空利润)。
- VIX 期货曲线通常有一个正斜率——这意味着如果你做多 VIX,你会不断地高买低卖。
第二点非常重要。让我们更深入地看看 VIX 期货曲线。首先什么是期货曲线? A 期货合约 是在未来某一时刻以预定价格买入资产的承诺。
下图显示了一条假设的 VIX 曲线。每个蓝点是一个有特定到期日的期货合约,例如,第一个蓝点是 VIX 期货合约,从今天起正好一个月后到期。现在,由于 VIX 不能直接以市场价格(在金融中这被称为现货价格)投资,像 XIV 这样专注于 VIX 的 ETF 的经理们被迫使用期货合约来获得他们的敞口。
Being Long the VIX Futures Curve — Buy High Sell Low
因此,正如我在上面的图表中所描绘的,经理们将购买两个月后到期的 VIX 期货合约(绿色的“在此购买”),并持有该合约以获得 VIX 的长期敞口。但是一个月后,开始时是两个月的期货合约现在变成了一个月的合约(红色的“在这里卖出”)。为了保持对 VIX 的持续敞口,经理卖出他所拥有的合同,现在是一个月的合同,并购买一个新的两个月的合同(该交易由标有“重复”的虚线描述)。
现在回想一下,VIX 曲线通常是向上倾斜的。这意味着每个月经理在红灯时低价卖出,以便在绿灯时高价买入,这个过程被称为“VIX 曲线滚动”。一些数字可能会使这一点更容易理解:
- 我们以 100 美元购买了两个月后的 VIX 期货合约。
- 一个月后,假设 VIX 期货曲线没有实质性的变化,我们之前购买的合同现在是一个月的合同,价值只有 97 美元。
- 现在是时候重置我们的敞口了——因此我们以 97 美元卖出一个月合约,以 100 美元买入一个新的两个月合约,在此过程中损失了 3 美元(-3%的回报率)。
- 因为我们每个月都需要这样做,只要 VIX 曲线保持向上倾斜,我们每个月都会损失 3%。
因此,在上涨或稳定的股市中做多 VIX 相当于月复一月地失血。那么,为什么不扭转这种局面,转而做空 VIX 期货,赚回做多 VIX 的投资者损失的所有钱呢?很多人都这么做了。
月复一月,投资者只需简单地改变交易方向,就可以在高位卖出(红色表示“做空”),在低位买回(T21 ):
Being Short the VIX Futures Curve — Buy Low Sell High
那么有什么问题呢?
早些时候我们指出,做空 VIX 和出售防范股市崩盘的保险是一回事。
销售保险是一种不对称的风险回报策略——大多数时候你赚了一点点,偶尔你会失去一切。
这就是为什么保险公司雇佣大批精算师来计算如何准确地向你和我收取费用,以此作为从我们手中拿走某些风险的回报(即便如此,为了以防万一,他们仍然会将一些风险转移给再保险人)。
让我们继续用这个保险类比。如果一家保险公司发现了一个特别有利可图的利基市场,并开始赚大钱,会发生什么?竞争对手会抓住机会,涌入市场供应,从而压低每家公司的保险价格。
当你投保的是股市崩盘时,这就更危险了。
随着越来越多的投资者争相提供股市崩盘保险,不仅每个投资者被迫收取更低的价格,而且每个投资者承担的风险也增加了。
这是因为当大多数投资者意识到这一交易时,股市已经大幅上涨(市场越涨,VIX 跌得越多,VIX 做空获利)。因此,投资者争相购买保险的股市更有可能是一个被高估的市场。此外,每一个做空 VIX 的增量投资者也会将市场推高一点——随着崩盘保险价格(又称对冲成本)下降,投资者会购买更多股票,推高价格。
因此,在拥挤的市场中做空 VIX 的投资者受到了无形的双重打击——他们的行为推高了他们面临的风险,同时压低了他们可以获得的作为承担这些风险的补偿的预期回报。
这是导致灾难的原因。可悲的是,FOMO(害怕错过)导致投资者对这种危险视而不见。老实说,当你看到 SVXY(和 XIV 几乎一样)六个月的回报导致其内爆时,你很难不去 FOMO。不幸的是,这种 FOMO 会导致许多投资者几乎失去一切。
The Six Months Before the Implosion (Source: Yahoo Finance)
失去一切
让我们回顾一下我们在迈向 2018 年 2 月时的情况:
- 有成千上万的投资者在不知情的情况下,通过交易所交易基金(如 XIV 和 SVXY)陷入了极其拥挤的 VIX 空头交易,由于历史表现强劲,这些交易所交易基金已经吸金数十亿美元。
- VIX 接近历史低点,这意味着投资者提供市场崩溃保险的价格也接近历史低点。
在市场中,很少有线性因果关系。很多时候,看似无关紧要的事件,通过一系列非线性和意料之外的关系,可以引发市场崩盘。
在 2018 年 2 月之前,股票市场是如此令人难以置信的平静(VIX 如此之低),以至于即使是小幅下跌也可能导致投资者恐慌。这正是所发生的事情——标准普尔 500 指数本应是例行的下跌,却导致 VIX(和 VIX 期货合约)像火箭一样飙升(几分钟内上涨近 50%)。
这迫使 XIV 和 SVXY 等做空 VIX 交易所交易基金的经理回补空头,以控制损失。但是你认为当所有人同时试图从同一个小门逃走时会发生什么?
每个人都试图逃离的赌注超过了市场的承受能力,因此每笔交易都以递增的价格执行。也就是说,每一次取消做空(相当于做多 VIX)的交易都会产生痛苦的效果,把 VIX 推得更高。感觉到 ETF 经理绝望的交易者,就像鲨鱼闻到水中的血腥味一样,决心要捞一磅肉。
当所有人都完成交易时,VIX 几乎翻了一倍,持有大量 VIX 空头头寸的投资者几乎损失殆尽。
以下是 Reddit 上的一些评论,强调了这场金融灾难给投资者带来的伤害和痛苦:
“我已经损失了 400 万美元,3 年的工作成果,还有别人的钱。”——u/Lilkanna
“我感觉到你,因为我今天失去了 1/2 的净资产,这可能是我生命中的 5 年”——u/Asian here
“这是对的!???还是某个系统故障?”—u/cheap DVD
“如果我错了,请纠正我,但我从 XIV 崩溃中最大的收获是,如果我想启动一个庞氏骗局,并声明‘你可能会失去一切’,那就没问题,对吗?”— u/mauimikes
经验教训
回首过去,说做空 VIX 是一个错误太容易了。回想起来,事情总是显而易见的,但当它们发生时,就不是了。然而,当我们面对可能是泡沫的投资时,仍有一些事情我们应该记住:
- 如果价格下降 50%,我会没事吗?不要将你投资组合的很大一部分投资于高波动性的资产。
- 流动性。金融市场中许多最严重的崩溃都是因为市场突然枯竭,每个人都需要卖出。当大多数人都站在热门交易的同一侧时,是时候诚实地评估该资产的市场是否足够深,以支持同时出现的离场蜂拥。
- 在金融市场,预期回报是你承担市场风险的补偿。换句话说,高预期回报保护你免受意外的伤害。但是当人群涌入你的交易时,预期回报会变为零,同时价格大幅下跌的可能性也会增加。因此,您的保护在您最需要的时候消失了。
感谢阅读,祝你好运,干杯!
我在投资和金融方面的更多帖子:
数据科学的理论基础—我应该关注还是仅仅关注动手技能?
数据科学和机器学习所需的基本数学和理论技能
数据科学是一个非常注重实践的领域。数据科学需要扎实的数学和编程基础。作为一名数据科学家,您必须了解数据科学的理论和数学基础,以便能够构建具有真实应用程序的可靠模型。
在数据科学和机器学习中,数学技能和编程技能一样重要。有很多好的软件包可以用来构建预测模型。一些最常见的描述性和预测性分析包包括
- Ggplot2
- Matplotlib
- 海生的
- Sci-kit 学习包
- 插入符号包
- 张量流
- PyTouch 包
- Keras 包
重要的是,在使用这些包之前,你要掌握数据科学的基础知识,这样你就不会把这些包简单地当作黑盒工具来使用。
理解机器学习模型功能的一种方法是让你理解每个模型背后的理论和数学基础。作为一名数据科学家,您构建可应用于现实世界问题的可靠高效模型的能力取决于您的数学技能有多好。
本文将讨论一些对数据科学实践至关重要的理论和数学基础。
(一)统计和概率
统计和概率用于特征的可视化、数据预处理、特征转换、数据插补、降维、特征工程、模型评估等。以下是您需要熟悉的主题:
- 平均
- 中位数
- 方式
- 标准偏差/方差
- 相关系数和协方差矩阵
- 概率分布(二项式、泊松、正态)
- p 值
- Baye 定理(精确度、召回率、阳性预测值、阴性预测值、混淆矩阵、ROC 曲线)
- 中心极限定理
- R2 分数
- 均方误差
- A/B 测试
- 蒙特 卡罗模拟
例如,表示、中值、和模式用于显示给定数据集的汇总统计数据。它们也用于数据插补(平均数插补、中位数插补和众数插补)。
相关系数和协方差矩阵用于研究数据集中各种特征之间的关系,也可用于特征选择和降维。
概率分布用于特征缩放,例如特征的标准化和规范化。概率分布和蒙特卡罗模拟也用于模拟数据。例如,如果样本数据根据具有已知平均值和标准偏差的正态分布分布,则可以使用正态分布的随机数生成器来生成群体数据集。
贝叶斯定理用于模型测试和评估,以及计算精度得分。
中心极限定理(CLT) 是统计学和数据科学中最重要的定理之一。CLT 认为,使用具有大量观测数据的样本数据集进行建模是有利的,因为样本越大,越接近总体。从这里了解更多关于 CLT 的知识: 利用蒙特卡罗模拟证明中心极限定理 。
R2 分数和 MSE 用于模型评估。下面是一篇用 R_2 分数和 MSE 进行模型评估的文章:
㈡多变量微积分
大多数机器学习模型是用具有几个特征或预测器的数据集构建的。因此,熟悉多变量微积分对于建立机器学习模型极其重要。以下是您需要熟悉的主题:
- 多元函数
- 导数和梯度
- 阶跃函数、Sigmoid 函数、Logit 函数、ReLU(校正线性单位)函数
- 价值函数
- 功能绘图
- 函数的最小值和最大值
关于如何在机器学习过程中使用多变量微积分的示例,请参见以下示例:
㈢线性代数
线性代数是机器学习中最重要的数学技能。数据集被表示为矩阵。线性代数用于数据预处理、数据转换、降维以及模型评估。
以下是您需要熟悉的主题:
- 向量
- 向量的范数
- 矩阵
- 矩阵的转置
- 矩阵的逆矩阵
- 矩阵的行列式
- 点积
- 本征值
- 特征向量
例如,协方差矩阵是一个非常有用的矩阵,可以显示特征之间的相关性。使用协方差矩阵,可以选择将哪些特征用作预测变量。下面举例说明协方差矩阵如何用于特征选择和降维: 利用协方差矩阵进行特征选择和降维 。
其他高级的特征选择和降维方法有主成分分析 (PCA),以及线性判别分析 (LDA)。要理解 PCA 和 LDA 是如何工作的,你需要理解线性代数的主题,比如转置一个矩阵;矩阵的逆矩阵;矩阵的行列式;点积;特征值;和特征向量。以下是 LDA 和 PCA 的一些实现:
㈣优化方法
大多数机器学习算法通过最小化目标函数来执行预测建模,从而学习为了获得预测标签而必须应用于测试数据的权重。以下是您需要熟悉的主题:
- 成本函数/目标函数
- 似然函数
- 误差函数
- 梯度下降算法及其变体(如随机梯度下降算法)
数据科学和机器学习中如何使用优化方法的例子可以在这里找到: 机器学习:使用梯度下降的 Python 线性回归估计器 。
总之,我们已经讨论了数据科学和机器学习中需要的基本数学和理论技能。有几门免费的在线课程会教你在数据科学中需要的必要的数学技能。作为一名数据科学家,一定要记住,数据科学的理论基础对于构建高效可靠的模型至关重要。
参考
- 在具有高度相关特征的数据集上训练机器学习模型。
- 使用协方差矩阵图进行特征选择和降维。
- 拉什卡、塞巴斯蒂安和瓦希德·米尔贾利利**。** Python 机器学习,第二版。帕克特出版社,2017 年。
- Benjamin O. Tayo,预测船只船员规模的机器学习模型,https://github . com/bot 13956/ML _ Model _ for _ Predicting _ Ships _ Crew _ Size。
Python 列表没有 Argmax 函数
有三种方法可以解决它
Photo by Talles Alves on Unsplash
亲爱的 Python 程序员们,
给定一个 Python 列表l = [50, 99, 67, 99, 48]
,如何找到列表的 argmax ?原来 Python list 没有内置的 argmax 函数!你不能只做argmax(l)
或max(l).index
。我能想到的第一件事是将列表转换成一个 numpy 数组,或者 pandas DataFrame,或者甚至 Tensorflow tensor,如果你认为可以过度使用的话:
import numpy as np
l_np = np.asarray(l)
print(l_np.argmax())import pandas as pd
l_pd = pd.DataFrame({'l': l})
print(l_pd.idxmax())import tensorflow as tf
l_tf = tf.constant(l)
with tf.Session() as sess:
print(sess.run(tf.argmax(l_tf)))
他们都返回1
,就像他们应该的那样。如果多个项目达到最大值,函数将返回遇到的第一个项目。但是将一个对象转换成另一种数据类型感觉像作弊,而且还需要更多的执行时间。以下是几种修复方法:
1.传统 C 逻辑
构建一个 for 循环,手动检查每一个元素,它工作得非常好。但是,它由多行可读性不太好的代码组成。与下面讨论的替代方案相比,它还需要稍微多一点的计算时间。
index, max_val = -1, -1
for i in range(len(l)):
if l[i] > max_val:
index, max_val = i, l[i]
print(index)
Photo by Nikhil Mitra on Unsplash
2.用计数器枚举
我们可以利用 Python 中enumerate
函数的内置计数器。注意max((x,i) for i,x in enumerate(l))[1]
返回最后一个最大项的索引,但是可以通过
-max((x,-i) for i,x in enumerate(l))[1]
但这绝不是可读的。我们还可以通过使用zip
来模仿enumerate
函数,使其更具可读性:
max(zip(l, range(len(l))))[1]
Photo by Tomas Sobek on Unsplash
3.更改密钥
这可能是最可读的黑客。原来我们还是可以使用默认的max
功能。但是我们没有将列表作为参数传递,而是将索引列表作为参数传递,并将一个函数作为“key”传递。该函数将索引映射到列表中相应的元素。
f = lambda i: l[i]
max(range(len(l)), key=f)
Photo by Silas Köhler on Unsplash
好了,Python 列表上执行 argmax 的三种方法。
相关文章
感谢您的阅读!如果您对 Python 感兴趣,请查看以下文章:
超越 lambda、map 和 filter 的 Python 技巧
towardsdatascience.com](/5-python-features-i-wish-i-had-known-earlier-bc16e4a13bf4) [## 使用交互式地图和动画可视化伦敦的自行车移动性
探索 Python 中的数据可视化工具
towardsdatascience.com](/visualizing-bike-mobility-in-london-using-interactive-maps-for-absolute-beginners-3b9f55ccb59)
原载于我的博客edenau . github . io。
必须有 50 种方法来制作一个[编码]播放列表
art by @marta.louzeiro
你只需要一首新歌,杰克
也许一些空气,克莱尔
这里有一些关于蜡的噩梦,马克斯
你只需要听这些
这是写给那些边听音乐边开夜车的程序员的博客。一篇关于将播放列表创建重新定义为集群问题的博客文章。最重要的是一篇关于理解制作这些“编码”播放列表的人的博文。
项目介绍
For reference, 123k minutes ≈ 25% of a Year
作为一名音乐消费者,我总是在寻找新的音乐。有了正确的配乐,编码就简单多了。问题是寻找新音乐依赖于探索周刊风格的播放列表和朋友的推荐。正是因为这个原因,我决定自己动手,有节奏地建立一些“编码”播放列表。
这些是我采取的步骤…
第一步:通过关键词抓取 Spotify 播放列表
正如你可能已经猜到的,创建播放列表的第一步是从 Spotify 上搜集播放列表集。这是通过获取一个 API 键并与 /search 端点交互来完成的。在这个上下文中,我们将搜索包含术语的播放列表:编码或编程。
这可以通过 python 包来实现, spotipy :
import os
from spotipy import Spotify
from spotipy.oauth2 import SpotifyClientCredentialscur_spotify = Spotify(
client_credentials_manager = SpotifyClientCredentials(
client_id=os.getenv("SPOTIFY_CLIENT_ID"),
client_secret=os.getenv("SPOTIFY_SECRET")
)
)cur_playlists = []for search_term in "coding programming".split():
cur_offset = 0
while cur_offset < 10000:
tmp_playlists = cur_spotify.search(
q=search_term, offset=cur_offset,
type='playlist', limit=50
)["playlists"]["items"]
if len(tmp_playlists) == 0 : break
cur_playlists.extend(tmp_playlists)
cur_offset += 50
一旦我们有了这些 ~10k 播放列表,我们将想要从其中提取曲目和艺术家的信息。这是通过循环遍历cur_playlists
并调用user_playlist_tracks
API 端点来完成的。与所有数据争论一样,会有一些缺失和无效的数据。此外,上面使用的cur_offset
方案将不得不重复读取超过 100 首歌曲的播放列表(根据 API-hit 的歌曲限制*)。*
此时,我们基本上有了一个 dictionary 对象,播放列表映射到其中出现的曲目。从这个数据集中可以做出的最简单的播放列表是在所有“编码”播放列表中找到出现最频繁的曲目。或者在伪代码中,
# find 250 most frequent tracks among all the playlistsfrom collections import Counterall_tracks = [ cur_item
for cur_list in playlist_tracks.values()
for cur_item in cur_list
]top_tracks = list(map(
lambda cur_tuple: cur_tuple[0],
Counter(all_tracks).most_common(250)
))
虽然这在技术上可行,但 播放列表 与我们预期的质量相差甚远。虽然你可能会认为 Air、蠢朋克和 Bonobo 会浮到顶部,但我们实际上最终会有 Billie Eilish、汉斯·季默和 Magic Sword 这样的流行歌曲占据空间——前两首歌曲实际上已经被禁止的艺术家名单明确删除(对于这个播放列表)。
这就是我们新项目的切入点。问题的解决方案是:比莉·埃利什紧随倭黑猩猩而来,将播放列表中的艺术家分成流派。
步骤 2:使用 TF-IDF 扩展磁道数
构建流派播放列表的关键是认识到艺术家和使用 NLP 技术的播放列表之间存在相似性。我的意思是,播放列表(或文档)包含一个有序的曲目(或单词)列表。这意味着大多数的自然语言处理技术都适用于这个问题,并且给了我们一个框架来理解这些类型是如何被聚类的。
具体地,在来自步骤 1 的数据中存在生成通过曲目计数连接艺术家和播放列表的矩阵的信息(例如,播放列表#42 包含 16 首蠢朋克歌曲)。
Example table showing how playlists and artists populate a track-counts matrix
该表说明了播放列表群集固有的几个问题:
- 对数正态播放列表长度
- 艺术家频率之间的极端差异
- 矩阵稀疏性(即很少非零值)
这些问题通过在轨道计数矩阵上使用 TF-IDF 标度来解决。这里,TF-IDF 中的 TF 代表词频,而 IDF 代表逆文档频。对于计数矩阵中的每个单元格,TF 项根据艺术家在单个播放列表(内部播放列表)中的相对重要性进行缩放。然而,IDF 在所有播放列表(内部播放列表)中衡量艺术家的重要性。与简单的原始计数相比,这两种效应放大了艺术家和播放列表对流派的重要性。
结合播放列表聚类,术语频率(TF)术语降低了来自长播放列表的重要性提升。仅仅因为播放列表 7 比播放列表 4 多了10 倍的蠢朋克,并不意味着它应该多算十倍(对他们的朋克来说)。假设相对艺术家重要性呈对数正态分布,则使用的 TF 术语为:
TF = log( 1 +跟踪计数)
这个 TF 公式极大地减少了选择艺术家时附加音轨计数的影响。例如,log _ 10(100)= 2–少了 50 倍。增加的一项是保持 0 计数固定在 0(为了保持稀疏)。这种 TF 缩放的实际效果是,汉斯·季默的全长专辑不会把他推到顶峰。
虽然 TF 术语关注某个艺术家在单个播放列表中的重要性,但是逆文档频率(IDF)淡化了出现在多个播放列表中的艺术家的重要性。例如,蠢朋克可能出现在超过 20%的“编码”播放列表中。如果使用原始计数的话,蠢朋克肯定会是顶级的星团之一,而且几乎完全是纯的。这意味着这个群体将被蠢朋克所主导,其他艺术家的意义将仅仅是随机的噪音。
因此,IDF 这个术语允许将蠢朋克的重要性分成几个流派。在这些流派中,蠢朋克现在几乎和其他流派一样重要,比如说,德玛乌和汉斯·季默。与自然语言处理类似,这些过于普通的艺术家(如汉斯·季默、倭黑猩猩和蠢朋克)被视为停用词(如“the”、“is”和“cause”)。从数学上讲,我们对 IDF 的处理如下:
简单地说,IDF 是一个由c_min
和c_max
限定的函数,它测量一个艺术家在不同播放列表中出现的频率。N(artist)
等式是对给定艺术家出现多少非零条目的度量。因此,N_max
是包含最频繁出现的艺术家的播放列表的数量。而N_min
是一个阈值计数,用于移除只出现在1, 2, ..., (N_min-1)
播放列表上的艺术家。
选择c_min
和c_max
的能力让你在如何对待稀有艺术家和普通艺术家方面拥有相当大的灵活性。例如,如果是c_min = 0
,那么最常用的词将被完全忽略,也就是说,它将是一个字面上的停用词。因为蠢朋克和汉斯·季默对我们很重要,所以我们将设置c_min = 1
。下一个参数,c_max
,是最罕见的艺术家从他们的计数中获得的额外权重。通过选择c_min = 1
,值c_max = 2
将导致稀有艺术家的每首曲目的价值是普通艺术家的两倍。
这个c_max
变量是一个旋钮,我们将在下一步调优集群时使用它。在最终的簇中使用的值是c_max = 3
。
The effects of changing c_min and c_max on the IDF scalar
步骤 3:使用 NMF 聚类创建流派
抓取 Spotify 和缩放曲目计数可能很有趣,但这与数据科学相去甚远。我们现在要把我们所有的努力放在一起,以便从播放列表和组成它们的音轨中制作流派。这将使用非负矩阵分解( NMF )来找到从“编码”播放列表语料库中出现的构造块。
The reason for using NMF to cluster playlists is much like the work done by the Zitnik group on facial analysis. Akin to genres, these masks – that add together to create someone’s face – are themselves face-like and easy to interpret.
NMF——或非负矩阵分解——是一种线性代数技术,可用于聚类文档。顾名思义,它只是将矩阵一分为二:
T =宽高
而且,就像原来的轨迹计数矩阵( T )一样,W 和 H 内部只有非负值。这迫使体裁具有建设性,并避免破坏性干扰的存在。
对于我们的问题,T
是一个曲目矩阵,它通过 TF-IDF 比例曲目计数将播放列表(行/条目)与艺术家(列/特征)联系起来。在经历 NMF 之后,我们剩下两个非负矩阵:W
和H
。由于它们必须相乘才能近似地重新创建T
,W
和H
在其外部维度上分别具有播放列表和艺术家。内在维度是从语料库中自然产生的原本潜在的体裁。他们就是我们一直在寻找的星团!
The goal of NMF clustering is to separate a matrix in two and find the latent clusters that separate the data
然而,构建和标记这些集群的过程与其说是科学,不如说是艺术。因为这是一个无人监督的学习问题,你能应用的唯一真正的度量是人类的直觉。即便如此,模式确实开始出现。使用流行的网站来贴标签(事后)—一次贴一个标签——给这个流派的内容增加了一些可信度。例如,blink-182 和 Sum 41 确实出现在“流行朋克群中,而巴赫和贝多芬安全地驻留在“古典群中。
现在缺少的是如何从这些播放列表( W )和艺术家( H )矩阵回到不同流派的顶级曲目选择的描述。在进入这一关键步骤之前,讨论一下在这一步骤中人为干预的位置是有益的。
第一点是,《vibrazebra.com》上使用的大约 30 个流派来自 75 个集群。这意味着我们丢弃了超过一半的集群!这可能是因为我没有认识到一些重要的流派,但这也是对杂乱数据进行聚类的必然结果。我们的数据集中有一些噪声,必须以某种方式将其聚类出来。
要说明的另一点是关于建模者为了改善结果而需要调整的参数。它们是:c_max
和集群的数量——n_clusters
——假设c_min
为 1。如前所述,我们使用了c_max = 3
,有 75 个集群。显然,这个n_clusters
决定了音乐类型变得有多粗糙。例如,蠢朋克会和语料库中的每一位电子艺术家归为一类吗(对少数人来说n_clusters
)还是基本上引领一个完整的流派(对许多人来说n_clusters
)。
对于最终用户来说,有趣的一点是c_max
和n_clusters
具有非线性关系。当改变n_clusters
时,必须根据经验缩放c_max
以保持相同的(无监督的)性能水平。
步骤 4:构建代表性播放列表
因此,我们剔除了 Spotify,创建了一个将播放列表与艺术家联系起来的矩阵,并将该矩阵分解为两部分,以发现潜在的流派。现在剩下的就是退出代表这些流派的曲目。这将利用一些漫不经心的方式从H
和W
矩阵中提取信息,以及一些有偏见的方法来定义什么是好的播放列表。举例来说,你就是这样得到下面这个标志性的编码者的播放列表的(现在没有了汉斯·季默、比莉·埃利什和德玛乌):
Now that’s what I call coding music! (Volume 12)
描述播放列表生成的起点是来自步骤 1 的原始的、天真的混合播放列表。我们很快讨论了构建代表性播放列表的最简单方法是如何找到在所有播放列表中出现频率最高的曲目。现在,我们只需要找到一种方法来概括和确定这种情况在每个集群中发生的范围——使用来自H
和W
因式分解矩阵的数据。
原始播放列表和聚类播放列表的主要区别在于,现在曲目的权重取决于艺术家和播放列表对流派的相对贡献。因此,代替每个轨道的原始计数,将使用一些正的浮点值,将几个分量相乘。虽然对读者来说这可能是显而易见的,但是现在详细说明以下内容是有用的:每个音轨与一个艺术家列表( A )和一个播放列表列表( P )相关联。利用这一知识,我们提出了以下用于给流派曲目评分的方法。
上面的第一个等式只是对上一段的重述:一首曲目的分数是基于其艺术家(A
)和播放列表(P
)对流派(j
)的相对重要性的合成值。这里,艺术家的成分, R ,只是在 H 中的给定音轨的艺术家中选择最大权重成分——对于一个流派。例如,当专注于电子音乐时,这可能会选择蠢朋克而不是法瑞尔·威廉姆斯作为 2013 年的热门歌曲《走运》。
另一方面,播放列表的组成部分 L 更符合最初的天真标准。这实际上意味着,我们基本上仍然在计算一首歌曲有多少个播放列表。关键区别在于,总和现在考虑了播放列表在流派上的相对重要性( W )以及播放列表有多少关注者(参见**S_i**
)。让我们对**S_i**
的描述更具体一点,我们希望它是一个基于播放列表的关注者数量来衡量重要性的因素——只是非常缓慢,从 1 开始。
在描述使用这个乐谱制作播放列表的实际过程之前,有必要解释一下出现在上述等式中 H 和 W 上方的~
。这些波浪符号(或旋转符号)意味着这些矩阵中的组件已经过规范化和重新缩放。为了清楚起见,这样做不是使用它们的原始值或它们的对数变换——因为根据经验发现两者都产生相当一般的播放列表。相反,每个矩阵中的类型向量首先被归一化,使得每个值都在范围[0,1]内。然后,每个值都被提升到某个幂:α代表艺术家,ρ代表播放列表。
我们的α和ρ值分别是3/4
和2/3
。请注意,这两个值都小于 1。这是因为范围[0,1]中的数字对于小于 1 的幂会变大(例如0.1 ^ 0.1 ≈ 0.8
)。实际上,这意味着使用更小的功率为越来越多不知名的播放列表/艺术家铺平了道路。对于我们的问题,这也证明了ρ小于α的原因:我们想要基于许多播放列表聚集列表,但是我们真的只想关注与流派最相关的艺术家。
有了所有的数字,我们现在可以做出定性的决定,让播放列表变得值得一听。首先要考虑的是两者的长度:整个播放列表和其中的歌曲。在这个练习中,我们选择制作播放列表 4 小时 长,并且只包含 2 到 12 分钟的歌曲。接下来,我们想防止任何一个艺术家完全控制一个群体。因此,我们将一位艺术家创作的歌曲数量限制为 3 首!
最后一个有点偏颇的决定是让播放列表按顺序播放。根据个人经验,这意味着一个艺术家永远不应该有两首歌背对背出现(或者甚至在另一首歌的两首歌内)。这一点在下面的 downtempo 播放列表中得到了强调,其中:Air、Bonobo 和 Gramatik 在列表顺序中被涂掉了。需要这样做的原因是因为聚类算法可能会对艺术家进行分层,导致自然混合的机会减少,从而加强已经发生的生日悖论行为。
open.spotify.com](https://open.spotify.com/playlist/2v9EqSueMu980UezpJ3J8t?si=JMPfUzttQgiLc8G6UcoBDA)
在进入探索创建这些播放列表的用户的部分之前,快速深入了解上一段中使用的重新排序算法是有用的。这种算法通过在曲目上不断向前和向后推送来防止艺术家在播放列表中聚集在一起。这种非确定性的方法在速度上可能不是最佳的,但确实产生了符合我们对聚集的限制的播放列表顺序——同时显著地限制了对初始顺序的扭曲。
第五步:了解 Spotify 上的编码员
在这个阶段,我们的功能已经完成:我们已经为所有的“编码”播放列表收集了 Spotify,并为 30 个不同的流派制作了一个代表性的播放列表。我们还没有问的一个问题是:为什么?为什么有人专门为编码制作播放列表?为什么他们选择某些艺术家作为他们的编程灵感?更重要的是,为什么我把变成了这个,而不是只听最初 Spotify 搜索中的热门播放列表?
除了亲自欣赏来自这些流派播放列表的音乐——并实际学习一些新歌——我认为在这次搜索中有真正的人类学数据。例如,编码是一个目前已经过时的词吗——是社交网络和 deadmau5 主宰的时代的残余吗?或者更一般地说,这些编码者是我们认为的那样吗:痴迷于“ chill- ”前缀音乐流派并喜欢听lofi hip hop radio-beats to relax/study to的千禧一代 redditors?
谁是用户这一更普遍的问题可能最好用下面的时间表来表示。投射一点,我相信这些用户:在中学 Rec 夜听林肯公园(流行朋克),在高中让花园之州真正改变他们的生活(Chillout),在大学期间实验天空中的爆炸( Ambient )。如果这种说法成立,那么用户似乎是在 2010 年左右(独立音乐)进入成年期,开始怀旧,其中一些在 2012 年被淘汰(Chillwave),到 Spotify 流行时,基本上已经放弃了歌词音乐( Chillhop )。
A violin plot showing coding music trends – or – what to project when you’re projecting
在另一条时间线上,同样的用户可能在他们上大学的时候就已经尝试过 house 了(蠢朋克和拉塔特)。想和杜布斯普(德莫和史奇雷克斯)一起继续派对。无法摆脱他们的固定,因为他们转向鼓和低音。并最终达到一种永久的 EDM 状态——被电雏菊嘉年华的灯光惊呆了。
虽然没有更多的人口统计数据,但这显然只是纯粹的猜测和传闻。然而,它确实引出了这样一个问题:制作这些编码播放列表的人是谁?这可以通过调查上述图的音轨部分得到部分解决。正如你所看到的,这种类型在 2010 年左右真正兴起——这一年《社交网络》问世——并一直保持到 2015 年的《机器人先生》。因此,这种类型很可能与千禧年科技文化有关。
然而,在我们对这些数字游牧民族的研究中,一个局限是我们对 Spotify 播放列表搜索的依赖。如上图所示,Spotify 直到 2014 年左右才成为占主导地位的音乐平台。因此,我们的许多类型都来自于史前时代,当时人们在各种服务中分散开来。因此,这些民间传说只是人们从故国带来的东西,无论是来自 Songza、Pandora、Grooveshark、iTunes 还是 SoundCloud。
虽然我们可能永远无法确切知道这些千禧一代的编码者是谁,但我们可以肯定地说,他们的音乐有许多共同的核心特征:重复的节拍,数字乐器,以及普遍缺乏歌词——这是代码使音乐成为可能。
结束语
这个项目的目标是:使用聚类技术创建流派播放列表。特别是,我们专注于通过搜索 Spotify 来“编码”播放列表而找到的播放列表语料库。为了构建在 vibrazebra 上发现的 30 个集群,我们随后使用了 NMF 集群和定制的 TF-IDF 轨道计数缩放。在找到带有 NMF 的聚类后,我们标记了这些组,并使用特殊的曲目选择过程构建了代表性的播放列表。
为了给这个项目画上一个圆满的句号,我们在聚类过程之后进行了一个快速调查,看看到底是谁制作了这些“编码”播放列表。然后我们做了一些总结性发言。现在,…
时光流逝,歌曲结束,我想我还有更多的话要说。
——时间(平克·弗洛伊德)
Common words used in “coding” playlist titles