如何选择数据科学项目,并最大限度地激发您完成项目的动力
我经常被问到这个问题:我如何挑选一个好的数据科学项目?这里是我的方法,提出一个想法候选名单,计划出具体的步骤,最重要的是,执行项目完成。
Source: Unsplash
本文将带您了解以下内容:
- 想出一个想法的候选名单
- 执行计划
想出一个想法的候选名单
你的“为什么”
首先,我们先来考察一下,你当初为什么有兴趣做一个数据科学方面的项目。
对自己诚实!在执行项目时,理解你的动机会产生最大的不同。当你对自己不诚实时,如果你不能保持动力,想法很容易被放弃。
原因可能是:
- 强迫自己学习[x]编程语言或技术
- 我想改善我的投资组合(学校或工作申请)
- 每个人都在做…为什么我不能?
我希望你真诚地思考你的理由。当我回顾我成功完成的项目和没有完成的项目时,为什么和项目对我的意义,使产生了最大的不同。与编程难度、对技术栈的不熟悉等等相比,这是项目完成的更好预测。
现在我希望你已经和自己谈过了,记下你做数据科学项目的原因。没有对错。可能是“我只是想在同学面前看起来酷一点”。你不必告诉我;你不用告诉任何人,只要对自己诚实。
你的技能清单
我希望你写下的第二件事是:你想通过这个副业项目学习和展示哪些技能?
你可以从写下你的技能愿望清单开始——随意写下多项,只要你能清楚地表达你想学什么,以及结果是什么。因为这是一个愿望清单——我鼓励你甚至写下你认为目前远不可及的事情!(我会列出我在亚马逊上买不起的东西,一直都是……)
一个例子:
- [x]语言,[y]包:比如“我想学 Flask (Python web framework)”
- 对结果的具体设想:“我想证明我可以制作一个 web 应用程序,它可以接受用户的输入,并向用户输出一些东西”(我们可以稍后细化细节,现在专注于大脑转储)
现在,你应该有一个愿望清单,列出你想学习的技能,并对结果有一些想法。这里有一个例子,当我刚开始从事数据科学时,我的愿望清单可能是这样的:
- 我想学习如何做我在 Stata(一个本科必修的统计软件)中做的所有事情,但是是用 Python
- 结果:复制我在 Stata 中完成的任务(线性和逻辑回归),但是只使用 Python
您可能会注意到,这个例子并没有特别给出 Python 包的名称(例如 NumPy、Pandas 等。).这是因为当时我刚刚起步,不太了解 Python 在数据科学方面的具体内容。这正是这个兼职项目的目的——学习细节并能够做到这一点!这也可能是你的情况——也许你想构建一个 web 应用程序,但不知道使用什么框架或技术…
这将我们带到下一个主要部分,创建您的执行计划。
执行计划
完善你的愿望清单
现在,最困难的部分来了。对我来说,现在有太多的东西我想学!
我试图想出一种方法,将我想学的所有技能融合到一个项目中。“我如何同时做一些涉及张量流,烧瓶和[x],[y],[z]的事情?”
经过一番试错,我发现这是个好东西!但是我建议你试着限制它,一旦它发展到一定程度,就会给项目带来严重的不便。例如,我想在一个项目创意中加入学习围棋(通常被称为 Golang)的想法。经过更多的研究,它似乎不能与我想学的其他技能“配合得很好”。虽然创造性地让多个部分协同工作本身可能是一个有趣且具有挑战性的兼职项目,但它不在我的技能清单上。
如果您曾经遇到范围蔓延(例如,“我想在这个项目中再多学一样东西!),我建议根据你的技能清单上的最高优先级,以及对你能负担得起的时间框架内的副业项目的研究,来缩小范围。从小处着手——这个副业不会成为你一生的工作或任何事情。(所以才叫一个边项目!)它的目的是帮助你去更高更远的地方。
设计项目
还记得第一节中的问题“我为什么要做这个数据科学方面的项目”吗?
在这里,我真的想提出一个一刀切的解决方案,你不必接受,但听我说…
不管你的真正原因是为了看起来很酷,炫耀,“这样我的 GitHub 就不是空的”,学习[x],还是其他什么——我建议你用一个一个标准来设计这个兼职项目。
Do something that is meaningful to you.
是的,我知道这很俗气,但我发现这是我完成项目与否的最大区别。
当我开发视频游戏时,我可能会想“什么最流行;卖什么?”并尝试制作多人对战 royale 第一人称射击游戏。(大众喜爱的各种类型的混搭。)
好吧,如果我只考虑了诸如流行语(或“就业能力”)之类的外部因素,我可能会一直在做一个对我来说如此普通和无聊的兼职项目,我可能会很痛苦,并开始找借口停下来。但是如果你没有完成它,你将无法展示你的**【技能清单】,你想学习它是因为【你做数据科学副业的理由】**!
所以,我专注于设计我的项目的方式是:什么会让我有乐趣?什么对我有意义?
你很少会独自拥有一个项目,并拥有完全的控制权;珍惜它去做一些与众不同的事情!
随便做点傻事!做一些只有你和你的朋友能理解的事情!
我知道你现在可能有疑问:
- 如果这是我想给[招聘人员],[其他外部方]留下深刻印象的东西呢?可能他们不会关心我关心的【话题 x】——我不应该做我关于【话题 x】的副业!
- 如果展示我在做吴恩达深度学习课程第[m]周的作业[n]时的代码更安全,以显示我真的在 Coursera 上做过那门课程,会怎么样?
好吧,我不是说你不能做到以上几点,但我感觉项目越独特,一个【招聘人员】,【其他外部方】就越会记住它。作为一个看过很多简历、面试过很多应聘者的人,看起来像课程或作业的副业很难记住,因此不会让应聘者脱颖而出。
与此同时,那些真正看起来像激情项目的项目往往会让我脱颖而出,作为一名面试官,我喜欢问应聘者这些问题。
作为思考练习,我鼓励你问自己的另一个问题是:我会在会议上谈论这个吗?
当你这样想的时候,没有人会在会议上谈论他们在[Coursera 课程]中的作业[n],[第四年的 x 课程,其他 100 多人也这样做了]。当我想到会议演讲时,我通常会想到演讲者创造的独特产品,或者他们解决的独特问题,这对他们来说意味着什么。
现在,花些时间用这些镜头设计你的副业,关注结果:
- 当这个项目“完成”时,它会是什么样子?即定义项目的终点
请记住,所有这些都是可变的:一旦你开始这个项目,事情可能会发生变化,这没关系。关键是开始行动,而不是陷入规划的困境。
一旦你写下了一些细节,你就可以开始下一步了。
Source: Unsplash
迈出你的第一步
好吧!我们到了,计划的最后一步,这将帮助你在你的副业上迈出第一步!
概括地说,您已经准备好了前面章节中的以下组件:
你的技能清单
- 示例:Tensorflow、Keras、Flask…
你的副业预期成果设计
- 例如:一个 web 应用程序,它将接收用户上传的图像,通过图像识别神经网络(由您训练)输入图像,并输出结果(例如,图像中出现了视频游戏《英雄联盟》中的哪个角色?)给用户。
在这个阶段,我经常会遇到一些瘫痪——也许我会因为对技术栈不熟悉而感到沮丧或卡住。没关系,我想帮你避免这种情况。你的兼职项目可能是你第一次实现某项技能,这是最重要的,所以遇到一些困难把你推到舒适区之外是很自然的。
在这里,对我来说最有效的方法是分解问题。在上面的例子中,结果是两个项目合二为一:
- 训练神经网络对视频游戏《英雄联盟》中的角色进行分类
- 有一个 web 应用程序(有一个前端),可以以某种方式连接用户上传的图像,通过神经网络传递它,并获得结果
在这一点上,我通常从我觉得稍微容易的部分开始,在这种情况下是1.
,并将其进一步分解成任务。
训练神经网络对视频游戏《英雄联盟》中的角色进行分类
- 收集训练图像
- 选择神经网络架构
- 建立训练神经网络的环境
然后,递归地将最容易的或顺序最早的步骤分解成更小的步骤:
采集训练图像
- 使用美丽的汤或 Scrapy 获得训练图像(从哪里?了解一下)
- 以适合神经网络输入的方式裁剪图像或调整图像大小
使用美汤或刺儿头获取训练图像
- 找个网站刮一下(谷歌图片搜索?)
- 写代码!但是我从来没有用过 Scrapy!
现在,这两个步骤看起来不像我们的大步骤那样令人生畏,“训练神经网络对视频游戏《英雄联盟》中的角色进行分类”。事实上,这两个步骤相当简单。第一步不需要编码,只需浏览一些图像托管网站,寻找对项目有用的东西。第二步可以从谷歌搜索开始,但在这一点上,很容易开始这个项目的实际步骤——即使有一个关于“如何使用 Scrapy”的堆栈溢出问题
你完成了一小步,太棒了!你刚刚开始你的副业!
接下来,你沿着树枝往上走,直到更大的步骤也完成。然后,你会慢慢看到你想象的结果变成现实。不仅如此,你还将学习和展示你的**【技能组合愿望清单】,以及实现【你做数据科学兼职项目的理由】**。
离别赠言
在我的兼职项目中,我曾多次陷入困境或失去动力。我从克服这些挑战中学到了很多,这篇文章是我根据自己的经验写的——帮助你制定行动计划,清楚地看到你的第一步!
如果您有任何问题或反馈,请随时联系我们。如果这个指南对你有帮助,我很想听听。
现在开始吧!
—最初发表于susanshu.com
如何在多种模式中选择
比较模型时要记住的关键概念
在之前的文章中,我们讨论了欠拟合和过拟合的概念,它们如何导致模型与可用数据不匹配,如何识别每个问题,以及如何识别与数据非常匹配的模型。这些概念可以帮助您避免重大失误,并生成合理准确地拟合数据的模型;然而,有令人难以置信的数量的模型符合这一描述。这意味着下一步,除了生成一个合适的模型之外,还要确定哪个模型最合适。
在确定模型与数据集的拟合程度时,将模型预测与数据集进行比较来计算统计值非常重要。这超出了这篇概念性文章的范围,但更多信息可以在从头开始的数据科学或数据科学家实用统计中找到。在本文中,我们将讨论开发、验证以及测试模型的过程。
什么是模型开发、验证和测试阶段,为什么它们是必要的?
这里要注意的基本问题是,你不能仅仅因为你开发的模型与训练数据吻合,就相信它。这是因为一个简单的原因:您强制模型很好地适应了训练数据。如果在创建一个模型后,统计计算显示它与数据匹配良好,这意味着可以使用数学方法来迫使模型与数据匹配良好。这并不意味着模型正在捕捉真正发生的趋势,或者模型能够预测其他情况。我在上一篇文章中的 overfit 模型的例子很好地突出了这一点。
这个问题的解决方案是模型验证。验证是使用模型来预测其他有数据的情况下的输出,并根据这些结果计算相同的统计度量的实践。请注意,这意味着您需要将数据集分成两个不同的数据文件。第一个是训练数据集,用于生成模型。第二个是验证数据集,用于对照未用于训练模型的数据来检查模型的准确性。
一般来说,一个项目中会创建多个模型。如果创建了许多模型,则将最适合训练数据的模型与验证数据进行比较。那么自然的选择是选择最符合验证数据的模型,然后继续前进。然而,这带来了另一个潜在的陷阱。类似于将模型与训练数据进行比较,仅仅因为模型最接近地匹配验证数据仍然不意味着它匹配现实。虽然它在这个测试中表现最好,但它仍然可能是错误的。
最后一步,也是该问题的解决方案,是将在验证阶段表现最佳的模型与第三组数据(测试数据)进行比较。这个测试数据也是来自原始数据源的数据的子集。它只包含模型开发或验证中未使用的点。只有当模型与测试数据进行比较,并且统计计算显示出令人满意的匹配时,才认为模型可以使用。
这整个过程是如何分解的?
这个过程分为以下七个步骤。
- 创建开发、验证和测试数据集:最初你有一个单一的大型数据集。正如上一节所讨论的,您需要将它分成三个独立的数据集,每个数据集只用于项目的一个阶段。在创建每个数据集时,确保它们包含位于每个变量的高/低极限值和中间值的混合数据点。这确保了该模型能够并且必须在光谱的所有范围内都是准确的。此外,确保大多数数据都包含在训练数据集中。模型只能和用来创建它的数据集一样精确,更多的数据给了它更多的机会。
- 使用训练数据集开发您的模型:将数据集输入到您的模型开发脚本中,并使用它来开发您选择的模型。根据可用的数据源和需要回答的问题,您可以开发几种不同的模型。关于模型类型的更多信息可以在从头开始的数据科学中找到。在这个阶段,您可能想要创建几个不同的模型。它们可以是不同结构的模型,或者是不同阶的几个回归模型。生成您认为可能表现良好的任何模型。
- 计算识别模型开发性能的统计值:一旦模型被开发出来,你需要将它们与用来创建它们的训练数据进行比较。性能较高的模型比性能较低的模型更适合数据。为此,您需要计算为此目的设计的统计值。例如,检查回归模型性能的一种常用方法是计算 r 值。根据这些统计计算,将模型与训练数据集进行比较,确定表现最佳的模型。
- 计算验证数据集中数据点的模型结果:在这一步中,您使用验证数据集中的输入来驱动模型,为这些数据点生成预测。一旦完成,您就有了真实值(来自数据集)和预测值(来自模型)。这允许您将不同模型的性能与验证数据集中的数据进行比较。
- 计算统计值,将模型结果与验证数据进行比较:现在您已经有了验证数据集中每个实例的数据值和模型预测,您可以计算与将模型预测与验证数据集进行比较之前相同的统计值。这是流程的关键部分。第一次统计计算确定了模型与被迫拟合的数据集的拟合程度。在这种情况下,您要确保模型能够匹配一个单独的数据集,一个对模型开发没有影响的数据集。对每个型号完成您选择的统计计算,然后选择性能最高的型号。
- 计算测试数据集中数据点的模型结果:使用测试数据集的输入来驱动模型,在这些点上生成模型的预测输出。仅使用验证阶段性能最高的模型来执行此任务。完成后,您将获得每个输入的输出的模型预测值和实际值。
- 计算统计值,将模型结果与测试数据进行比较:最后一次,执行您选择的统计计算,将模型预测与数据集进行比较。在这种情况下,你只有一个模型,所以你不是在寻找最合适的。相反,您正在检查以确保您的模型与测试数据集足够接近,令人满意。
一旦你开发了一个令人满意地匹配测试数据集的模型,你就可以开始生成预测了。但是,不要认为这意味着您已经完全完成了模型开发;很有可能有一天你会决定需要根据新的可用数据集来调整你的模型。
包装它
本文中讨论的基本挑战是,模型在特定数据集上的性能并不能保证它在其他数据集上也能表现良好。强制一个数学模型匹配一个给定的数据集只能确保它匹配那个特定的数据集,并不能说明它的预测能力。
这个挑战可以通过使用不同数据集的开发、验证和测试来克服。第一步是根据训练数据集开发模型,并确保模型可以在最简单的情况下准确预测结果。第二步是将表现最好的模型与第二个数据集(验证数据集)进行比较。验证数据集应包含跨越训练数据集中包含的值范围的点,但不应包含任何相同的点。这第二次检查对确保模型具有合理的预测能力大有帮助。最后一步是选择对验证数据集表现最好的模型,并将其与第三个数据集(测试数据集)进行比较。如果模型在数据集上表现良好,那么它可以合理地用于创建预测。
如何选择最好的开源软件
Photo by Pankaj Patel on Unsplash
在阅读了 Ted Malaska 和 Jonathan Seidman 所著的 O’Reilly 一书“ 架构数据解决方案 的基础”之后,我反思了我过去是如何选择软件/工具/解决方案的,以及我今后应该如何选择它们。
作为一名生物信息学家,你需要能够快速辨别一个出版物/工具是否真的是一个重大进步,或者只是稍微好一点。我不只是在谈论最新的单细胞 RNA-seq 技术或另一种文件格式,而是针对你遇到的每一个问题。无论是数据可视化工具、展示工具、分布式存储系统、等等。
这不仅仅是关于工具有多有用,还取决于文档的质量,安装有多简单,它在开源生命周期中的位置等等。
Xkcd 很有趣,但与之竞争的标准并不有趣。不相信我?看看有多少管道工具就知道了!
面对如此多的选择,如何选择适合自己需求的解决方案呢?
为什么要开源?
我过去曾使用过一些授权软件解决方案;比如 BLAST2GO ( plug:用 Camille Scott 的 dammit 代替!)、 Matlab 以及一款名为 Autopano Giga (现已不存在)的图像拼接软件。我最大的挫折之一是学习这些工具只是为了以后改变角色,并且不再使用它们。作为渔业和海洋部的顾问,Matlab 的高昂成本促使我开始学习另一种高级编程语言 R. FWIW:
“[Matlab]他们在许多情况下混淆源代码,这意味着 bug 更难被发现,也不可能在不冒被起诉的风险的情况下被 T2 编辑。此外,将 Matlab 用于科学会导致为我们的代码付费。从定义上来说,我们正在封闭我们的计算科学。”—摘自 我讨厌 Matlab:一个 IDE、一种语言、一种心态如何伤害
大多数公司避开第三方解决方案,或者将他们的产品构建为专有和开源的混合体,以保持较低的成本。例如,亚马逊网络服务(AWS)提供收费的简单存储服务(亚马逊 S3),但它是建立在开源软件如 Apache Hadoop 之上的。我不是说不使用 AWS(或任何其他云提供商),因为有时你会被迫使用来;我实际上在 Docker 的一个项目中使用了 AWS(珊瑚物种的转录组组装)。目前,我正在处理必须在现场上锁保存的敏感信息,因此使用了替代解决方案。
大多数较新的大数据平台和成功的开源项目在经历外部孵化阶段之前,最初几年都是作为公司或大学的内部项目开始的。例如:
- LinkedIn—“阿帕奇卡夫卡”
- 加州大学伯克利分校—“阿帕奇火花”
- cloud era—“黑斑羚
- 雅虎!—“Apache Hadoop”
- 谷歌— " Kubernetes"
- 脸书——“阿帕奇蜂房”
选择由可靠的赞助商支持的开源项目是有好处的,这些赞助商拥有良好的声誉、可靠的开发人员以及赞助成功项目的记录。您可以相当自信地认为,这些项目拥有坚实的代码基础、优秀的文档、在会议上获得的会话时间,以及相当大的公众认可度(通过围绕它的博客帖子和文章)。
当考虑开源解决方案时,衡量它们在开源生命周期中的位置也很重要。根据马拉斯卡和塞德曼的说法,基于石榴石炒作周期,项目生命周期有九个(潜在)阶段;然而,我认为这里只讨论几个相关的问题:
你应该选择哪个周期?
Photo by @Matthew_T_Rader on Unsplash
不要相信炒作
周期的这个阶段被称为“*治愈癌症”*阶段。这个阶段的宣传对于吸引提交者和贡献者是很重要的,但是除非你想在很大程度上提供帮助,否则你应该避开。除非你想走在最前沿(风险承受能力),或者扮演积极的贡献者角色,否则最好等 6-12 个月再尝试任何新技术。通过让其他人先碰壁,你会遇到更少的错误,并获得更好的文档和博客帖子。
失信不是谎言
在"治愈癌症"阶段之后是失信阶段。此时,人们正在使用项目,并发现问题或限制。例如,一个解决方案可能无法很好地与其他现有系统集成,或者可能存在可伸缩性问题。在这个阶段,您应该以谨慎乐观的态度对待任何开源项目。
尽可能寻求可靠的解决方案
处于硬化或企业阶段的项目已经成为成熟技术。承诺的数量将标志着一个项目的投资水平。提交的类型讲述了一个故事,告诉作者代码的发展方向,通过表明对项目不同特性的兴趣来揭示他们想要做什么。到目前为止,最初的兴奋已经消退,对稳定性的需求超过了对新功能的需求。最初的开发团队可能正在开发其他项目,因为它已经开发了一个可靠的社区——这通常是项目成功的好迹象。
很明显,最近的活动表明这个项目是活跃的,并且得到了维护。请记住,Github 上有许多已经死亡和废弃的项目。也就是说,活动并不总是需要非常近!一位多产的“摇滚明星 Dev”这样说:
上下文切换是很昂贵的,所以如果我同时处理许多包,我永远也不会完成任何事情。相反,在任何时候,我的大多数包都处于休耕状态,不断积累新特性的问题和想法。一旦积累到一定的数量,我会花几天的时间来包装。— 哈雷·威克姆
最终,项目进入了衰落的 T21 阶段,没有人愿意采纳或贡献一个死去的项目。
我能信任你吗?
Photo by Purnomo Capunk on Unsplash
我主要使用 R,所以让我花点时间谈谈项目托管在哪里。代码通常托管在 Github、 ROpenSci 、 Bioconductor 或 CRAN 上。全面的 R 档案网络 (CRAN) 是 R 包的主要储存库。
“作为 R 用户,我们被惯坏了。在 R 历史的早期,Kurt Hornik 和 Friedrich Leisch 构建了对 R 中的包的支持,并开始了全面的 R 存档网络(CRAN)。R 和 CRAN 有一个奇妙的运行。大约二十年后,我们看到超过 12,000 个软件包可以(通常)绝对轻松地安装,没有任何意外。没有任何其他(相关的)开源语言具有类似的严谨性和质量。”—摘自 Dirk Eddelbuettel
在 CRAN 上,几乎任何类型的包都是受欢迎的(只要符合严格的政策),并且包每天都被测试(在多个系统上)。 rOpenSci 是 CRAN 的完美对立面。众所周知,曲柄不透明、不一致且冷漠。它不能处理起重机自动化的数量,但在质量方面推销自己。
对于生物信息学领域来说,生物导体是一个包的最终归宿。只存在于 Github 上的项目应该被更加谨慎地看待,因为它们没有清单或同行评审。
让我们来谈谈依赖性(一个沉重的话题——没有双关语的意思)
安装依赖项太糟糕了!你有多长时间安装了一个包却得到了一船货?您应该尝试并避免包含许多(变化的)包的包,因为这将禁止确定您的工作是否正确(从而确保可再现性),因为依赖关系很难管理风险。
tinyverse 的支持者倾向于远离臃肿的依赖,没有人想在地狱里度过时光!
如果您是开发人员,请记住:
"并不是所有的依赖都是相同的……一些流行的软件包[有]不稳定的 API(突破性变化的历史)和高历史错误率(复杂的历史和增加功能而不是修复东西的历史)。
您还可以为您的 repo 添加一个标记,显示您的包依赖于的依赖项的数量
透明度是好的
Photo by Aleks Dahlberg on Unsplash
在 Github 上查看项目时,你应该寻找有许多明星、观察者、分叉、贡献者、等的人/包。这些社区支持的可见线索表明社区关心一个人、一个项目或一项行动,并且许多其他人将从中受益。
请记住,提交、发布和拉动请求(PRs)的数量可能是对项目投资和承诺的信号。问题和减贫战略是否得到处理?后者实际上是一个提供的代码,它被忽略,而不是被接受、拒绝或评论。
通过遵循代码上的操作,您可以确定谁创建了项目,在不同的版本中发生了什么,并推断出项目的结构和合作者角色(谁对系统的哪些部分有专长)。链接的提交和发布传达了代码更改背后的原因。
你也可以通过查看聚会、和会议的数量(以及他们的出席水平),或者电子邮件列表、用户组、社区论坛等来衡量社区兴趣。
谷歌趋势也可以很好地衡量人们对项目或技术的兴趣程度。
Using metrics to track the decline of SPSS
要寻找的东西
Photo by Arnaud Papa on Unsplash
- 易于安装
- 易于运行
- 是否有问题和公关提出
所有者是否在照顾他们(修复 bug、帮助用户、添加功能)?还是被抛弃了?
它是否列出了硬件要求(RAM 和磁盘大小)、示例命令、玩具数据、示例输出、屏幕截图/记录
要查找的其他内容,请参见此处的和此处的和。
基准标记
如果您是一名软件开发人员,并且正在考虑整合众多竞争技术中的一种,那么您可以使用您的用例和数据来执行内部基准测试。
如果您使用 R,基准可以提供不同的放大级别。对于宏观分析(当计算更密集时),你应该使用[rbenchmark](https://cran.r-project.org/web/packages/rbenchmark/index.html)
包。对于微观时间比较(例如纳秒时间)使用[microbenchmark](https://cran.r-project.org/web/packages/microbenchmark/index.html)
包
有时其他联合体已经为你做了基准测试(例如【组装】 )。尽管如此,人们应该意识到试图进行不公平比较的隐藏的或有动机的偏见(一种工具明显具有优势的用例)。也要理解测试人员本可以诚实地尝试一个公平的测试,但是却产生了导致无效结果的误解。因此,重要的是执行你自己的内部基准,并将其他基准保持在可重复性和验证的开放标准上。
最后的话
最终选择一个软件解决方案归结于你的项目的需求(时间表,预算,等等),你有多愿意走在前沿(风险承受能力),以及有能力的团队成员基于他们的技能水平(内部技能集)有多能够掌握这些解决方案。然后,在完全提交之前测试解决方案。这项工作可以交给团队中的原型角色;喜欢试验/研究新软件的人。
如何选择合适的数据库
我们将讨论可用的数据库类型以及不同项目类型的最佳实践。
无论你是一个有经验的软件工程师还是一个正在做大学项目的学生,在某些时候你都需要为你的项目选择一个数据库。
如果您以前使用过 DB,您可能会说“我会选择 X,这是我熟悉并使用过的 DB”,如果性能不是您系统的重要需求,这完全没问题。否则,当项目增长时,选择错误的数据库可能会成为一个障碍,有时很难解决。即使您正在从事一个成熟的项目,并且暂时使用了特定的数据库,了解它的限制并确定何时应该向堆栈中添加另一种类型的数据库也是很重要的(组合几个数据库是很常见的)。
了解不同数据库及其属性的另一个原因是,这是求职面试中很常见的问题!
在本帖中,我们将回顾两种主要类型的数据库:
·关系数据库(基于 SQL)。
·NoSQL 数据库。
我们将讨论不同类型的 NoSQL DBs 以及何时使用每种 DBs。
最后,讨论关系数据库与 NoSQL 数据库的优缺点。
这篇文章将不包括为每种类型的数据库提供的不同产品之间的比较(例如 MySQL 与 MS SQL Server)。
TL;DR:如果你正在寻找一个快速的小抄,跳到文章的末尾。
关系数据库(基于 SQL)
这个 DB 由一组相互连接的表(如 CSV 表)组成。表中的每一行代表一条记录。
为什么叫关系型?该数据库中存在哪些“关系”?
假设您有一个学生信息表和一个课程成绩表(课程、年级、学号),每个成绩行将关联到一个学生记录。
参见下图,其中“学生 ID”列中的值通过“ID”列的值指向“学生”表中的行。
所有关系数据库都使用类似 SQL 的语言进行查询,这种语言是常用的,并且本身支持连接操作。
它们允许对列进行索引,以便基于这些列进行更快的查询。
由于其结构化的本质,关系数据库的模式是在插入数据之前决定的。
常见的关系数据库: MySQL、PostgreSQL、Oracle、MS SQL Server
NoSQL 星展银行
在关系数据库中,一切都是按照行和列来组织的,而在 NoSQL 数据库中,没有一个通用的结构化模式来管理所有的记录。大多数 NoSQL 数据库包含 JSON 记录,不同的记录可以包含不同的字段。
这一系列数据库实际上应该被称为“非主要 SQL”——因为许多 NoSQL 数据库支持使用 SQL 的查询,但是使用 SQL 并不是它们的最佳实践。
有 4 种主要类型的 NoSQL 数据库:
1.面向文档的数据库
这个数据库的基本单位是文档。每个文档都是一个 JSON,不同文档之间的模式可以不同,并且包含不同的字段。
文档数据库允许对文档中的一些字段进行索引,以允许基于这些字段进行更快的查询(这迫使所有文档都具有该字段)。
什么时候该用?
数据分析——由于不同的记录彼此不相关(逻辑和结构方面)该数据库支持并行计算。
这使您可以轻松地对我们的数据进行大数据分析。
常见的基于文档的数据库 : MongoDB 、 CouchDB、DocumentDB 。
2.柱状 DBs
这个数据库的原子单位是表中的一列,这意味着数据是逐列存储的。这使得基于列的查询非常高效,而且由于每一列上的数据都非常相似,因此可以更好地压缩数据。
什么时候该用? 当您倾向于查询数据中列的子集时(不需要每次都是相同的子集!).
columnard DB 执行这种查询的速度非常快,因为它只需要读取这些特定的列(而基于行的 DB 必须读取整个数据)。
- 这在数据科学中很常见,每一列代表一个特征。作为一名数据科学家,我经常用特征的子集来训练我的模型,并倾向于检查特征和分数之间的关系(相关性、方差、显著性)。
- 这在日志中也很常见——我们经常在日志数据库中存储更多的字段,但是在每个查询中只使用了很少的字段。
常用列 DB 数据库: Cassandra。
Column vs. raw based DBs.
3.键值数据库
该查询只是基于键的—您需要一个键并获得它的值。
不支持跨不同记录值的查询,如“select all records where city = = New York”
该数据库中的一个有用功能是 TTL 字段(生存时间),该字段可以针对每个记录进行不同的设置,并声明何时应从数据库中删除。
优点— 速度非常快。首先是因为使用了唯一的键,其次是因为大多数键值数据库将数据存储在允许快速访问的内存(RAM)中。
缺点— 您需要定义惟一的键,这些键是很好的标识符,并且是根据您在查询时知道的数据构建的。通常比其他种类的数据库更贵(因为在内存上运行)。
我应该什么时候使用它?
主要用于缓存,因为它非常快,不需要复杂的查询,而且 TTL 功能对缓存非常有用。
它也可以用于其他任何需要快速查询并符合键值格式的数据。
常用键值数据库: Redis,Memcached
4.图形数据库
图形数据库包含表示实体的节点和表示实体之间关系的边。
我应该什么时候使用它?
当你的数据是一个图形时,就像知识图形和社会网络。
常用图数据库: Neo4j,InfiniteGraph
关系数据库与文档数据库
现在你可能已经知道了,没有正确的答案,也没有“一个数据库可以统治所有的数据库”。
最常见的“常规”数据库是关系数据库和文档数据库,因此我们将对它们进行比较。
关系优势
- 它有一个简单的结构,可以匹配程序中通常拥有的大多数类型的数据。
- 它使用了 SQL ,这是常用的,并且天生支持 JOIN 操作。
- 允许快速数据更新。所有的数据库都保存在一台机器上,记录之间的关系被用作指针,这意味着您可以更新一个记录,所有相关的记录都会立即更新。
- 关系数据库也支持原子事务。
什么是原子事务:假设我想从 Alice 转账 X 美元给 Bob。我想执行 3 个操作:将 Alice 的余额减少 X,将 Bob 的余额增加 X,并记录交易。我想把这些行为作为一个原子单位来对待——要么所有的行为都会发生,要么什么都不会发生。
关系—缺点
- 因为每个查询都是在一个表上完成的,所以查询执行的时间取决于表的大小。这是一个很大的限制,它要求我们保持表相对较小,并对数据库进行优化,以便进行伸缩。
- 在关系数据库中,扩展是通过向保存数据库的机器添加更多的计算能力来完成的,这种方法被称为’垂直扩展’。
为什么是劣势?因为机器能够提供的计算能力是有限的,而且向机器添加资源可能需要一些停机时间。 - 关系型不支持基于 OOP 的对象,即使表示简单的列表也非常复杂。
文档数据库—优势
- 它允许你用不同的结构来保存物体。
- 你可以使用优秀的 JSON 来表示几乎所有的数据结构,包括基于面向对象的对象、列表和字典。
- 尽管 NoSQL 本质上是无模式化的,但它通常支持模式 验证,这意味着您可以将集合模式化,模式不会像表那样简单,它将是一个具有特定字段的 JSON 模式。
- 查询 NoSQL 非常快,每个记录都是独立的,因此查询时间与数据库的大小无关,支持并行性。
- 在 NoSQL,扩展数据库是通过添加更多的机器并在它们之间分配你的数据来完成的,这种方法被称为**‘水平扩展’**。这允许我们在需要时自动向数据库添加资源,而不会导致任何停机。
文档数据库—缺点
- 更新数据在文档数据库中是一个缓慢的过程,因为数据可以在机器之间划分,并且可以复制。
- 原子事务本身不受支持。您可以通过使用验证和恢复机制在代码中自己添加它,但是由于记录是在机器之间划分的,所以它不能是一个原子过程,并且可能会出现争用情况。
备忘单:
- 对于缓存 —使用一个键值数据库。
- 对于图形- 之类的数据-使用图形数据库。
- 如果您倾向于查询列 /features 的子集,请使用列数据库。****
- 对于所有其他用例,关系数据库或文档数据库。
如何在高维空间中聚类
生命科学的数理统计和机器学习
自动检测集群数量的方法
这是 生命科学的数理统计与机器学习 栏目的第三篇文章。之前,我们强调了**scRNAseq是有前途的大数据资源,讨论了 tSNE 是 scrna seq 的中心降维技术,并学习了如何调优 tSNE 的超参数。tSNE 应用于 scRNAseq 数据后的下一步是进行聚类分析,以检测细胞群之间的边界。**
然而,聚类同时受到算法的限制和 维数灾难 的影响,这经常导致 tSNE 图的视觉解释和聚类分析输出之间的矛盾。在本文中,我提出了一种自动化的方法,在 scRNAseq 数据分析的维度缩减和聚类之间达成一致。****
聚类分析的假象
对 scRNAseq 进行聚类的一个问题是,我们在 tSNE 图中看到的与聚类分析报告的在聚类数量和聚类的单元分配方面存在差异。例如,我们在下图中清楚地观察到三个集群,甚至可以手动绘制集群之间的边界。然而,运行聚类算法可能会导致诸如错误的聚类数或错误的单元分配给聚类之类的假象,即当一个均匀的聚类碰巧被算法分割成多个块时。****
Results of clustering contradict our visual interpretation, image source
此外,由于聚类算法缺乏鲁棒性,试图复制科学论文的结果可能会令人困惑。例如,使用来自 Kolodziejczyk 等人的数据,细胞干细胞 2015 、八个集群在 tSNE 图中可见,然而本文中使用的聚类算法似乎只检测到三个集群**。**
Contradiction between tSNE (left) and clustering (right) from Kolodziejczyk et al., Cell Stem Cell 2015
降维和聚类之间的矛盾具有双重性。一方面,由于维数灾难,在高维 scRNAseq 空间中定义数据点之间的距离是出了名的困难;另一方面,聚类算法通常使用理想化的假设,而这些假设对于现实世界的数据来说并不成立。
高维空间中的欧氏距离突变
高维数学是一个活跃的研究领域,我将为这个主题专门写一篇文章。高维空间中会出现很多怪异的现象。其中之一是数据点和坐标系原点之间的距离随着维数 d 的平方根增长。这可以被视为数据点耗尽中心,并集中在 n 维球的壳中。
Data points occupy the surface and deplete the center of the n-ball in high dimensions, image source
因此,数据点之间的平均距离发散并失去其意义,进而导致欧几里德距离的发散,欧几里德距离是用于聚类的最常见距离。曼哈顿距离对于 scRNAseq 来说是更好的选择,但是它在高维度上也没有完全的帮助。****
聚类方法的假设和限制
尽管维数灾难是 scRNAseq 聚类分析的主要障碍,但由于其内部假设和限制,许多聚类算法即使在低维中也可能表现不佳。所有的聚类方法大致可以分为四组:****
- 分层聚类
- 基于质心的聚类
- 基于图的聚类
- 基于密度的聚类
Python 库 Scikit-learn 提供了一组聚类方法,并提供了一个出色的概述,强调了它们的优缺点:
Clustering methods overview at scikit-learn Python library web-page
分层(凝聚)聚类对数据中的 噪声过于敏感。基于质心的聚类(K-means,高斯混合模型)只能处理具有球形或椭球形对称性的聚类。****
基于图的聚类(Spectral,SNN-克里格,修拉)对于高维数据来说可能是最健壮的,因为它使用了图上的距离,例如共享邻居的数量,与欧氏距离相比,这在高维数据中更有意义。****
Graph-based clustering uses distance on a graph: A and F have 3 shared neighbors, image source
然而,为了构建图形,这种方法仍然** 使用欧几里德距离。此外,集群的数量必须通过“分辨率”超参数隐式指定先验。改变超参数可以容易地导致更少或更多的聚类,这在某种程度上是任意的,因此非常不令人满意,因为没有明显的方法来定义用于自动调整超参数的目标函数。**
在所有聚类算法中,只有基于密度的算法(Mean-Shift、DBSCAN、OPTICS、HDBSCAN)允许聚类,而无需指定聚类数**。这些算法通过向高密度点移动的滑动窗口来工作,即它们发现存在许多密集区域。**
DBSCAN clustering finds dense regions in the data, image source
此外,这些算法与聚类形状无关**,并且可以捕获任何拓扑结构的 scRNAseq 数据。下面我将展示如何通过最小化一个目标函数来调整算法的超参数。**
如何调整 HDBSCAN 的超参数
聚类是一个无监督的学习问题,这意味着我们不知道基本事实(聚类的数量),并且不能使用交叉验证来优化算法的超参数。然而,有一种方法可以自动优化 HDBSCAN 的超参数。
HDBSCAN 是一种强大的基于密度的聚类算法,它 1)与聚类的形状无关,2)不需要指定聚类的数目,3)对不同密度的聚类具有鲁棒性。此外,HBDSCAN 非常有吸引力,因为它只有一个超参数 minPts ,这是一个聚类中的最小点数。对于大型数据集来说,它的速度相对较快,检测外围小区,并为每个小区报告一个分配给一个集群的概率**。分配到一个簇的概率低的单元部分可以用作优化 minPts 的目标函数,min pts 进而给出最优的簇数。**
下面,我将继续使用癌症相关成纤维细胞(CAFs) 从以前的帖子中,我们发现了最佳的困惑( optPerp )和主成分数( optPC )。现在,我们将针对不同最小规模的集群,即范围从 3 到 N_pt=50 的 minPts ,对 tSNE 维数缩减运行 HDBSCAN。对于每个 minPts,我们将分数函数计算为具有低置信度(概率< 5%)分配给聚类的细胞的分数,通过分数函数的最小化,我们希望减少未分配的数据点的数量。由于内部随机性,tSNE 从运行到运行是变化的,因此为了稳健性,我们重复聚类 N_iter=50 次,并对结果进行平均。绘制分数函数与 minPts 的关系揭示了某个最小聚类大小 minPts 的明确最小值。
现在,在我们为我们的数据集 minPts 找到簇的最佳最小大小后,我们将使用该值在 tSNE 上运行最终的 HDBSCAN,这将为我们提供簇的数量、分配给簇的单元,以及不能确定分配给任何簇的单元,即外围单元**。与其他算法相比,后者是一个优势,也是基于密度的聚类的一个特点,但是这里为了简单起见,我使用 KNN 分类器将外围单元分类到它们最近的邻居聚类中。注意,tSNE 运行 N_tsne=50 次,最小 Kullback-Leibler 散度图被选为最终图。下面,我展示了几个 scRNAseq 数据集。**
Results of HDBSCAN clustering on tSNE
将 HDBSCAN 应用于各种 scRNAseq 数据集的结果看起来并不太差,因为这些图是通过只提供原始表达式矩阵而不手动调整聚类参数获得的。该函数的代码只接受一个原始的 scRNAseq 表达式矩阵,对所有超参数进行自动优化,并返回一个带有 HDBSCAN 聚类结果的 tSNE 图,可以在 my github 上找到。
基于 tSNE、PCs 和原始表达式矩阵的聚类
由于维数灾难,在原始表达式矩阵上运行聚类可能非常具有挑战性。因此,几乎总是建议在进行聚类分析之前进行任何类型的维数缩减。这里我比较了 9 种流行的聚类算法在 CAFs 数据集上的性能:HDBSCAN(如上所述), Kmeans ,高斯混合模型(GMM) ,层次聚类,谱聚类, SC3 , Bootstrap 一致性聚类,SNN-克里格和修拉。事实上,在原始 CAFs 表达矩阵上运行聚类算法带来了不令人满意的结果:
Clustering on raw expression matrix
虽然所有的算法都无法在原始表达式矩阵上进行聚类,但是只有 SC3 提供了令人惊讶的好的单元分配网络,它跨不同的算法和距离度量执行一致性聚类。现在,我们将把聚类算法应用于通过在先前帖子中置换表达式矩阵找到的重要 PCs** 。**
现在,SC3 的表现不太好,HDBSCAN 也不完美。相反,基于修拉图的社区检测和层次聚类似乎表现最好。最后,让我们将 9 种聚类算法应用于 scRNAseq 表达数据的 tSNE 降维表示:
尽管 tSNE 图是 2D 维数缩减,但是诸如 K-means、高斯混合模型(GMM)、分级聚类、谱聚类、Bootsrap 一致性聚类和 SC3 的许多算法都不能正确地将细胞分配到它们的聚类。HDBSCAN 和基于图的聚类方法(修拉和 SNN-克里格)似乎表现最好。然而,后者需要手动调整超参数,由于上述自动优化程序,HDBSCAN 不需要这样做。
摘要
在本文中,我们已经了解到,由于聚类方法的维数灾难和限制,对高维度 scRNAseq 数据进行聚类是具有挑战性的。令人沮丧的是,大多数聚类算法需要预先指定的个簇个,由于交叉验证不适用于聚类,这很难优化。然而,HDBSCAN 作为一种只有一个超参数的算法在这里很突出,该算法很容易通过最小化未分配单元的数量来优化。****
在下面的评论中让我知道生命科学中的哪些分析对你来说似乎特别神秘,我会在这个专栏中尽力解答。在媒体关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我,在我的 github 上查看这篇文章的代码。我计划写下一篇关于如何标准化你的数据,你可以在传统统计学打破的单一空间中结束**,敬请关注。**
如何用 Python 编写公平抛硬币的代码
要掌握任何东西,你需要从头开始,理解基础。对于 python 来说,编写一个公平的抛硬币程序是一种通行的权利。这似乎是一个简单的项目,但它可以用许多不同的方式来完成。你甚至可以洞察你的编码习惯。能够评论你的代码是一个关键的程序员技能,但那是另一篇文章。让我们来看看你点击了什么,用 python 编写了一个公平的抛硬币程序。
创建蓝图
在我们开始敲敲打打,创建一个程序让计算机做一个平凡的任务,如抛硬币,让我们飞到 10,000 英尺,并得到我们的方向。程序员有惊人的创造自由(取决于你的(微观)经理,如果你在那条船上,我同情你❤),你可能开始失去对项目的主要关注。是的,即使是这样一个简单的项目,习惯也是在公众视线之内和之外形成的。你的标准只有在没人看的时候才算高。
好的。现在我们有了项目的鸟瞰图,让我们来分析它。我们知道我们将进行一次公平的掷硬币。一枚硬币由两部分组成,头和尾。由于项目描述中使用了“公平”,我们知道获得任何一方的概率都是 50%。如果描述提到有偏见或加权硬币,那么概率将被调整。
二项分布
我们如何模拟抛硬币?我们不能给电脑一个比特币,让它翻转。因为我们使用 python,一种数学语言,我们将使用概率。具体来说就是 numpy 的二项分布,np.random.binomial(n,p)。这是随机类中的一种方法,它接受试验次数(n)和事件发生的概率§。二项式分布,顾名思义,可以对两个事件的发生进行“抛硬币”。该调用返回 0 或 1 来表示这两个事件中的一个。这是它使用的等式:
p 是概率,N 是试验次数,N 是成功次数。我们现在知道,要使二项分布成立,我们需要两个变量,n 和 p。
这就引出了一个问题,我们是想看到每一次翻转并跟踪它,还是仅仅满足于积极事件发生的次数?我在船上看到一系列事件,只是为了好玩。所以我们需要一个数组来存储结果。谢天谢地,numpy 能做到这一切!
密码
让我们回到现实,开始编码。
首先,我们需要导入 numpy。
import numpy as np
接下来,我们将创建 n 和 p 变量。我们知道我们需要这些,因为我们将使用 np.random.binomial,这需要试验次数和概率。
'''Main Area'''
#probability of heads vs. tails. This can be changed.
probability = .5
#num of flips required. This can be changed.
n = 10
在我的代码中,我喜欢标记主要区域的位置以便于搜索,以防程序中创建了多个方法。标记任何可以安全改变而不会产生负面影响的变量也是一个很好的实践。
我们还知道,我们将需要启动一个数组来存储翻转。使用 np.arange 将创建一个包含 10 个元素的数组,用 0 到 9 的值按升序填充。
#initiate array
fullResults = np.arange(n)
在这一点上,我们可以走两条路。它要求我们创建一个函数,在 for 循环中进行翻转。由于我们关注代码的可重用性,我们将创建一个包含二项式分布的函数。如果我们决定在未来建立该程序,这将使访问变得容易。
抛硬币
有了这个决定,让我们创建这个方法。我们称之为硬币翻转,它需要考虑事件的概率。它将返回二项式的结果。一定要把这个放在主要区域之前。程序需要定义它才能使用。
def coinFlip(p):
#perform the binomial distribution (returns 0 or 1)
result = np.random.binomial(1,p)
#return flip to be added to numpy array
return result
如您所见,试验次数将被设置为 1。这将只返回 0 或 1,假或真。现在可以创建调用 coinFlip 的 for 循环了。
For 循环
#perform desired numbered of flips at required probability set above
for i in range(0, n):
fullResults[i] = coinFlip(probability)
i+=1
For 循环是基本的代码结构,但是为了以防万一,让我们看看发生了什么。头中的 I 将是控制循环是否再次进行的索引。range(0,n)是我可以取的值。我们使用 n,这样我们就不会超过我们的数组大小。当程序可以进入循环时,我们创建的 coinFlip 被调用,结果被保存到数组的元素中。然后,索引 I 递增。
展示掷硬币的发现
我们差不多完成了。最后一步是计数和打印结果。这部分很简单。对于打印部分,我们将打印用于验证是否使用了正确的概率,以及 fullResult 数组。Numpy 有许多针对数组的不同函数。我们将使用 np.count _ 非零()来遍历数组,并计算我们给它的数字出现的次数。在这种情况下,我们将检查 1 和 0 出现的次数。
#print results
print("probability is set to ", probability)
print("Tails = 0, Heads = 1: ", fullResults)
#Total up heads and tails for easy user experience
print("Head Count: ", np.count_nonzero(fullResults == 1))
print("Tail Count: ", np.count_nonzero(fullResults == 0))
就是这样!我们已经创建了一个程序来模拟公平的掷硬币。代码应该是这样的:
import numpy as npdef coinFlip(p):
#perform the binomial distribution (returns 0 or 1)
result = np.random.binomial(1,p)#return flip to be added to numpy array
return result'''Main Area'''
#probability of heads vs. tails. This can be changed.
probability = .5
#num of flips required. This can be changed.
n = 10#initiate array
fullResults = np.arange(n)#perform desired numbered of flips at required probability set above
for i in range(0, n):
fullResults[i] = coinFlip(probability)
i+=1#print results
print("probability is set to ", probability)
print("Tails = 0, Heads = 1: ", fullResults)
#Total up heads and tails for easy user experience
print("Head Count: ", np.count_nonzero(fullResults == 1))
print("Tail Count: ", np.count_nonzero(fullResults == 0))
那很有趣!做一些简单的项目,比如抛硬币,是理解像 numpy 这样的库的好方法。Numpy 是一个强大的库,除了模拟抛硬币和创建数组之外,它还能做更多的事情。如果您有兴趣了解更多,请查看了解 python Numpy的基础知识。这篇文章将对 numpy 有一个更广泛的理解。
感谢您的阅读!
在我们重新学习之前,
原载于 https://reginaoftech.com 的
如何有效地编码而不在尝试中死去
面向非程序员的编程
更好编程的 5 条建议
Image from Émile Perron available at Unsplash
编码似乎是一项简单的任务。但是有效编码可能不行。如今,许多程序员遇到了对他们的工作效率有重大影响的多种情况;一些人帮助他们以更快更有效的方式实现目标,而另一些人只是给他们压力,让他们坚持完成任务。每个人都有不同的策略来处理这些情况,要么是因为他们经历过,要么是因为别人告诉他们如何处理。基于我的同事和个人经验,并期待着在职业生涯中帮助新的和有经验的程序员,我想分享以下 5 条我为有效地编写而编写的建议,这些建议在我的职业生涯中对我有用。希望你觉得有用。**
1.找到一个舒适的工作空间
大多数编程和编码工作都足够灵活,允许在家里、公共场所、图书馆甚至咖啡厅工作,而不必每周 5 天、每天 8 小时呆在办公室。然而,工作环境总是对你的工作效率有很大的影响。找一个让你感觉舒适的地方工作,远离干扰、噪音和可能会降低你工作节奏的情况。找出让你更有效率的因素(例如一杯咖啡或茶、背景音乐、照明、便利设施),并善加利用。想一想:谷歌和其他公司的工作空间远离传统的工作隔间是有原因的。由于他们非常清楚每天花 8 个小时在电脑前编码会带来多大的压力,所以他们一直在寻找让员工在他们的设备前感到舒适的方法。毫无疑问,他们被认为是最适合工作的公司。
2.如有疑问,请使用帮助资源
不可能知道现存的所有库和函数的语法。因此,当您对某个特定的函数或算法有疑问时,可以使用大量的文档和帮助资源。只需在谷歌搜索栏中输入“如何在 ( 编程语言)”;很可能其他人已经有了同样的问题,并把它张贴在堆栈溢出。您也可以直接访问库和包的网站进行参考(例如,Python 的 scikit-learn 库,R 的 ggplot2 包),或者查看编程软件本身的可用帮助文档(例如,在 R 中,您可以键入 help(“函数名”),您将看到显示的关于该特定函数的 R 文档)。同样,如果你参加过任何提供代码模板或笔记的在线编程课程(如 Udemy、Coursera、DataCamp ),把它们放在你的电脑上作为快速参考。最后,你可以购买/下载多本编程“食谱”(例如 Paul Teetor 的 R Cookbook )和与统计学习和机器学习相关的教科书(例如 Gareth James、Daniela Witten、Trevor Hastie 和 Robert Tibshirani 的统计学习介绍)。请记住,这些资源旨在用于学习和参考,但掌握算法及其编码语法背后的概念仍然取决于您。
3.遇到困难时,寻求帮助
我知道你在想什么:冒名顶替综合症。你可能害怕向他人寻求帮助会让你和/或他们觉得你不具备作为程序员或软件开发员的技能或专业知识。但是不用担心。向比你懂得更多、更有专业知识的人寻求帮助代表着一个学习新事物、提高技能和获得建议的大好机会,同时还能解决你的编码错误。同样的,它代表了谦逊的标志;你在含蓄地告诉别人,你愿意接受反馈和不断学习。数据科学和数据分析公司应该鼓励员工之间的团队合作和协作,而不是竞争和对抗;这可能会导致更高的生产力水平和更高的员工积极性。知识是用来分享的,而不是留给自己的。
4.让你的眼睛休息一下
长时间盯着笔记本电脑的显示器会导致头痛、视力模糊和头晕,尤其是对戴眼镜的人来说。停止盯着它看几分钟,让你的眼睛休息一下,可以给你的精力充电,继续高效地工作。每当你需要开发一个需要很长时间才能完成的代码时,每小时给自己 5-10 分钟的休息时间,不要盯着笔记本电脑的显示器。在你的办公室周围散散步,吃些健康的零食,舒展一下身体,去一个公共区域,或者简单地休息一下。这对你的视力和健康都有好处。即使编码有时会令人沮丧和耗时,你也不是一台编码机器;记住,你的健康与你的工作效率密切相关。
5.不要过度紧张。明天继续工作
你的代码不能运行。你试着调试它。还是不跑。你认为你可能发现了错误。还是不跑。你要么想把你的笔记本电脑扔到墙上,要么继续调试直到它运行。如果这种情况你听起来很熟悉:放松,不要紧张。总有一天,我们在代码中如此循环,以至于我们不能简单地找到错误,不管它是多么微不足道(例如分号、错别字)。在一天的剩余时间里暂停你的任务,做不同的任务,或者如果太晚了,休息一下。没有什么比一双新鲜的眼睛更能恢复你的工作了。众所周知,程序员会花很长时间在工作、家庭或两者兼而有之的地方编码和开发程序。社交媒体充满了关于程序员社交生活和日常事务的模因。尽管我想说他们很有趣也很虚假,但有时他们很悲伤也很真实。享受做程序员的乐趣,平衡你的生活,充分利用这个令人惊叹的职业。让机器做他们的工作,让你自己成为你自己。
— —
如果你觉得这篇文章有用,欢迎在 GitHub 上下载我的个人代码。你也可以直接在 rsalaza4@binghamton.edu 给我发邮件,在LinkedIn上找到我。有兴趣了解工程领域的数据分析、数据科学和机器学习应用的更多信息吗?通过访问我的媒体 个人资料 来探索我以前的文章。感谢阅读。
罗伯特
如何用 Python 从头开始编写高斯混合模型
使用 NumPy 的 GMMs 和最大似然优化
在无监督学习算法领域,高斯混合模型或 GMM 是特殊公民。GMM 基于这样的假设,即所有数据点都来自具有未知参数的高斯分布的精细混合。它们是试图了解真实数据分布的参数化生成模型。因此,一旦我们知道了高斯参数,我们就可以从与源相同的分布中生成数据。
我们可以认为 GMMs 是 K-Means 聚类算法的软推广。像 K-Means 一样,GMM 也需要聚类的数 K 作为学习算法的输入。然而,这两者之间有一个关键的区别。K-Means 只能学习圆形的聚类。另一方面,GMM 可以学习任何椭圆形状的簇。
此外,K-Means 只允许一个观察值属于一个且仅属于一个聚类。不同的是,GMM 给出了将每个例子与给定的聚类相关联的概率。
换句话说,GMM 允许一个观测值属于多个聚类——具有一定程度的不确定性。
对于每个观察,GMM 学习该例子属于每个聚类 k 的概率
一般来说,GMM 试图将每个聚类学习为不同的高斯分布。它假设数据是从有限的高斯混合高斯产生的。
假设一维数据和聚类数 K 等于 3,GMM 试图学习 9 个参数。
- 平均值的 3 个参数
- 方差的 3 个参数
- 3 个缩放参数
这里,每个聚类由一个单独的高斯分布表示(在本例中,总共 3 个)。对于每个高斯,它从数据中学习一个均值和一个方差参数。3 个缩放参数(每个高斯 1 个)仅用于密度估计。
为了学习这些参数,GMM 使用期望最大化(EM)算法来优化最大似然。在此过程中,GMM 使用贝叶斯定理计算给定观测值 xᵢ 属于每个聚类 k 的概率,其中 k = 1,2,…,k
让我们来看一个例子。为了简单起见,让我们考虑一个合成的一维数据。但是,正如我们将在后面看到的,该算法很容易扩展到 D > 1 的高维数据。你可以用这个 jupyter 笔记本跟着做。
为了建立一个玩具数据集,我们从从不同的高斯分布中采样点开始。每一个(有自己的均值和方差)代表我们合成数据中的一个不同的聚类。为了让事情更清楚,让我们用 K 等于 2。
下面,你可以看到合成的数据。我们将使用它作为训练数据,使用 GMM(从数据中)学习这些聚类。请注意,某些值在某些时候确实会重叠。
我们可以把 GMM 看作高斯分布的加权和。聚类数 K 定义了我们想要拟合的高斯数。
正如我们所说的,集群的数量需要预先定义。为简单起见,让我们假设我们知道聚类的数量,并将 K 定义为 2。在这种情况下,GMM 将尝试学习 2 个高斯分布。对于一维数据,我们需要了解每个高斯的均值和方差参数。
在开始运行 EM 之前,我们需要给出可学习参数的初始值。我们可以猜测均值和方差的值,并将权重参数初始化为 1/k。
然后,我们可以使用 EM 算法开始最大似然优化。EM 可以简化为两个阶段:E(期望)和 M(最大化)步骤。
在 e 步骤中,我们使用估计的参数计算每个观测值 xᵢ 的可能性。
1-d gaussian distribution equation
对于每个聚类 k = 1,2,3,…,K,我们使用均值和方差的估计值来计算数据的概率密度(pdf)。在这一点上,这些值仅仅是随机猜测。
然后,我们可以计算给定示例 xᵢ 属于 kᵗʰ 集群的可能性。
利用贝叶斯定理,我们得到了第 k 个高斯的后验概率来解释数据。那就是观测值 xᵢ 由 kᵗʰ 高斯产生的可能性。请注意,参数φ充当我们的先验信念,即一个例子来自我们正在建模的一个高斯模型。由于我们没有任何额外的信息来支持一个高斯模型而不是另一个,我们开始猜测一个例子来自每个高斯模型的概率相等。然而,在每次迭代中,我们改进我们的先验直到收敛。
然后,在最大化或 M 步骤中,我们如下重新估计我们的学习参数。
Parameter update equations
这里,对于每个聚类,我们更新均值( μₖ )、方差( σ₂ )和缩放参数φₖ。为了更新平均值,请注意,我们使用条件概率 bₖ 对每个观察值进行加权。
我们可以重复这些步骤,直到收敛。这可能达到参数更新小于给定容限阈值的程度。在每一次迭代中,我们更新我们的参数,使其类似于真实的数据分布。
Gaussian Mixture Models for 1D data using K equals 2
对于高维数据(D>1),只有很少的东西会改变。现在我们估算平均值和协方差,而不是估算每个高斯的平均值和方差。协方差是形状为(D,D)的方阵,其中 D 表示数据的维度。下面,我展示了一个不同的例子,其中一个 2-D 数据集被用来拟合不同数量的高斯混合。
Gaussian Mixture Models for 2D data using K equals 2
Gaussian Mixture Models for 2D data using K equals 3
Gaussian Mixture Models for 2D data using K equals 4
请注意,上面的合成数据集是从 4 种不同的高斯分布中提取的。然而,GMM 为两个、三个和四个不同的集群提供了一个很好的案例。
这就是高斯混合模型。这是这篇文章的一些要点。
- GMM 是一族生成参数无监督模型,试图使用高斯分布对数据进行聚类。
- 和 K-Mean 一样,你还是需要定义你要学习的聚类数 K。
- 与 K-Means 不同,GMM 将聚类表示为概率分布。这允许一个数据点属于多个具有一定不确定性的聚类。
感谢阅读。
如何结合 LaTeX 和 R 生成报表
如何获得最佳数据科学报告的简单教程!
(Image by author)
如果您必须创建一个报告,LaTeX 绝对是您的选择,LaTeX 中的所有内容看起来都更好!
在你的学术生涯中,你可能用它来写论文或做一些分析。事实上,像 texstudio 或最近的 Overleaf 这样的环境非常适合单个项目。但现在在你的日常工作中,他们真的能扩大它的规模,让乳胶仍然有用吗?
你需要的是一个自动生成报告**(也许,就在你的管道末端)的方法,它根据你的代码的发现动态地改变它的内容,它是视觉上令人愉快的(对你的老板来说),并且它能够快到足以批量产生结果。**
如果是这种情况,我最喜欢的解决方案是 R 包“knitter”,它允许我们直接从 R 代码创建 LaTeX 文档。结合了编程语言的灵活性和 LaTeX 美学。
在这篇文章中,我想给你一个基本的教程和一些如何成功做到这一点的技巧。
我知道知道 R 和 LaTeX 的人不多,我会尽量保持简单,但这两种编码系统及其组合无法在一篇文章中解释,我希望,如果您点击了这篇文章并一直阅读到现在,您已经对这两者有了一些基本的了解。
事不宜迟,让我们用 r 创建一个 LaTeX 报表。
其中两个是创建 R/tex 报告所需的步骤,一个创建最终 pdf 的“starter”R 脚本,以及。Rnw 文件,我们所有的代码都将存储在这里。让我们看看他们:
起始脚本
“starter”脚本必须完成两个基本功能:
- 加载并传递 Rnw 文件到编织器包的编织命令。这个过程叫做编织。这将执行 R 代码并生成一系列文件,其中有一个我们通常在 texstudio 中使用的. tex 文件。
- 执行两次。tex 文件,以便创建最终的 PDF 报告
为了使整个过程更容易,我建议创建一个名为 starter 的独立文件。r 使用上面的代码,并在需要时使用下面的命令运行它:
R CMD BATCH starter.R
或者,您可以在命令行或任何您可能需要的地方复制/粘贴这三行代码。
别忘了在 r 中安装 knitr。
install.packages('knitr')
Rnw 文件
的。Rnw(或 Sweave)文件本质上是一个. tex 文件,可以在其中插入 R 代码块。
文件的结构遵循 LaTeX 作品的正常结构,从 document 类的声明开始,但是在代码中的任何地方,您都可以添加 R 个代码块。
这些是在两个元素之间定义的:a < < … > > ,在其中传递 R 代码块的设置,以及一个 @ ,定义其结束。
让我们看一个例子:
到目前为止,这是一个常见的代码,你可以在任何。tex 文件并生成图 1 中的页面。它产生一个首页、目录和第一章:****
Fig 1: The first three pages of the final document (Image by author)
让我们继续,看看第一个 R 块,它用 R 创建了一个图,并在 LaTeX 中导入图形:
其余块:
在小于号和大于号之间,我们可以传递执行 R 块的参数;要通过的最重要的要素是:
- Name :这只是程序块的名字,每个程序块必须有一个唯一的名字。在调试阶段,识别名称将对您有很大帮助
- echo :该 TRUE 或 FALSE 值将指示代码是否会显示在最终的 pdf 文档中。
- ****结果可以取值‘asis’或‘hide ’,它将显示或隐藏程序块的结果(输出)
其他参数是布尔值“include ”,它决定该块是否必须运行,“warning”和“message”都决定调试消息是否必须在 pdf 最终版本中显示。
让我们看一个实际的例子,其中我们将加载 Iris 数据库并在 R 代码块中创建一个绘图:
在上面的代码中,我们定义了一个名为 CreatePlot 的代码块,其中我们要求在 pdf 中显示下划线 R 代码,但是结果图是隐藏的,它将使用\includegraphics{}加载。
Fig 2: The resulting pages from the code above. (Image by author)
在图 2 的中,我们可以看到上面代码的结果。
标题、显示为 echo 请求的 R 代码块和用于加载图形的 latex 代码。****
我已经手动添加了图 2 中的图来显示 R 代码的结果,否则它将在不同的 pdf 页面中。
让我们以表格为例来看另一种情况:
通过这段代码,我们使用 iris 数据库创建了一个表格,代码和表格立即显示出来,如下图图 3** 所示。**
Fig 3: PDF page resulting from the code. (Image by author)
我们可以看到,上面的块创建了一个表,其中包含 Iris 数据库(没有完全报告)和生成它的 R 代码。
提示和有用的链接
就我个人的经验来说。Rnw 文件是一个非常强大的工具,但是,我建议将 R 代码保持在最低限度,并且只使用块来导入在其他地方生成的图和 CSV/表。
如果你更喜欢直接在 Rnw 中计算和生成内容,它很快就会变得混乱。
我希望这篇文章能为你使用 LaTeX 和 r 创建第一个报告提供具体的帮助。
完整的扩展代码存在于这个 GitHub 库中:
上面的脚本会产生下面的 pdf (点击链接内的图片)。
我希望这篇文章对你有所帮助
关于机器学习如何沟通清楚?
你的用词很重要。
众所周知,机器学习领域被认为偏重于理论:主要是数学、统计学和计算机科学。虽然从业者倾向于享受深度,但“泡沫之外”的同事和客户很难参与对话。那会导致不良的沟通。
沟通不畅,项目就会失败。
糟糕的沟通扭曲了期望,影响了计划,而且,总的来说,它会降低你的团队的效率。作为从业者,这也是你的问题。
机器学习工程师的刻板印象:
📊大数据的拥护者。
✨的🧠笔杆子神奇的学习算法 ✨.
🤔聪明又好奇。
😅难以沟通(如果你不喜欢 ML)。
因为你正在做的事情看起来复杂而陌生,你的一些合作者不会乐意挑战你。
尤其是因为这个原因,你有责任改善你的工作沟通方式,以打破“难以沟通”的刻板印象。
考虑业务需求。
更清晰沟通的第一步是停止将你的工作与技术需求联系起来,开始将它与业务需求联系起来。
技术需求 是 你的需求 建立一个数学上正确、统计上合理、技术上健壮、计算上高效的机器学习模型。
这非常重要,但是这些信息与你的非技术合作者无关。他们关心业务需求你所做的事情如何让我们更接近解决实际的业务问题?
假设你正在开发一个欺诈检测系统,而你的模型召回率很低,产生了很多误报。在团队会议中,你需要交流你的进展和下一步的计划。
回忆这个词对你的同事来说没有任何实际意义,所以不要使用它。相反,你可以说你的系统是*“制造很多假警报”。你的下一步可能是“调整系统的某些部分,以降低误报率”*。
这种语言可以确保你的同事处于循环中——它还可以促进关于可接受的假警报率的有益讨论。
制定有意义的 KPI。
损失、准确度、精确度、召回率、F1 分数、ROC 曲线和混淆矩阵是衡量模型性能的重要指标。
当模型的性能本身就是一个目标时,这些度量就很有意义,比如在研究或原型中测试一个想法是否可行。
对于真正的商业案例来说,这是一个罕见的目标!
更多的时候,你是在用数据来解决业务问题。举几个例子,你可能会尝试:
- 自动执行任务以节省劳动力成本。
- 帮助某人以更少的错误完成任务。
- 通过提出智能建议来改善产品用户体验。
你的工作应该根据你正在解决的问题来衡量。
当开始一个新项目时,首先尝试从业务需求的角度来理解问题。**哪些 KPI 能准确反映需求是否得到满足?**有了这些知识,你就可以对制定解决方案所需的内部指标做出更好的决策。
假设您正在构建一个系统来优化一项劳动密集型任务,以便降低成本。如果你的系统是高度精确的,实际上会使过程变慢,这有什么关系吗?那不会真正降低成本。
对于这种情况,一个好的 KPI 可能是类似于平均工作流速度的东西。定义一个工作流程并测量它需要多长时间。使用您的系统测量相同的工作流程(控制经验水平)。您是否加快了流程?
您可能会了解到,模型必须达到一定程度的准确性才是有用的。也许你会发现,只有当模型的准确率至少达到 75%时,这个过程才会变得更快。如果你的目标是*,你可能会尝试更高容量的模型,增加计算成本。相反,如果你的目标是 工作流速度 ,你会发现在一个足够精确的*模型和快速计算 的 模型之间存在一个折中点。**
与你的合作者确定有意义的 KPI 将业务需求放在第一位,并围绕你正在解决的实际问题进行沟通。
使用一致的术语。
用不同的词来描述同一件事可能是混淆的主要来源,使所有的交流效率降低。
当你开始一个项目时,你可能想和你的团队以及你的客户一起讨论这些词语。
对于你的项目,试着回答如下问题:
什么是用户**?什么是系统?什么是型号?什么是管道?什么是工作流程?确保写下这些定义。**
让每个人都使用相同的词汇可以减少“在翻译中丢失”的交流量。这确实需要一些前期工作,但是从相互理解中获得的效率是值得的。
简单点,傻瓜。
处理数据并不总是简单的。事实上,数据中的信息通常是复杂的、细微的、多方面的。但是我们应该试着记住接吻。
现在我喜欢接吻。这个古老的缩写经常在编程中使用,它告诉我们“保持简单,傻瓜”*。*
信息过载 是真实存在的事情,在呈现一个复杂的主题时很难避免。输出大量信息可能会让你看起来很聪明,但这会让你的合作者感到无能为力。
向别人展示你的作品的全部目的是让他们能够做出决定或采取行动。如果你给他们提供过多的信息,他们将无法有效地工作。
处理数据的许多微妙之处是你的工作。**当与你的同事和客户交流时,试着关注哪些信息对业务需求至关重要。这样集中信息通常需要额外的准备,所以记得为此分配一些时间。
如何在实践中比较两种分布
现实世界中的数据科学
应用离散 Kolmogorov-Smirnov 检验的快速指南
Photo by Raquel Martínez on Unsplash
我们将在本文中讨论的内容
本文演示了如何进行离散 Kolmogorov-Smirnov(KS)测试并解释测试统计。作为一个非参数检验,KS 检验可以用来比较任何两个分布,不管你假设它是正态分布还是均匀分布。在实践中,KS 检验是非常有用的,因为它在区分一个样本与另一个样本或理论分布(如正态或均匀分布)时是高效和有效的。
首先,为什么我们需要研究我们的数据?
假设你的公司推出了一款新产品,你的首席执行官问你该新产品是否比旧产品更受欢迎。你进行了 A/B 测试,发现新产品比旧产品卖得多。兴奋地分享好消息,你告诉首席执行官新产品的成功,却看到困惑的表情。
By visual inspection, the two groups have different incomes and are of similar ages. How can we verify this?
首席执行官看起来很困惑,因为我们没有提供任何故事。毕竟,首席执行官最不愿意做的事情就是在不了解谁在购买这些产品的情况下停止旧产品并销售新产品。
为了传递一个故事,我们可以分析人口统计数据并回答以下问题:
- 这两组客户的年龄相似吗?
- 这两组顾客的收入相似吗?
回答这些问题需要比较两个样本分布,这可以通过离散 KS 检验来完成。作为这次测试的结果,我们将发现我们是否应该保留或停止旧的产品线。
离散 KS 测试背后的思想是什么?
KS 检验背后的思想很简单:如果两个样本属于彼此,它们的经验累积分布函数 (ECDFs)一定非常相似。这表明我们可以通过测量 ECDFs 之间的差异来评估它们的相似性。
为了实现这一点,KS 测试找到了 ECDFs 之间的最大距离。更重要的是,测试需要评估距离是否大到足以声称两个样本不属于彼此。
在下图中,入站 ECDFs 之间的最大距离用红色箭头表示。红色箭头对应于 KS 测试统计数据,显示在红色圆圈中。
实际上,由于四舍五入的原因,离散型的 KS 检验通常是合适的。虽然时间和价格等变量在理论上是连续的,但由于四舍五入的原因,这些变量通常被视为离散变量,这给了我们一组有限的可能值。KS 测试的连续版本仍然可以使用,尽管它将导致更保守的结果(即,如果拒绝水平为 5%,实际水平将小于 5%)。
我们如何应用离散 KS 检验?
以下是对两个样品进行离散 KS 测试的程序:
第一阶段:宁滨山脉
- 找出组合样本的最小值和最大值,以定义我们的范围。
- 对范围进行分箱,使每个分箱至少有 10 个样本:
例如,对于 500 个样本,通过选择 20 个桶,我们可以预计每个分箱有 25 个样本。
Stage 1: Binning the range
阶段 2:计算箱频率
- 利用阶段 1 中的仓集,使用上一节中的 ECDF 公式计算每个样本的所有仓的频率。
阶段 3:计算最大距离
- 对于每个箱,计算两个样本之间的频率差。
- KS 检验统计量等于所有频段中频率的最大差值。
我们如何理解 KS 检验统计量?
为了找出我们是否可以拒绝零假设,我们必须使用统计技术推导出一个 KS 检验统计分布。
1.样本分布与理论分布
当我们将样本与理论分布进行比较时,我们可以使用蒙特卡罗模拟来创建测试统计分布。
例如,如果我们想要测试一个 p 值分布是否是均匀分布的(即 p 值均匀性测试,我们可以模拟均匀随机变量并计算 KS 测试统计量。通过重复这个过程 1000 次,我们将有 1000 个 KS 测试统计,这给了我们下面的 KS 测试统计分布。
The red line is the actual test statistic and the green line is the test statistic for 1000 random normal variables.
通过插入实际样本的 KS 检验统计量(即红线),我们可以看到实际的 KS 检验统计量包含在分布中。这意味着没有强有力的证据反对 p 值样本遵循均匀分布的零假设。
此外,绿线代表使用 1000 个正态随机变量模拟的 KS 测试统计数据,位于分布之外。这表明了均匀分布和正态分布之间的不同。
2.样本分布与另一个样本分布
在我们前面的年龄和收入分布的例子中,我们将一个样本分布与另一个样本分布进行比较,而不是与理论分布进行比较。在这种情况下,我们需要应用重采样技术,如置换测试或自举,以获得 KS 测试统计分布。
在这一节中,我们将进行一个排列测试,它包括组合两个样本,以随机创建两个样本的新集合。对于每个新集合,我们将计算 KS 检验统计量,并将所有这些统计量组合起来,以生成 KS 检验统计量分布。
The red vertical line is the KS test statistic value of the two original samples.
不出所料,实际收益样本的 KS 检验统计量远离分布。这表明我们可以拒绝收入样本相同(即 p 值为零)的零假设。相反,实际年龄样本的 KS 检验统计量位于其 KS 检验统计量分布内。因此,我们不能拒绝零假设,即年龄样本是相同的。
我们学到了什么
由于我们的发现,我们可以得出结论,新老产品可能针对相似的年龄组,但不同的收入组。这告诉我们,我们不应该停止旧产品,必须在两种产品之间找到最佳平衡。
除了 KS 检验外,还有卡方检验和 Anderson-Darling (AD)检验等替代方法,可在[2]中找到。与将所有重点放在具有最大差异的箱上的 KS 测试相比,这些备选方案在所有箱之间分配权重。卡方检验根据箱的预期频率分配权重,而 AD 检验则更加强调尾部。
我想鼓励读者应用离散 KS 检验或探索其他替代方法作为他们分析程序的一部分。这些测试不仅帮助我们从里到外理解数据,而且实现起来也很简单。这里有一个 R 包可用。玩得开心,好好享受吧!
来源
[1]埃尔莫尔,K. L. (2005 年)。根据集合预报评估等级直方图的卡方检验的替代方法。天气与预报, 20 (5),789–795。doi:10.1175/884.1
[2]m .斯蒂尔和 j .蔡斯林(2006 年)。拟合优度检验离散拟合优度检验统计量对一个均匀空值和一组备选分布的功效。
如何使用 Docker 在 Ubuntu 16.04 上编译 TensorFlow 1.12
本教程将帮助您在 Ubuntu 16.04 上使用 Docker 和 nvidia-docker 的 GPU 设置 TensorFlow 1.12。
TensorFlow 是最受欢迎的深度学习库之一。它由谷歌创建,并于 2015 年作为一个开源项目发布。TensorFlow 用于研究和生产环境。安装 TensorFlow 可能很麻烦。难度因环境限制而异,如果你是一名只想构建神经网络的数据科学家,难度会更大。
在 GPU 上使用 TensorFlow 时—设置需要几个步骤。在下面的教程中,我们将回顾设置 TensorFlow 所需的过程。
要求:
- NVIDIA GPU 机器
- Docker 和 Nvidia-Docker 已安装。(阅读我们博客中的“如何安装 docker 和 nvidia-docker”)
- Ubuntu 16.04
步骤 1-使用 Docker 和 Nvidia-Docker 准备您的环境
Docker 是一个工具,旨在通过使用容器来简化应用程序的创建、部署和运行。容器到底是什么?容器允许数据科学家和开发人员用环境需要的所有部分——比如库和其他依赖项——包装环境,并将其全部打包。
要将 docker 与 GPU 配合使用,并能够在您的应用程序中使用 TensorFlow,您需要安装 Docker 和 Nvidia-Docker。如果您已经安装了这些,请转到下一步。否则,你可以按照我们之前的指南安装 nvidia docker 。
先决条件:
第 2 步— Dockerfile
Docker 可以通过读取 docker 文件中的指令来自动构建映像(环境)。Dockerfile 是一个文本文档,它包含用户可以在命令行上调用的所有命令来组合一个图像。
在我们的例子中,这些命令将描述 Python 3.6、CUDA 9 和 CUDNN 7.2.1 的安装—当然还有从源代码安装 TensorFlow 1.12。
对于这种环境,我们将使用以下 docker 文件-
步骤 3 —运行 Dockerfile
要从 docker 文件构建映像,只需运行 docker build 命令。请记住,这个构建过程可能需要几个小时才能完成。我们建议使用 nohup 实用程序,这样,如果您的终端挂起,它仍将运行。
$ docker build -t deeplearning -f Dockerfile
这将输出设置过程,并以类似于以下内容结束:
>> Successfully built deeplearning (= the image ID)
您的图像已经可以使用了。要启动环境,只需输入以下命令。但是,不要忘记更换您的图像 id:
$ docker run --runtime=nvidia -it deeplearning /bin/bashStep 4 — Validating TensorFlow & start building!
验证 TensorFlow 确实在您的 docker 文件中运行
$ python
import tensorflow as tf
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
2019-02-23 07:34:14.592926: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports
instructions that this TensorFlow binary was not compiled to use: AVX2 FMA2019-02-23 07:34:17.452780: I
tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:964] successful NUMA
node read from SysFS had negative value (-1), but there must be at leastone NUMA node, so returning NUMA node zero
2019-02-23 07:34:17.453267: I
tensorflow/core/common_runtime/gpu/gpu_device.cc:1432] Found device 0 with
properties:
name: Tesla K80 major: 3 minor: 7 memoryClockRate(GHz): 0.8235pciBusID: 0000:00:1e.0
totalMemory: 11.17GiB freeMemory: 11.10GiB
2019-02-23 07:34:17.453306: I
tensorflow/core/common_runtime/gpu/gpu_device.cc:1511] Adding visible gpu
devices: 0
2019-02-23 07:34:17.772969: I
tensorflow/core/common_runtime/gpu/gpu_device.cc:982] Device interconnect
StreamExecutor with strength 1 edge matrix:
2019-02-23 07:34:17.773032: I
tensorflow/core/common_runtime/gpu/gpu_device.cc:988] 0
2019-02-23 07:34:17.773054: I
tensorflow/core/common_runtime/gpu/gpu_device.cc:1001] 0: N
2019-02-23 07:34:17.773403: I
tensorflow/core/common_runtime/gpu/gpu_device.cc:1115] Created TensorFlow
device (/job:localhost/replica:0/task:0/device:GPU:0 with 10757 MB memory)
-> physical GPU (device: 0, name: Tesla K80, pci bus id: 0000:00:1e.0,
compute capability: 3.7)
Device mapping:
/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device
/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device
/job:localhost/replica:0/task:0/device:GPU:0 -> device: 0, name: Tesla K80,
pci bus id: 0000:00:1e.0, compute capability: 3.7
2019-02-23 07:34:17.774289: I
tensorflow/core/common_runtime/direct_session.cc:307] Device mapping:
/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device
/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device
/job:localhost/replica:0/task:0/device:GPU:0 -> device: 0, name: Tesla K80, pci bus id: 0000:0
恭喜你。您的新 TensorFlow 环境已经设置完毕,可以开始训练、测试和部署您的深度学习模型了!
结论
Tensorflow 通过提供一种大规模构建生产就绪模型的解决方案,真正颠覆了机器学习世界。但是 Tensorflow 并不总是最用户友好的。很难顺利地融入你的机器学习管道。 cnvrg.io 数据科学平台利用 Tensorflow 和其他开源工具,以便数据科学家可以专注于算法这一魔法。你可以找到更多关于如何轻松利用开源工具的教程,如 Tensorflow 、如何为你的机器学习工作流设置 Kubernetes,以及如何在 Kubernetes 上运行 Spark。找到简单的方法来集成这些有用的工具将使您的模型更接近生产。
如何计算 Keras 中命名实体识别的 f1 值
在命名实体识别中,f1 分数用于评估训练模型的性能,特别是,该评估是针对每个实体的,而不是令牌。
在许多机器学习框架中都实现了评估 f1 分数的功能。然而,它的目标是分类任务,而不是像命名实体识别那样的序列标记。
幸运的是,Keras 允许我们通过回调类在训练期间访问验证数据。通过扩展回调,我们可以评估命名实体识别的 f1 值。
下面是一个示例代码,用于在每个时期结束时计算和打印 f1 分数、召回率和精确度。你不必自己实现这段代码,它包含在 seqeval 包中:
import numpy as np
from keras.callbacks import Callback
from seqeval.metrics import f1_score, classification_report
class F1Metrics(Callback):
def __init__(self, id2label, pad_value=0, validation_data=None):
"""
Args:
id2label (dict): id to label mapping.
(e.g. {1: 'B-LOC', 2: 'I-LOC'})
pad_value (int): padding value.
"""
super(F1Metrics, self).__init__()
self.id2label = id2label
self.pad_value = pad_value
self.validation_data = validation_data
self.is_fit = validation_data is None
def find_pad_index(self, array):
"""Find padding index.
Args:
array (list): integer list.
Returns:
idx: padding index.
Examples:
>>> array = [1, 2, 0]
>>> self.find_pad_index(array)
2
"""
try:
return list(array).index(self.pad_value)
except ValueError:
return len(array)
def get_length(self, y):
"""Get true length of y.
Args:
y (list): padded list.
Returns:
lens: true length of y.
Examples:
>>> y = [[1, 0, 0], [1, 1, 0], [1, 1, 1]]
>>> self.get_length(y)
[1, 2, 3]
"""
lens = [self.find_pad_index(row) for row in y]
return lens
def convert_idx_to_name(self, y, lens):
"""Convert label index to name.
Args:
y (list): label index list.
lens (list): true length of y.
Returns:
y: label name list.
Examples:
>>> # assumes that id2label = {1: 'B-LOC', 2: 'I-LOC'}
>>> y = [[1, 0, 0], [1, 2, 0], [1, 1, 1]]
>>> lens = [1, 2, 3]
>>> self.convert_idx_to_name(y, lens)
[['B-LOC'], ['B-LOC', 'I-LOC'], ['B-LOC', 'B-LOC', 'B-LOC']]
"""
y = [[self.id2label[idx] for idx in row[:l]]
for row, l in zip(y, lens)]
return y
def predict(self, X, y):
"""Predict sequences.
Args:
X (list): input data.
y (list): tags.
Returns:
y_true: true sequences.
y_pred: predicted sequences.
"""
y_pred = self.model.predict_on_batch(X)
# reduce dimension.
y_true = np.argmax(y, -1)
y_pred = np.argmax(y_pred, -1)
lens = self.get_length(y_true)
y_true = self.convert_idx_to_name(y_true, lens)
y_pred = self.convert_idx_to_name(y_pred, lens)
return y_true, y_pred
def score(self, y_true, y_pred):
"""Calculate f1 score.
Args:
y_true (list): true sequences.
y_pred (list): predicted sequences.
Returns:
score: f1 score.
"""
score = f1_score(y_true, y_pred)
print(' - f1: {:04.2f}'.format(score * 100))
print(classification_report(y_true, y_pred, digits=4))
return score
def on_epoch_end(self, epoch, logs={}):
if self.is_fit:
self.on_epoch_end_fit(epoch, logs)
else:
self.on_epoch_end_fit_generator(epoch, logs)
def on_epoch_end_fit(self, epoch, logs={}):
X = self.validation_data[0]
y = self.validation_data[1]
y_true, y_pred = self.predict(X, y)
score = self.score(y_true, y_pred)
logs['f1'] = score
def on_epoch_end_fit_generator(self, epoch, logs={}):
y_true = []
y_pred = []
for X, y in self.validation_data:
y_true_batch, y_pred_batch = self.predict(X, y)
y_true.extend(y_true_batch)
y_pred.extend(y_pred_batch)
score = self.score(y_true, y_pred)
logs['f1'] = score
id2label = {1: 'B-LOC', 2: 'I-LOC'}
f1score = F1Metrics(id2label)
定义模型,并在拟合函数中添加回调:
model.fit(x_train, y_train,
validation_data=(x_valid, y_valid),
epochs=1,
batch_size=32,
callbacks=[f1score])
培训期间的打印输出如下所示:
Epoch 1/1
541/541 [==============================] - 46s 85ms/step - loss: 9.5729
- f1: 53.24
precision recall f1-score support
PER 0.5754 0.4484 0.5040 1617
ORG 0.3798 0.4395 0.4075 1661
MISC 0.4202 0.4387 0.4293 702
LOC 0.6886 0.7650 0.7248 1668
avg / total 0.5320 0.5381 0.5315 5648
格式类似于 scikit-learn 的分类 _ 报告功能。
就是这样。祝您愉快!
https://github.com/chakki-works/seqeval
如何计算卫星图像统计数据并将其用于熊猫
从 Sentinel 2 影像中获取分区统计数据,并与您的 Pandas 数据框合并。洪灾区 NDWI 水指数计算演练。
Example of Zonal Statistics — Elevation DEM overlayed on derived statistics (Max elevation in each grid shown)
卫星数据非常密集,使用像元来存储值。但是,在许多情况下,您只想将卫星(栅格)影像的摘要转换为表格格式 CSV 或 Pandas 数据框。
让我们举例说:您有一个数字高程模型(DEM)。DEM 图像清晰地显示了该区域的高程和地形。现在,如果您想要获取高程值并整合您拥有的表格数据(例如,建筑物)来获取每座建筑物的高程,该怎么办呢?
从光栅图像导出表格输出(汇总统计)的过程称为区域统计。
在本教程中,我们将学习如何从栅格数据中提取值,并以表格格式存储这些值(Pandas Dataframe)。
本教程的数据集和代码可以在 Github 中找到。让我们从探索数据开始。
数据探索
本教程的数据集是 2019 年 11 月 1 日在索马里贝莱德文拍摄的哨兵图像。该地区在此期间被淹没,我们计算 NDWI 来测量水压力水平。我们用的是 Google Colab 笔记本,直接从 URL 下载笔记本里的数据。
让我们导入本教程中使用的库。
import pandas as pd
import numpy as np
import geopandas as gpd
import rasterio as rio
from rasterio.plot import show
from rasterio.mask import mask
import matplotlib.pyplot as plt
使用 Rasterio,您可以读取 Sentinel 2 图像的不同波段。在这种情况下,我们读取波段 8 (NIR)、4(红色)、3(绿色)和 2(蓝色)。
b8 = rio.open(“/content/Data/20191101/B08–20191101.tif”)
b4 = rio.open(“/content/Data/20191101/B04–20191101.tif”)
b3 = rio.open(“/content/Data/20191101/B03–20191101.tif”)
b2 = rio.open(“/content/Data/20191101/B02–20191101.tif”)
让我们看看图像的宽度和高度。我只使用 b4,但你可以检查是否所有带都有相同的重量和长度。
b4.width,
b4.height
我们绘制数据,以查看和探索我们拥有的卫星图像。在这里,我只绘制波段 3。
fig, ax = plt.subplots(1, figsize=(12, 10))
show(b3, ax=ax)
plt.show()
如果你想知道如何用 Rasterio 制作 RGB 图像,我这里有一个教程你可能想看。
[## Python 和 Jupyter 笔记本中的卫星图像访问和分析
使用 Python 在 Jupyter 笔记本中访问、预处理、分析和可视化卫星图像。
towardsdatascience.com](/satellite-imagery-access-and-analysis-in-python-jupyter-notebooks-387971ece84b)
该区域的哨兵 2 影像(仅波段 3)如下所示。
让我们也读一下 buildings 表,我们将使用它来存储从卫星图像得到的统计概要。请注意,您可以使用其他多边形,如区域、矩形网格,而不是本例中的建筑多边形。
我们使用 Geopandas 来读取数据。
buildings = gpd.read_file(“/content/Data/shapefiles/osm_buildings.shp”)
buildings = buildings[[“osm_id”,”building”, “geometry”]]
buildings.head()
这是表格的前五行。我们有每个建筑的几何列、osm_id 和建筑列。
我们也可以在哨兵 2 号图像的顶部绘制建筑物。
fig, ax = plt.subplots(figsize=(12, 10))
show(b4, ax=ax)
buildings.plot(ax=ax, color=”white”, alpha=.50)
plt.show();
建筑物在图像中被标记为白色并被覆盖,如下所示。图为蜿蜒的谢贝利河延伸到城市(居民区)的范围。
现在让我们计算哨兵 2 号图像的 NDWI 值。
计算归一化差异水指数(NDWI)
为了计算 NDWI 值,我们使用以下公式:
(频段 3 —频段 8)/(频段 3 +频段 8)
所以,让我们用拉斯特里奥的这个公式来计算。
green = b3.read()
nir = b8.read()
ndwi = (nir.astype(float)-green.astype(float))/(nir+green)
可以使用 Rasterio 绘制 NDWI 阵列,如下所示。
fig, ax = plt.subplots(1, figsize=(12, 10))
show(ndwi, ax=ax, cmap=”coolwarm_r”)
plt.show()
NDWI 图清楚地显示了谢贝利河(蓝色)附近未标注日期的区域。洪水淹没了居民区。
我们可以将 NDWI 阵列保存为光栅图像,以便以后使用。
meta = b4.meta
meta.update(driver='GTiff')
meta.update(dtype=rio.float32)
with rio.open('NDWI.tif', 'w', **meta) as dst:
dst.write(ndvi.astype(rio.float32))# Read the saved
ndwi_raster = rio.open(“NDWI.tif”)
现在,我们已经计算了 NDWI 值,是时候从 NDWI 栅格影像中获取统计数据并将其合并到建筑物表中了。我们使用栅格掩膜功能从 NDWI 栅格图像中获取像元值。
以下是一个小函数,它将像元值屏蔽到数据框表中。
def derive_stats(geom, data=ndwi_raster, **mask_kw):
masked, mask_transform = mask(dataset=data,
shapes=geom,)crop=True, all_touched=True, filled=True)
return masked
我们可以像这样得到我们想要的值。假设我们对获取每栋建筑的 NDWI 平均值感兴趣。我们为“mean_ndwi”创建了一个列,并传递我们函数来应用建筑物的几何图形,还使用 Numpy 应用到 mean。
buildings[‘mean_ndwi’] = buildings.geometry.apply(derive_stats).apply(np.mean)
或者,获取每个建筑物的最大 NDWI 值。
buildings[‘max_ndwi’] = buildings.geometry.apply(derive_stats).apply(np.max)
我们的表现在有两个新列,mean_ndwi 和 max_ndwi,我们在其中存储每个建筑物的平均和最大 ndwi 值。
该数据集还包括该地区以前数据(洪水之前)中的哨兵图像。在“数据”文件夹中,有“20191002”文件夹。尝试使用这些图像计算平均和最大 NDWI 值。将此与我们为 20191101(洪水期)计算的图像得出的统计数据进行比较。
结论
在本教程中,我们已经了解了如何从 Sentinel 2 影像中计算 NDWI 值,并从中获取汇总统计数据。代码和 Google Colab 笔记本可以在这个 Github 存储库中找到。
python-Rasterio 和 Geopandas 计算分区统计数据卫星数据密集,并使用像元存储值…
github.com](https://github.com/shakasom/zonalstatistics)
或者直接在这个谷歌合作实验室笔记本上
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/shakasom/zonalstatistics/blob/master/Zonal_Statistics_Sentinel.ipynb)
如何用强大的临床研究工具征服队列分析
为什么你的医生比你更了解客户维系
在 SaaS 或消费者订阅环境中,客户流失的微小变化会从根本上影响收入增长。
产品经理、增长黑客、营销人员、数据科学家和投资者都需要了解商业决策如何影响用户留存。
随着如此多的经常性收入业务上市,硅谷现在应该了解情况了。
然而,信不信由你,医学研究人员比你更擅长衡量客户保持率。
什么?
听起来很大胆,但事实并非如此。几十年来,临床研究人员已经提炼出精确而严谨的方法来衡量保留率——只不过他们衡量的不是客户保留率,而是患者存活率。
生死的严重性意味着研究人员在衡量治疗效果时非常小心。
为此,临床研究人员使用一种叫做 卡普兰-迈耶估计量 的统计方法。该公式巧妙地解决了队列保持分析中经常出现的一个问题:在不同寿命的队列组内部和之间进行有效的比较:
尽管公式很花哨,但使用 Kaplan-Meier (KM)的生存分析实际上非常简单,并且比其他方法提供了更好的结果:
在这篇文章中,我将解释这些结果,用简单的术语分解 KM 估算器,并说服你用它来进行保留分析。
底线:如果你是一名经营者或投资者,想要恰当地衡量客户群保持率,那么卡普兰-迈耶是一个不错的选择。
两个必然:死亡和流失
KM 估算器帮助我们处理的核心问题是缺失数据。
队列数据本身就有缺陷,因为最近的队列与老队列相比数据点更少。例如,一个五个月大的队列只能与一个十个月大的队列的前五个月进行比较。七个月前获得的客户群的保留率只能合理地与前七个月老客户群的保留率进行比较。
假设您有前 12 个月的完整保留历史,并且您想要预测新获得客户的 12 个月保留曲线。怎么做一点都不明显。
为了更好地理解这一点,让我们想象一个只有五个群组的简单例子:
你可以先试着计算不同群体的平均保持率。这是有问题的,原因有二:
- 如果我们的队列大小不同,简单的平均值就不具有代表性
- 对于任何给定的月份,我们只能对至少存活了这么长时间的群体进行平均,因此随着时间的推移,我们实际上对越来越少的群体进行平均
下面可以看到第二期。无论是简单平均值还是加权平均值,当表现在群组间波动时,我们都会得到奇怪的结果:
假设我们不重新添加之前涌入其原始群体的返回用户,那么在下降后,保留率不可能上升——这是一条单行道。这是我们有缺陷的方法的产物,因为根据定义,5 个月的保留期不能超过 4 个月。
第三个相关的问题出现在将一组群组与其他群组进行比较时,例如,将 2016 年的每月群组与 2017 年的群组进行比较。正如我们刚刚展示的,使用平均值来估计每个群组的保留曲线是行不通的,这意味着我们也无法将一个群组与另一个群组进行比较。
有问题吗?问问你的医生
信不信由你,临床研究人员一直在处理同样的问题。
客户群类似于在不同时间开始治疗的患者群体。这里的“治疗”是获得客户的时间,“死亡”只是流失。
或者,想象一下,如果“2016 年队列”和“2017 年队列”,而不是按年份分组的队列,是在临床试验中接受不同治疗的群体。我们想量化两组患者存活率(客户保持率)的差异。
制药公司和其他研究机构经常面临这一问题。患者在不同的时间开始治疗。患者因死亡而退出研究,但也因搬家或决定停止服药而退出研究。
这在任何患者的临床试验记录的开始、中间和结尾都造成了大量的数据缺失问题,使有效性和安全性的分析变得复杂。
为了解决这个问题,1958 年,数学家爱德华·卡普兰和统计学家保罗·迈耶,共同创建了卡普兰-迈耶估计量。也被称为乘积限估计器,该方法有效地处理了缺失数据问题,提供了对任何一点的生存概率的更精确的估计。
到任何时间点的估计存活概率是每个先前时间间隔的累积存活概率,计算为先前存活概率的乘积
上面那个奇怪的公式只是简单地将一堆概率彼此相乘,以找到在某一点上生存的累积概率。
这些概率从何而来?直接来自数据。
KM 表示,我们对从一个月到下一个月的生存概率的最佳估计正是我们数据集中该月的加权平均保留率(在统计学术语中也称为 最大似然估计器 )。因此,如果在一组群组中,我们有 1000 个客户从第一个月开始,其中 600 个存活到第二个月,我们对从第一个月到第二个月存活的“真实”概率的最佳猜测是 60%。
下个月我们也这样做。将第 3 个月存活的客户数除以第 2 个月存活的客户数,得到第 2 个月到第 3 个月的估计存活概率。如果我们没有一个群组的第 3 个月的数据,因为它只有两个月大,我们从我们的第 3 个月生存计算中排除这些客户。
重复尽可能多的群组/月份,在每次计算中排除当前期间的任何群组缺失数据。然后,要计算任何给定月份的生存概率,将该月份的个人月度(条件)概率相乘。
虽然这是一种病态的想法,但是测量患者存活率在功能上等同于测量客户保持率,因此我们可以很容易地将知识管理转化为客户群组分析!
测试卡普兰-迈耶
让我们通过将 Kaplan-Meier 估计量应用到前面的例子中来更清楚地说明这一点。
第 1 个月存活的概率是 69% (第 1 个月存活的客户总数除以第 0 个月的总数)。假设客户在第 1 个月存活,则第 2 个月存活的概率为 72% (第 2 个月存活的客户总数除以第 1 个月的总数,不包括缺少第 2 个月数据的最后一组客户)。所以至少存活两个月的累计概率是 69% x 72% = 50% 。冲洗,洗涤,然后每个月重复一次。
并排比较揭示了 KM 的优越性:
知识管理的伟大之处在于它利用了我们拥有的所有数据,甚至是我们观察较少的年轻群体。例如,虽然第 3 个月时所有可用群组的平均值仅使用群组 1-3 的数据,但由于其累积性,KM 估计值有效地纳入了较新群组的早期保留。这产生了 38%的 3 个月保留率估计值,高于我们在第 3 个月实际测量的任何群组。
这正是我们想要的-第 4 组和第 5 组都比第 1 组和第 3 组更大,也更容易保持。因此,从这些群组中随机挑选的客户的 3 个月保留率很可能会超过历史平均水平,因为该客户很可能在群组 4 或 5 中。
使用所有的数据也很好,因为这使得我们对尾部概率的估计比我们只依赖我们保留了那么久的客户数据要精确得多。
卡普兰-迈耶曲线还通过遵守概率基本定律修正了留存曲线右尾的不稳定行为:累积概率只会随着数字的增加而下降。
95%的医生推荐
这种分析很容易扩展。让我们回到 2016 年与 2017 年的例子——我们可以对每一组队列进行卡普兰-迈耶计算,然后比较所得的生存曲线,突出两组之间预期保留的差异。
虽然我不会在这里讨论,但是你也可以计算卡普兰-迈耶曲线的 p 值、置信区间和统计显著性检验。这让你可以做出严格的陈述,如“2018 年相对于 2017 年的队列保留率的改善具有统计学意义(在 5%的水平)”-酷的东西:
对于那些花时间分析客户群体数据的人来说,Kaplan-Meier 是一个强大的工具。知识管理已经在严格的临床试验中经受了考验——如果说有什么不同的话,那就是它没有在技术运营商和投资者中更受欢迎。
如果你是一名产品经理、增长黑客、营销人员、数据科学家、投资者或任何其他理解客户保持分析的重要性的人,卡普兰-迈耶估算器应该是你的分析武库中的一件有价值的武器。
在 推特 上关注我,听听我对科技商业和经济的看法。
如何在 docker 中包含你的第一个 Django 并从 AWS 中访问它
今天,许多初级软件工程工作需要一系列技术,包括 Django、python、docker 和 AWS。如果你是一个初学者,并且想了解这些技术,那么你来对地方了。通过这篇文章,我将指导您操作这些工具并创建您的第一个 Python Django 应用程序,该应用程序包含在 docker 中并可从 AWS 访问。
您只需要:
Docker Hub 免费账户:https://hub.docker.com/
AWS 免费账户https://aws.amazon.com/account/
MAC 或 Windows 笔记本电脑。
Pycharm 框架https://www.jetbrains.com/pycharm/download/
一个大脑:)
从你的宝贵时间最多一个小时。
你准备好了吗?
所以让我们开始吧…第一步是打开你的 pycharm 并创建一个新项目。
接下来,您必须在 pycharm 中安装 Django 库:
pip install django
然后,只需通过下面的命令启动您的项目(我们将项目命名为 DjangoDocker ,但是您可以命名为其他名称)。
django-admin startproject DjangoDocker
这将创建项目目录和里面的几个文件,我们稍后将详细检查它们。
之后,您需要使用下面的命令在 Django 项目中创建第一个应用程序:
python manage.py startapp myApp
这将在您的主项目目录中创建一个新目录,它将包含以下文件:
您可能会注意到,我们可以在同一个 Django 项目中创建多个应用程序,这在我们想要创建一个后端 API 并在多个项目中使用它的情况下非常有用。
现在让我们试着在我们的第一个应用程序中填充代码,看看我们如何从云中 dockerize 和访问它!!
在接下来的步骤中,我们将填充获得“Salam World”Django 应用程序所需的最低编码。
首先,打开 myApp 目录下的文件“views.py ”,并填入以下代码:
from django.shortcuts import render
# Create your views here.
from django.http import HttpResponse
def index(request):
return HttpResponse(**" Django on Docker on AWS "**)
然后,您应该向 Django 表明该文件将成为您的索引,为此,只需将以下代码放入 myApp => urls.py
并且只需在 urls.py (根文件夹)填充根 URL 路径即可。
from django.contrib import admin
from django.urls import include, path
path(‘<appname>/’, include(‘<appname>.urls’)),
之后,您可以启动数据库并检查您的应用程序是否正常工作:
## initiate the database
python manage.py migrate## check it works
python manage.py runserver
只要启动下面的网址,你应该会看到类似的结果:
将代码推送到 docker image 之前的一个中间步骤是将它放在 GITHUB 中,以跟踪对每次迭代所做的各种更改。这也将允许您实施您的 DevOps CI/CD 管道,以便直接将更改和“自动”推送到 AWS,而无需重复所有这些步骤。
## initiate GIT
# Go to directory and tape:
git init
## Create and fill .gitignore to untrack files
git status #( to check )
git add — all .
git commit -m “My first commit”## push to GitHIB
## Connect to Github (login/password)
## create a new repo
git remote add origin [https://github.com/](https://github.com/)<login>/<repo>.git
git push -u origin master
现在,我们将看到如何将代码包含在 docker 映像中,并将其推送到 docker hub。
要创建 docker 映像,只需在 Django 根目录中创建以下 3 个文件:
docker file
docker-compose . yml
requirements . txt
— — — — — Dockerfile
# We Use an official Python runtime as a parent image
FROM python:3.6
# The enviroment variable ensures that the python output is set straight
# to the terminal with out buffering it first
ENV PYTHONUNBUFFERED 1
# create root directory for our project in the container
RUN mkdir /<dir-in-container>
# Set the working directory to /music_service
WORKDIR /<dir-in-container>
# Copy the current directory contents into the container at /music_service
ADD . /<dir-in-container>/
# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt
EXPOSE 8000
CMD exec gunicorn <Django-project_name>.wsgi:application — bind 0.0.0.0:8000 — workers 3— — — — docker-compose.yml
version: ‘3’
services:
web:
build: .
command: bash -c “python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000”
container_name: <dir-in-container>
volumes:
— .:/<dir-in-container>
ports:
— “8000:8000”
— — — — requirements.txt
Django==2.1.5
gunicorn==19.9.0
之后,通过运行以下命令创建 docker 映像:
docker build -t <DockerLogin>/<Docker-Image_name> .
# PS: don’t forget the “.” at the end of the above command
其中 DockerLogin 是您的 docker Hub 登录名, Docker-Image_name 是您想要的图像名称。
要测试您的映像是否正常工作,只需启动:
docker run -p 8000:8000 -i -t <DockerLogin>/<Docker-Image_name>
您应该会在路由端口 8000 上看到您声明的 IP 0.0.0.0 的结果,如下所示:
要将图像推入 docker hub,只需启动以下命令:
docker push <DockerLogin>/<Docker-Image_name>
PS :检查你的本地 docker 是否在运行,如下图所示(针对 Mac 用户)。
现在,您的 Django 应用程序被推送到 Docker Hub 映像,任何人都可以从 Hub 中取出它,并在 Docker 中实例化它,以便在他的本地环境中运行它,而不用担心依赖关系,也不用使用 Django、Python 等。本地安装。
现在让我们看看如何托管您的 docker 映像并从 AWS 访问它。
首先,您需要创建一个 JSON 文件,告诉 AWS 如何找到您的 docker 映像,请参见下面的示例:
— — — -Dockerrun.aws.json
{
“AWSEBDockerrunVersion”: “1”,
“Image”: {
“Name”: “abdel1979/<image-name>”,
“Update”: “true”
},
“Ports”: [
{
“ContainerPort”: “8000”
}
],
“Logging”: “/var/log/nginx”
}
之后,只需在您的 AWS 帐户中导航至管理控制台:
然后交给弹性豆茎。
之后,通过命名创建一个新的应用程序,然后选择 web 服务器环境:
接下来,填写所需信息,环境名,域名并选择 docker 作为平台,最后上传你的 JSON 文件。
最后,等待几分钟来创建您的环境,一旦准备好,打开您选择的域,您将看到您的应用程序显示如下:
我们到了:)
一如既往,我希望你学到了新的东西。
萨拉姆,
如何使用 R 正确解释连续变量和分类变量的相互作用
理解你的回归
当你有互动时,解释系数可能是棘手的。你对它们的解释正确吗?
Photo by Jon Robinson on Unsplash
理解和解释回归结果的能力是有效数据分析的基础。了解每个术语在模型规范中是如何表示的,对于准确解释模型的结果至关重要。然而,通常很容易通过查看 t 统计或 p 值得出结论,并假设模型做了您想要它做的事情,而没有真正理解在引擎盖下发生了什么。我在给本科生讲授统计学时经常观察到的一个错误是,当包含带有分类变量的交互项时,如何解释连续变量的主要效应。
这里我提供了一些 R 代码来演示为什么你不能简单地把系数解释为主要效果,除非你指定了一个对比度。
TLDR:当你指定你的分类变量是一个以 0 为中心的对比变量时,你应该只把与分类变量相互作用的连续变量的系数解释为平均主效应。如果分类变量是虚拟编码的,你不能把它解释为平均主效应。
步骤 1:模拟数据
为了举例说明,我将创建一个包含变量Income
、Age
和Gender
的假数据集。我的规范是,对于男性,Income
和Age
的相关系数 r = .80,对于女性,Income
和Age
的相关系数 r = .30。
从这个规格来看,****Age**
对 **Income**
的平均作用,对 **Gender**
**的控制应为. 55 (= (.80 + .30) / 2)。至于平均群体差异,假设男性平均收入 2 美元,女性平均收入 3 美元。
分别生成每个性别的数据,然后连接起来创建一个组合数据帧:data
。使用包MASS
中的mvrnorm
在R
中生成数据:
这段代码还检查随机生成的数据是否具有我们指定的相关性和平均值。
## Correlation between Income & Age for Male: 0.8
## Correlation between Income & Age for Female: 0.3
## Mean Income for Male: 2
## Mean Income for Female: 3
第二步:错误地估计你的主要效果
现在我们有了样本数据,让我们看看当我们天真地运行一个线性模型,从Age
、Gender
以及它们的相互作用预测Income
时会发生什么。
##
## Call:
## lm(formula = "Income~Age*Gender", data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4916 -0.4905 -0.0051 0.5044 3.2038
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 3.00000 0.02521 118.99 <2e-16 ***
## Age 0.30000 0.02522 11.89 <2e-16 ***
## GenderMale -1.00000 0.03565 -28.05 <2e-16 ***
## Age:GenderMale 0.50000 0.03567 14.02 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## ## Residual standard error: 0.7973 on 1996 degrees of freedom
## Multiple R-squared: 0.4921, Adjusted R-squared: 0.4913
## F-statistic: 644.6 on 3 and 1996 DF, p-value: < 2.2e-16
上面的模型摘要打印了Intercept
、Age
、GenderMale
、Age:GenderMale
的系数。我们可以清楚地看到,Age
的效果是 0 . 30,这当然不是控制性别的平均效果,而仅仅是对女性群体的效果。GenderMale
的效果是$-1,即男性组比女性组少挣多少钱,截距为$3。最后,相互作用Age:GenderMale
代表与Age
的相关性男性比女性多多少(0.5 = 0.8-0.3)。
重要的是,这是带有分类变量的默认 R 行为,它按字母顺序将第一个变量设置为参考级别*(即截距)。所以在我们的例子中,女性被设定为我们的参考水平。然后,我们的分类变量被虚拟编码(也称为治疗对比),因此女性为 0,男性为 1,这可以通过使用函数contrasts
来验证。**
contrasts(data**$**Gender)## Male
## Female 0
## Male 1
第三步:估计你的主要效果的正确方法
那么,我们需要做什么来获得Age
对Income
控制Gender
的平均效果,同时保持交互?答案是:指定以 0 为中心的对比度,使女性编码为-.50,男性编码为. 50。让我们看看当我们指定对比度并重新运行我们的模型时会发生什么。
##
## Call:
## lm(formula = "Income~Age*Gender", data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4916 -0.4905 -0.0051 0.5044 3.2038
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.50000 0.01783 140.23 <2e-16 ***
## Age 0.55000 0.01784 30.84 <2e-16 ***
## Gender1 -1.00000 0.03565 -28.05 <2e-16 ***
## Age:Gender1 0.50000 0.03567 14.02 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.7973 on 1996 degrees of freedom
## Multiple R-squared: 0.4921, Adjusted R-squared: 0.4913
## F-statistic: 644.6 on 3 and 1996 DF, p-value: < 2.2e-16
我们再次得到四个术语,但是它们被指定为Intercept
、Age
、Gender1
和Age:Gender1
。Age
效应是 0.55,这正是我们在生成数据时指定的跨性别平均效应(0.55 =(0.8+0.3)/2)。Age:Gender1
交互作用为 0.5,这是性别间年龄效应的差异**(0.5 = 0.8–0.3)。Gender1
的效果是$-1,它代表两性之间的平均差异($2-$3),正如我们的对比所指定的。参考值Intercept
是 2.5 美元,这是不同性别的平均收入(($2+$3) / 2)。**
再一次,我们可以用下面的例子来验证我们的对比:
contrasts(data**$**Gender)## [,1]
## Female -0.5
## Male 0.5
结论
我希望这个例子表明,当你建立连续变量和分类变量之间相互作用的线性模型时,你需要注意它们是如何指定的(虚拟编码或对比),因为这将改变你解释系数的方式。
最重要的是,当您已经将分类变量指定为以 0 为中心的对比度时,您应该只将与分类变量相互作用的连续变量的系数解释为平均主效应。如果分类变量是虚拟编码的,你不能把它解释为主要效应,因为它们成为参考水平效应的估计值。
如需了解更多信息,请访问 stackexchange 和 r-bloggers 查看这个问题的更多答案,这个问题已经在网上被多次询问。
附加信息
如果希望分类变量被视为虚拟代码,可以将其设置为处理对比。它复制了 r 提供的默认结果。
##
## Call:
## lm(formula = "Income~Age*Gender", data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4916 -0.4905 -0.0051 0.5044 3.2038
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 3.00000 0.02521 118.99 <2e-16 ***
## Age 0.30000 0.02522 11.89 <2e-16 ***
## Gender2 -1.00000 0.03565 -28.05 <2e-16 ***
## Age:Gender2 0.50000 0.03567 14.02 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 ##
## Residual standard error: 0.7973 on 1996 degrees of freedom
## Multiple R-squared: 0.4921, Adjusted R-squared: 0.4913
## F-statistic: 644.6 on 3 and 1996 DF, p-value: < 2.2e-16
如果你运行模型而没有交互作用,那么即使你的分类变量是虚拟编码的,年龄的主要影响也是如你所料控制性别的平均影响。****
##
## Call:
## lm(formula = "Income~Age+Gender", data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.5525 -0.5102 -0.0055 0.5424 3.2461
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 3.00000 0.02642 113.56 <2e-16 ***
## Age 0.55000 0.01869 29.43 <2e-16 ***
## Gender2 -1.00000 0.03736 -26.77 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.8354 on 1997 degrees of freedom
## Multiple R-squared: 0.4421, Adjusted R-squared: 0.4416
## F-statistic: 791.3 on 2 and 1997 DF, p-value: < 2.2e-16
感谢您的阅读,并随时查看我的其他与数据科学相关的帖子。
统计模型中的重要变量不能保证预测性能
towardsdatascience.com](/why-models-with-significant-variables-can-be-useless-predictors-3354722a4c05) [## 机会是不够的:用排列评估模型的重要性
当训练机器学习模型进行分类时,研究人员和数据科学家经常比较他们的模型…
towardsdatascience.com](/chance-is-not-enough-evaluating-model-significance-with-permutations-e3b17de6ba04)**
如何使用 cobra 在 golang 中创建 CLI
Photo by Marvin Meyer on Unsplash
你有没有想过为什么在 GUI 的世界里,CLI 依然存在?当你自己造一个的时候你会更好的理解它。
当你学习 golang 的时候,你经常会碰到“golang 是构建 cli 工具的好工具”。这也让我着迷。所以,我试着动手做,找到了一些创建 cli 的教程,但大多数都不是基础的。在本教程中,我将尝试平滑学习曲线。
这是基本的 CLI 应用程序,我们将在其中介绍基本的 CLI 操作。我计划写另一篇关于高级 CLI 操作的文章,但那是以后的事了。
我们正在创建一个简单的数学 cli,它将能够完成以下两项工作:
- 数字相加
- 仅偶数或奇数的加法
我知道这些工作不符合你的期望,但相信我,在这之后,你会觉得建立一个 cli 很舒服。
什么是 CLI?(命令行界面)
CLI 根据软件工程的基本原理工作,接受输入,对其进行处理,然后给出输出。在 CLI 工具中,它不是一个闪烁的前端,而是从 黑色窗口 获取输入。记住,黑客帝国三部曲。
如果你使用的是 窗口 只需在start
中键入cmd
或powershell
并回车,黑色窗口或蓝色窗口就是 cli。
cmd
在 Mac 或 Linux 中,它被称为terminal
。
命令行界面 ( CLI ) 是一种与计算机程序交互的手段,其中用户(或客户端)以连续文本行(命令行)的形式向程序发出命令。处理接口的程序被称为命令行解释器或命令行处理器。— 维基百科
你可以使用很多 CLIs,比如 npm、node、go、python、docker、Kubernetes 等等。所有这些都是与软件交互的理想界面。
为什么他们还在使用 CLI?
- 它重量轻,速度快。
- 最少或没有依赖性。
- 最适合系统管理和基于任务的自动化等。
理论讲够了,让我们从需求开始:
- golang 已安装(我使用的是 go1.11.5 windows/amd64)
- 眼镜蛇库(
go get -u github.com/spf13/cobra/cobra
) - 您选择的任何代码编辑器。(我用的是 VS 代码)
我推荐使用 VS 代码,安装微软的
go
扩展。这将根据代码更新导入语句。如果您使用新包,它将在保存时导入该包。如果某个包已导入但未被使用,它会将该包从导入中移除。
Cobra
图书馆简介。我们将使用 cobra cli 来使用 cobra 库。我们将使用命令行界面来构建命令行界面😃
Cobra 既是一个用于创建强大的现代 CLI 应用程序的库,也是一个用于生成应用程序和命令文件的程序。
很多应用最广泛的 Go 项目都是用 Cobra 搭建的,比如: Kubernetes 、 Hugo 、 Docker (distribution) 等。— Github
Cobra 概念
Cobra 建立在命令、参数和标志的结构上。
- 命令代表动作
- Args 是事物
- 标志是那些动作的修饰符
基本结构就像一个简单的句子
APPNAME Command Args --Flags
或APPNAME Command --Flags Args
为 Ex。
git clone URL -bare
go get -u URL
npm install package --save
docker run image -d
在 GOPATH 的之外创建一个新的项目目录**。我已经为本教程创建了**my-calc**
项目目录。你可以说出你的任何选择。**
在 GOPATH 之外创建项目简化了本地文件的导入。初始化项目中的模块。该模块将保留该项目中需要和使用的所有库和依赖项。它类似于 nodejs 中的package.json
。要了解关于这些模块的更多信息,请阅读这篇伟大的文章。
一个模块是一个相关 Go 包的集合,这些包作为一个单元一起被版本化。
模块记录精确的依赖需求,并创建可重复的构建。— [维基](http://A module is a collection of related Go packages that are versioned together as a single unit. Modules record precise dependency requirements and create reproducible builds.)
在您选择的命令行中打开项目目录。我用的是**bash**
。进入项目目录后,运行下面的命令来初始化模块。
**go mod init my-calc**
你可以注意到它在项目中创建了一个**go.mod**
文件。
**注意:**默认情况下,在
***$GOPATH***
内创建模块是禁用的。如果你运行上面的命令—***go: modules disabled inside GOPATH/src by GO111MODULE=auto; see ‘go help modules’***
,你会得到这个错误。如果您仍然想在**$GOPATH**
中创建 go 模块,那么首先将***GO111MODULE***
环境变量设置为***on***
。**export GO111MODULE=on**
现在,无论何时你在项目中使用任何第三方包,它都将把它保存为与他们版本的依赖关系。这样,即使库的新版本中引入了重大更改,您的项目也会按预期工作。
如果你还没有安装**cobra**
库,你可以使用下面的命令来安装。
go get -u github.com/spf13/cobra/cobra
使用cobra init
命令初始化项目的 cli 脚手架。
cobra init --pkg-name my-calc
**编辑:**感谢布兰登卡顿,对 init 命令的更新。
它将用cobra
库初始化my-calc
项目。您可以观察到它在项目中创建了几个文件。
▾ my-calc/
▾ cmd/
root.go
main.go
main.go
是cli
的切入点。在main.go
内部,它正在调用cmd/root.go
的Execute
函数。
// main.go
package main**import "my-calc/cmd"**func main() {
**cmd.Execute()** }
我们来考察一下root.go
。
rootCmd
Root 命令是任何 cli 的基本命令。为了前任。go get URL
— **go**
是这里的根命令,**get**
是**go**
的子命令。在**root.go**
中,用cobra
命令初始化**rootCmd**
结构变量。cli 中的所有其他命令都将是**rootCmd**
和的子命令。
在编辑器中打开root.go
并在rootCmd
中取消注释
**Run: func(cmd *cobra.Command, args []string) {},**
并将fmt.Println("Hello CLI")
粘贴在花括号内。
**Run: func(cmd *cobra.Command, args []string) {fmt.Println("Hello CLI"},**
⚠️不要去掉右花括号后面的逗号。它将抛出语法错误。
打开my-calc
项目内的终端并运行
go install my-calc
该命令将在**$GOPATH/bin**
文件夹中生成项目的二进制或可执行文件。
现在在终端中运行**my-calc**
。因为它保存在 bin 文件夹中,所以您不必为此设置环境变量。
cli 的名称是 rootCmd 。 my-calc 是 rootCmd。
您将看到类似如下的输出。
hello-cli
初始化
每当一个包在 golang 中初始化时,这是第一个被调用的函数。**cobra.OnInitialize(funcDeclarations)**
在命令的初始化中追加用户自定义函数。 每当运行或调用命令时,它将首先执行命令初始化中的所有函数,然后运行 execute 方法。 这种初始化可以用于加载配置文件或者可以用作构造函数。这完全取决于你的用例以及你的创造力。
我想我在这里失去了你。我们用一个例子来理解这个。
在root.go
中,命令是**rootCmd**
。
**cobra.OnInitialize(initConfig)**
在**rootCmd**
的初始化中追加了**initConfig**
函数声明。所以,每当**rootCmd**
的 execute 方法(**RUN: func**
)运行时,**rootCmd**
将首先运行**initConfig**
函数。一旦所有初始化函数的执行结束,它将运行**rootCmd**
的**RUN: func**
执行方法。
为了可视化,在**initConfig**
功能中添加一条打印消息。
func initConfig() { **fmt.Println("inside initConfig")
...**
保存更改。打开my-calc
项目内的终端。
重建二进制**go install my-calc**
。
go install my-calc
运行**my-calc**
。无论何时在 cli 中进行任何更改,都必须重新构建二进制文件。运行**go install my-calc**
以在 cli 命令中反映它。
initConfig
可以看到initConfig
先运行,后Hello CLI
运行。
为了理解 cli 的完整流程,在**main.go**
中的**init**
函数内添加一条消息,在**main**
函数内添加一条消息。
// root.gofunc init() {
**fmt.Println("inside init")** cobra.OnInitialize(initConfig)
...// main.gofunc main() {
**fmt.Println("inside main")**
cmd.Execute()
}
保存更改。重建二进制**go install my-calc**
并运行**my-calc**
。
cli flow
现在,您已经了解了 cli 命令流。
init
函数中最后一个是flags
。
标志就像命令的修饰符。你可以把它们看作是条件动作。我们将在教程的后面了解更多。
有两种类型的旗帜**Persistent Flags**
和**Local Flags**
。
- **持久标志:**该标志可用于分配给它的命令以及该命令的所有子命令。
- **本地标志:**该标志仅适用于其被分配到的命令。
初始化配置
该功能设置主目录中的配置路径,配置文件名为.my-calc
。如果配置文件存在,它将使用该文件。
**viper**
库以 go 应用的配置解决方案而闻名。它读取 JSON、TOML、YAML、HCL、envfile 和 Java 属性配置文件。它不仅仅是读取配置。要了解更多信息,请点击此链接。
通过该功能,我们完成了root.go
检查。它有点长,但是理解我们正在使用的东西是有好处的。
**注意:**现在如果你愿意,你可以从
*root.go*
和*main.go*
中删除所有的打印语句。为了保持代码的整洁,我已经删除了所有的打印语句。
是时候在我们的 cli 中添加一些命令了。我们已经创建了一个命令**my-calc**
作为rootCmd
,它返回 Hello CLI。
添加数字
在项目目录中打开终端并创建一个名为**add**
的命令。添加新命令的 cobra 命令是
**cobra add <commandName>**
**cobra add add****// output**
add created at C:\Work\golang\my-calc
查看cmd
文件夹,里面添加了一个add.go
文件。
打开add.go
。它类似于root.go
。
首先,一个addCmd
结构变量被声明为*cobra.Command
类型。
*cobra.Command
有RUN
,它是一个 func,带***cobra.Command**
的指针和一段字符串**[]string**
。
然后在init
函数中初始化。在init
中,添加到**rootCmd**
T34 中。我们可以理解为**rootCmd**
的子命令或子命令**addCmd**
。
func init() {
**rootCmd.AddCommand(addCmd)**
在终端中,使用**go install my-calc**
命令重建二进制文件,并运行**my-calc add**
。
add called
add
命令工作正常。是时候修改一下加一串数字了。
这些命令只接受一部分字符串作为参数。为了将数字相加,我们首先必须将string
转换成int
,然后返回结果。我们将使用strconv
库将string
转换成int
。
导入strconv
包。
import (
"fmt"
**"strconv"** "github.com/spf13/cobra"
)
在add.go
内部,创建一个新的**addInt**
函数。
// add.go**func addInt(args []string) {** var sum int // iterate over the arguments
// the first return value is index of args, we can omit it using _
for _, ival := range args { // strconv is the library used for type conversion. for string
// to int conversion Atio method is used.
** itemp, err := strconv.Atoi(ival)**
if err != nil {
fmt.Println(err)
}
sum = sum + itemp
} fmt.Printf("Addition of numbers %s is %d", args, sum)
}
保存更改。
在addCmd
变量中,更新RUN
功能。删除打印信息,用args
调用addInt
功能。
// addCmd
Run: func(cmd *cobra.Command, args []string) {
addInt(args)
},
使用go install my-calc
重建二进制文件。
运行my-calc add 2 3
。
⚠️Don’t 忘记了争论之间的空隙。
add int
您可以传递任意多个参数。如果你记得 args 是一段字符串。但是这个功能是有限制的。它只能将整数相加,不能将小数相加。在addInt
函数中,我们将字符串转换成int
而不是float32/64
。
是时候在addCmd
中引入一只flag
了。该标志将帮助 cli 决定是int
操作还是float
操作。
在add.go
中,在init
func 内,创建一个 bool 类型的本地标志**Flags().BoolP**
。其名称为float
,简称f
,默认值false
和描述。默认值非常重要。意味着即使命令中没有调用标志,标志值也会是false
。对于 bool 类型,如果调用一个标志,它将切换默认值。
// add.gofunc init() {
rootCmd.AddCommand(addCmd)
**addCmd.Flags().BoolP("float", "f", false, "Add Floating Numbers")**
}
在add.go
中创建新的addFloat
功能
// add.gofunc addFloat(args []string) { var sum float64 for _, fval := range args {
// convert string to float64
**ftemp, err := strconv.ParseFloat(fval, 64)** if err != nil {
fmt.Println(err)
}
sum = sum + ftemp
} fmt.Printf("Sum of floating numbers %s is %f", args, sum)
}
该功能与addInt
相同,只是将**string**
转换为**float64**
。
在addCmd
RUN
函数中,会根据标志调用addInt
或addFloat
。如果标志--float or -f
通过,那么它将调用addFloat
。
// add.go
// addCmdRun: func(cmd *cobra.Command, args []string) {
**// get the flag value, its default value is false**
**fstatus, _ := cmd.Flags().GetBool("float")** if fstatus { // if status is true, call addFloat
** addFloat(args)**
} else {
**addInt(args)**
}
},
保存所有更改。使用go install my-calc
重建二进制文件。
运行my-calc add 1.2 2.5 -f
或my-calc add 1.2 2.5 --float
add float flag
你可以用旗子做很多事情。您甚至可以将值传递给标志,如一片 int、float、string 等。
操作实现的基本添加完成。
让我们通过向addCmd
添加子命令来扩展它。
添加偶数
在项目目录中打开终端,创建一个新的**even**
命令。
cobra add even
even
命令作为even.go
添加到cmd
文件夹中。
在编辑器中打开even.go
。将init
中的**rootCmd**
改为**addCmd**
。
// even.go
func init() {
**addCmd.AddCommand(evenCmd)** ...
}
addCmd.AddCommand(evenCmd)
将添加evenCmd
作为addCmd
的子命令或子命令。
更新evenCmd
结构变量的RUN
方法。
// even.go
Run: func(cmd *cobra.Command, args []string) { var evenSum int
for _, ival := range args {
** itemp, _ := strconv.Atoi(ival)**
**if itemp%2 == 0 {**
evenSum = evenSum + itemp
}
} fmt.Printf("The even addition of %s is %d", args, evenSum)
},
它将首先使用strconv
包将string
转换为int
,然后只添加偶数。
保存所有更改。使用go install my-calc
重建二进制文件。
运行**my-calc add even 1 2 3 4 5 6**
add even
my-calc
是根命令,add
是rootCmd
的命令,even
是addCmd
的命令(子命令)。
添加奇数
这个和evenCmd
一样。它不加偶数,反而会加奇数。
在项目目录中打开终端并创建一个新的 odd 命令。
cobra add odd
odd
命令作为odd.go
添加到cmd
文件夹中。
在编辑器中打开odd.go
。将init
中的**rootCmd**
改为**addCmd**
。
// odd.go
func init() {
**addCmd.AddCommand(oddCmd)** ...
}
addCmd.AddCommand(oddCmd)
将添加oddCmd
作为addCmd
的子命令或子命令。
更新oddCmd
结构变量的RUN
方法。
// odd.goRun: func(cmd *cobra.Command, args []string) {
var oddSum int
for _, ival := range args {
**itemp, _ := strconv.Atoi(ival)**
** if itemp%2 != 0 {**
oddSum = oddSum + itemp
}
} fmt.Printf("The odd addition of %s is %d", args, oddSum)
},
它将首先使用strconv
包将string
转换为int
,然后只添加偶数。
保存所有更改。使用go install my-calc
重建二进制文件。
运行**my-calc add odd 1 2 3 4 5 6**
my-calc
是根命令,add
是rootCmd
的命令,odd
是addCmd
的命令(子命令)。
恭喜🎉
恭喜你!您使用 cobra 在 golang 中创建了自己的 cli。
完整的代码保存在 Github 上。
cli 项目已经完成。本教程的主要目的是理解 cobra 库的基础知识。本教程涵盖了创建 cli 所需的大多数基本操作。如果需要,我会不断更新更多的基本操作。我希望我平滑了创建 cli 的学习曲线。感谢您花时间阅读教程。我希望你学到了一些东西,这是值得你花时间。
请对本教程提出宝贵的反馈意见。我会做出相应的改变。
如果你喜欢这个教程,你可以在我的博客上阅读我的最新教程。 📑