成功实现数据可视化的通用原则
数据可视化是一个热门话题。让我们通过理解使用的 4 个主要原型来发现如何掌握这一新学科。
近年来,数据可视化一直是大家谈论的话题!它是数据民主化过程的升华,使小型和大型企业都能够根据数据和见解而不是直觉或预先固定的想法来制定业务战略。数据可视化是赋予我们手边大量数据生命的学科。好处多多!它使企业能够理解他们跟踪的数据并据此采取行动。它允许记者和科学家对他们的发现给出严格的解释。数据可视化为全世界成千上万的人提供了操作和试验数据的乐趣。它支持世界各地的社区解决全球性问题,如贫困、健康和环境。它将数据提升到一个前所未有的新维度和新能力!
如今,与数据可视化密切相关的新角色激增(数据可视化工程师、分析师、专家、主管等)。)越来越多的数据可视化爱好者聚集在社区中,寻找灵感,尝试新的方法并从中获得乐趣(#改头换面星期一和#datafam 就是最好的例子)。
谷歌“数据可视化”趋势,全球
有几个不断发展的 BI 工具可以轻松制作出令人惊叹的可视化效果。最近的例子包括像 Power BI、Looker 和 Tableau 这样的工具,它们都渴望在这样一个充满挑战和要求苛刻的市场中升级和强化它们的产品。
这篇文章旨在回答一个简单的问题:是什么让可视化变得成功和有效?简答:不仅仅是选择的工具!实现目标-观众契合是视觉化伟大而有效的原因。目的是什么——受众契合度?从增长词汇(“产品-市场匹配”)中获取概念,目的-受众匹配可视化是一个 viz,它成功地将主要核心信息与受众和最终用户的特征联系起来,这些特征包括人口统计数据、主题知识、公司/社会中的角色、可用于消化 viz 和人物角色的时间。
有四个主要的原型,其中包括最常见的目的-观众适合。每个原型使用不同的方法来实现这种匹配。
1)绩效记分卡
目的— 绩效记分卡旨在向注意力和信息消费时间有限的受众传达一些关键信息。
设计— 绩效记分卡是冷静、必要且直截了当的可视化工具。用户一眼就能知道业务中正在发生什么。这意味着设计应该是最小化的,包括带有大格式数字的简单的基本图形,这对于在仪表板上合成信息是有用的。对颜色的关注不那么重要,但可以帮助区分积极和消极的趋势和表现(经典的好绿坏红)。
受众— 对主题有丰富知识但消化信息的时间有限的人。
示例— 绩效记分卡原型可以包括不同种类的仪表板。它可以参考财务/执行记分卡,这是一个由高层管理人员广泛使用的仪表板,用于浏览业务的健康状况和主要 KPI。它可以是一个顶级营销仪表板,营销人员可以在其中立即发现渠道的流量表现、活动 ROI 和客户洞察,或者它可以是一个带有医院成就和业绩等的医疗保健记分卡。
由 Austin Distel 在 Unsplash 上拍摄的照片
2)自助服务工具
目的— 自助服务工具使用户能够主动操作和使用可视化工具,找到自己的结论和结果。
设计— 自助服务工具通常通过清晰的说明和清晰的步骤来指导最终用户如何使用可视化。该原型嵌入了要修改的过滤器和参数的阵列,以便根据用户的需求进行定制。颜色的重要性较低,文本框支持具有用户友好的结构,对如何定制分析和结果解释有清晰的指导。
受众— 自助工具的最终用户对该主题有很好的理解,他们对此感兴趣,并且他们有时间深入研究该主题,并将可视化用于自己的发现和目的。
示例— 自助服务工具通常由业务分析师生成,使最终用户能够在业务方面进行自我分析。如果您需要填写表格或使用过滤器来获得结果,也可以在互联网上找到大量的示例。一个著名的例子(带有业绩记分卡的特征)是约翰·霍普斯金大学冠状病毒资源中心仪表板。
3)讲故事可视化
目的— 讲故事可视化用于支持、加强和促进对见解和信息的理解。它们使消费者能够理解和吸收作者的结论。
设计— 讲故事可视化使用各种图表来传达见解。通常,这些图表是根据数据科学已经建立的通用最佳实践构建的(时间线的折线图、不同类别维度的条形图、定量与定量分析的散点图等)。颜色对于区分不同的维度/类别以及便于阅读图表/故事流至关重要。
受众— 这种原型的受众通常对主题的理解为中低水平,没有足够的时间自己检索信息。
示例— 在几乎所有需要分析和解释所做工作的演示中,都可以找到讲述故事的图表。近年来,媒体行业已经成功地利用结构良好的可视化效果来提高文章和信息的质量。《纽约时报》是一个成功利用数据可视化机会的媒体参与者的典型例子。
4)数据艺术
目的— 如今,数据拥有巨大的力量,可以作为让观众惊叹的有效工具。这种原型的主要目的是让 viz 消费者惊讶和着迷,并强烈强化创作者的信息。
设计— 在这个原型中,所有的通用标准都被抛弃了。在如何显示数据,使用什么颜色,应用什么图像或装饰,以及可视化的基调上有绝对的自由。在数据艺术原型中,所有关于数据可视化的最佳实践都倾向于创作者的设计风格和 viz 旨在传达的信息。
观众— 数据艺术可视化通常面向普通大众,他们可能了解也可能不了解所探索的主题。
例子— 数据艺术可视化可以在新闻文章和文章、政治家的演讲和专业网站中找到。
结论
面对我们在现实生活中发现的无数情况,并简单地将它们分成严格的类别,总是具有挑战性。尽管如此,这篇文章中列出的四个原型涵盖了一个优秀的 data viz 创建者在构思和构建阶段应该知道的大多数用例。理解它们是达到成功目的的关键——适合观众,因此是有效的视觉化。
Unix 权限—最简单的方法
所有 chmod 置换的索引
我最近不得不交付一个托管在 Linux 中的项目,我已经记不清我不得不摆弄文件和目录权限的次数了……是的我了解它们是如何工作的;是的我可以在脑子里算出来;是的,到目前为止,我已经记住了最常见的一些;是的我可以使用stat
命令;是的,我可以用符号符号来代替!但是为什么我要这样做。单身。有时间让我看看吗?所以我写了一个快速的 python 脚本来生成完整的权限列表,然后我用它作为参考来查找我遇到的任何组合。
对于那些你更喜欢把你的脑力用在重要的事情上,而你只是想要平凡事情的简单答案的情况,这篇文章是给你的。我会很快地介绍一下基础知识,以免成为有史以来最没有启发性的文章,但是…
我的主要意图是提供一个从 0 到 777 的所有权限排列的“备忘单”!
基础知识
Unix 权限被授予三个不同的实体:
- 物主
- 小组成员
- 其他人(即世界)
有三个权限属性:r、w 和 x。
read ® 读取一个文件的内容或者列出一个目录中所有文件的能力。
写(w)改变一个文件的内容或在目录中创建新文件。
execute (x)
这意味着有权执行一个文件(如 bash 或程序)。对于目录,执行权限授予进入目录和访问其中任何文件的权利。
权限不仅可以用 rwx 来表示,还可以用八进制的符号来表示(0 到 7)。每个八进制是二进制数字系统中其组成位的总和:
一组三个八进制反映了三个实体:从左到右,第一个数字代表用户的权限,第二个数字代表组的权限,第三个数字代表其他的权限。
就是这样!这确实很简单!
简单的方法
正如承诺的那样,你可以在下面随意查找:
**0**----------**1**---------x**2**--------w-**3**--------wx**4**-------r--**5**-------r-x**6**-------rw-**7**-------rwx**10**------x---**11**------x--x**12**------x-w-**13**------x-wx**14**------xr--**15**------xr-x**16**------xrw-**17**------xrwx**20**-----w----**21**-----w---x**22**-----w--w-**23**-----w--wx**24**-----w-r--**25**-----w-r-x**26**-----w-rw-**27**-----w-rwx**30**-----wx---**31**-----wx--x**32**-----wx-w-**33**-----wx-wx**34**-----wxr--**35**-----wxr-x**36**-----wxrw-**37**-----wxrwx**40**----r-----**41**----r----x**42**----r---w-**43**----r---wx**44**----r--r--**45**----r--r-x**46**----r--rw-**47**----r--rwx**50**----r-x---**51**----r-x--x**52**----r-x-w-**53**----r-x-wx**54**----r-xr--**55**----r-xr-x**56**----r-xrw-**57**----r-xrwx**60**----rw----**61**----rw---x**62**----rw--w-**63**----rw--wx**64**----rw-r--**65**----rw-r-x**66**----rw-rw-**67**----rw-rwx**70**----rwx---**71**----rwx--x**72**----rwx-w-**73**----rwx-wx**74**----rwxr--**75**----rwxr-x**76**----rwxrw-**77**----rwxrwx**100**---x------**101**---x-----x**102**---x----w-**103**---x----wx**104**---x---r--**105**---x---r-x**106**---x---rw-**107**---x---rwx**110**---x--x---**111**---x--x--x**112**---x--x-w-**113**---x--x-wx**114**---x--xr--**115**---x--xr-x**116**---x--xrw-**117**---x--xrwx**120**---x-w----**121**---x-w---x**122**---x-w--w-**123**---x-w--wx**124**---x-w-r--**125**---x-w-r-x**126**---x-w-rw-**127**---x-w-rwx**130**---x-wx---**131**---x-wx--x**132**---x-wx-w-**133**---x-wx-wx**134**---x-wxr--**135**---x-wxr-x**136**---x-wxrw-**137**---x-wxrwx**140**---xr-----**141**---xr----x**142**---xr---w-**143**---xr---wx**144**---xr--r--**145**---xr--r-x**146**---xr--rw-**147**---xr--rwx**150**---xr-x---**151**---xr-x--x**152**---xr-x-w-**153**---xr-x-wx**154**---xr-xr--**155**---xr-xr-x**156**---xr-xrw-**157**---xr-xrwx**160**---xrw----**161**---xrw---x**162**---xrw--w-**163**---xrw--wx**164**---xrw-r--**165**---xrw-r-x**166**---xrw-rw-**167**---xrw-rwx**170**---xrwx---**171**---xrwx--x**172**---xrwx-w-**173**---xrwx-wx**174**---xrwxr--**175**---xrwxr-x**176**---xrwxrw-**177**---xrwxrwx**200**--w-------**201**--w------x**202**--w-----w-**203**--w-----wx**204**--w----r--**205**--w----r-x**206**--w----rw-**207**--w----rwx**210**--w---x---**211**--w---x--x**212**--w---x-w-**213**--w---x-wx**214**--w---xr--**215**--w---xr-x**216**--w---xrw-**217**--w---xrwx**220**--w--w----**221**--w--w---x**222**--w--w--w-**223**--w--w--wx**224**--w--w-r--**225**--w--w-r-x**226**--w--w-rw-**227**--w--w-rwx**230**--w--wx---**231**--w--wx--x**232**--w--wx-w-**233**--w--wx-wx**234**--w--wxr--**235**--w--wxr-x**236**--w--wxrw-**237**--w--wxrwx**240**--w-r-----**241**--w-r----x**242**--w-r---w-**243**--w-r---wx**244**--w-r--r--**245**--w-r--r-x**246**--w-r--rw-**247**--w-r--rwx**250**--w-r-x---**251**--w-r-x--x**252**--w-r-x-w-**253**--w-r-x-wx**254**--w-r-xr--**255**--w-r-xr-x**256**--w-r-xrw-**257**--w-r-xrwx**260**--w-rw----**261**--w-rw---x**262**--w-rw--w-**263**--w-rw--wx**264**--w-rw-r--**265**--w-rw-r-x**266**--w-rw-rw-**267**--w-rw-rwx**270**--w-rwx---**271**--w-rwx--x**272**--w-rwx-w-**273**--w-rwx-wx**274**--w-rwxr--**275**--w-rwxr-x**276**--w-rwxrw-**277**--w-rwxrwx**300**--wx------**301**--wx-----x**302**--wx----w-**303**--wx----wx**304**--wx---r--**305**--wx---r-x**306**--wx---rw-**307**--wx---rwx**310**--wx--x---**311**--wx--x--x**312**--wx--x-w-**313**--wx--x-wx**314**--wx--xr--**315**--wx--xr-x**316**--wx--xrw-**317**--wx--xrwx**320**--wx-w----**321**--wx-w---x**322**--wx-w--w-**323**--wx-w--wx**324**--wx-w-r--**325**--wx-w-r-x**326**--wx-w-rw-**327**--wx-w-rwx**330**--wx-wx---**331**--wx-wx--x**332**--wx-wx-w-**333**--wx-wx-wx**334**--wx-wxr--**335**--wx-wxr-x**336**--wx-wxrw-**337**--wx-wxrwx**340**--wxr-----**341**--wxr----x**342**--wxr---w-**343**--wxr---wx**344**--wxr--r--**345**--wxr--r-x**346**--wxr--rw-**347**--wxr--rwx**350**--wxr-x---**351**--wxr-x--x**352**--wxr-x-w-**353**--wxr-x-wx**354**--wxr-xr--**355**--wxr-xr-x**356**--wxr-xrw-**357**--wxr-xrwx**360**--wxrw----**361**--wxrw---x**362**--wxrw--w-**363**--wxrw--wx**364**--wxrw-r--**365**--wxrw-r-x**366**--wxrw-rw-**367**--wxrw-rwx**370**--wxrwx---**371**--wxrwx--x**372**--wxrwx-w-**373**--wxrwx-wx**374**--wxrwxr--**375**--wxrwxr-x**376**--wxrwxrw-**377**--wxrwxrwx**400**-r--------**401**-r-------x**402**-r------w-**403**-r------wx**404**-r-----r--**405**-r-----r-x**406**-r-----rw-**407**-r-----rwx**410**-r----x---**411**-r----x--x**412**-r----x-w-**413**-r----x-wx**414**-r----xr--**415**-r----xr-x**416**-r----xrw-**417**-r----xrwx**420**-r---w----**421**-r---w---x**422**-r---w--w-**423**-r---w--wx**424**-r---w-r--**425**-r---w-r-x**426**-r---w-rw-**427**-r---w-rwx**430**-r---wx---**431**-r---wx--x**432**-r---wx-w-**433**-r---wx-wx**434**-r---wxr--**435**-r---wxr-x**436**-r---wxrw-**437**-r---wxrwx**440**-r--r-----**441**-r--r----x**442**-r--r---w-**443**-r--r---wx**444**-r--r--r--**445**-r--r--r-x**446**-r--r--rw-**447**-r--r--rwx**450**-r--r-x---**451**-r--r-x--x**452**-r--r-x-w-**453**-r--r-x-wx**454**-r--r-xr--**455**-r--r-xr-x**456**-r--r-xrw-**457**-r--r-xrwx**460**-r--rw----**461**-r--rw---x**462**-r--rw--w-**463**-r--rw--wx**464**-r--rw-r--**465**-r--rw-r-x**466**-r--rw-rw-**467**-r--rw-rwx**470**-r--rwx---**471**-r--rwx--x**472**-r--rwx-w-**473**-r--rwx-wx**474**-r--rwxr--**475**-r--rwxr-x**476**-r--rwxrw-**477**-r--rwxrwx**500**-r-x------**501**-r-x-----x**502**-r-x----w-**503**-r-x----wx**504**-r-x---r--**505**-r-x---r-x**506**-r-x---rw-**507**-r-x---rwx**510**-r-x--x---**511**-r-x--x--x**512**-r-x--x-w-**513**-r-x--x-wx**514**-r-x--xr--**515**-r-x--xr-x**516**-r-x--xrw-**517**-r-x--xrwx**520**-r-x-w----**521**-r-x-w---x**522**-r-x-w--w-**523**-r-x-w--wx**524**-r-x-w-r--**525**-r-x-w-r-x**526**-r-x-w-rw-**527**-r-x-w-rwx**530**-r-x-wx---**531**-r-x-wx--x**532**-r-x-wx-w-**533**-r-x-wx-wx**534**-r-x-wxr--**535**-r-x-wxr-x**536**-r-x-wxrw-**537**-r-x-wxrwx**540**-r-xr-----**541**-r-xr----x**542**-r-xr---w-**543**-r-xr---wx**544**-r-xr--r--**545**-r-xr--r-x**546**-r-xr--rw-**547**-r-xr--rwx**550**-r-xr-x---**551**-r-xr-x--x**552**-r-xr-x-w-**553**-r-xr-x-wx**554**-r-xr-xr--**555**-r-xr-xr-x**556**-r-xr-xrw-**557**-r-xr-xrwx**560**-r-xrw----**561**-r-xrw---x**562**-r-xrw--w-**563**-r-xrw--wx**564**-r-xrw-r--**565**-r-xrw-r-x**566**-r-xrw-rw-**567**-r-xrw-rwx**570**-r-xrwx---**571**-r-xrwx--x**572**-r-xrwx-w-**573**-r-xrwx-wx**574**-r-xrwxr--**575**-r-xrwxr-x**576**-r-xrwxrw-**577**-r-xrwxrwx**600**-rw-------**601**-rw------x**602**-rw-----w-**603**-rw-----wx**604**-rw----r--**605**-rw----r-x**606**-rw----rw-**607**-rw----rwx**610**-rw---x---**611**-rw---x--x**612**-rw---x-w-**613**-rw---x-wx**614**-rw---xr--**615**-rw---xr-x**616**-rw---xrw-**617**-rw---xrwx**620**-rw--w----**621**-rw--w---x**622**-rw--w--w-**623**-rw--w--wx**624**-rw--w-r--**625**-rw--w-r-x**626**-rw--w-rw-**627**-rw--w-rwx**630**-rw--wx---**631**-rw--wx--x**632**-rw--wx-w-**633**-rw--wx-wx**634**-rw--wxr--**635**-rw--wxr-x**636**-rw--wxrw-**637**-rw--wxrwx**640**-rw-r-----**641**-rw-r----x**642**-rw-r---w-**643**-rw-r---wx**644**-rw-r--r--**645**-rw-r--r-x**646**-rw-r--rw-**647**-rw-r--rwx**650**-rw-r-x---**651**-rw-r-x--x**652**-rw-r-x-w-**653**-rw-r-x-wx**654**-rw-r-xr--**655**-rw-r-xr-x**656**-rw-r-xrw-**657**-rw-r-xrwx**660**-rw-rw----**661**-rw-rw---x**662**-rw-rw--w-**663**-rw-rw--wx**664**-rw-rw-r--**665**-rw-rw-r-x**666**-rw-rw-rw-**667**-rw-rw-rwx**670**-rw-rwx---**671**-rw-rwx--x**672**-rw-rwx-w-**673**-rw-rwx-wx**674**-rw-rwxr--**675**-rw-rwxr-x**676**-rw-rwxrw-**677**-rw-rwxrwx**700**-rwx------**701**-rwx-----x**702**-rwx----w-**703**-rwx----wx**704**-rwx---r--**705**-rwx---r-x**706**-rwx---rw-**707**-rwx---rwx**710**-rwx--x---**711**-rwx--x--x**712**-rwx--x-w-**713**-rwx--x-wx**714**-rwx--xr--**715**-rwx--xr-x**716**-rwx--xrw-**717**-rwx--xrwx**720**-rwx-w----**721**-rwx-w---x**722**-rwx-w--w-**723**-rwx-w--wx**724**-rwx-w-r--**725**-rwx-w-r-x**726**-rwx-w-rw-**727**-rwx-w-rwx**730**-rwx-wx---**731**-rwx-wx--x**732**-rwx-wx-w-**733**-rwx-wx-wx**734**-rwx-wxr--**735**-rwx-wxr-x**736**-rwx-wxrw-**737**-rwx-wxrwx**740**-rwxr-----**741**-rwxr----x**742**-rwxr---w-**743**-rwxr---wx**744**-rwxr--r--**745**-rwxr--r-x**746**-rwxr--rw-**747**-rwxr--rwx**750**-rwxr-x---**751**-rwxr-x--x**752**-rwxr-x-w-**753**-rwxr-x-wx**754**-rwxr-xr--**755**-rwxr-xr-x**756**-rwxr-xrw-**757**-rwxr-xrwx**760**-rwxrw----**761**-rwxrw---x**762**-rwxrw--w-**763**-rwxrw--wx**764**-rwxrw-r--**765**-rwxrw-r-x**766**-rwxrw-rw-**767**-rwxrw-rwx**770**-rwxrwx---**771**-rwxrwx--x**772**-rwxrwx-w-**773**-rwxrwx-wx**774**-rwxrwxr--**775**-rwxrwxr-x**776**-rwxrwxrw-**777**-rwxrwxrwx
感谢阅读!
我经常在媒体上写关于领导力、技术&的数据——如果你想阅读我未来的帖子,请‘关注’我 !
Matplotlib 的无限灵活性
你可以在你的土地上做任何事。
韦斯利·廷吉在 Unsplash 上拍摄的照片
Matplotlib 是一个广泛使用的 Python 数据可视化库,它提供了大量的 2D 和 3D 绘图,对于数据分析和机器学习任务非常有用。
创建一个复杂情节的语法可能看起来吓人,但它提供了很大的灵活性。您几乎可以接触到图上的任何组件并对其进行定制。
在这篇文章中,我将向你展示如何创建一个基本的情节,并通过各种方式定制它。我们将在脚本层工作,这是 matplotlib.pyplot 接口。您最有可能在您的分析中使用这个界面。
import matplotlib.pyplot as plt
%matplotlib inline #render plots in notebook
一切都是从创建一个图形开始的,它是将所有东西结合在一起的主要艺术家对象。
plt.figure(figsize=(10,6))
我们创建了一个大小为(10,6)的图形。让我们在这个图上画一个基本的柱状图。
plt.figure(figsize=(10,6))plt.bar(x=[3,4,2,1], height=[3,5,1,6])
x 轴上的刻度太多。如果只显示条形的刻度,效果会更好。此外,我想扩展 y 轴上的范围,以便最高的栏不会到达顶部。我们也可以在 x 记号上分配标签,我认为这比简单的数字更好看。
plt.figure(figsize=(10,6))plt.bar(x=[3,4,2,1], height=[3,5,1,6])plt.xticks(ticks=[1,2,3,4],
labels=['Rome','Madrid','Istanbul','Houston'])plt.yticks(ticks=np.arange(10))
更高的 punto 标签看起来更好。让我们增加它,并使用旋转参数旋转标签。加个标题也不错。
plt.figure(figsize=(10,6))plt.title("Use Your Own Title", fontsize=15)plt.bar(x=[3,4,2,1], height=[3,5,1,6])plt.xticks(ticks=[1,2,3,4], labels=['Rome','Madrid','Istanbul','Houston'], rotation="45", fontsize=12)plt.yticks(ticks=np.arange(10), fontsize=12)
我们可以用 facecolor 参数给 Figure 对象添加一个框架。xlabel 和 ylabel 函数可用于添加轴标签。也可以使用条形功能的宽度参数来调整条形的宽度。
plt.figure(figsize=(10,6), facecolor="lightgreen")plt.title("Use Your Own Title", fontsize=15)plt.bar(x=[3,4,2,1], height=[3,5,1,6], width=0.5)plt.xticks(ticks=[1,2,3,4], labels=['Rome','Madrid','Istanbul','Houston'], rotation="45", fontsize=12)plt.yticks(ticks=np.arange(10), fontsize=12)plt.xlabel("Cities", fontsize=14)plt.ylabel("Measurement", fontsize=14)
注释和文本可以用在绘图上,使它们更具信息性或传递信息。Matplotlib 也允许添加它们。
我们将在图上使用 Axes 对象来添加文本和注释。为了在轴上定位这些附件,我们需要使用坐标。因此,添加网格线是有意义的,这可以通过 plt.grid()函数来实现。
让我们首先添加网格线和一个文本框。
ax = plt.figure(figsize=(10,6), facecolor="lightgreen").add_subplot(111)plt.title("Use Your Own Title", fontsize=15)plt.bar(x=[3,4,2,1], height=[3,5,1,6], width=0.5)plt.xticks(ticks=[1,2,3,4], labels=['Rome','Madrid','Istanbul','Houston'], rotation="45", fontsize=12)plt.yticks(ticks=np.arange(10), fontsize=12)plt.xlabel("Cities", fontsize=14)plt.ylabel("Measurement", fontsize=14)plt.grid(which='both')ax.text(3.5, 8, 'Daily Average', style='italic', fontsize=12,
bbox={'facecolor': 'grey', 'alpha': 0.5, 'pad': 5})
ax.text()函数的前两个参数分别是 x 和 y 坐标。指定文本和样式后,我们使用 bbox 参数在它周围添加一个框。
让我们也添加一个注释。我不想重复代码的相同部分,所以我只写注释部分。您可以将它添加到生成前一个图的代码中。
ax.annotate('highest', xy=(1, 6), xytext=(1.5, 7), fontsize=13, arrowprops=dict(facecolor='black', shrink=0.05))
我们添加了一个指向最高栏顶部的箭头。 xy 参数包含箭头的坐标, xytext 参数指定文本的位置。 Arrowprops ,顾名思义,是用来给箭头造型的。
一旦你对你的绘图满意,你可以使用 plt.savefig() 函数保存它。
这篇文章的目的是向你展示 Matplotlib 的巨大灵活性。我们制作的情节可能一点用也没有,但它们传达了信息。语法可能看起来不必要的长,但这是灵活性的代价。
值得一提的是,这只是你可以用 Matplotlib 创建的一部分。例如,支线剧情的结构是一个完全不同的话题。一旦您熟悉了 Matplotlib,您可以创建的东西就没有限制了。
感谢阅读。如果您有任何反馈,请告诉我。
无限免费虚拟主机和域名
在 Unsplash 上由 Austin Schmid 拍摄的照片
非常适合投资组合、开博客、自由职业或做生意
有一个网络个人资料很酷,对吧?它保持在线,任何人都可以看到,与任何人分享都非常容易。您可以在一个网址上分享您的作品或作品集。
你不必成为一个前端开发人员,使投资组合!
有一些技巧可以让你在不了解 HTML、CSS 或前端开发的情况下制作一个网络作品集
如果你是一名开发很酷的机器学习和深度学习项目的机器学习工程师,让它具有交互性将是向人们展示你的模型有多强大的完美方式。
如果你是一个入门级的 web 开发人员或软件开发人员,你可能正在寻找一种方法来托管你的代码,以便你可以更好地表达你的工作。不然光看代码就能无聊!视觉是更有效的方式。
如果你是一名数据科学家、数据分析师、自由职业者,或者计划开始一项新业务,需要一个登录页面,但在预算紧张的情况下,你会感谢这些免费资源。
我想分享一些很酷的资源,可以帮助你免费获得网络作品集。
这是无限的免费托管服务。它甚至有一个漂亮的 c 面板和主机服务的常规功能,如数据库、WordPress 安装选项等等。你可以完全免费地托管任意多的网站。是不是很神奇!
在我开始科技之旅的时候,我做过一段时间的 WordPress 开发者。我需要制作一个作品集来展示我找工作的技能。我用这个网站托管了几个我建的虚拟网站。
你也可以在开始的时候为你的小企业使用这种托管服务。
如果你愿意,你可以稍后将你的网站转移到其他主机服务。伟大的事情是,你仍然可以保持你相同的网站地址或域名。
所以,这可以作为你的一个临时占位符或者一个起点。如果你不喜欢这项服务,只需将你的网站转移到付费托管服务。
这是免费托管。域名呢?
你也可以获得无限的免费域名。
这是一个你可以获得无限免费域名的网站。你不太可能得到一个. com。网,或者。org 域名免费。你必须为那些高级域名付费。因为他们总是随叫随到。
免费的域名:。tk,。ml,。嘎,。cf,或者. gq .所以,你的网站看起来会像 myDomain.tk 或者 mydomain.ml。
这些听起来可能不吸引人,但它们服务于我托管虚拟网站和创建投资组合的目的。
如果你正在寻找一个免费的投资组合,这是伟大的!但是如果你想要一个. net。com,或者。org 网站,你可以为域名付费,但仍然可以享受免费托管。
你将获得一个为期一年的域名。一年后你可以再次免费续费。如果您决定更改域名,您也可以进行转让。
现在,不用学 HTML,CSS 就学会自己制作作品集吧!
如果你有主机和域名,你可以免费安装 WordPress。这是一个惊人的资源。如果你不是一个前端开发人员,建立一个网络投资组合可能会很可怕。简单地使用 WordPress。
它有数百个主题。根据您的需求选择一个。youtube 上有数百个教程。这可能需要一两天甚至更长的时间来找到一个合适的主题和一个适合你的教程。
我简单地使用了一个名为 Ocean-wp 的主题和一个页面生成器 Elementor。免费版本足以构建一个伟大的投资组合。这只是一个简单的拖放方法。
还有其他页面生成器。我说 Elementor 是因为那是我用的。最方便的是,youtube 上有很多教程展示如何使用它。
这是我用海洋 wp 主题和元素创建的博客:
在你获得成为数据科学家的技能后,最重要的事情是什么?必须是为了炫耀你的…
regenerativetoday.com](https://regenerativetoday.com/)
如果你能学会如何使用一个好的页面生成器,你就可以用任何主题创建一些非常酷的东西!
结论
我写这篇文章是因为我不得不花很多时间去寻找一些免费的主机和域名,在那里我可以托管我的代码。我花了一段时间才找到他们。这些类型的免费资源对初学者甚至是刚起步的公司都非常有用。希望对你有帮助。如果你和他们一起做了很酷的事情,请不要犹豫在评论区分享。
阅读推荐:
让我们炫耀一下
towardsdatascience.com](/how-to-show-off-your-data-science-or-software-engineering-skills-effectively-dca18e059b38) [## 想在 12 周内成为数据科学家?
花钱前再想一想
towardsdatascience.com](/want-to-become-a-data-scientist-in-12-weeks-3926d8eacee2) [## 学习编程、软件工程、机器学习等的最佳免费资源
找到所有高质量的计算机科学课程,从麻省理工学院,哈佛大学,和其他大的大学成为专家…
towardsdatascience.com](/best-free-resources-to-learn-programming-software-engineering-machine-learning-and-more-89ee724b90c3) [## 使用 Python 从零开始的多类分类算法:分步指南
本文介绍两种方法:梯度下降法和优化函数法
towardsdatascience.com](/multiclass-classification-algorithm-from-scratch-with-a-project-in-python-step-by-step-guide-485a83c79992) [## 使用 Python 的 Scikit-Learn 库:机器学习,用几行代码实现简单的 KNN 分类器
用一个项目清晰地解释一些核心的机器学习概念
towardsdatascience.com](/simple-knn-classifier-with-four-lines-of-code-for-beginners-machine-learning-5344d125360f) [## Pandas 的 Groupby 功能详细,可进行高效的数据汇总和分析
学习对数据进行分组和汇总,以使用聚合函数、数据转换、过滤、映射、应用函数…
towardsdatascience.com](/master-pandas-groupby-for-efficient-data-summarizing-and-analysis-c6808e37c1cb)**
解锁您已经拥有的物联网数据
物联网
您的建筑物中已经存在的 5 种传感器数据来源
扎克·沃尔夫在 Unsplash 上的照片
M 机器学习模型极大地受益于物联网传感器数据。AI 和 IoT 早就是一丘之貉,就问连线。
物联网(IoT)是所有的乐趣和游戏,直到是时候购买传感器。众所周知,物联网难以扩展和管理— 30%的物联网项目从未通过概念验证阶段。
但是,如果我告诉您,您的建筑物中已经有未开发的物联网传感器数据,会怎么样?每座现代商业建筑都布满了传感器。它们已经大规模安装和管理。问题是它们是用于单一用途的封闭系统。
但是,如果您可以解锁传感器数据,它们可以在不需要新硬件的情况下为许多数据科学项目提供动力。
这里有 5 个你应该探索利用的企业系统:
1.建筑管理系统(BMS)
楼宇管理系统包括一些控制建筑环境的技术。最常见的包括照明控制系统和 HVAC(空调)系统。
最基本的照明控制系统包括房间和走道中的运动传感器——以前当灯熄灭时,你可能不得不在房间里挥舞手臂。更先进的照明控制系统包括窗户附近的流明传感器,可以在自然光较多的区域调暗灯光以节省电力。
HVAC 系统包括遍布整个建筑的温度传感器,以根据需要调节冷却和加热。
这两个系统在整个建筑中都有环境传感器,只需要一些创造性的集成,就可以将这些传感器反馈用于物联网应用。事实上,这种转变已经发生,因为 BMS 应用受益于物联网集成。
这是一个伪数据集,显示了您可能从 BMS 运动传感器获得的信息:
{
"zoneID": 129043805,
"zoneName": "**Front Lobby**",
"events": [
"movement": **FALSE**, "timestamp": "**2012-04-21T18:00:00-05:00**"},
"movement": **FALSE**, "timestamp": "**2012-04-21T18:30:00-05:00**"},
"movement": **TRUE**, "timestamp": "**2012-04-21T19:00:00-05:00**"}]
}
2.实时定位系统(RTLS)
作为物联网的鼻祖, RTLS 十多年来已经证明了物联网如何提供价值。事实上,预计到 2024 年,RTLS 市场将增长至近 120 亿美元。
RTLS 包括放置在企业想要跟踪位置的资产或人员上的标签。这些标签与传感器和网关的专用基础设施进行通信。基于这些通信,一种算法计算出标签在室内平面图上的估计位置。
RTLS 用于减少寻找物品所花费的时间,并自动化具有位置驱动事件的工作流。RTLS 是物联网应用背景数据的金矿。
以下是您可能从 RTLS 获得的伪数据集:
{
"tagID": 129043805,
"assetName": "**Kevin's backpack**",
"zone": "**Front Lobby**",
"location": [
"X": 1.34095, "Y": "5.3257", "variance": 2, "timestamp": "**2012-04-21T18:00:00-05:00**"},
],
}
3.安全监控系统
由paweczerwiński在 Unsplash 上拍摄的照片
几乎每个商业建筑都有安全摄像头进行基本监控。至少,它们用于记录活动,以防现场犯罪需要证据。
一段时间以来,这些安全监控系统已经从老式的学校同轴电缆连接过渡到可以在您的内部网络上访问的基于 IP 的连接。蜂窝视频摄像头连接的增长预计将从 2019 年的 370 万增长到 2024 年的超过 2000 万。
可以在这些现有的视频流上使用计算机视觉(CV)来收集上下文数据并生成警报。CV 由机器学习(ML)模型提供支持,这些模型经过训练,可以识别一系列图像中的对象、人和事件。
这是一个伪数据集,你可以从 CV 算法中得到:
{
"cameraID": 129043805,
"location": '**Front Lobby**',
"objects": [
{"type": "object", "name": "**coffee**", "confidence": 75},
{"type": "person", "name": "**Kevin Ferris**", "face mask": **TRUE**, "confidence": 98}]
}
4.门禁系统
照片由 Mauro Sbicego 在 Unsplash 拍摄
大多数办公室不会让任何人大摇大摆地进来——你需要证明你被允许在那里。门禁系统为你用来开门的徽章阅读器供电。
通常,这些标签阅读器使用无源 RFID 技术。这意味着读取器完成所有工作,因此徽章不需要电池。
员工徽章实际上是一种物联网设备。它是员工身份和凭证的小型物理表示。
一些物联网应用需要识别人员来提供个性化体验。但是,没有人想携带另一个“东西”来启动物联网解决方案。也不是每个人都希望他们的手机被追踪——去年主要新闻媒体敦促消费者禁用位置追踪。
员工徽章和您的访问控制系统是将人类身份输入物联网应用程序的可重复模型。
以下是您可能从访问控制系统中获得的伪数据集:
{
"readerID": 129043805,
"location": '**Front Lobby**',
"events": [
{"eventID": 452902, "badgeID": 12345, "name": "**Kevin Ferris**", "timestamp": "**2012-04-21T18:25:43-05:00**"}]
}
5.无线网络接入点
米沙·费舍切克在 Unsplash 上拍摄的照片
人们需要连接到互联网。以太网已经不再适用了——无线互联网连接在几乎所有的商业环境中都是不可或缺的。
天花板上的每个 Wi-Fi 接入点都是网络连接的网关。但是,您是否考虑过它们也可以成为物联网网关?
一些传感器端点可以通过 Wi-Fi 回程,因此您不必购买独立的物联网网关。代价是 Wi-Fi 连接增加了小型物联网设备的功耗。
为了帮助解决这个问题,许多 Wi-Fi 供应商正在为他们的接入点增加其他无线通信无线电,例如:
这些无线电对于物联网设备来说更加省电,这意味着它们可以在不牺牲性能的情况下使用电池持续更长时间。
检查您的 Wi-Fi 接入点,了解他们有哪些可用于物联网回程的无线无线电。
最后,企业 Wi-Fi 供应商经常向客户的 IT 网络支持团队提供分析工具,如思科的 DNA 平台或 Aruba 的运营智能平台。虽然这些系统侧重于问题解决的性能指标,但它们也经常捕获可用于了解谁的设备连接到哪里的客户端数据。
将数据整合在一起,讲述一个故事
根据上面的伪数据集,如果没有被孤立起来,这些系统有数据来讲述一个令人信服的故事。
一个引人注目的物联网数据云平台可以实现这一梦想,但这并不容易:
[## 如果我们想要发展物联网行业,我们需要重新设计物联网平台体验
扩展物联网时的 3 大客户痛点
medium.com](https://medium.com/datadriveninvestor/does-the-customer-need-an-iot-platform-10c52286586d)
作为一个示例事件,当我进入办公室的主大厅时,我们可以利用现有的基础架构捕捉到以下内容:
- BMS 系统知道至少有一个人在主大厅
- RTLS 系统知道凯文的背包在大厅里
- 安全监视系统证明凯文在大厅里
- 门禁系统知道凯文在大厅扫描了他的徽章
- Wi-Fi 接入点知道 Kevin 的 iPhone 连接到网络
您已经拥有物联网传感器,去解锁它们吧
3 分钟解锁 GitHub 的隐藏功能
关于如何定制 GitHub 个人资料页面的简单说明
视频版本
介绍
GitHub 最近有一个巨大的更新,显然他们增加了一个隐藏的功能!这可以让你自定义你的个人资料页面,我认为这是一个很酷的功能。这只要几步就能做到!
步伐
- 前往你的 GitHub 页面
- 创建新的存储库
- 用您的用户名命名您的存储库以解锁隐藏功能
- 初始化 READ.me 文件
- 玩降价游戏,根据自己的喜好定制
- 尽情享受你酷炫的个人资料页面吧!
最后一眼
希望你觉得这个教程有用!感谢您的阅读!
在 AB 测试中解锁偷看
(图片来自 Unsplash 由 Pawel Szvmanski 拍摄)
入门
像 Optimizely 这样的实验平台是如何忽略 AB 测试中最基本的原则之一的
在 AB 测试的世界里,你学到的第一条原则是:在你达到最小样本量之前,不要看你的结果!
像 Optimizely 这样的 AB 测试平台使用户能够在实验进行的任何时候监控甚至解释测试结果。但是这些产品怎么能允许从业者忽略 AB 测试的这个非常基本的原则呢?
在这篇文章中,我阐明了:
- 为什么偷看可以帮助提高业务数字但同时会损害传统 AB 测试的结果,
- 顺序测试如何允许连续监控 AB 测试和
- 最大的 AB 测试平台如何优化利用顺序测试来允许具有不同测试偏好的用户在其可视界面中监控他们的测试:
优化监控 AB 测试(图片由作者提供)
我们为什么要偷看
收集足够的样本可能需要几周甚至几个月的时间,我们有充分的理由想要更早地检查我们的结果(也称为偷看)。
我们希望 peek 能够最大限度地减少不良测试的危害,最大限度地提高良好测试细胞的益处。
让我们假设我们正在测试网站结账页面的新设计。如果设计损害了转换率,我们希望尽早停止实验,以防止任何进一步的收入损失。另一方面,如果设计迫使更多的用户完成他们的购买,我们也希望尽早停止测试,这样所有的用户都可以接触到新的设计。在这两种情况下,提前结束实验都会对我们的收入产生积极影响。
为什么你不应该偷看 AB 测试
在获得所需的最小样本量之前不检查结果是 AB 测试的基本原则之一。这是由于固定样本量测试的性质以及它们是如何进行的:
- 设置参数,如显著性水平、功率水平和最小可检测效应
- 计算最小所需样本量nn
- 运行测试并收集每个测试单元的 n 个样品
- 计算 p 值,如果 p 值低于选定的显著性阈值,则拒绝零假设
人们可能会试图持续监测 p 值,一旦它低于我们的显著性阈值,就反驳零假设。看看下面的图表。当我们获得更多样本时,p 值会随时间波动。假设我们在获得所需的样本大小 n 之前查看了 8 次,由黑点和红点表示。每次我们查看并且 p 值达到显著性阈值(用红点标记)时,我们都会拒绝零假设:
随着时间的推移监测 p 值(图片由作者提供)
听起来很直观,不是吗?
不幸的是,持续监控我们的测试统计数据(或偷看)增加了反驳零假设的可能性,尽管没有真正的效果。当建立一个实验时,我们使用显著性水平来设置我们想要的假阳性率(或 I 型错误率)。显著性水平决定了我们错误地得出实验中存在真实效应的概率。因此,如果我们将显著性水平设定为 5%,那么我们错误地推断我们的新设计、活动或功能具有真实效果的可能性不超过 5%。
控制错误拒绝零假设的风险在 AB 测试中至关重要。
在 AB 测试中,控制我们做出错误决定的概率并拒绝零假设是至关重要的,尽管它是真实的。想象一下,我们错误地认为一个昂贵的活动对我们的测试用户有积极的影响,现在我们把它推广给其他人。我们会白白花很多钱!
窥视和假阳性率(或 I 型错误)
测试的假阳性率是基于这样的假设,即在获得足够的样本后,我们只检查结果一次。计算所需的最小样本量,以便当我们有足够的样本时,在查看结果时错误地反驳零假设的概率是选定的显著性水平。相反,如果我们在进行测试时检查结果 k 次,我们有 k 次机会根据结果做出错误的决定。因此,每次查看结果时,我们测试的 I 型错误率都会增加!
通过窥视,我们最终会得到比实际值小得多的名义 p 值。因此,我们更有可能反驳一个真正的假设。
一旦 p 值低于显著性水平,我们就不能停止测试并反驳零假设。
因此,在经典 AB 测试设置中获得可靠测试结果的唯一方法是等待测试收集足够的样本,然后决定是否反驳零假设。在我们进行测试和收集样本时,不管你的测试结果看起来有多离谱。
但幸运的是,所谓的连续 AB 测试允许连续监测 p 值,并根据观察到的结果更早地做出否定假设的决定!让我们来看看这些是如何工作的。
允许通过连续的 AB 测试进行窥视
在序贯 AB 检验中,AB 检验停止时的最终样本量取决于我们在检验过程中观察到的数据。因此,如果我们在开始时观察到更极端的结果,测试可以更早结束。为了实现这一点,绘制了一对统计边界,例如基于我们希望在测试中获得的 I 类错误率。对于我们获得的每个新数据点,我们的数据的对数似然比之和将与这些边界进行比较:
连续 AB 测试(图片由作者提供)
如你所见,我们收集的样本越多,界限就越窄。这意味着在开始时,我们将不得不看到非常极端的影响,以尽早停止测试。随着样本越来越多,我们获得了更高的精度(或功效),因此我们可以检测到更小的影响,从而边界可以变得更窄。
基于此,可以在测试运行的任何时间点实施以下决策规则:
- **没有越界:**继续测试
- **上界交叉:**反驳零假设
- **下边界交叉:**停止测试,不反驳零假设
有了这套规则,如果存在强烈的影响,测试可以在少量样本后就停止!因此,如果我们从实验一开始就看到非常积极的效果,我们的奇妙设计可能已经发布给用户了。
Optimizely 的统计引擎背后的逻辑
免责声明:根据我的经验,所有主要的 AB 测试平台都提供了实时监控实验的可能性。我特别提到 Optimizely,因为他们的方法已经发表在 科学论文 中。
Optimizely 将自己描述为*世界领先的渐进式交付&实验平台。*与其他供应商相比,他们的竞争优势之一是其复杂的统计引擎。其中,该模型允许用户实时监控他们的测试,并就何时停止实验做出明智的决定。
优化监控 AB 测试(图片由作者提供)
顺序 AB 测试的问题是
当使用顺序 AB 测试时,我们通常权衡检测率(或功率)和运行时间。我们越愿意进行更长时间的测试,我们的检出率就越高。
有各种顺序 AB 测试模型,在功耗和运行测试的时间量之间有不同的权衡。当然,我们总是可以为我们的特定用例选择正确的模型,并在检测率和我们希望运行测试的时间量之间取得平衡。但是这在 AB 测试平台中是不可行的,AB 测试平台有大量的用户基础,并且关于这种平衡有非常不同的偏好。
因此,Optimizely 团队认为用户需要自己决定功率和样本大小之间的平衡。在这个目标的基础上,开发了总是有效的 p 值的概念。
构建始终有效的 p 值
如果你走到这一步,请忍耐。下一段会有一点技术性,但是我尽量简单明了的把它分解了!下面是始终有效的 p 值的定义:
给定一个序贯检验,观察值 I 处始终有效的 p 值被定义为最小的α*【或显著性水平】*,因此α水平检验将在点 I 处停止并拒绝 H0。
这意味着,在测试过程中的任何时候,你都可以要求一个最小的显著性阈值,在这个阈值上,你已经拒绝了零假设。如果该水平低于用户期望的显著性水平(例如 5%),则可以拒绝零假设,并且用户可以结束测试。这个过程保证了当我们决定拒绝零假设时,零假设事实上为真的概率(I 型错误率)在任何时间点都等于或小于我们期望的显著性水平。
为了计算测试统计数据,Optimizely 的统计引擎建立在一种特定类型的顺序测试之上,称为混合顺序概率比测试 (mSPRT)。这种类型的顺序测试提供了运行时间和功率之间的最佳平衡。mSPRT 背后的逻辑很简单:
- 使用我们拥有的 n 个样本,计算针对零假设(没有影响)的似然比。
- 如果似然比λ高于我们的显著性阈值,则拒绝零假设。
可以证明,如果存在真正的差异,mSPRT 将总是在某个点拒绝零假设。否则,它将永远继续运行。
数据点 n 的始终有效的 p 值被定义为前一数据点的 p 值和新的对数似然比的最小值,包括所有可用的数据点:
始终有效的 p 值可以在任何时间点以流式方式计算。可以在 Optimizely UI 中持续监控该指标,一旦达到预定义的显著性阈值,用户就可以停止测试。用户也可以决定在他们个人的最大运行时间提前结束测试,从而以统计能力为代价减少运行时间。
摘要
众所周知,在获得最小样本量之前,不应该检查 AB 测试的结果。与此同时,我们有充分的理由想要偷看。尽早停止测试会对企业产生重大的经济影响。
幸运的是,有一些方法,如连续 AB 测试和始终有效的 p 值,可以持续监控实验结果。我希望这篇文章很好地概述了这些方法是如何工作的,以及它们背后的思想是什么。快乐偷看!
参考资料和进一步阅读
- 在线 AB 测试中的统计方法(乔治·乔尔杰夫)
- 窥视 AB 测试:为什么它很重要,以及如何处理它(乔哈里等人)
- 新的统计引擎
- 始终有效的推断:持续监控 A/B 测试
更多关于 AB 测试的文章:
脸书和他的同事如何在 AB 测试中克服互联用户的挑战。
towardsdatascience.com](/ab-testing-challenges-in-social-networks-e67611c92916) [## 为 AB 测试找到正确的显著性水平
为什么使用默认的 95%显著性和 80%功效水平的 AB 测试可能无法评估业务风险…
towardsdatascience.com](/finding-the-right-significance-level-for-an-ab-test-26d907ca91c9)**
释放支持向量回归的真正力量
图片来自 Pixabay
用支持向量机解决回归问题
支持向量机是机器学习中处理分类问题的最流行和最广泛使用的算法之一。然而,支持向量机在回归中的使用并没有很好的记录。该算法承认数据中非线性的存在,并提供了一个熟练的预测模型。
在本文中,我将首先通过深入研究算法背后的理论,让您对算法有一个直观的理解。然后,我们将建立我们自己的 SVM 回归模型。最后,我们将探讨使用支持向量回归机的一些优点。
SVM 回归算法被称为支持向量回归或 SVR 。在开始学习算法之前,我们有必要对支持向量机有一个直观的了解。
支持向量机
在机器学习中,支持向量机是具有相关学习算法的监督学习模型,这些算法分析用于分类和回归分析的数据。在支持向量回归中,拟合数据所需的直线被称为超平面。
图片来自维基共享资源
支持向量机算法的目标是在 n 维空间中找到一个超平面,该超平面清楚地分类数据点。超平面两侧最接近超平面的数据点称为支持向量。这些影响超平面的位置和方向,从而有助于建立 SVM。
SVR 中的超参数
既然我们对什么是支持向量机有了直观的认识,我们将研究支持向量回归中使用的各种超参数。使用的一些关键参数如下所述:
1。超平面:
超平面是用于预测连续输出的决策边界。超平面任一侧最接近该超平面的数据点称为支持向量。这些用于绘制显示算法预测输出的所需线条。
2。内核:
内核是一组数学函数,它将数据作为输入,并将其转换为所需的形式。这些通常用于在高维空间中寻找超平面。
图片来自 Sci Kit Learn
应用最广泛的核包括线性、非线性、多项式、径向基函数和 Sigmoid 。默认情况下,RBF 用作内核。这些核的使用取决于数据集。
3。 边界线:
这是在超平面周围距离**ε(ε)**处画的两条线。它用于在数据点之间创建边距。
支持向量回归
支持向量回归是一种监督学习算法,用于预测离散值。支持向量回归机使用与支持向量机相同的原理。支持 SVR 的基本思想是找到最佳拟合线。在 SVR 中,最佳拟合线是具有最大点数的超平面。
图片来自 Semspirit
与试图最小化实际值和预测值之间的误差的其他回归模型不同,SVR 试图在阈值内拟合最佳直线。阈值是超平面和边界线之间的距离。SVR 的拟合时间复杂度大于样本数量的二次方,这使得它很难扩展到具有超过 10000 个样本的数据集。
对于大型数据集,使用线性 SVR 或 SGD 回归器。线性 SVR 提供了比 SVR 更快的实现,但是只考虑线性核。由支持向量回归产生的模型仅依赖于训练数据的子集,因为成本函数忽略了预测接近其目标的样本。
图片来自 MathWorks 博客
现在我们有了什么是支持向量回归机的要点,我们将尝试构建我们自己的 SVR 回归机。构建这个回归模型的代码和其他资源可以在这里找到。
第一步:导入所需的库
我们的第一步是导入构建模型所需的库。没有必要在一个地方导入所有的库。Python 给了我们在任何地方导入库的灵活性。首先,我们将导入 Pandas、Numpy、Matplotlib 和 Seaborn 库。
#Import the Libraries and read the data into a Pandas DataFrameimport pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as snstest = pd.read_csv("california_housing_test.csv")
train = pd.read_csv("california_housing_train.csv")
一旦导入了这些库,我们的下一步将是获取数据集并将数据加载到我们的笔记本中。对于这个例子,我使用了加州住房数据集。
第二步:可视化数据
成功加载数据后,我们的下一步是可视化这些数据。海滨是一个优秀的库,可以用来可视化数据。
#Visualise the dataplt.figure()
sns.heatmap(data.corr(), cmap='coolwarm')
plt.show()sns.lmplot(x='median_income', y='median_house_value', data=train)
sns.lmplot(x='housing_median_age', y='median_house_value', data=train)
第三步:特征工程
特征工程是利用领域知识通过数据挖掘技术从原始数据中提取特征的过程。对于这个模型,我选择了只有数值的列。为了处理分类值,应用了标签编码技术。
#Select appropriate featuresdata = data[[‘total_rooms’, ‘total_bedrooms’, ‘housing_median_age’, ‘median_income’, ‘population’, ‘households’]]
data.info()data['total_rooms'] = data['total_rooms'].fillna(data['total_rooms'].mean())
data['total_bedrooms'] = data['total_bedrooms'].fillna(data['total_bedrooms'].mean()
要素缩放基本上有助于在特定范围内归一化数据。通常,几个常见的类类型包含特征缩放功能,以便自动进行特征缩放。
第四步:拟合模型
选择所需参数后,下一步是从 sklearn 库中导入 train_test_split,该库用于将数据集拆分为训练和测试数据。
#Split the dataset into training and testing dataimport train_test_split
X_train, X_test, y_train, y_test = train_test_split(train, y, test_size = 0.2, random_state = 0)y_train = y_train.reshape(-1,1)
y_test = y_test.reshape(-1,1)
在此之后,从 sklearn.svm 导入 SVR ,并且模型适合训练数据集。
# Fit the model over the training datafrom sklearn.svm import SVR
regressor = SVR(kernel = 'rbf')
regressor.fit(X_train, y_train)
这里,在这个特殊的例子中,我使用了 RBF 核。模型的其他参数保留其默认配置。一旦模型适合训练数据,我们的模型就可以使用了。
支持向量回归的优势
尽管支持向量回归很少使用,但它具有如下优点:
- 它对异常值是鲁棒的。
- 决策模型可以很容易地更新。
- 它具有良好的泛化能力,预测精度高。
- 它的实现很容易。
图片由戴尔·阮
支持向量回归的缺点
支持向量机在处理回归问题时面临的一些缺点如下所述:
- 它们不适合大型数据集。
- 如果每个数据点的特征数量超过了训练数据样本的数量,则 SVM 将表现不佳。
- 当数据集具有更多噪声时,即目标类重叠时,决策模型的表现不是很好…
至此,我们已经到了这篇文章的结尾。我希望这篇文章能够帮助您了解 SVR 算法背后的思想。如果你有任何问题,或者如果你认为我有任何错误,请联系我!您可以通过邮箱或 LinkedIn 与我联系。
Python 中的解包运算符
在 Python 中使用*和**解包运算符
马库斯·斯皮斯克在 Unsplash 上的照片
介绍
在本教程中,我们将学习如何使用星号()操作符解包可迭代对象,以及如何使用两个星号(**)解包字典。此外,我们将讨论如何使用同一个运算符将几个值打包到一个变量中。最后,我们将讨论什么是args 和**kwargs 以及何时可以使用它们。
*操作员
假设我们有一个列表:
num_list = [1,2,3,4,5]
我们定义了一个函数,它接受 5 个参数并返回它们的和:
def num_sum(num1,num2,num3,num4,num5):
return num1 + num2 + num3 + num4 + num5
我们想找出 num_list 中所有元素的总和。嗯,我们可以通过将 num_list 的所有元素传递给函数 num_sum 来实现这一点。由于 num_list 中有五个元素,因此 num_sum 函数包含五个参数,每个参数对应于 num_list 中的一个元素。
一种方法是通过使用元素的索引来传递元素,如下所示:
num_sum(num_list[0], num_list[1], num_list[2], num_list[3], num_list[4])# 15
然而,有一种更简单的方法,那就是使用*操作符。*操作符是一个解包操作符,它将解包来自任何可迭代对象的值,例如列表、元组、字符串等…
例如,如果我们想解包 num_list 并传入 5 个元素作为 num_sum 函数的独立参数,我们可以这样做:
num_sum(*num_list)# 15
就是这样!星号*或解包操作符解包 num_list ,并将 num_list 的值或元素作为单独的参数传递给 num_sum 函数。
注意:为了实现这一点, num_list 中的元素数量必须与 num_sum 函数中的参数数量相匹配。如果它们不匹配,我们会得到一个类型错误。
*带内置函数的运算符:
我们还可以在 python 的内置函数中使用星号,*或解包操作符,比如 print:
print(*num_list)# 1 2 3 4 5
解包多个列表:
假设我们有另一个列表:
num_list_2 = [6,7,8,9,10]
我们希望打印出 num_list 和 num_list_2 中的所有元素。我们可以使用解包操作符*来实现这一点,如下所示:
print(*num_list, *num_list_2)# 1 2 3 4 5 6 7 8 9 10
两个 num_list 和num _ list _ 2都被解包。然后,所有的元素都作为单独的参数传递给 print。
合并多个列表:
我们还可以创建一个新的列表,包含来自 num_list 和 num_list_2 的所有元素:
new_list = [*num_list, *num_list_2]# [1,2,3,4,5,6,7,8,9,10]
num _ list和num _ list _ 2被解包,导致它们的元素构成新制作的 list 的元素,new _ list。
注意:我们可以简单地添加 num_list 和 num_list_2 来创建 new_list 。然而,这只是为了描述拆包操作员的功能。
了解什么是 walrus 操作符以及如何在 Python 中使用它
towardsdatascience.com](/the-walrus-operator-in-python-a315e4f84583)
*运算符的其他用途:
假设我们有一个字符串分配给变量 name :
name = ‘Michael’
我们想把这个名字分成三部分,第一个字母分配给一个变量,最后一个字母分配给另一个变量,中间的所有字母分配给第三个变量。我们可以这样做:
first, *middle, last = name
就是这样!由于 name 是一个字符串,而字符串是可迭代的对象,所以我们可以对它们进行解包。赋值操作符右边的值将根据它们在 iterable 对象中的相对位置被赋给左边的变量。因此,“Michael”的第一个字母被分配给变量 first ,在本例中为“M”。最后一个字母‘l’被分配给变量 last 。而变量中间会以列表的形式包含‘M’和‘l’之间的所有字母:[‘I’,‘c’,‘h’,‘a’,‘e’]。
注意:上面的第一个和最后一个变量被称为强制变量,因为它们必须被赋予具体的值。由于使用了*或解包操作符,中间的变量可以有任意数量的值,包括零。如果没有足够的值来解包强制变量,我们将得到一个 ValueError。
例如,如果我们使用下面的赋值语句:
first, *middle, last = ‘ma'
然后,变量第一个将被赋值为“m”,变量最后一个将被赋值为“a”,而变量中间的将只是一个空列表,因为没有其他值要赋值给它。
用三元运算符改进您的 Python 代码!
towardsdatascience.com](/ternary-operators-in-python-49c685183c50)
用*运算符打包:
我们还可以使用*运算符将多个值打包到一个变量中。例如:
*names, = ‘Michael’, ‘John’, ‘Nancy’# names
['Michael', 'John', 'Nancy']
在* 名称后使用尾随逗号的原因是因为赋值的左边必须是元组或列表。因此, names 变量现在以列表的形式包含了右侧的所有名字。
注意:这就是我们在定义可以接收不同数量参数的函数时所做的事情!那就是*args 和**kwargs 的概念!
*参数:
例如,假设我们有一个函数, names_tuple ,它接受名字作为参数并返回它们。然而,我们传递给这个函数的名字的数量是可变的。我们不能只选择这个函数的一些参数,因为位置参数的数量会随着函数的每次调用而变化。我们可以使用*运算符将传入的参数打包成一个元组,如下所示:
def names_tuple(*args):
return argsnames_tuple('Michael', 'John', 'Nancy')
# ('Michael', 'John', 'Nancy')names_tuple('Jennifer', 'Nancy')
# ('Jennifer', 'Nancy')
无论我们在调用 names_tuple 函数时传入多少个位置参数,* args 参数都会将位置参数打包到一个元组中,类似于上面的* names 赋值。
*** 夸格斯*
为了传递不同数量的关键字或命名参数,我们在定义函数时使用了**操作符。**解包操作符将把我们传入的不同数量的命名参数打包到一个字典中。
def names_dict(**kwargs):
return kwargsnames_dict(Jane = 'Doe')
# {'Jane': 'Doe'}names_dict(Jane = 'Doe', John = 'Smith')
# {'Jane': 'Doe', 'John': 'Smith'}
注意:当使用*运算符创建一个在定义函数时接收不同数量的位置参数的参数时,通常使用参数名 args(和 kwargs 来接收不同数量的关键字或命名参数)。但是,可以为这些参数选择任何名称。
Python 中的可迭代对象、迭代器和迭代
towardsdatascience.com](/iterables-and-iterators-in-python-849b1556ce27)
字典
当我们尝试在字典中使用*操作符时会发生什么?
num_dict = {‘a’: 1, ‘b’: 2, ‘c’: 3}print(*num_dict)# a b c
注意它是如何打印字典的键而不是值的?要解包一个字典,我们需要使用**解包操作符。但是,由于每个值都与一个特定的键相关联,所以我们将这些参数传递给的函数必须具有与被解包的字典的键同名的参数。例如:
def dict_sum(a,b,c):
return a+b+c
这个 dict_sum 函数有三个参数: a 、 b 和 c 。这三个参数的命名与 num_dict 的键相同。因此,一旦我们使用**操作符传入解包的字典,它将根据相应的参数名分配键值:
dict_sum(**num_dict)# 6
因此,对于 a 、 b 和中的参数 dict_sum 将分别为 1、2 和 3。这三个值之和是 6。
合并字典:
**就像列表一样,操作符可以用来合并两个或更多的字典:
**num_dict = {‘a’: 1, ‘b’: 2, ‘c’: 3}num_dict_2 = {‘d’: 4, ‘e’: 5, ‘f’: 6}new_dict = {**num_dict, **num_dict_2}# {‘a’: 1, ‘b’: 2, ‘c’: 3, ‘d’: 4, ‘e’: 5, ‘f’: 6}**
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。
阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
lmatalka90.medium.com](https://lmatalka90.medium.com/membership)**
结论
在本教程中,我们学习了如何使用*运算符解包可迭代对象,以及如何使用运算符解包字典。我们还学习了利用这些操作符完成许多不同任务的许多方法。此外,在定义接收不同数量的位置或命名参数的函数时,我们通过使用和**操作符简要讨论了用args 和kwargs 打包的概念。
解开客户流失及其挑战
Jornada Produtora 在 Unsplash 上拍摄的照片
了解降低业务流失率的原因和方法
关系管理是企业健康发展的决定性因素之一。这种联系的最重要因素之一是识别客户何时可能取消服务的能力。因此,有必要采取措施最大限度地留住客户。
因此,识别易流失客户的项目已成为组织经常关注的问题,因为保留客户的成本通常低于获取客户的成本。
虽然它获得了许多公司的关注,但没有解决流失问题的神奇公式。此外,该解决方案可能有许多复杂性,如确定客户流失原因以应用不同的保留策略。
挑战
获取新客户的成本是否大于留存成本?
为了获得和留住客户,观察财务和战略支出是至关重要的,因为对于一些公司来说,获得成本可能比留住成本高 5 倍。
什么类型的客户流失将被处理?
需要强调的是,产品或服务的流失会以多种方式增加,例如:
- 志愿者:当客户因对竞争对手不满或偏爱而选择取消服务时。
- 静默:当客户长时间停止使用服务,并且不产生费用时发生——就像使用没有月费的信用卡一样。
- 非自愿:当消费者不打算取消服务,但由于疏忽,他的计划可能因不正常使用、未付款等原因而未能延期或取消。
你的专家对这个问题了解多少?
拥有一个熟练的团队对于分析项目是否可以内部执行或者是否需要外包帮助是非常重要的。个性化的解决方案和有准备的专业人员可以帮助克服问题的挑战,并获得丰富和适用的结果。
您是否有一个数据库可以让您提取关于企业及其客户的信息?
一个可靠的数据库使项目执行更加可行,并产生可靠的结果。这是获得客户知识,进而了解如何规划和开发您的解决方案的基本步骤。这就引出了下一个问题:
你有多了解你的客户?
还需要诊断您的行为如何影响客户,为此,您需要收集定义客户个人特征和行为的信息。这种分析是确定他们是否容易流失的关键。
解决的方法
说到解决问题,专家团队还需要克服一些挑战。第一个是关于结合技术知识和业务理解,因为探索性分析和特性工程必须考虑组织模型才能成功。
在特征整合和业务洞察插入之后,是时候开始建模了。在这一阶段,你可能会遇到不平衡的数据,换句话说,通过划分客户群和忠实客户群,你可能会发现更高比例的忠实客户。
不平衡数据的最大问题是,如果不解决这个问题,机器学习算法往往只会对大多数类别有良好的响应。这意味着许多假阴性的产生,因为有一种倾向将可能离开的客户归类为忠诚客户。
处理不平衡数据的技术
在这一点上,有必要使用技术来解决不平衡的数据集问题,并优化客户行为的过滤器。其中我们可以提到一些最常见的:过采样、欠采样、SMOTE 和 ADASYN。值得一提的是,他们都不是通才,这就解释了为什么每个问题都要根据其特殊性来对待。
欠采样和过采样是更基本的技术,分别意味着代表性大的类的减少和代表性小的类的扩大。
SMOTE 和 ADASYN 更复杂,它们对数据进行合成采样。两者都是类似的策略,但 ADASYN 使用密度分布来创建合成元素。
了解您的客户流失解决方案的性能
客户流失模型必须建立在预期响应的基础上,关注性能和输出应该如何呈现。衡量模型性能时,选择正确的评估指标非常重要。例如,准确性可能会给我们一个令人震惊的模型的错误感觉,然而,结果可能是由于只对多数类进行了正确的分类——其中不存在流失。
这种评估可以集中在解决方案在多大程度上改进了您当前的保留策略。如果我们认为保留行为是在随机客户上进行的,我们可以评估模型所指示的样本将在多大程度上改进对易于流失的客户的选择。
传统的评估指标,如精确度和召回率,也非常有用。前者是正确指示的数量占指示总数的比例,而第二个是正确分类的流失客户占流失总数的百分比。另一种方法是 f1 分数,可描述为:
F1 = 2 (精度召回)/(精度+召回)
了解结果
为了评估要使用的指标,考虑到预期未来收入的潜力(终身价值——LTV),了解留住客户的运营成本至关重要。
LTV 高的客户可能认为保留客户需要更高的费用,而 LTV 低的客户可能认为保留客户的投资不值得。
从保留客户的参数的知识中,该操作可以被标记出来,不管它是否使得错误分类的消费者的接受更加灵活。这个因素与产生误报的惩罚直接相关——当一个忠诚的客户被归类为流失时。
如果保留操作的成本较低,您可以选择标记更多的客户,从而获得大多数真正的客户。然而,这将导致出现更多的假阳性。同样,如果成本很高,为了避免不必要的开支,关注所选组的准确性是至关重要的。
在分类模型中,默认情况下,将客户分类为搅动者的阈值是离开服务的概率超过 50%。这个限制可以根据业务而改变,例如,如果需要更高的精度,我们可以将概率超过 70%的元素评估为仅流失元素。
模型
预期的输出会影响用来解决问题的策略。除了具有二元响应的分类算法之外,还有使用生存和混合模型的方法。
生存分析模型并不将客户归类为易流失或不易流失。生成的响应是一条曲线,可以用来跟踪每个客户随时间流失的概率。
为了克服涉及复杂和非线性风险函数的生存分析问题,已经开发了扩展二元分类并将它们的结果转化为生存分析的模型。这种模型被称为混合模型,其中一些是:RF-SRC,deepSurv 和 WTTE-RNN。
结论
总之,很明显,客户流失建模对于公司留住客户和降低成本至关重要。因此,有必要意识到这些资源的成功经历了几个方面——从公众的知识,到模型的复杂性和稳健性。如有任何疑问,请随时联系我!
解开 Python 的线程之谜。
Python 如何实现计算并行化?
现在我们的大多数计算机都有多核架构,多线程等术语经常在我们耳边响起,作为提高应用程序处理效率的一种方式。Python 确实提供了一些并行计算的工具,但是它们并不为人所知。让我们在这篇文章中揭开他们的秘密。
马修·施瓦茨在 Unsplash 上的照片
首先是关于线程的一个小提示。什么是线程?这是一个轻量级的进程,运行在你的计算机上,执行它自己的指令集。当你在计算机上运行两个程序时,你实际上创建了两个进程。它们中的每一个都有一组指令(打开你的浏览器或提高音量),它希望调度程序(决定向处理器提供什么的裁判)读取这些指令。线程相对于进程的特殊性在于它们可以共享变量。
就编码而言,当我们运行两个线程时,我们允许两段代码同时运行。然而,这不同于同时执行两个程序,因为线程给了我们更多的控制权。例如,我们可以在线程之间共享一些变量,或者我们可以等待线程完成,合并结果,然后继续执行其余的代码。这是一个非常强大的工具,可以允许更快的计算或处理并发事件的能力(想想有多个传感器数据要处理的机器人)。
让我们稍微跑题一下,分析一下 Python 提供的并行运行计算的不同可能性。这三位获奖者分别是:线程、线程池和多处理。
为了清楚起见,让我们首先介绍我们希望并行化的函数。睡眠功能,其目的是…睡眠。
- 线程:Python 可以提供的最基本的线程工具
Python 库允许我们手动创建线程,为此我们可以指定目标(我们希望在这个线程中执行的函数)及其参数。该接口还包括一个 start 函数和一个 join 函数,它将等待线程的执行结束。当我们想要利用线程返回的结果时,加入线程通常是可取的。但是基本的穿线。Thread 的局限性很大,它不允许我们访问 sleep 函数返回的变量。
2.线程池:(并且是库 concurrent.futures)
线程池执行器为线程提供了一套更完整的接口。然而,它的底层实现仍然使用线程库,这提供了与前一选项相同的优点和缺点。在接口差异方面,它提出了 future 的概念,这对于 C++ 14 的用户来说似乎会很熟悉。对我们来说,未来的最大优势在于它允许我们使用 result()接口线程化函数返回变量。
3.多重处理(来自库多重处理)
多重处理是能够提供线程能力的最完整的库。除了提供更多接口之外,它与其他两个工具的主要区别在于,它能够使用名为 pickle 的第三方库来序列化和反序列化数据。序列化是转换数据类型(int、array 等)的能力。)转化为二进制,0 和 1 的序列。这样做需要能够使用协议(tcp/ip、http、UDP……)发送数据,因为这些协议对我们使用的数据类型是不可知的:发送者可能在 Python 中运行他的代码,而接收者可能使用 C++。在多处理的情况下,当我们将函数和参数传递给池对象时,就会发生序列化。这允许我们做一些不可思议的事情:发送这个线程来执行…另一台电脑!因此,多重处理库旨在支持多台计算机之间的共享计算。
注意,多处理库提供了 apply (sync)和 apply_async 接口,代表同步和异步。在第一种情况下,线程被迫以它们启动时的顺序返回,而在第二种情况下,线程一结束就返回。apply_async 提供了一个额外的参数“callback ”,它提供了在线程返回时执行函数的可能性(例如,存储结果)。
现在是时候比较不同线程方法的结果了。我们首先使用前面提到的“sleep”函数对结果进行基准测试:
我们还依次计算睡眠函数,以提供用于比较的基本结果。每次睡眠 2 秒,我们得到顺序方法的总计算时间,这似乎是合乎逻辑的。对于线程化和多处理方法,我们获得了 2s 的计算时间,这意味着所有线程都可以成功地并行运行,但是对于线程池执行器,我们获得了 4s 的计算时间,这表明在该过程中有一些额外的计算时间开销。现在,只有 3 个线程并行运行是非常好的,但是我们可能想要运行超过 1000 个线程。让我们看看在更高的难度下,比如说 100 个线程:
至于线程和线程池,从 3 个线程到 100 个线程,结果没有变化。然而,对于多处理方法,计算时间跳到了 50 秒!为了理解发生了什么,让我们来看看我们精心放置的警告:“警告,试图创建比可用内核更多的线程”。因此,多处理将尝试将线程分派到可用的内核(在我的例子中是 4 个),但是如果没有内核可用,我们可以猜测线程的计算被排队,从而变得有序。
我们可以结束这个话题,以“多处理很糟糕,线程库很棒”结束。但是等一下。到目前为止,我们在线程函数中所做的只是休眠,这意味着在处理指令方面:什么都不做。现在,如果我们有一个对计算资源更加贪婪的线程会怎么样。让我们以计数函数为例:
我们使用 4 个线程(我个人电脑上的内核数量),可以看到以下结果:
这里的多处理方法至少比其他两种方法快 4 倍。但更重要的是,当最初的目标是优化计算时间时,线程化花费的时间几乎是顺序方法的两倍,而线程池方法花费的时间是顺序方法的两倍!
为了理解为什么会发生这种情况,我们需要看一下 GIL(全局解释器锁)。Python 是一种解释型语言,而 C 或 C++是编译型语言。编译所做的是将编写的代码转换成处理器可以理解的语言:二进制文件。因此,当代码被执行时,它直接被调度程序读取,然后被处理器读取。然而,在解释语言的情况下,在启动程序时,代码仍然是以人类可读的方式编写的,这里是 python 语法。为了被处理器读取,它必须在运行时被所谓的 Python 解释器解释。然而,当线程化时会出现一个问题。Python 解释器不允许同时解释多个线程,因此有一个锁,GIL 来强制执行。让我们看一张图来更好地理解这种情况:
在这种情况下,GIL 会成为瓶颈,抵消线程的优势。这很好地解释了为什么线程和池线程在对比分析中表现如此之差。每次线程各自的计数需要增加十亿次时,每个线程都会争着通过 Python 解释器进行访问,而当线程休眠时,Python 解释器在线程存在的整个过程中只被请求一次。如果是这样的话,为什么多重处理方法提供了明显更好的结果?该库实际上是通过为每个线程创建一个 Python 解释器实例来欺骗 GIL:
因此,瓶颈消失了,可以释放全部线程潜力。
值得一提的是,由于序列化过程,多处理仍然有一些缺点:不是所有东西都可以序列化。尤其是 pythongenerators或者任何有类似指针行为的东西都不能序列化(这看起来很正常)。假设我们有一个带有一些未知内容的“代理”对象(例如这里的一些 pytorch 对象),那么我们最终会得到一个错误:
我们可以从计算效率的角度来解释线程和多进程之间的区别。在第二部分中,我们可以仔细看看资源和变量管理的主要区别,尤其是对于共享资源。让我们考虑下面的代码,它让线程使用一个全局变量:
即使我们分派不同的处理器(子进程)访问同一个全局变量来评估代码片段,这段代码也运行得很好。这意味着所有的 python 解释器都能够访问这个全局变量。那么 python 解释器可以在运行时互相通信吗?这背后的巫术是什么?让我们考虑下面代码的变体:
这段代码的目的是在运行时从不同的线程中有意更改全局变量的值。不仅如此,他们还用不同的计时来做这件事(观察不同输入的睡眠)。
如果这个变量真的是全局变量,当第二个线程输出全局变量的值时,它应该已经增加了不少。然而,我们看到当线程打印它时,这个值始终为 1。所以现在我们对正在发生的事情有了更好的了解。当线程被创建时(python 解释器也随之创建),所有的变量都被复制到新线程中,全局变量也不例外。从某种意义上说,所有线程都是您正在运行的更大进程的相同副本,只是传递给线程的参数略有不同。现在,多重处理提供了与如何创建子进程相关的不同选项。我们可以选择三种启动方法,即 spawn、fork 和 forkserver。我们将分析前两个。根据 Python 文档:
因此,主要区别在于创建子流程时从父流程继承了哪些变量。考虑上面介绍的代码。如果您仔细观察,您会注意到 start 方法被指定为“fork”。让我们看看这段代码实际输出了什么:
这里没有什么特别的,我们知道子进程能够访问全局变量,因为它已经复制了它。现在让我们看看当我们切换到“产卵”时会发生什么:
我能看到你眼中惊讶的表情,不,你不是在做梦,你是多次看到这个。“print(“应该只看到这一次”)”是在程序的最开始制作的,完全在线程被调度的循环之外。然而这本书被印了 4 次。那么发生了什么?python 文档只告诉我们“父进程启动一个新的 Python 解释器进程”,并且它只继承 run()方法所必需的对象(理解您正在尝试线程化的函数)。由此你需要明白的是继承=复制**,非 _ 继承=重求值。所以当我们选择“spawn”时,进程的每条指令都被重新解释,函数调用以及变量内存分配。
现在你可以注意到“if name == 'main ':”语句。这对解释器来说意味着,内部的任何东西都属于主进程,因此应该由子进程继承。对于每个子进程,不在该语句中的所有内容都将被重新评估。这意味着默认情况下“spawn”试图重新评估一切,但我们确实可以控制继承什么,而对于“fork”,默认情况下每个变量都会被继承。你可能想知道这有什么关系,在我们的全局变量中,它变化不大。但是对于某些对象,复制构造函数(当您使用=操作符时)可能没有很好地定义,这使得在某些情况下使用 fork 方法不安全。因此,在 python 文档中可以看到 spawn 方法正在成为所有平台的默认启动方法。**
你可能想知道这是一个相当大的限制,如果由多重处理创建的线程被完全封闭,我们就失去了线程间一定程度同步的能力。这并不完全正确,因为该库提供了一些工具供我们使用:管道。
结果:“休眠时间:2.00”,这意味着线程实际上是在等待接收父进程提供的数据,然后再继续。在某种程度上,管道相当于 C++的未来,我们能够等待不同线程提供的一些数据的获取。当然,在这种情况下,由于多重处理可以在不同的计算机上发生,通过管道发送的数据也需要在幕后序列化。
我们谈到了多重处理的资源管理。对于线程库(和线程池执行器),事情有点不同,因为我们只有一个 python 解释器。让我们看一个例子:
我们同时启动 4 个线程,每个线程实际开始计数时会有一点不同的延迟。
与多重处理相反,全局变量在这里是跨线程共享的,并且不保存本地副本。如果你习惯了被操纵的线程,你可能会被上面的代码吓坏,喊出“竞争条件”或“锁”这样的术语。锁(可以跨线程共享)是一种看门人,只允许一个线程同时打开代码执行的门,以防止变量同时被访问或修改。等等,我们实际上已经在某个地方听说过了:GIL(全局解释器锁)。
所以 python 确实已经有了一个锁机制来防止两个线程同时执行代码。好消息是,上面的代码可能没有那么可怕。让我们通过打印三个线程执行后生成的总计数来证明这一点(并删除睡眠)。由于 GIL 保护我们免受线程同时执行指令的影响,无论哪个线程正在递增全局变量,我们都应该将 10e5 * 4 乘以 1 相加,因此总数为 4.000.000。
好吧,我们需要一些解释。尤其是 GIL 实际上是如何工作的。“同时只让一个线程运行”的描述可能不足以准确地解释这种情况。如果我们更深入地研究细节,我们可以看到这一点:“为了支持多线程 Python 程序,解释器定期释放和重新获取锁——默认情况下,每十个字节码指令”。所以这与同时只允许读取一行实际的 python 代码是不同的。为了充分理解这一点,我们需要降低一个级别。
当你执行一个 python 程序时会发生什么?python 解释器首先将代码(在运行时)编译成更容易转换成字节的东西(给处理器单元的食物)。对于 python,这种中间代码称为字节码。字节码操作的完整描述可以在这里找到。dis 模块使我们能够看到特定函数的字节码是什么样子的。如果我们试着看一下一个给全局变量加 1 的函数:
要简单地将一个常量添加到一个变量中,我们需要 4 个字节码操作,获取变量,将它们相加并存储结果。
现在我们对什么是字节码操作有了更好的理解,我们可以注意到 GIL 附带了我们可以控制的设置: sys.setcheckinterval() 使我们能够控制我们想要锁定 GIL 的字节码的数量。但是,即使我们将它设置为 1(GIL 在每个字节码指令中都被锁定),这对我们也没有什么帮助。让我们分析一下在 GIL 每三个字节码锁定一次的情况下会发生什么:
数字 1 到 4 代表 GIL 允许处理字节码组的顺序。现在让我们假设全局变量的初始值为 0。在每个线程中,我们都试图给这个变量加 1。在字节码组“1”执行结束时,它从 LOAD_FAST 获得的变量副本增加了 1(in place _ ADD)。但是全局变量本身没有被修改,因为 STORE_FAST 没有被执行。现在轮到第二个线程了:由于变量没有被存储,它仍然会复制一个值为 0 的全局变量,并对其加 1。现在,当组 3 被执行时,global_variable 最终被存储为值 1。但是你可以想象,在执行组 4 时,本地副本的值也是 1,全局变量将再次存储为 1,而我们期望它是 2。坏消息是,不管我们多频繁地锁定 GIL,只要“global_variable += 1”等价字节码的部分被混淆,我们就有了竞争条件。
因此,GIL 不足以保护我们的变量,我们别无选择,只能使用锁来强制解释器跨线程一次只执行一段代码:
工作正常,线程计数到正确的数目,同时并行执行这项工作。然而,如果你看看总的计算时间,这是超出了屋顶。获取和释放锁确实很耗时,当我们需要像这里这样频繁地访问变量时,它会累积起来,形成非常繁重的计算时间。
现在是时候总结一下我们在 Python 中并行计算的经验了:
- 我们有两个主要的库允许我们进行并行计算:线程和多重处理。
- 全局解释器锁(GIL)在并行计算效率方面限制了 python 程序,但是多重处理通过创建多个解释器绕过了它。
- 多处理可以充分利用多核架构,甚至可以通过序列化/反序列化每个线程所需的数据,在不同的计算机上并行化计算。但是,它确实会创建一个拷贝或重新评估环境的资源。每个线程都在其受限的环境中进化,除非使用特定的工具,否则无法与其他线程交换。
- 线程库使子进程能够访问和修改相同的变量,但是 GIL 不能防止竞争情况,我们必须使用锁来防止这种情况发生。
那么什么时候用多处理,什么时候用线程呢?我们可以分析应用程序并行化的两个用例。
- 计算效率:目标是节省计算时间。在这种情况下,我们希望充分利用多核架构,而不受 GIL 的困扰。选择:多重处理。
- I/O 通信:当您可能从多个数据源接收数据,并且希望能够同时监控不同数据源的输入时。在这种情况下,您可能希望所有线程拥有相同的环境,因为它们可能想要修改相同的变量,并且您可能不关注计算效率。您可能还想要比可用内核数量多得多的线程。选择:穿线。
解开 Apache Spark 中的分阶段执行
APACHE SPARK 执行指南
Spark 中的 Stage 表示并行计算的逻辑单元。许多这样的阶段组装在一起,构建了 Spark 应用程序的执行框架。这个故事试图解开火花阶段的概念,并描述了重要的相关方面。
火花阶段可以被理解为计算分布式集合的数据分区的计算块,该计算块能够在计算节点的集群中并行执行。Spark 使用单个或多个阶段为 Spark 应用程序构建并行执行流。Stages 提供模块化、可靠性和弹性来激发应用程序执行。以下是与火花阶段相关的各个重要方面:
**阶段由 DAG 调度程序创建、执行和监控:**每个正在运行的 Spark 应用程序都有一个与之相关联的 DAG 调度程序实例。该调度器响应于作业的提交而创建阶段,其中作业实质上表示对应于 Spark 应用中采取的动作的 RDD 执行计划(也称为 RDD DAG)。如果在一个 Spark 应用程序中执行多个操作,那么多个作业可能会被提交给 DAG 调度程序。对于提交给它的每个作业,DAG 调度程序创建一个或多个阶段,构建阶段 DAG 以列出阶段依赖关系图,然后根据阶段 DAG 为创建的阶段规划执行调度。此外,调度程序还监视阶段执行完成的状态,结果可能是成功、部分成功或失败。相应地,调度器尝试阶段重新执行,推断作业失败/成功,或者按照阶段 DAG 调度相关阶段。
以下是针对作业的 RDD 执行计划(DAG)示例:
(200) MapPartitionsRDD[36]
| ShuffledRowRDD[35]
+-(500) MapPartitionsRDD[34]
| MapPartitionsRDD[33]
| MapPartitionsRDD[32]
| ShuffledRowRDD[31]
+-(3) MapPartitionsRDD[30]
| MapPartitionsRDD[29]
| FileScanRDD[28]
DAG 调度程序为上述 RDD 执行计划创建了以下三个阶段:
图(2):根据图(2)所示的 RDD 执行计划创建的三个阶段
以下是 DAG 计划程序根据上述阶段创建的阶段 DAG,它清楚地表明了阶段间的依赖关系:
图(3):根据图(1)所示的作业的 RDD 执行计划,为图(2)所示的三个阶段创建的阶段 DAG
DAG 调度程序创建的每个阶段在 Spark 应用程序的所有作业中都有一个唯一的 ID。此外,根据阶段 DAG 来调度阶段以供执行,这意味着在已经计算了所有相关阶段(如阶段 DAG 中所列)之后,调度阶段以供执行。如果两个阶段互不依赖,并且它们的所有其他依赖阶段都已经计算过,则可以同时执行这两个阶段。
在混洗边界上创建阶段 : DAG 调度程序通过在计划中由 ShuffleRDD 指示的混洗边界处分割 RDD 执行计划/DAG(与作业相关联)来创建多个阶段。因此,在这个拆分过程中,RDD 执行计划的一个片段(本质上是 RDD 管道)成为了阶段的一部分。对于 Spark 应用程序中提到的大范围转换,Shuffle 是必需的,例如聚合、连接或重新分区操作。
下面是在各种洗牌边界的舞台创作的插图。
洗牌边界的舞台创作插图。
此外,在图(1)所示的例子中,如 RDD 执行计划中的两个 ShuffleRowRDD 所示,发生了两次洗牌,因此创建了三个阶段,如图(2)所示。从图(2)可以明显看出,三个阶段中每一个都包含一个 RDD 流水线(作业的原始 RDD 执行计划/DAG 的一部分)。
如果提交的作业中不需要重排,DAG 调度程序将仅为该作业创建和调度单个阶段
**阶段有 ShuffleMapStage 或 ResultStage 两种类型:**ShuffleMapStage 类型的阶段是一个作业执行计划的中间阶段,该作业由 Spark 应用程序中提到的动作触发。ShuffleMapStage 实质上产生由 ShuffledRDD 在后续阶段中使用的混洗数据文件。但是,在产生输出数据之前,ShuffleMapStage 必须执行 ShuffleMapStage 中包含的作业的 RDD 执行计划段(实质上是一个 RDD 管道)。ShuffleMapStage 产生的混洗数据量可作为称为 ShuffleWrite 的阶段度量。此外,由于 SortShuffle 过程主要用于产生混洗数据文件,所以由 ShuffleMapStage 产生的混洗数据文件的数量等于由该级的 RDD 流水线计算的数据分区的数量。
ShuffleMapStage 生成一组随机文件的图示。shuffle 文件的数量等于 ShuffleMapStage 中分区的数量
ResultStage 是作业执行计划中的最后一个阶段,其中一个函数(对应于启动作业的动作)被应用于所有或一些分区,这些分区是通过执行包含在 ResultStage 中的 RDD 执行计划的段来计算的。该函数产生 spark 应用程序中相应动作执行的最终期望输出。此处列出了 spark 应用的可能操作列表。
由 Spark 应用程序中的操作触发的作业,要么仅由单个 ResultStage 组成,要么由中间 ShuffleMapStage 和单个 ResultStage 的组合组成。但是,对于自适应查询规划或自适应调度,一些仅由 ShuffleMapStage 组成的特殊作业可以由 DAG 调度程序根据请求执行。
ShuffleMapStage 或 ResultStage 的数据是从输入文件、来自先前 shuffle map stage 的 shuffle 文件或缓存的 rdd 单独或组合提供的。
**每个阶段都有一个相关的任务集用于执行:**对于一个阶段的执行尝试,DAG 调度程序会创建一个相应的任务集。阶段任务集基本上是任务的集合,其中每个任务执行特定数据分区的阶段 RDD 管道,并产生所需的输出。
与阶段类型类似,阶段任务集中的任务属于 ShuffleMapTask 或 ResultTask 类型。ShuffleMapTasks 是为 ShuffleMapStage 创建的,而 ResultTasks 是为 ResultStage 创建的。
为 stage 的执行尝试创建的 Taskset 被提交给 Spark 应用程序的任务调度器实例。反过来,任务调度器在集群中的适当位置调度每个任务(包含在任务集中)的执行。在任务集中的所有任务被执行之后,任务集中每个任务的相应执行状态被报告回 DAG 调度器。因此,DAG 调度器将阶段执行标记为完全成功、部分成功或完全失败。
DAG 调度程序为某个阶段创建任务集,将任务集提交给任务调度程序,以及 DAG 调度程序调度(已提交任务集的)任务,以便在 Spark 集群中的执行器上执行
在部分成功的情况下,使用仅包含先前失败的任务的部分任务集重新尝试阶段执行。在完全失败的情况下,将使用成熟的任务集重新尝试阶段执行。
一个阶段只重试一定的次数,当所有的重试都不能将阶段执行标记为完全成功时,阶段执行被标记为失败,导致提交给 DAG 调度程序的相应作业的执行失败。
此外,大多数情况下,由于父级产生的部分或全部混洗数据文件不可用,一个级会部分失败。这导致在报告失败的阶段被再次重试之前,父阶段的部分重新执行(以便计算丢失的混洗文件)。此外,如果在祖先中的连续级别处存在丢失的混洗文件,则这种重新执行也可以到达父级祖先中更深级别处存在的级。当托管文件的执行器由于内存溢出或集群管理器强制终止而丢失时,无序文件变得不可用。此外,当相应的 ShuffledRDD 被垃圾收集时,shuffle 文件仍然不可用。
阶段计算可在时间跳过:DAG 调度程序可决定跳过已提交作业中 ShuffleMap 阶段的计算,前提是已针对提交给调度程序的前一个作业计算了类似的阶段。如果这两个阶段执行相同的 RDD 流水线,则它们被认为是相似的。这种跳过是可能的,因为混洗输出文件保留在磁盘中,直到混洗的 RDD 引用保留在 Spark 应用程序中。
此外,在另一实例中,如果从属下游阶段所需的混洗 RDD 已经被缓存,DAG 调度器可以决定跳过混洗映射阶段的计算,该缓存是在针对提交给调度器的先前作业的另一阶段的执行期间完成的。
下面是一个示例 Spark 应用程序,用于说明 DAG 调度程序的阶段跳过,这个特定的应用程序缓存一个重新分区的数据集(在第 10 行),该数据集分别在第 15 行和第 20 行的两个文件写入操作中使用:
下面是上述 Spark 应用程序中属于两个不同作业(由两个操作触发)的 RDD 执行计划(带有阶段标记)。在应用程序中,数据集“ds”被缓存,导致相应的“ShuffledRowRDD[11]”被缓存。
以下是 DAG 调度程序为这两个作业构建的阶段计算 DAG:
作业 1 的阶段计算 DAG
作业 2 的阶段计算 DAG
从两个作业的阶段计算 DAG 可以清楚地看出,在作业-2 的阶段计算 DAG 中,阶段 4 被跳过用于计算(被跳过的阶段是灰色的),因为在作业-2 的阶段 5 中使用的“ShuffledRowRDD[11]”已经被计算并缓存在作业-1 的阶段 2 中,作业-1 提前提交用于执行。
总结:现在应该很明显了,Spark 中执行流的分段如何为 Spark 应用程序的整体执行提供模块化和弹性。用户可以跟踪 Spark 应用程序的阶段进度,可以访问多个阶段指标来评估阶段执行效率。最后,也是最重要的一点,阶段进度和相关指标可以为应用优化提供线索。
如果对 Spark stages 有任何疑问,或者对这个故事有任何反馈,请写在评论区。
解开主成分分析
从 MNIST 数据库看主成分分析
来自 MNIST 数据库的示例图像
在现实世界中,我们可能会在数据集中获得比预测目标变量所需更多的要素。有时,特征的数量可能会扩展到几百个甚至几千个,导致我们失去对特征的跟踪。事实上,它们中的许多实际上可能彼此高度相关,因此可以被去除!
降维不仅可以让我们压缩数据,还有助于加快机器学习过程。将三维地球转换成二维地图是最简单的降维示例。
PCA 是一种降维技术,也是一种无监督学习算法,这意味着它只考虑解释变量,而不考虑目标变量。它有助于可视化高维数据,减少噪音,并帮助其他算法更好地工作。
在下图中,我们有两个解释变量 X1 和 X2,分别绘制在 X 轴和 Y 轴上。数据在两个轴上的差异(V1 和 V2)或分布是显著的。因此,我们不能放弃任何一个功能。
在 draw.io 上创建
但是,如果我们仍然不得不放弃其中一个特征,我们可以将轴转换到 P1 和 P2。现在 P1 的方差比 P2 的方差大得多,因此我们现在可以保留 P1,放弃 P2。
在 draw.io 上创建
在这个过程中,我们可能会丢失某个特定变量提供的某些百分比的信息或方差。然而,目标是在处理成本和差异之间达成一个平衡。P1 和 P2 是主成分,它们只不过是 X1 和 X2 的轴变换。
我们将检查在 MNIST 数字数据集上使用 PCA 的效果。数据集包含手绘数字的像素值,从 0 到 9。每个图像的高度和宽度都是 28 像素,总共是 784 像素。像素值是从 0 到 255 的整数,包括 0 和 255。
训练数据集包含 784 列,表示 784 个像素值。“标签”列是我们的目标列,值从 0 到 9。从 pixel0 到 pixel783 填充的像素值包含从 0 到 255 的整数值,包括 0 和 255。
train.head()
#Training dataset
标签列中的值几乎均匀分布:
train.label.value_counts()
我们将把数据分成训练集和标签,然后再进行其余的处理。
y=train["label"]
X=train.loc[:, train.columns != "label"]
接下来,使用标准标量对数据进行标准化
X_values = X.values
X_std = StandardScaler().fit_transform(X_values)
我们将数据分为训练集和测试集:
X_train, X_test, y_train, y_test = train_test_split(X_std, y, test_size = 0.25, random_state = 42, stratify = y)
接下来,我们运行逻辑回归对数据进行分类,并计算将模型拟合到 784 列所需的时间:
import time
log = LogisticRegression(random_state = 42, multi_class="multinomial", solver="saga", max_iter=200)
start_time = time.time()
log.fit(X_train, y_train)
end_time = time.time()
time1 = end_time-start_time
print("Time elapsed: ",time1)
y_pred = log.predict(X_test)# Accuracy Estimation
print('Accuracy Score (Train Data):', np.round(log.score(X_train, y_train), decimals = 3))
print('Accuracy Score (Test Data):', np.round(log.score(X_test, y_test), decimals = 3))# Classification Report
logistic_report = classification_report(y_test, y_pred)
print(logistic_report)
逻辑回归花了大约 455 秒来拟合数据,并给了我们 92%的平均准确率。
我们将检查我们是否可以用 PCA 得到更好的结果。我们会选取 30%的数据进行 PCA,这样就不用花很多时间去拟合 PCA。根据我们的目标,我们将指定我们希望通过我们的模型解释多少差异。这里我们选择 0.98 来表示我们希望解释最大 98%的方差。
X_data, X_pca = train_test_split(X_std, test_size=0.3, random_state=1)
pca = PCA(0.98).fit(X_pca)
我们现在将绘制一个肘形图来检查能够解释 98%数据差异的最佳特征数量
var=np.cumsum(np.round(pca.explained_variance_ratio_, decimals=4)*100)
plt.ylabel('% Variance Explained')
plt.xlabel('Number of Features')
plt.title('PCA Analysis')
plt.ylim(30,100.5)
plt.style.context('seaborn-whitegrid')
plt.plot(var)
“肘图”表示我们需要达到预期的解释方差百分比的最佳主成分数。除了这些组成部分,解释方差的增量可以忽略不计,因此这些特征可以删除。下图解释了这一概念。
406 个主成分解释了 98%的数据差异。因此,大约只有 50%的特征解释了数据中的大部分差异。
我们现在将对 PCA 简化的数据集再次运行逻辑回归。
因此,逻辑回归在 242 秒内拟合缩减的数据集,同时保持平均准确率为 92%!因此,主成分分析帮助逻辑回归以几乎一半的计算时间达到相同的精度。
当我们处理更大的数据集时,这变得更加重要,因为减少计算时间是不可避免的。
PCA 广泛用于人脸和图像识别,因为这种数据通常具有大量的特征,并且维数减少可以帮助减少进行预测所需的特征数量。
全部代码可在这里获得。
拿出你的幻灯片,杀死一条龙,拯救企业
办公时间
五个技巧,让你更好地讲述故事,展示数据科学实验的最佳价值。
我知道… 幻灯片。大多数数据科学家讨厌构建它们。这是令人沮丧的,因为你花了大量的时间来设计它们,结果往往不如你预期的好。但是不要难过,我是来帮你让你的滑梯制作更有趣的。
你可能已经注意到了,幻灯片是公司(无论大小)内部交流知识最流行的工具。没有他们,很少有人会注意你要说的话。演示如此受欢迎是因为人类是视觉学习者。麻省理工学院 2014 年的一项研究测量出我们的大脑可以在大约 13 毫秒内处理图像。这比阅读哪怕是很小的文本节选都要快得多。
如果你想为你的数据科学成果带来关注,你应该掌握你的 Powerpoint fu 或者你的 Google slides fu。
为了让建造滑梯更有趣,我把它变成了建造世界的任务;创造一个小世界并讲述一个故事的挑战。而且,在每一个叙述中,你首先需要考虑两个主要问题:
- 这个故事是关于什么的?(你需要用一个短语来回答这个问题)
- 故事的受众是什么?
第一个问题会告诉你 你需要展示什么 。你的回答听起来有趣吗?第二个问题应该让你知道你应该如何展示它。
一旦你回答了这些问题,你就可以组织你的情节结构,并决定你应该使用哪些元素。下面,我给你五个建议,也许能帮到你。
1.制造一个诱饵
一些著名的说书人说你应该用一个钩子开始你的故事。或者,著名小说家杰夫·范德米尔在他令人惊叹的神奇书籍中,讲述了一些不同的东西:
“事实是,任何事情都可以变得有趣,所以你应该[……]更多地考虑在特定的情况或场景中,兴趣在哪里,以及从什么角度出发。一个鱼钩也不能仅仅是一个鱼钩——它必须是一个诱饵和诱惑,它还必须是一个锚。你在邀请读者享受某种乐趣或挑战……”
你可以在演讲中使用这个策略。对你的观众来说,最有价值的信息是什么?
例如,假设您构建了一个 ML 模型来预测未来几个月的销售额。因此,不要在开始演示时解释您用来准备数据和构建模型的所有技术,而是从您的预测中获得的有趣数字开始。
此外,从右侧视角创造诱惑。例如,销售团队可能对预测的交易中涉及的金额感兴趣。相比之下,营销团队可能更喜欢每个客户群和人口统计数据中售出的商品数量。
用你的诱饵作为锚。随着你在演示中的进展——详细介绍你的发现,深入研究你的结果——观众很容易迷失。因此,用你的诱惑让他们和对他们最重要的东西保持联系。
回到我们的例子,你可能已经开始展示在预测的交易中涉及了多少钱,但是接下来你可以解释这些钱如何转化为市场份额。你从你的锚(钱)跳到一个新的彼岸(市场份额数据)。如果你坚持这样做,观众会觉得所有的事情都与他们最重要的事情有着恰当的联系。更容易理解。
此外,帮助观众理解,诱惑只是邀请一些宏伟的未来。如果你在诱惑中破坏了你所有的洞察力,观众很快就会变得厌烦,并相信他们已经从你那里得到了一切。如果他们知道你有更多有价值的信息可以分享,他们会保持联系。
2.恰当地描述你的角色
就像在一个故事里,我们也应该把我们的结果当成人物。
你故事中的英雄是谁?
也许是你的新颖算法,或者是你数据挖掘实验的一些感悟。作为科学家,我们热衷于详细描述一切。然而,这可能是一个吸引你的观众的糟糕策略,因为太多的信息很容易变得太难或太无聊。
在一本书里,小说作者通常不会给你他们的英雄的所有特征。通常,他们描述什么是更重要的或最引人注目的痕迹。我们的大脑根据已知的原型和直觉想象缺失的部分。试着对你的结果使用同样的策略。只描述你研究中最重要的方面。对于那些想了解所有细节的人,您可以在幻灯片底部留下详细文档的链接。
此外,抽象是良好描述的关键。使用易于理解的图表、图表、隐喻,任何引导观众直觉和理解的东西,即使他们没有技术背景。就像是给他们的头脑指了捷径。
3.充分利用一种语言
2016 科幻电影降临看了吗?如果没有,看完这个帖子再看。
展示数据科学成果就像和外星人聊天一样困难。想象他们需要理解你,否则他们可能会摧毁你的世界。为了避免这种情况,像路易丝·班克斯一样,尝试以下方法:
- 找出或建立一些你和你的听众之间的“共同语言”。
- 传达开发该语言用途的信息。
你可以用符号、颜色、关键词作为你语言的组成部分。任何能让你的观众更容易理解你的故事并保持联系的东西。
一旦你定义了你的语言,就要保持一致。如果你选择一种颜色来代表某样东西(一个变量,一个类别,或者一个策略),在整个演示过程中始终使用同一种颜色。如果你需要色调的变化,改变透明度,但保持色调。此外,保持变量名、图表、类别等一切的一致性。
4.剧情变得复杂了!
在数据科学实验之后,我们通常会有几个图表和示意图要展示。你展示结果的顺序可以帮助你讲述一个更好的故事。请记住,就像在书中一样,时间顺序不一定是使故事有趣的最佳选择。
不幸的是,我不相信在结果排序方面有一个黄金法则。我考虑的两个方面是:
- 结果的复杂性;
- 他们的影响。
首先,从简单的片段构建复杂的结果。从基本前提开始,不断添加元素,直到完成更大的图片。这是一个让你的听众理解你的推理的简单策略。这就像拼图,你应该让每个人都玩,一次一块。
其次,最有冲击力的结果应该是接近尾声的。这样,观众更容易记住他们。然而,这种影响很难衡量,因为它取决于视角。你可以试着去猜,随着时间的推移,随着你获得经验,你的选择会更好。
5。没有开放式结尾
科学是开放的。你的幻灯片不需要。我认为结论幻灯片如果具体客观,效果会更好。通常,我会用外卖代替总结幻灯片。我列出了我的观众应该记住的最有价值的作品。
根据具体情况,听众可能希望听到后续步骤(或未来工作)。在这种情况下,有开放结局的空间,但不要忘记先非常好地关闭你当前的故事。
一些演示指南告诉你在结论中对你的工作做一个总结。我个人觉得很无聊,观众会分心,愿意走开。如果你发表了引人入胜的演讲,人们会记住你的故事。你最好突出你有影响力的见解,像英雄一样结束。
你有没有自己的妙招让自己讲故事更有趣?请在评论里告诉我!此外,如果你想保持联系并在电子邮件中收到一些数据科学技巧和极客文化,请订阅我即将开始的每月简讯。
关于无符号、有符号整数和 Rust 中的造型,你应该知道什么
理解符号和幅度、一的补码和二的补码
由 Freepik 设计
[更新于 2021 年 2 月 18 日。代码更改为要点并添加了链接]
**Table of Contents**[**Introduction**](#a73e)🦀 [Unsigned Integer Types](#f4ce)
🦀 [Signed Integer Types](#3e54)
🦀 [Signed, Ones’ Complement and Two’s Complement](#ac74)
🦀 [Sign-and-Magnitude](#7b01)
🦀 [Ones’ Complement](#fbfa)
🦀 [Two’s Complement](#41ce)
🦀 [4-bit Signed Binary Number Comparison](#6677)
🦀 [Rust signed two’s complement integer types](#0a26)
🦀 [Casting in Rust](#0883)
🦀 [Casting to an Unsigned Type](#edbc)
🦀 [Casting to a Signed Type](#47e3)
🦀 [Bitwise Negation](#108b)
🦀 [Adding a Negative Number](#baae)[**Conclusion**](#6f4b)
介绍
Rust 有两个数据类型子集,标量和复合。标量类型有整数、浮点数、布尔值和字符。复合类型是数组和元组。
在本文中,我们将了解以下代码失败的原因:
fn main() {
let a: i16 = 2;
let b: u16 = 4;
println!("{}", a+b);
}
还有为什么选角128
到i8
是-128
。
输出:
128 as a i8 is : -128
为了更好地理解造型,我们需要回顾一下有符号、一的补码和二的补码。
我们还将讨论添加负数、按位求反以及将二进制转换为无符号和有符号十进制。
让我们先从 Rust 整数类型开始。
[## 通过将 Python 转换成 Rust 来学习 Rust
Rust 基础入门教程
towardsdatascience.com](/learning-rust-by-converting-python-to-rust-259e735591c6)
无符号整数类型
Rust 中的无符号整数类型以u
开始,它有 8 位、16 位、32 位、64 位和 128 位。最小值和最大值从 0 到 2ⁿ-1 。
例如u8
有 0 到 2⁸-1,也就是 255。下表显示了无符号整数的所有详细信息。
信任无符号整数类型。图片由作者提供。
寻找无符号整数类型的最大值。网上试试这个 Rust lang 代码。
输出:
u8 has the max value of 255.
u16 has the max value of 65535.
u32 has the max value of 4294967295.
u64 has the max value of 18446744073709551615.
u128 has the max value of 340282366920938463463374607431768211455.
如果试图将负数赋给无符号类型,将会失败。
fn main() {
let u:u32 = -1;
println!("{} in binary is {:b}", u, u);
}error[E0600]: cannot apply unary operator `-` to type `u32`
--> main.rs" data-line="2" data-column="17">main.rs:2:17
|
2 | let u:u32 = -1;
| ^^ cannot apply unary operator `-`
|
= note: unsigned values cannot be negated
-
是 Rust 的 一元 运算符 之一,是有符号整数类型和浮点类型的非运算符。
有符号整数类型
Rust 中的默认整数类型为i32
。Rust 中的有符号整数类型以i
开始,它有 8 位、16 位、32 位、64 位和 128 位。最小值和最大值是从 -(2ⁿ⁻到 2ⁿ⁻ -1 。n-1
中的-1
是符号位(正或负),我们将在下一节中介绍。
例如i8
有-(2⁷)到 2⁷-1,也就是-128 到 127。下表显示了有符号整数的所有详细信息。
信任有符号整数类型。图片由作者提供。
寻找有符号整数类型的最小值和最大值。在线尝试这个 Rust lang 代码。
输出:
i8 has the min value of -128.
i8 has the max value of 127.
i16 has the min value of -32768.
i16 has the max value of 32767.
i32 has the min value of -2147483648.
i32 has the max value of 2147483647.
i64 has the min value of -9223372036854775808.
i64 has the max value of 9223372036854775807.
i128 has the min value of -170141183460469231731687303715884105728.
i128 has the max value of 170141183460469231731687303715884105727.
有符号、一的补码和二的补码
在计算中,需要用有符号的数字表示法来对二进制数字系统中的负数进行编码。让我们检查一下符号和幅度、一进制补码和二进制补码。
符号和幅度
符号和幅度也称为符号幅度。第一位(称为最高有效位或 MSB)表示它是正 0 还是负 1。其余的称为幅度位。
+5 的 4 位有符号幅度。图片由作者提供。
正如我之前提到的,有符号整数类型具有从 -(2ⁿ⁻到 2ⁿ⁻ -1 的最小值和最大值,其中 n 代表位数。因为我们将第一位用于正负符号,所以在 2ⁿ⁻ 中有n-1
。
对于 4 位,最小值和最大值从-(2)到 2–1,即-8 到+7。
正如你在上图中看到的,除了符号位之外,正数和负数有相同的数字。
有符号幅度的问题是有两个零,0000
和1000
。
一的补码
第一位(MSB)与带符号幅度相同。它以 0 表示正,以 1 表示负。其余的位将接受补码,这意味着如果它为 1,补码为 0,如果它为 0,则补码为 1。
+5 的一进制补码(4 位)。图片由作者提供。
有符号一的补码与有符号幅度具有相同的问题。有两个零,0000
和1111
。
二进制补码
在二进制计数系统中,基数(基数)是二。这就是为什么基数补码被称为二进制补码,而减基数补码被称为一进制补码。
二进制补码可以避免 0 的多重表示,并且避免在溢出的情况下跟踪进位。
+5 的二进制补码(4 位)。图片由作者提供。
我们再次将第一位(MSB)用于+
和-
符号。我们取这个数的补数,加上1
得到相反的数。这从正到负起作用,反之亦然。
-5 的二进制补码(4 位)。图片由作者提供。
对于二进制中的零0000
,补码是1111
,加上1
得到1 0000
。1
被称为“溢出”位。当最高有效(最左边)列的总和产生结转时,发生溢出。该溢出位或进位位可以忽略。
下表显示了三种有符号数字表示法的比较。
4 位有符号二进制数比较
4 位有符号二进制数比较。图片由作者提供。
Rust 有符号二进制补码整数类型
现在让我们将上表中的有符号二进制补码的负数(从-1 到-7)与 Rust 的有符号整数进行比较。
输出:
-1 in binary is 11111111111111111111111111111111
-2 in binary is 11111111111111111111111111111110
-3 in binary is 11111111111111111111111111111101
-4 in binary is 11111111111111111111111111111100
-5 in binary is 11111111111111111111111111111011
-6 in binary is 11111111111111111111111111111010
-7 in binary is 11111111111111111111111111111001
除了 Rust 使用默认的 32 位之外,它们完全相同。Rust 的有符号整数类型被称为有符号二进制补码整数类型。
生锈的铸件
造型 是将一条数据的数据类型从一种类型改变为另一种类型。
[as](https://doc.rust-lang.org/std/keyword.as.html)
关键字将原始类型转换为其他原始类型。我们可以使用as
关键字来解决简介中的代码。
铸造 u16 到 i16。网上试试这个 Rust lang 代码。
当你从小长度转换到大长度时,比如从 8 位转换到 16 位,不会有任何问题,但是当你向下转换时,就有问题了。
强制转换为无符号类型
方法一
当您强制转换为无符号类型时,会加上或减去 T,T::MAX + 1,直到该值适合新类型。
示例 1:从默认的i32
到u8
铸造 1000
u8
的最大数是 255,所以我们减去 255+1,也就是从 1000 减去 256,直到小于 255。
1000 - 256 = 744
1000 - 256 - 256 = 488
1000 - 256 - 256 - 256 = 232
铸造 1000 个 i32 到 u8。网上试试这个 Rust lang 代码。
输出:
1000 as a u8 is : 232
方法二
十进制的 1000₁₀是二进制的 1 1110 1000₂。我们可以取后 8 位,也就是1110 1000
,十进制是 232。
一个有趣的数字是 256₁₀.
铸造 256 i32 到 u8。网上试试这个 Rust lang 代码。
输出:
256 as a u8 is : 0
256₁₀是二进制的 100000000₂。如果取最后 8 位,就是00000000
。
强制转换为有符号类型
方法一
这与上面的相同,但是您需要注意数据类型的最小/最大数量。
i8
的最小和最大数字是-128 和 127。所以这个数字必须介于两者之间。
铸造 128 到i8
:
128-256=-128
铸造 1000 到 i8:
1000 - 256 = 744
1000 - 256 - 256 = 488
1000 - 256 - 256 - 256 = 232
1000 - 256 - 256 - 256 - 256 = -24
将 i32 数字转换为 i8。网上试试这个 Rust lang 代码。
输出:
128 as a i8 is : -128
1000 as a i8 is : -24
232 as a i8 is : -24
257 as a i8 is : 1
130 as a i8 is : -126
514 as a i8 is : 2
方法二
十进制的 1000₁₀是二进制的 11 1 110 1000₂。8 位以内的 MSB 是 1,所以是负数。然后用 2 的补码。补足语是 00010111₂,我们加上 1₂就成了 00011000₂.最后,是-24₁₀.
图片由作者提供。
当 8 位以内的 MSB 为 0 时,为正数。然后取前 8 个最低有效位(LSB)。
图片由作者提供。
逐位求反
Rust 使用!
进行逐位求反(逐位非)。这根据类型产生不同的结果。
输出:
u is 2
u in binary is 10
i is 2
i in binary is 10
Bitwise negation !u is 4294967293
Bitwise negation !i is -3
正如你看到的!2
带有无符号类型返回4294967293
,带有有符号类型返回-3
。
有符号整数上的按位求反返回二进制补码,正如我们之前在信任有符号二进制补码整数类型中看到的。
添加负数
减法和负数的加法是一样的。
5 - 2 = 5 + (-2) = 3
这也适用于二进制。
0101 - 0010
= 0101 + (-0010) // (1)
= 0101 + 1110 // (2)
= 0011 // this is 3 in decimal number.
我们通过找到0010
的二进制补码即1110
来找到-0010
。
结论
我们讨论了 Rust 整数类型、三种类型的有符号二进制数、转换为无符号类型和有符号类型。我希望你在处理 Rust integers 和 casting 时,对发生的事情有更好的了解。
请继续关注下一篇文章。
通过 成为 会员,可以完全访问媒体上的每一个故事。
https://blog.codewithshin.com/subscribe
Rust 初学者的完整资源
towardsdatascience.com](/you-want-to-learn-rust-but-you-dont-know-where-to-start-fc826402d5ba)
自然语言理解数据集未解决的问题
即使是最受欢迎的 NLP 基准也面临着这些挑战
杰森·德沃尔在 Unsplash 上拍摄的照片
垃圾进来,垃圾出去。你不必是一个 ML 专家也能听到这个短语。模型揭示了数据中的模式,因此当数据被破坏时,它们会发展出破坏的行为。这就是为什么研究人员分配大量资源来管理数据集。然而,尽管尽了最大努力,收集完全干净的数据几乎是不可能的,尤其是在深度学习所需的规模上。
本文讨论了流行的自然语言数据集,尽管这些数据集是由该领域的专家制作的,但结果却违反了机器学习和数据科学的基本原则。这些缺陷中的一些在数据集公布和大量使用多年后被暴露和量化。这是为了说明数据收集和验证是一个艰巨的过程。以下是他们的一些主要障碍:
- 机器学习是数据饥渴。ML(特别是深度学习)所需的庞大数据量要求自动化,即挖掘互联网。数据集最终会从互联网上继承不需要的属性(例如,重复、统计偏差、虚假),这些属性很难检测和删除。
- **desire data 无法详尽捕获。**即使 oracle 能够根据一些预定义的规则产生无限的数据,要列举所有的需求实际上也是不可行的。考虑对话机器人的训练数据。我们可以表达一般的愿望,如不同的话题,尊重的交流,或对话者之间的平衡交流。但是我们没有足够的想象力去指定所有的相关参数。
- 人类选择阻力最小的道路。一些数据收集工作在人类规模上仍然是可管理的。但是我们自己并不是完美无缺的,尽管我们尽了最大努力,我们还是下意识地倾向于走捷径。如果你的任务是写一个与前提“狗在睡觉”相矛盾的陈述,你的答案会是什么?继续阅读,看看你是否会成为问题的一部分。
重叠的训练和评估集
ML 从业者将他们的数据分成三部分:有一个训练集用于实际学习,一个验证集用于超参数调整,一个评估集用于测量模型的最终质量。众所周知,这些集合应该大部分是析取的。在评估训练数据时,您测量的是模型的记忆能力,而不是它识别模式并将其应用于新环境的能力。
这个指导方针听起来很容易应用,但是 Lewis 等人[1]在 2020 年的一篇论文中显示,最流行的开放领域问答数据集(open-QA)在它们的训练集和评估集之间有很大的重叠。他们的分析包括 WebQuestions 、 TriviaQA 和开放式自然问题——由知名机构创建的数据集,被大量用作 QA 基准。
我们发现 60–70%的测试时答案也存在于训练集中的某个地方。我们还发现,30%的测试集问题在其对应的训练集中有近似重复的释义。
当然,培训和测试之间 0%的重叠也是不理想的。我们确实需要某种程度的记忆——模型应该能够回答训练中看到的问题,并知道何时展示之前看到的答案。真正的问题是在训练/评估高度重叠的数据集上对模型进行基准测试,并对其泛化能力做出仓促的结论。
Lewis 等人[1]在将评估集划分为三个子集后,重新评估了最先进的 QA 模型:(a) 问题重叠— ,其中相同或转述的问答对出现在训练集中,(b) 答案仅重叠— ,其中相同的答案出现在训练集中,但与不同的问题配对,以及© 没有重叠。QA 模型在这三个子集上的得分差别很大。例如,当在开放式自然问题上测试时,最先进的解码器融合模型在问题重叠上的得分约为 70%,仅在答案重叠上的得分约为 50%,在没有重叠上的得分约为 35%。
很明显,这些数据集上的性能无法通过总体 QA 准确性来正确理解,这表明在未来,应更加重视更多行为驱动的评估,而不是追求单一数字的总体准确性数字。
虚假相关
就像人类一样,模型走捷径,发现解释数据的最简单模式。例如,考虑一个狗对猫图像分类器和一个天真的训练集,其中所有的狗图像都是灰度的,所有的猫图像都是全色的。该模型将最有可能抓住颜色和标签的存在/不存在之间的伪相关性。在全彩狗身上测试,大概会贴上猫的标签。
Gururangan 等人[2]表明,在两个最受欢迎的自然语言推理(NLI)数据集、 SNLI (斯坦福·NLI)和 MNLI (多体裁 NLI)中出现了类似的*虚假相关性。给定两个陈述,一个前提和一个*假设,自然语言推理的任务是决定它们之间的关系:蕴涵、矛盾或中立。以下是 MNLI 数据集中的一个示例:
来自 MNLI 数据集的示例
解决 NLI 问题需要理解前提和假设之间的微妙联系。然而,Gururangan 等人[2]揭示,当模型仅显示假设时,它们在 SNLI 上可以达到高达 67%的准确度,在 MNLI 上可以达到 53%的准确度。这明显高于最频繁类基线(~35%),暴露了数据集中不可否认的缺陷。
这是怎么发生的?SNLI 和 MNLI 都是众包;给人类一个前提,要求他们提出三个假设,每个标签一个。这又把我们带回了前提“狗在睡觉”。你会如何反驳它?“狗没有睡觉”是一个完全合理的候选词。然而,如果否定一直作为一种启发来应用,模型就学会了通过简单地检查假设中“不是”的出现来检测矛盾,甚至不需要阅读前提就可以获得高准确度。
Gururangan 等人[2]揭示了其他几个这样的注释人工制品:
- 蕴涵假设是通过概括在前提中发现的词语(狗→动物,3 →一些,女人→人)产生的,使得仅从假设中就可以识别蕴涵。
- 中性假设是通过注入修饰语 ( 高,第一,最 ) 而产生的,作为一种简单的方法来引入不被前提所包含但又不与之矛盾的信息。
尽管有这些发现,MNLI 仍然在 GLUE 排行榜之下,这是自然语言处理最流行的基准之一。与其他 GLUE 语料库(约 400,000 个数据实例)相比,MNLI 具有相当大的规模,因此在摘要中非常突出,并用于消融研究。虽然它的缺点开始被更广泛地认识到,但在我们找到更好的替代品之前,它不太可能失去它的受欢迎程度。
偏见和代表性不足
在过去的几年里,机器学习中的偏见已经在多个维度上暴露出来,包括性别和种族。为了应对有偏见的单词嵌入和模型行为,研究社区已经将越来越多的努力指向偏见缓解,如 Sun 等人[3]在其综合文献综述中所述。
2018 年图灵奖的共同获奖者 Yann LeCun 指出,有偏差的数据导致有偏差的模型行为:
他的推文吸引了研究界的大量参与,反应不一。一方面,人们几乎一致承认在许多数据集中确实存在偏见。另一方面,一些人不同意偏见仅仅源于数据的暗示,还指责建模和评估选择,以及设计和构建模型的人的无意识偏见。Yann LeCun 后来澄清说,他不认为数据偏差是模型中社会偏差的唯一原因:
尽管正在讨论的数据集是用于计算机视觉的图像语料库,但自然语言处理同样会受到有偏见的数据集的影响。暴露出性别偏见的一个突出任务是共指消解,其中指称表达(如代词)必须与文本中提到的实体相关联。下面是 Webster 等人的一个例子[4]:
五月,藤泽作为球队的队长加入了本桥麻里的溜冰场,从轻井泽回到她曾经度过初中时光的北见。
作者指出,维基百科上不到 15%的传记是关于女性的,而且他们倾向于比关于男性的页面更突出地讨论婚姻和离婚。鉴于许多 NLP 数据集是从维基百科中提取的,这影响了许多下游任务。特别是对于共指消解来说,缺少女性代词或者它们与某些定型的关联是有问题的。例如,你如何解释这句话“当玛丽走进房间时,她看到了她的医生”?
从训练数据中消除偏差是一个尚未解决的问题。首先,因为我们不能详尽无遗地列举偏见表现的轴;除了性别和种族之外,还有许多其他微妙的方面会引起偏见(年龄、专有名称、职业等。).第二,即使我们选择了一个像性别这样的单一轴,消除偏见将意味着要么丢弃大部分数据,要么应用容易出错的试探法将男性代词变成代表不足的性别代词。相反,研究界目前正专注于产生无偏的评估数据集,因为它们较小的规模更有利于人工干预。这至少让我们有能力更真实地测量我们的模型的性能,通过人口的代表性样本。
构建自然语言数据集是一个永无止境的过程:我们不断地收集数据,验证数据,承认数据的缺点并解决它们。然后,每当有新的来源时,我们就冲洗并重复。与此同时,我们取得了进展。上面提到的所有数据集,尽管有缺陷,但不可否认地帮助推动了自然语言理解的发展。
参考
- Lewis 等人,开放领域问答数据集的问答测试序列重叠 (2020)
- Gururangan 等人,自然语言推理数据中的标注工件 (2017)
- 孙等,减轻自然语言处理中的性别偏见:文献综述 (2019)
- 韦伯斯特等人,注意差距:性别歧义代词的平衡语料库 (2018)
时间序列上的无监督异常检测
包括真实生活经历的概述
理解时间轴上任何流的正常行为并检测异常情况是数据驱动研究中的突出领域之一。这些研究大多是在无人监督的情况下进行的,因为在现实生活项目中标记数据是一个非常艰难的过程,如果你已经没有标签信息,就需要进行深入的回顾性分析。请记住,异常值检测和异常值检测在大多数时候可以互换使用。
没有一种神奇的银弹能在所有异常检测用例中表现良好。在这篇文章中,我谈到了一些基本的方法,这些方法主要用于以非监督的方式检测时间序列上的异常,并提到了它们的简单工作原理。在这个意义上,本文可以被认为是对包括现实生活经验在内的时间序列异常检测的一个综述。
基于概率的方法
使用Z-score是最直接的方法之一。z 分数基本上代表样本值低于或高于分布平均值的标准偏差的数量。它假设每个要素都符合正态分布,计算样本中每个要素的 z 值可以帮助我们发现异常。具有很多特征的样本,其值位于远离平均值的位置,很可能是异常。
在估计 z 分数时,您应该考虑影响模式的几个因素,以获得更可靠的推断。让我给你举个例子,你的目标是检测电信领域设备流量值的异常。小时信息、工作日信息、设备信息(如果数据集中存在多个设备)可能会形成流量值的模式。因此,在本例中,应该通过考虑每个设备、小时和工作日来估计 z 得分。例如,如果您预计设备 A 在周末晚上 8 点的平均流量为 2.5 mbps,那么您应该在决定相应的设备和时间信息时考虑该值。
这种方法的一个缺点是,它假设要素符合正态分布,而这并不总是正确的。另一个可以认为是忽略了上述解决方案中特征之间的相关性。重要的一点是 z 分数也可以用作其他异常检测模型的输入。
基于四分位数的 解决方案实现了与 Z-score 非常相似的想法,不同的是它以简单的方式考虑了中值而不是平均值。有时,根据数据的分布情况,与 z 分数相比,它可以获得更好的结果。
椭圆包络 是离群点检测的另一种选择,适合数据上的多元高斯分布。但是,它可能无法很好地处理高维数据。
基于预测的方法
在这种方法中,使用预测模型对下一个时间段进行预测,如果预测值超出置信区间,则样本被标记为异常。作为预测模型,可以使用ARIMA模型。这些方法的优点是它们是时间序列上表现良好的模型,并且在大多数情况下可以直接应用于时间序列而无需特征工程步骤。另一方面,估计置信区间并不是一件简单的事情。此外,预测模型的准确性直接影响异常检测的成功。好消息是,即使在没有任何标签信息的情况下执行异常检测,您也能够以受监督的方式评估预测模型的准确性。
预言家也值得一看,它基本上是一个针对时间序列设计的预测算法,由脸书开发,但我在异常检测用例中遇到很多这种算法的实现。
基于神经网络的方法
auto encoder是一种无监督型神经网络,主要用于特征提取和降维。同时,对于异常检测问题,这是一个很好的选择。Autoencoder 由编码和解码部分组成。在编码部分,提取代表数据中模式的主要特征,然后在解码部分重构每个样本。正常样本的重建误差最小。另一方面,该模型不能重建表现异常的样本,导致高重建误差。因此,基本上,样本的重构误差越高,它就越有可能是异常的。
Autoencoder 对于时间序列是非常方便的,因此它也可以被认为是时间序列异常检测的优先选择之一。注意,自动编码器的层可以同时由 LSTMs 组成。因此,时序数据中的相关性就像时间序列中的相关性一样可以被捕获。
******【SOM】也是另一种基于无监督神经网络的实现,与其他神经网络模型相比,其工作原理更简单。尽管它在异常检测用例中没有广泛使用,但最好记住它也是一种替代方法。
基于聚类的方法
在异常检测中使用聚类背后的思想是异常值不属于任何聚类或有自己的聚类。k-means是最著名的聚类算法之一,易于实现。然而,它带来了一些限制,如选择合适的 k 值。此外,它形成球形团簇,这并不适用于所有情况。另一个缺点是,它不能在将样本分配给聚类时提供概率,特别是考虑到在某些情况下聚类可能重叠。
高斯混合模型(GMM) 针对 k-means 的上述弱点,提出一种概率方法。它试图在数据集中找到有限数量的高斯分布的混合。
DBSCAN 是一种基于密度的聚类算法。它确定数据集中的核心点,这些核心点在其周围ε距离内至少包含 min_samples ,并根据这些样本创建聚类。此后,它从聚类中的任何样本中找到所有密集可达(在ε距离内)的点,并将它们添加到聚类中。然后,迭代地对新添加的样本执行相同的过程,并扩展聚类。DBSCAN 自己确定聚类数,离群样本会被赋值为-1。换句话说,它直接服务于异常检测。请注意,对于大型数据集,它可能会遇到性能问题。
基于邻近的方法
首先想到的算法是k-最近邻(k-NN)** 算法。背后的简单逻辑是异常值远离数据平面中的其余样本。估计所有样本到最近邻的距离,并且远离其他样本的样本可以被标记为异常值。k-NN 可以使用不同的距离度量,如欧几里德距离、曼哈顿距离、闵可夫斯基距离、汉明距离距离等。**
另一种替代算法是局部异常值因子(LOF) ,其识别相对于局部邻居而非全局数据分布的局部异常值。它利用一个名为局部可达性密度(lrd) 的度量来表示每个点的密度水平。样本的 LOF 简单地说就是样本相邻样本的平均 lrd 值与样本本身的 lrd 值之比。如果一个点的密度远小于其相邻点的平均密度,那么它很可能是一个异常点。**
基于树的方法
隔离林 是一种基于树的,非常有效的异常检测算法。它构建了多个树。为了构建树,它随机选取一个特征和相应特征的最小值和最大值内的分割值。此过程适用于数据集中的所有样本。最后,通过对森林中的所有树进行平均来构成树集合。
隔离森林背后的想法是,离群值很容易与数据集中的其余样本不同。由于这个原因,与数据集中的其余样本相比,我们预计异常样本从树根到树中的叶节点的路径更短(分离样本所需的分裂次数)。
扩展隔离林 对隔离林的分裂过程进行了改进。在隔离森林中,平行于轴进行分割,换句话说,以水平或垂直的方式在域中产生太多冗余区域,并且类似地在许多树的构造上。扩展隔离林通过允许在每个方向上发生分裂过程来弥补这些缺点,它不是选择具有随机分裂值的随机特征,而是选择随机法向量以及随机截取点。
基于降维的方法
主成分分析 主要用于高维数据的降维方法。基本上,通过提取具有最大特征值的特征向量,它有助于用较小的维度覆盖数据中的大部分方差。因此,它能够以非常小的维度保留数据中的大部分信息。
在异常检测中使用 PCA 时,它遵循与自动编码器非常相似的方法。首先,它将数据分解成一个更小的维度,然后再次从数据的分解版本中重建数据。异常样本往往具有较高的重建误差,因为它们与数据中的其他观测值具有不同的行为,因此很难从分解版本中获得相同的观测值。PCA 对于多变量异常检测场景是一个很好的选择。
时间序列的异常检测
真实生活经历
- 在开始研究之前,请回答以下问题:您有多少追溯数据?单变量还是多变量数据?进行异常检测的频率是多少?(接近实时,每小时,每周?)你应该对哪个单位进行异常检测?(例如,您正在研究流量值,您可能仅对设备或设备的每个插槽/端口进行异常检测)
- 您的数据有多项吗?让我澄清一下,假设您要对电信领域中上一个示例中的设备流量值执行异常检测。您可能有许多设备的流量值(可能有数千种不同的设备),每种设备都有不同的模式,并且您应该避免为每种设备设计不同的模型,以解决生产中的复杂性和维护问题。在这种情况下,选择正确的功能比专注于尝试不同的模型更有用。考虑到小时、工作日/周末信息等属性,确定每个设备的模式,并从其模式中提取偏差(如 z 分数),并将这些特征提供给模型。注意上下文异常大多是在时间序列中处理的。所以,你可以只用一个真正珍贵的模型来处理这个问题。从预测的角度来看,基于多头神经网络的模型可以作为一种先进的解决方案。
- 在开始之前,如果可能的话,你必须向客户询问一些过去的异常例子。它会让你了解对你的期望。
- 异常的数量是另一个问题。大多数异常检测算法内部都有一个评分过程,因此您可以通过选择最佳阈值来调整异常的数量。大多数时候,客户不希望被太多的异常所打扰,即使它们是真正的异常。因此,你可能需要一个单独的假阳性排除模块。为简单起见,如果一个设备的流量模式为 10mbps,并且在某一点增加到 30mbps,那么这绝对是一个异常。然而,它可能不会比从 1gbps 提高到 1.3gbps 更受关注
- 在做出任何关于方法的决定之前,我建议至少对一个子样本的数据进行可视化,这将给出关于数据的深刻见解。
- 虽然有些方法直接接受时间序列,而没有任何预处理步骤,您需要实施预处理或特征提取步骤,以便将数据转换成某些方法的方便格式。
- 注意新奇检测和异常检测是不同的概念。简而言之,在新奇检测中,你有一个完全由正常观测值组成的数据集,并决定新接收的观测值是否符合训练集中的数据。与新奇检测不同,在异常检测中,训练集由正常样本和异常样本组成。 单类 SVM 对于新颖性检测问题可能是个不错的选择。
- 我鼓励看看 python 中的 pyod 和 pycaret 库,它们在异常检测方面提供了现成的解决方案。
有用的链接
DBSCAN、隔离森林、局部异常因子、椭圆包络和一类 SVM
medium.com](https://medium.com/learningdatascience/anomaly-detection-techniques-in-python-50f650c75aaf) [## 使用异常检测技术检测机器故障的开始
介绍
towardsdatascience.com](/detecting-the-onset-of-machine-failure-using-anomaly-detection-techniques-d2f7a11eb809) [## 用于异常检测和状态监控的机器学习
从数据导入到模型输出的分步教程
towardsdatascience.com](/machine-learning-for-anomaly-detection-and-condition-monitoring-d4614e7de770) [## 用于异常检测的最佳聚类算法
让我首先解释一下任何通用的聚类算法是如何用于异常检测的。
towardsdatascience.com](/best-clustering-algorithms-for-anomaly-detection-d5b7412537c8) [## 虚拟异常检测
单变量和多变量数据的无监督异常检测。
towardsdatascience.com](/anomaly-detection-for-dummies-15f148e559c1) [## 异常值检测——理论、可视化和代码
五种算法来统治他们,五种算法来发现他们,五种算法来把他们带到黑暗中…
towardsdatascience.com](/outlier-detection-theory-visualizations-and-code-a4fd39de540c) [## 离群点检测技术概述
什么是离群值,如何处理?
towardsdatascience.com](/a-brief-overview-of-outlier-detection-techniques-1e0b2c19e561) [## 检测流数据中的实时和无监督异常:一个起点
传感器通过在各种系统中收集数据以做出更明智的决策,实现了物联网(IoT)。数据…
towardsdatascience.com](/detecting-real-time-and-unsupervised-anomalies-in-streaming-data-a-starting-point-760a4bacbdf8) [## 价格异常检测的时间序列
异常检测会检测数据中与其余数据不匹配的数据点。
towardsdatascience.com](/time-series-of-price-anomaly-detection-13586cd5ff46)**
可解释句子表示的无监督创建
对于句子相似性/文档搜索应用
**图一。**用于句子相似性任务的句子表示签名的无监督创建。插图使用 BERT(BERT-大型案例)模型。
TL;速度三角形定位法(dead reckoning)
迄今为止,模型学习句子的固定大小表示,通常有某种形式的监督,然后用于句子相似性或其他下游任务。这方面的例子有谷歌的通用句子编码器(2018) 和句子变形金刚(2019) 。固定大小表示的监督学习往往优于句子表示的非监督创建,只有少数例外,如最近发表的作品 (SBERT-WK,2020 年 6 月),其中固定句子表示是通过从 BERT 模型的不同层提取的词向量的信息内容驱动的加权平均来创建的*(然而,已经有类似的跨层* 的词向量池的先前方法,这些方法在任务中表现不太好)。
对于句子相似性的特定任务,下面描述的替代简单方法将句子表示为单词表示的无序集合,并且按原样使用该集合,而不将其转换为固定大小的向量。单词表示由 BERT 在没有任何标记数据的情况下在预训练/微调期间学习。这种简单的句子集合表示,看起来似乎像一个单词袋表示,对于短句,其表现几乎与上面提到的模型一样好,甚至在质量上比它们更好*(需要通过全面测试进行量化)。这可能部分是因为,组成句子的单词(这里的“单词”用于 BERT 词汇表中的完整单词和子单词标记),所有这些单词都是从 BERT 的 30,000 个单词的固定大小的词汇表中提取的,并且它们的学习向量是上下文不敏感的(例如,单词“cell”的所有含义都被压缩成一个向量)*, 仍然可以用于表示单词的上下文敏感方面,方法是将这些单词映射到 BERT 词汇表中的其他单词,这些单词捕获它们在句子中的含义,这种映射是由具有掩蔽语言模型(MLM)头的 BERT 模型完成的。
这种方法的优点是
- 与上面提到的两个监督模型不同,我们避免了对标记数据的需要。
- 句子相似性任务的表示质量不会随着句子长度而退化——这是我们在通用句子编码器、句子转换器*(下图显示了定性比较)*和无监督模型- SBERT-WK 中观察到的限制
- 将句子表示为单词能够解释句子相似性任务的结果——学习固定表示的有用的属性模型通常是缺乏的。例如,在上面提到的所有模型中,与输入句子*(在我们可以收获相似句子的分布尾部内)*相似的句子的排序列表通常包含至少几个句子,其中与输入句子的语义关系根本不明显,即使存在一个。
这种方法的简单性不仅使我们能够在没有标记数据的情况下执行相似性任务,而且还可以作为基准性能来测试输出固定大小句子表示的未来模型,并且可能优于这种简单方法。
参考实施细节
正如在的一篇早期文章中所研究的,BERT 的原始嵌入捕获了关于任何单词的独特和可分离的信息,无论是独立的还是在句子*(使用 BERT MLM 头)*的上下文中,就其固定大小词汇表中的单词和子单词而言。这用于为句子相似性任务创建由这些单词的子集组成的句子签名。
图二。 BERT 的原始单词嵌入捕捉有用的和可分离的信息(不同的直方图尾部),关于 BERT 词汇表中其他单词的单词。该信息可以从原始嵌入和它们的变换版本中获得,在它们通过具有屏蔽语言模型(MLM)头的 BERT 之后。图中显示了在通过带有屏蔽语言模型头的 bert 模型之前和之后,BERT 的原始嵌入(28,996 个术语—基于 BERT 的大小写)中术语单元的前 k 个邻居。(1)输入句子。(2)输入句子的标记化版本——柯南不存在于伯特的原始词汇中。它被分成两个术语“Con”和“##nan ”,这两个术语都出现在 BERT 的词汇表中。(3)伯特的 MLM 输出在伯特的词汇中找到最接近变换向量的预测。对于单词“cell ”,前 k 个邻居(显示在右边——该图显示匹配——不是精确的排序)只包含捕获监禁的语义概念的术语。相比之下,单词“cell”在输入到 BERT 之前的前 k 个邻居(显示在左侧)捕获了该单词在其前 k 个邻居中的所有不同意义——“监禁”意义以及“生物”(蛋白质、组织)和移动电话意义(电话、移动电话)。输入之前和之后的前 k 个邻居落在距离/预测分数与计数的直方图的尾部,使得这些邻居与词汇表中的其余单词不同且可分离
例如,考虑下面更长的句子,“康南带着手机去牢房从囚犯身上提取血细胞样本”。这个句子的标记化版本将这个句子映射到 BERT 的大约 30,000 个标记的词汇表 (bert-large-cased) 。除了两个输入词“Connan”和“手机”,其余都是一对一的映射。这里的关键点是输入的标记化版本可以用来在 BERT 的词汇中找到相应的学习向量。
**图三。**细胞的不同义项在一定程度上被同一个词“细胞”在不同句子位置所映射的不同词所分隔。然而,手机和血细胞上下文之间仍然有一些重叠——“汽车”清楚地区分了手机上下文。然而,考虑到“细胞”、“细胞”和“蜂窝”这三个词,生物语境仍然与手机语境有重叠。这些意义可以通过取更多的前 k 项来分离。例如,生物上下文的第五个术语通过单词组织(上面未示出)来区分它。因此,这是我们希望从分布尾部挑选多少前 k 项与增加签名矩阵大小的性能影响之间的权衡。
当通过伯特的模型(MLM 头)时,这些标记向量被转换成表示这些单词的上下文敏感含义的向量。这可以通过检查上面句子中使用的“细胞”一词得到最好的说明。单词“cell”的前 3 个邻居*(这个选择是任意的——我们可以挑选前 k 个邻居,只要它们来自分布尾部)一旦它们通过模型,就映射到 BERT 词汇表中的不同单词。代表监狱的“细胞”有一个 房间 的意思,而在手机上下文中使用的“细胞”则表达了一辆 汽车 的意思。“细胞”一词在生物学上下文中的含义具有生物细胞的概念(未示出的第五个邻居是* 组织 *)。*本质上,即使单词“cell”的含义随着上下文而变化,我们仍然可以在 BERT 的学习词汇中找到捕捉其上下文敏感意义的相应向量。鉴于此,我们可以使用标记化文本的向量以及每个标记在通过 BERT 模型(MLM 头)后的前 k 个邻居作为该句子的签名。
选择前 k 个邻居时,会忽略显示为预测的单字符标记,如标点符号。只要尾部有足够的标记可供选择,我们就可以安全地做到这一点,实际情况就是这样。
本质上,给定一个长度为 N 的句子,假设标记化版本的长度为 M,该句子的签名将是 M*(1 + k),其中 k 是在将该句子传递给 BERT 后我们挑选的顶部邻居的数量。句子的签名将是一个具有 M*(1+k)行和 D 列的矩阵,其中 D 是维度*(对于 bert-large-cased 为 1024)*。
计算句子相似度得分的步骤
一旦如上所述计算了一个句子的签名,我们就可以计算两个句子之间的相似性得分,如下面的原型/参考实现所示
**图 4。**两个句子 A 和 B 之间的相似度计算
当计算输入句子与已知句子集合*(例如文档标题)*的相似性时,上面计算的成对分数用于计算输入句子与已知集合中所有句子的接近度的相对分数。
上述得分计算中的加权函数是基于参考语料库中术语的出现频率。本质上,从两个句子签名中创建的词对之间的余弦相似性的贡献由该分数对相似性计算的重要性来加权,该相似性计算是这些术语在参考语料库中出现的函数。这确保了句子中出现的像“the”、“of”这样的粘合词比真正抓住意思的词贡献少。句子对的相对长度也在分数计算中考虑,以确保短句选择相似的长句,而不是相反。当使用这种方法进行文档搜索,将文档中的句子转换为句子签名时,这尤其有用。
句子相似性任务中模型绩效的定性比较
使用表征短句*(平均句子长度 8 个单词)和长句(平均句子长度 51 个单词)的两组句子来定性地比较三个模型(通用句子编码器-使用、句子转换器和 SBERT-WK)* 与上述相似性计算方法。
短句集主要由 USE 和句子转换器在其出版物中展示的测试句子组成。
长句集是从快速文本数据中提取的,人类已经将句子分为 14 类。这些类别不是严格的类别,在某些情况下,一个句子可能属于多个类别。此外,一些测试句子是多个句子——几乎代表一个小段落。
属于单个簇/类别的三个句子被用来表示一个组,总共 42 个句子属于 14 个簇*(具有前面提到的关于较长句子集合具有属于多个簇的一些句子的警告)*。来自这两个测试的几个集群如下所示。
**图 5。**由使用和句子转换器出版物中展示的测试句子组成的例句组。在标记化之前,平均句子长度约为 8 个单词
**图 6。**来自 Fasttext 测试数据的例句组。在标记化之前,平均句子长度约为 51 个单词
总的来说,
- 所有四个模型在小句测试中都表现良好。我们可以看到所有型号的 3x3 单元沿对角线的浅色阴影。
- 在更大的测试中,使用和句子变形器的性能都有明显的下降。沿着对角线大约有 3 个浅色的 3×3 网格,而不是预期的 14 个网格。SBERT-WK 似乎沿对角线有更多的 3×3 网格,尽管它们与热图中的其余单元没有明显区别。此外,对于 SBERT-WK,热图的整体亮度从短句到长句增加,表明句子之间的相似性度量的分布尾部对于长句情况不明显。相比之下,使用和句子变形器保留了短句和长句尾部的区别,但尾部的 3x3 网格较少。
图 7。 USE 以高精度捕捉句子相似性,并从左侧热图中沿对角线的 3x3 网格中回忆短句。在更长的句子对测试中,回忆明显下降——我们几乎看不到对角线上预期的 14 个网格中的 4 个。无论是短测试还是长测试,结果的分离都是一样的——热图颜色阴影范围不受句子长度的影响。
图 8 。Sentence transformer,像 USE 一样,可以高精度地捕捉句子相似性,并从左侧热图对角线上的 3x3 网格中回忆短句。在更长的句子对测试中,回忆明显下降——我们几乎看不到对角线上预期的 14 个网格中的 3 个。无论是短测试还是长测试,结果的分离都是一样的——热图颜色阴影范围不受句子长度的影响。
图九。SBERT-WK,像 USE 和句子转换器一样执行,适用于短文本和句子。一个明显的区别是较长句子的结果分离度下降——热图颜色阴影范围随句子长度而变化,较长句子测试中的 3×3 网格变得难以分离。
图 10 。对于短句,句子签名方法的表现几乎不如其他模型。然而,对于更长的句子测试,它比其他人捕获了更多的聚类——14 个中的 9 个(其中 3 个是半正方形,因为分数计算的不对称性质)。此外,它似乎从暗示属于多个聚类的句子的对角线中捕获语义上接近的邻居。这一点在下面的长句子情况的附加注释部分进行了检查。
句子签名方法与其他三种模型相比有几个独特的方面
- 热图是不对称的,不像其他的。这只是前面描述的不对称分数计算的结果。
- 在远离对角线的地方有很多亮点,特别是在长句测试中——这在所有其他模型中都明显不存在。这部分是因为这些聚类有可能属于多个聚类的句子。还存在一定程度的错误匹配,部分原因是图 3 中捕捉上下文意义的术语的语义扩散。大的 k 会增加扩散*(除了影响矩阵大小增加的性能之外),而小的 k 可能不足以消除歧义——k 的选择是一种权衡。然而,与其他模型不同,我们可以根据对分数有贡献的主要描述符来检查这些浅色斑块(在附加注释部分完成)*的原因。这是这种方法相对于结果很不透明的其他模型的一个明显优势。
在句子相似性任务的基准测试集上,模型的定量比较仍有待完成。
限制
一些限制是
- 对于短句,这种方法的表现不如其他模型。只有当句子长度增加时,它的表现才优于*(在定性测试中)*。
- 这种方法不适用于除句子相似性之外的任务,尽管个体标记的上下文敏感签名可以用于标记任务,如无监督 NER 。
- 这种方法就像任何其他模型一样容易出现假阳性,尽管它们可以根据上面提到的解释性描述符被剔除。
最后的想法
像 BERT 这样的基于转换器的模型的未开发的潜力之一是表示其词汇的学习向量的固定集合。虽然这些向量在本质上与 word2vec 等模型学习的单词向量没有什么不同,但这些模型有两个明显的优势
- 固定大小而不是可变大小的词汇。词汇表的大小是固定的,留给我们选择*(我们只需要用我们选择的词汇表预先训练模型)*使它能够作为一个固定的参考库——这是 word2vec 之类的模型所缺乏的。
- **捕捉句子语境。**使用 MLM 中心模型将上下文相关向量映射回 BERT 词汇表中的向量,使我们能够根据单词出现的句子上下文来捕捉单词的上下文相关含义。
这两个事实使得即使是使用上下文敏感词来间接捕获序列信息的签名词包也能够在短句上表现得几乎一样好,甚至在长句上比其他模型更好。
原型参考实现 可在 Github 上获得。
参考/相关工作
用于对当前方法进行定性基准测试的三个模型。这三者在 Github 上都有参考实现
使用 word2vec 等模型评估句子嵌入的基线模型 (2016)。这个简单模型在句子相似性任务中胜过序列模型 (RNNS/LSTMs) 。
附加注释
在长句测试中,导致浅色阴影单元格远离对角线的句子对(每句话约 51 个单词)将在下面进行检查。下图是图 10 的放大版,右侧热图。白色单元格是得分为 1 的句子对*(该得分是一个相对度量得分,与余弦距离度量不同,余弦距离度量通常只对完全相同的两个句子得分为 1)*
图 11。图 10 中的长句测试(约 51 个单词)的放大热图。距离对角线有 13 个高相似性得分。
远离对角线的白色方块对应的句子对如下所示。
图 12。十三对中的三对似乎是错误的相似性度量,这从句子对(由“|”符号分隔)中可以明显看出
来自句子签名的最高贡献对下面检查三个错误句子对。这些提供了为什么这些句子匹配的洞察力,并可以作为过滤句子对的手段。
句子签名中的第一个句子对及其最佳匹配描述符
句子签名中的第二个句子对及其最佳匹配描述符。这些句子接近的原因从描述符对中显而易见——蝴蝶和飞机常见的翅膀的概念通过几个微弱的成对交互作用叠加成一个信号发挥了主导作用。
在最后一个句子对中,除了一个事实,除了一个事实,即这些句子对没有太大的意义之外,这些句子对的解释力不如前一个句子。
本文由 Quora*【https://qr.ae/pNKmJ7】*手动导入
基于 Python 的无监督土地覆盖分类
你并不总是需要训练数据。。。
航空影像的用途从军事行动到检查你可能购买的房子的后院。我们的人类大脑可以很容易地识别这些照片中的特征,但对计算机来说就不那么简单了。航空影像的自动分析需要将每个像素分类为土地覆盖类型。换句话说,我们必须训练计算机知道它在看什么,这样它才能知道要找什么。
有两种主要的分类方法。有人监督和无人监督。监督分类使用观察到的数据来指导一种算法,即红色、绿色和蓝色光的组合(图像中的像素值)代表草地、树木、泥土、路面等。无监督分类根据每个像素与其他像素的相似性将像素分配到组中(不需要真实数据或观察数据)。
我之前描述了如何实现一个复杂的、基于对象的算法用于监督图像分析。本文描述了一个简单的实现的 K-均值算法的无监督图像分类。因为无监督分类不需要观察数据(收集这些数据既费时又费钱),所以它可以应用于任何地方。
我们将使用国家农业图像项目(NAIP,如下所示)的一部分图像。文章最后给出了包含所有代码的要点。
入门指南
这个分析只需要三个 Python 模块。scikit-learn
(或sklearn
)gdal
和numpy
。
导入模块,用gdal
加载镜像。用RasterCount
查询图像(gdal
数据集)中的波段数。根据用于收集图像的传感器,您可能有 3 到 500 个波段(用于高光谱图像)。NAIP 有 4 个波段,量化反射红、绿、蓝和近红外光。
另外,创建一个空的numpy
数组来保存来自每个图像波段的数据。我们将展平数据,以便更好地使用sklearn
k-means 算法。空数组需要与影像中行和列的乘积一样多的行,以及与栅格波段一样多的列。
from sklearn.cluster import KMeans
import gdal
import numpy as np naip_fn = 'path/to/image.tif'
driverTiff = gdal.GetDriverByName('GTiff')
naip_ds = gdal.Open(naip_fn)
nbands = naip_ds.RasterCount data = np.empty((naip_ds.RasterXSize*naip_ds.RasterYSize, nbands))
读取每个栅格波段的数据。用numpy.flatten()
将每个 2D 栅格波段阵列转换成 1D 阵列。然后将每个数组添加到data
数组中。现在,所有波段数据都在一个数组中。
for i in range(1, nbands+1):
band = naip_ds.GetRasterBand(i).ReadAsArray()
data[:, i-1] = band.flatten()
k-均值分类
我们可以用三行代码实现 k-means 算法。首先用您想要将数据分组到的集群(类)的数量设置KMeans
对象。通常,您将使用不同数量的分类对此进行测试,以找到最佳分类计数(在不过度拟合的情况下最能描述数据的分类数量)。我们不会在这篇文章中讨论这个问题,只讨论如何进行分类。在对象被设置后,使聚类适合图像数据。最后,使用拟合的分类来预测相同数据的类。
km = KMeans(n_clusters=7)
km.fit(data)
km.predict(data)
保存结果
用labels_
从 k-means 分类中检索类。这将返回输入数据的每一行的类别号。调整标签形状以匹配 NAIP 图像的尺寸。
out_dat = km.labels_.reshape((naip_ds.RasterYSize,\
naip_ds.RasterXSize))
最后,使用gdal
将结果数组保存为光栅。
clfds = driverTiff.Create('path/to/classified.tif',\
naip_ds.RasterXSize, naip_ds.RasterYSize, 1, gdal.GDT_Float32)clfds.SetGeoTransform(naip_ds.GetGeoTransform())
clfds.SetProjection(naip_ds.GetProjection())
clfds.GetRasterBand(1).SetNoDataValue(-9999.0)
clfds.GetRasterBand(1).WriteArray(out_dat)
clfds = None
结论
对任何图像实现无监督分类算法都非常简单。无监督分类结果有一个主要缺点,您应该时刻注意。使用非监督方法创建的类不一定对应于现实世界中的实际特征。这些类别是通过对所有四个波段中具有相似值的像素进行分组而创建的。房子的屋顶可能和水有相似的光谱属性,所以屋顶和水可能会混淆。解释无监督的结果时必须谨慎。
原载于 2020 年 7 月 1 日【https://opensourceoptions.com】。
无监督学习:聚类算法
从头开始的数据科学
使用 K-means 和凝聚聚类算法对无标签数据进行分组
预测模型通常需要所谓的“标记”数据来进行训练——也就是说,数据中有一些你已经填写好的目标变量。当然,我们的目标是在您不知道目标变量的值的未知数据上使用模型,但是如果没有正确标记的训练数据,您就无法验证您的模型。因此,数据生产通常是数据科学项目中最困难的部分。比方说,你想教一台计算机阅读手写内容。收集几百页的文字扫描进去是不够的。你还需要给这些数据贴上标签,让每个单词都有一个相应的“正确读数”。对于像手写这样复杂的东西,您可能需要成千上万的训练样本,并且手工标注许多条目会很费力。
也许如果您遇到一个没有标记目标变量的数据集,有一种方法可以从它那里获得一些洞察力,而不需要经过标记它的麻烦。也许你只是没有时间或资源来标记数据,或者也许没有一个明确的目标变量来预测,是否仍然有一种方法来使用这些数据?这就是‘无监督学习’的世界。无监督学习的一个更常见的目标是对数据进行聚类,以找到合理的分组,其中每个组中的点看起来比其他组中的点更相似。最常见的两种聚类方法是 K 均值聚类和凝聚聚类。
K-均值聚类
所以,你有一些数据,你没有任何类别标签,但你很确定你可以将数据分成合理的组,你只是不知道那些组是什么。让我们生成一些数据进行实验:
一些假设的数据,生成为三个 blobs
如你所见,这些数据是由三个簇生成的。对于本例,这保证了有一个合理的分组为三个集群,但请记住,这里的要点是看您是否能自己找到该分组,因此让我们删除颜色编码:
我们的观点没有颜色编码的好处
从视觉上看,你可能仍然能看到星团,至少能看到一点点,但是我们如何让计算机自己发现星团呢?我们要讨论的第一个策略是 k-means。在 k-means 策略中,首先指定要寻找多少个聚类,在这种情况下,我们假设是 3 个,计算机开始在图上随机放置这么多新点:
我们的数据有三个新的点——我们的质心——用红色表示
这些新点被称为“质心”——它们将成为我们星团的中心。为什么要随机放置?实际上,计算机没有办法提前知道哪里是放置聚类中心的合适位置,所以策略是随机放置它们,然后逐步移动它们以改善聚类。要决定如何移动质心,首先将每个点分配到离它最近的质心:
这些点已经被分配到最近的质心,现在是星形
我已经把形心做成星形,然后根据哪个形心最近来给这些点标上颜色。您可以看到红色和绿色的质心彼此靠近,并朝向数据的边缘,这似乎没有太大的意义-首先,红色聚类只有两个点。所以我们试着把这些质心移到更中心的地方。我们将每个质心移动到聚类的中心,这是聚类内的平均位置。一旦我们移动了质心,我们就把这些点重新分配给最近的质心。这是流程中新任务的第一步:
在将每个质心移动到其聚类的中心并重新分配这些点之后
红色质心没有移动很远,它仍然只有两个点在它的集群中。然而,绿色的质心向星团的中心迈了一大步。当它这样做的时候,它从蓝色星团中捕获了一堆点。因此,当我们重复这个过程时,蓝色质心将进一步移动到顶角,将底部留给绿色和红色:
这一过程又进了一步
再次重复该过程会产生:
我们继续这个过程,一步一步地移动质心,并将这些点重新分配给相关的簇,直到我们找到一组稳定的停止移动的中心。在这种情况下,均衡看起来像这样:
这是分割数据集的一个不错的方法!这也有助于可视化质心本身的运动:
每个质心是如何逐步移动的
在这个过程中,第一个 fews 步骤相当大,因为质心被拉向其簇的中心,但后来它们采取的步骤变得越来越小,因为在每个步骤中只有少数点被重新分配。
在这种情况下,您会注意到集群算法最终基本上发现了我生成数据时创建的组。然而,重要的是,记住这是一个无监督的学习问题,算法没有办法以任何方式验证这些组。这些分组是在没有参考这些点属于哪一个“类”的情况下进行的,仅仅是它们的位置,不管怎样,计算机都无法判断这些分组代表了什么。如果您想要对聚类进行解释,则需要查看这些组并对它们进行思考,并且无法保证人们能够立即看到哪些特征代表了已创建的聚类,尤其是如果有两个以上的特征变量使得数据难以可视化。
另一个挑战是,您创建的分组取决于您的初始质心所在的位置以及有多少个质心,并且,同样,您可能无法验证您找到的分组是否是最佳分组,或者您是否有适当数量的簇!考虑这个例子,它也有三个簇,但是质心从不同的地方开始:
红色和绿色集群最终都迁移到右上角的斑点,在它们之间分裂,留下蓝色质心独自拥有三分之二的数据。k-means 算法无法知道这组聚类是否比我们发现的第一组更有意义。类似地,我们必须指定我们想要将数据分组到多少个簇中,我们不需要选择 3,我们可以选择任何数字。这是另一个分组,有 5 个集群,而不是 3 个:
在某些情况下,您可能有特定的原因来选择一个特定数量的组—也许您正在将客户支持案例划分给一些支持人员,以便您只需要与员工人数一样多的组—但是一般来说,无监督聚类的挑战之一是决定聚类的数量。
在建立 k-means 算法或我们马上要讨论的凝聚算法时要记住的另一件事是,实际上有几种不同的方法来计算抽象空间中两点之间的“距离”。也许最常见的是“欧几里德距离”,你会记得毕达哥拉斯定理。还有“曼哈顿距离”之所以这么叫,是因为它将每个轴上的距离单独累加起来,就好像你正沿着直角街道网格从曼哈顿的一个十字路口走到另一个十字路口。更一般地说,欧几里德距离和曼哈顿距离实际上都是称为闵可夫斯基距离的一般度量的特定情况。如果您正在处理多维但稀疏的数据,您可能还会使用余弦相似度作为相似度的度量——虽然余弦相似度本身并不是距离的度量,但如果您有许多变量,并且每行都趋于“稀疏”,那么余弦相似度在某种程度上可以更快地计算出相似的东西(也许您正在查看的客户可能都从拥有数千种不同产品的在线商店购买了少量产品)。不同的距离度量也可能在最终分组中产生略微不同的结果。
层次凝聚聚类
K-means 是一种“自上而下”的方法;它一次将所有东西分组,然后分几步调整这些分组。聚集聚类是一种自下而上的方式。它开始时没有聚类,只是所有的单个点,然后慢慢地一次一个地将点组合在一起。这个过程很简单。首先你要计算每个点和其他点之间的距离。选择彼此最接近的点,并将它们组合在一起。您继续重复这个过程,计算任何单个点或簇到所有其他点和簇之间的距离,慢慢地将点连接成簇,簇连接成其他簇,直到您只剩下您开始寻找的簇。举例来说,下面是一个在只有 20 个点的较小数据集上工作的算法示例:
这是这个凝聚算法如何结束划分第一个数据集(你会看到一些点自己浮出来,看起来好像是自己分组的,但该算法实际上已经确定了三个集群,这些点实际上正在与附近更大的组进行分组。它们看起来像是自己浮动的,这是我用来自动绘制集群周围的围栏的核密度估计器的一个怪癖):
使用凝聚聚类对原始数据集进行分组
使用这样的算法,除了决定两点之间的距离度量之外,实际上还需要决定如何度量两个聚类之间的距离。您可以测量一个集群中的一个点到另一个集群中的一个点之间的最短距离,有点像两个集群内侧边缘之间的距离,这被称为“单一关联”。你也可以测量其中一个点到另一个点的最远距离——这被称为“完全链接”。或者你可以取一个集群中任意点到另一个集群中任意点的平均距离——“平均连接”。选择不同的距离度量或链接类型可能会导致不同的分组决策。
这种类型的集群的一个好处是它是稳定的。它不像 k-means 方法那样依赖于随机种子。一旦决定了距离的度量和聚类的数量,最终总是会得到相同的聚类。一个缺点是计算量很大。您需要测量每两组点之间的距离(对于 n 个点,此类链接的数量公式为 n(n-1)/2),这只是为了决定您进行的第一个链接!
决定集群的数量
我前面提到过,决定一些集群是这些集群算法的挑战之一。那么,你如何选择集群的数量呢?没有硬性的规则,但是有一些方法可以用来指导你的选择。判断集群的度量标准倾向于关注集群的紧密程度,也就是说,集群中的点有多相似。一个简单的度量可能是组内平方和(WSS ),如果组内的点更分散,则该值会更大,如果点更靠近,则该值会更小。单独使用这种方法的挑战在于,随着您添加更多集群,WSS 将始终下降。你可以通过拥有和点一样多的聚类来最小化 WSS,那么 WSS 就是 0!
您如何知道何时停止添加集群?目标是找到一些聚类,在这些聚类中,再增加一个聚类对 WSS 度量不再有真正显著的好处。如果集群定义良好,那么这种情况通常会非常明显。考虑这个数据集,它被生成为具有 4 个相当不同的斑点:
现在,我将使用 k-means 将这些数据多次分组,每次都增加分组的数量。我将记录每一个的 WSS 值,然后将它与聚类数对应起来绘制成图表:
您会注意到,当我将聚类数从 2 增加到 3,再从 3 增加到 4 时,我的 WSS 度量下降了很多,但每次增加都不是很多。一旦我找到 4 个聚类,算法通常会找到合理的分组。当我添加更多超过 4 的聚类时,算法开始分裂自然的聚类,我的紧凑性度量的边际效益并没有那么大。
这种确定适当聚类数的方法有时被称为“肘方法”;你在寻找图中的弯曲处或肘部,在那里增加 k 不再产生紧凑性方面的大的步骤。这是一个合理的起点,但是没有一种选择集群数量的方法是绝对可靠的。这种方法可能会被重叠、有点模糊或大小非常不同的簇所欺骗。
选择聚类方法
虽然这些不同的算法通常都在处理同一类事情,即紧密分组的簇,但它们以不同的方式完成任务,因此以不同的方式对不同的形状和分布做出响应。举个极端的例子,考虑以下几点:
也许我通过颜色编码泄露了一点,但是我想很多人会同意这里确实有两个不同的类:内环和外环。k-means 算法如何处理这些点?结果并不好:
k-means 不能正确区分嵌套环
这个例子可能会让你觉得有点做作,甚至可能不公平。一个简单的算法可能挑出嵌套的形状吗?然而,答案实际上是肯定的;在这种情况下,使用单个链接来寻找聚类之间的距离的凝聚方法实际上识别了嵌套环:
单链聚合聚类成功!
不幸的是,没有对所有形状都同样有效的主聚类算法,但是在使用聚类方法一段时间后,希望您会有一种直觉,知道哪种情况更适合一种算法或一种距离度量。