2020 年再看 10 篇人工智能论文
更新的阅读建议让你了解人工智能和数据科学的最新和经典突破
苏珊·尹在 Unsplash 上的照片
答几周前,我发布了一篇关于人工智能(AI)论文的文章,将在 2020 年阅读。如果算上所有的附加阅读建议,总计 27 篇。然而,这份清单还远未完成。许多宝石被遗漏或只是简单提及。在这篇文章中,为了你的阅读乐趣,我列出了今年人工智能论文的十个建议(和其他几个进一步阅读的建议)。
在这个列表中,我主要关注那些没有提出新架构的文章。这不会是最近的 YOLO 或 ResNet 变种。相反,它强调了损失公式、理论突破、更新的优化器等方面的最新进展。
至于上一个列表,我将重点介绍计算机视觉和 NLP,因为这些是我最熟悉的话题,并从一两个经典开始。对于每一篇论文,我都会给出其主要贡献的摘要和阅读它的理由列表。最后,我在每篇文章的结尾都给出了关于该主题的具体阅读建议,并将其与其他最新进展或类似观点联系起来。
我们继续:)
排名第一的手套(2014)
彭宁顿、杰弗里、理查德·索彻和克里斯托弗·d·曼宁。“手套:单词表示的全局向量。”2014 自然语言处理经验方法会议论文集。2014.
虽然今天的社区非常关注神经网络,但许多早期结果是通过简单得多的数学获得的。从经典算法开始,GloVe 是一个基于降低单词共现矩阵维度的单词嵌入模型。与以前的方法不同,GloVe 使用隐式公式,这使得它可以扩展到大规模文本语料库。
原因#1: 如果你从自然语言处理(NLP)开始,这是一个很好的阅读材料,可以了解单词嵌入的基础知识以及它们为什么重要。
理由#2: 曾几何时,并不是一切都以变形金刚为原型。阅读早期的作品是找到那个“被遗忘的想法”的一个极好的方式,这个想法可以将艺术水平推得更远。
原因 3: 本文中提到/提出的许多概念后来被许多其他作者扩展。今天,单词嵌入是自然语言处理(NLP)文献中的主要内容。
**延伸阅读:**大约在同一时期,Google 发布了 Word2Vec ,另一个众所周知的生成语义向量的模型。不久之后,这些想法被生物学界采用,作为表示大蛋白质和基因序列的方法。今天, BERT 是单词表示和语义理解的主导人物。
#2 AdaBoost (1997 年)
弗氏,Yoav 罗伯特·沙皮雷(1997 年)。在线学习的决策理论概括和 boosting 的应用。
经典的机器学习模型一点也不灵活。大多数配方都有惊人的局限性,这使得它们无法扩展到越来越复杂的任务。这个问题的第一个解决方案是将最佳可用模型集成到民主投票中。1997 年, Freund 和 Schapire 提出了 AdaBoost 算法,这是一种元启发式学习器,能够将许多“弱”模型转化为“强”分类器。
简而言之,该算法基于迭代训练更多的分类器,并将每个训练样本重新加权为“容易”或“困难”随着训练的进行,集合通过更多地关注更难分类的样本而进化。该算法非常有效,以至于即使是复杂的问题也容易过度拟合。
理由#1: 可以认为神经网络是弱分类器(神经元/层)的集合。然而,神经网络文献已经独立于整体而发展。读一篇关于这个主题的论文可能会对神经网络为什么工作得这么好有一些见解。
原因#2: 许多新手认为经典的机器学习方法过时且“薄弱”,在几乎所有事情上都倾向于使用神经网络。AdaBoost 是经典机器学习一点也不弱的一个很好的例子。而且,与网络不同,这些模型是高度可解释的。
原因#3: 有多少论文是从一个赌徒因与朋友赌马屡输而沮丧的故事开始的?我希望我敢开一份那样的报纸。
**延伸阅读:**其他流行的集成方法是随机森林分类器、梯度增强技术,以及广受好评的 XGBoost 包,这些方法因在几个机器学习比赛中获胜而闻名,同时相对容易使用和调整。该家族的最新成员是微软的 LightGBM ,面向大规模分布式数据集。
第三大胶囊网络(2017 年)
萨布尔、萨拉、尼古拉斯·弗洛斯特和杰弗里·e·辛顿。“胶囊间动态路由” 神经信息处理系统的进展。2017.
神经网络文献从感知器模型开始,并到达卷积神经网络(CNN)。下一次大跃进是一个备受争议的话题。Sara Sabour、Nicholas Frosst 和图灵奖获得者 Geoffrey Hinton 提出的胶囊网络就是其中之一。
理解胶囊网络的一个简单方法是用“胶囊”代替“物体探测器”每一层“对象检测器”试图识别图像中的相关特征,以及其姿态(方向、比例、倾斜等)。).通过堆叠检测器,可以得到物体的鲁棒表示。本质上,胶囊并不像 CNN 那样将本地信息聚集到高级特征上。取而代之的是,他们检测对象的各个部分,并对它们进行分层组合,以识别更大的结构和关系。
理由 1: 作为科学家,我们都应该寻找下一件大事。虽然我们不能说胶囊网络将成为下一个摇滚明星,但我们可以说他们试图解决的问题是相关的。并且,至于所有相关的问题,最终都会有人来回答。
理由#2: 这篇论文提醒我们,CNN 并不完美。它们对于旋转和缩放不是不变的。虽然我们使用数据增强来缓解这种情况,但没有创可贴曾经治愈过一个人。
原因#3: 在深度学习成为主流之前,许多对象检测方法依赖于识别容易发现的对象部分,并针对数据库/本体进行模式匹配。Hinton 和他的团队正在做的是使这种早期的方法现代化。这就是为什么我们都应该时不时地阅读经典著作。很多东西都可以更新。
**延伸阅读:**在过去的一年中,有一件事得到了很多关注,那就是注意力机制。虽然它没有试图取代或增加卷积,但它确实为全局推理提供了一个途径,这是现代网络的许多阿基里斯脚跟之一。
#4 关系归纳偏差(2018)
彼得·w·巴塔格利亚等人“关系归纳偏差、深度学习和图形网络” arXiv 预印本 arXiv:1806.01261 (2018)。
部分立场文件,部分评论,部分统一,这篇文章总结了 Deep Mind 团队认为深度学习中的下一件大事:图形神经网络(GNNs)。用作者自己的话说:
(…).我们认为,组合概括必须是人工智能实现类似人类能力的首要任务,而结构化表示和计算是实现这一目标的关键。正如生物学合作利用先天和后天,我们拒绝在“手工工程”和“端到端”学习之间的错误选择,而是提倡一种从它们的互补优势中受益的方法。我们探索了在深度学习架构中使用关系归纳偏差如何促进对实体、关系和组成它们的规则的学习。(…)
**旁注:**归纳偏差是学习算法对数据做出的所有假设。例如,线性模型假设数据是线性的。如果一个模型假设数据有特定的关系,它就有一个关系归纳偏差。因此,图形是一种有用的表示。
原因#1: 当前的 CNN 模型是“端到端”的,这意味着它们处理原始的、大部分未经处理的数据。特征不是由人类“设计”的,而是由算法自动“学习”的。我们大多数人被告知特征学习更好。在本文中,作者提供了相反的观点。
原因 2: 大多数早期的人工智能文献都与计算推理有关。然而,计算直觉占了上风。神经网络不考虑输入;它们会产生相当准确的数学“预感”图表可能是通向直觉推理的桥梁。
原因 3: 组合问题可以说是计算机科学中最关键的问题。大多数都处于我们认为易处理或可能的边缘(或更远)。然而,我们人类自然地、毫不费力地进行推理。图形神经网络可能是答案吗?
延伸阅读: GNNs 是一个令人兴奋且不断发展的领域。从图论中,我们知道几乎任何东西都可以被建模为图。谢尔盖·伊万诺夫在 GNNs 中列出了一份极好的新趋势列表,其中引用了大量来自即将到来的 2020 年 ICLR 会议的论文。
2020 年刚刚开始,但我们已经可以在最新的研究中看到图形机器学习(GML)的趋势…
towardsdatascience.com](/top-trends-of-graph-machine-learning-in-2020-1194175351a3)
#5 训练批次标准和唯一批次标准(2020 年)
Frankle,Jonathan,David J. Schwab,Ari S. Morcos .“训练批处理范式和唯一批处理范式:论细胞神经网络中随机特征的表达能力” arXiv 预印本 arXiv:2003.00152 (2020)。
您相信仅 ResNet-151 的批量标准化层就能在 CIFAR-10 上实现+60%的准确率吗?换句话说,如果你锁定所有其他层的随机初始权重,并训练网络五十个左右的时期,它将比随机的表现更好。我不得不复制这张纸来亲眼看看。“魔力”来自于经常被遗忘的批处理规范的γ和β参数:
批处理规范化操作的完整定义。γ和β是两个可学习的参数,允许图层在标准化发生后缩放和移动每个激活图。
理由 1: 这是一个足够疯狂的想法,值得一读。打破常规的想法总是受欢迎的。
原因#2: 你可能会问自己批处理规范层怎么能学到任何东西,你可能还会想为什么有人会关心这个。对于数据科学中的很多东西,我们认为批量范数是理所当然的。我们相信这只会加速训练。显然,它可以做得更多。
原因#3: 本文可能会引起您的兴趣,让您了解所有常见图层都具有哪些参数和超参数。
**延伸阅读:**大部分课程讲授的是批量范数层对抗所谓的内部协方差移位问题。最近的证据显示情况并非如此。相反,作者认为 BN 层使整体损失情况更加平稳。另一个别出心裁的想法是彩票假说,也是由 Frankle 等人提出的。
#6 光谱标准(2018)
宫藤,Takeru,等人“生成性对抗网络的谱规范化” arXiv 预印本 arXiv:1802.05957 (2018)。
在 GAN 文献中, Wasserstein 损失改进了训练 GAN 的几个关键挑战。然而,它要求梯度必须具有小于或等于 1 的范数( 1-Lipschitz )。损失的原始作者建议将权重修剪为[-0.01,0.01],作为一种加强小梯度的方式。用计算机科学术语来说,就是黑客。作为回应,谱范数被提出作为一个平滑的替代方案来约束权重矩阵,以产生最多一个单位梯度。更清洁的解决方案。
原因 1: 标准化是一个比大多数人意识到的要大得多的话题。许多特殊属性可以通过专门的规范化和精心的激活功能设计来实现。
原因#2: 这除了是一种规范,也是一种正则化,是神经网络设计中经常被忽略的话题。除了辍学之外,阅读关于这个问题的成功论文令人耳目一新。
**延伸阅读:**归一化技术的其他最新进展是组归一化和自适应实例归一化技术。前者解决了小批量批量标准的一些缺点,而后者是任意风格转换的关键突破之一。
#7 感知损失(2016 年)
约翰逊、贾斯汀、亚历山大·阿拉希和李菲菲。“实时风格转换和超分辨率的感知损失”T2【欧洲计算机视觉会议】T3。施普林格,查姆,2016。
大多数神经网络背后的驱动力是损失函数。亏损越能描述什么是好什么是坏,我们就能越快地收敛到有用的模型。在文献中,大多数损失相对简单,只能测量低水平的属性。除此之外,捕捉高级语义是众所周知的棘手。
感知损失论文认为,预先训练的网络可以用来测量语义相似度,而不是手工设计复杂的损失函数。在实践中,生成的和地面真实的结果通过预先训练的 VGG 网络,并比较特定层的激活。相似的图像应该有相似的激活。早期图层捕捉广泛的特征,而后期图层捕捉更细微的细节。
原因#1: 亏损是培养优秀模特最重要的方面之一。没有适当的反馈信号,任何优化过程都不会收敛。这就是好老师的作用:给予反馈。
原因#2: 成功的新亏损往往是一个里程碑。甘斯所达到的质量,是在感知丧失被发明出来之后的飞跃。理解这部作品对于理解后来的大部分文献是必不可少的。
原因#3: 这些神经损失既神秘又有用。虽然作者对这些模型的工作提供了合理的解释,但它们的许多方面仍然是开放的,就像神经网络中的大多数事情一样。
**延伸阅读:**神经网络的一个迷人之处是它们的可组合性。这项工作使用神经网络来解决神经网络问题。拓扑损失论文将这一思想扩展到图像分割问题。神经架构搜索(NAS) 文献利用神经网络寻找新的神经网络。至于计算机视觉的其他损失,这里有一个综合指南。感谢Sowmya yelapragada将这个伟大的名单放在一起:)
选择正确的损失函数可以优化模型的收敛性,也有助于集中在正确的特征集上
medium.com](https://medium.com/ml-cheat-sheet/winning-at-loss-functions-2-important-loss-functions-in-computer-vision-b2b9d293e15a)
第八名那达慕(2016)
多扎特蒂莫西。“把内斯特洛夫的动力融入亚当” (2016)。
我们大多数人都熟悉 SGD、Adam 和 RMSprop 等术语。有些人还知道一些不太熟悉的名字,如阿达格拉德、阿达德尔塔和阿达马克斯。然而,很少有人花时间去理解这些名字的含义,以及为什么亚当是现今的默认选择。Tensorflow 捆绑了 Nadam,这是对 adam 的改进,但大多数用户都没有意识到这一点。
理由#1: 这份技术报告对大多数神经网络优化器提供了全面而直接的解释。每一种都是对其他产品的直接改进。很少有论文能在两页半的篇幅内涵盖如此数学化的主题。
原因 2: 我们都认为优化者理所当然。理解它们的基本原理对改进神经网络非常有用。这就是为什么当 RMSprop 不收敛时,我们用 Adam 代替它,后来又用 SGD 代替它。
**延伸阅读:**自 2016 年以来,已经提出了许多其他对优化器的改进。有些会在某个时候并入主流图书馆。看看拉达姆、前瞻和游侠的一些新想法。
#9 双重下降假说(2019)
Nakkiran,Preetum,et al. 《深度双重下降:更大的模型和更多的数据带来的伤害》 arXiv 预印本 arXiv:1912.02292 (2019)。
传统观点认为,小型号不足,大型号过多。然而,在彩虹之上的某处,更大的模型仍然闪耀着光芒。
在这篇论文中,Nakkiran 等人展示了几个模型在规模增长时表现出“双重下降”现象的证据。测试精度下降,然后上升,再下降。此外,他们认为拐点在“插值阈值”:模型大到足以插值数据的点。换句话说,当一个模型被训练得超越了该领域所建议的一切,它就开始改进了。
理由 1: 大多数课程都教授偏差/方差权衡。显然,这个原则只在一定程度上适用——是时候复习基础知识了。
原因#2: 如果增加历元数也穿过插值点,我们都应该放弃早期停止,看看会发生什么。集体来说,我们都可以做科学。
原因 3: 这一点和第五点都很好地提醒了我们还有很多我们不知道的。不是所有我们学到的都是对的,也不是所有直觉的都是正确的。
**延伸阅读:**更轻松的阅读是图像分类锦囊论文。在这本书里,你会找到几个简单可行的建议来从你的模型中提取额外的性能下降。
智力指标排名第十(2019)
弗朗索瓦,乔莱。《论智力的衡量》 arXiv 预印本 arXiv:1911.01547 (2019)。
大多数人都在努力多走一步,弗朗索瓦·乔莱(Franç ois Chollet)正在努力实现梦想。
在这个列表中,所有提到的文章都将实践和理论的最新水平推进了一步。有些已经被广泛采用,有些支持这种或那种技术,还有一些为融合提供了很好的改进。然而,房间里的大象智力仍然是一个神秘而难以捉摸的话题,更不用说神秘莫测了。
时至今日,人工智能领域向一般智能的进步只能用“成就”来粗略衡量。每隔一段时间,一种算法就会在一项复杂的任务中击败人类,比如国际象棋、Dota 2 或围棋。每当这种情况发生时,我们就说我们离目标更近了一步😃。然而,这不足以衡量智力的技能获取效率组成部分。
在这篇(很长的)文章中,Chollet 认为:“为了朝着更智能、更像人类的人工系统稳步前进,我们需要遵循适当的反馈信号。”换句话说,我们需要一个合适的机器智能基准。一种智商测试。由此,作者提出了抽象与推理语料库(ARC) :
“ARC 可以看做通用的人工智能基准,可以看做程序合成基准,也可以看做心理测量智能测试。它的目标是人类和人工智能系统,旨在模仿类似人类的一般流体智能形式。”
理由 1: 虽然数据科学很酷很时髦,但人工智能才是真正的东西。如果没有人工智能,就不会有数据科学。它的最终目标不是在数据中寻找洞察力,而是建造能够拥有自己想法的机器。花点时间思考一些基本问题:什么是智力,我们如何衡量它?这篇论文是一个良好的开端。
原因#2: 在过去的几十年里,IA 社区被来自数理逻辑和演绎推理的思想所主导。然而,在没有任何形式的显式推理的情况下,支持向量机和神经网络在该领域的发展远远超过了基于逻辑的方法。ARC 会引发经典技术的复兴吗?
原因 3: 如果 Chollet 是对的,我们距离创建能够解决 ARC 数据集的算法还有好几年的时间。如果你正在寻找一个数据集在你的业余时间玩,这里有一个会让你忙起来:)
**延伸阅读:**2018 年,Geoffrey Hinton、Yosha Bengio 和 Yan LeCun 因其在深度学习基础方面的开创性工作获得了图灵奖。今年,在 AAAI 会议上,他们分享了他们对人工智能未来的看法。可以在 Youtube 上看:
我想引用杰弗里·辛顿的一句话来结束这篇文章,我认为这句话概括了一切:
“未来取决于某个对我所说的一切深感怀疑的研究生。”
GloVe 通过隐式的方式抑制了共现矩阵。AdaBoost 制造了数百个最先进的弱分类器。胶囊网络挑战 CNN,而图形神经网络可能会取代它们。关键进步可能来自规范化、损失和优化器,而我们仍然有空间质疑批量规范和训练过度参数化的模型
我想知道还有多少关于辍学和重新学习的事情有待发现。
我希望这本书对你和我来说都是令人兴奋的。请让我知道你认为符合这个列表的其他文件。我将很高兴阅读和考虑他们的未来列表😃
编辑:写完这个单子,我用十篇 GAN 论文编了三分之一,2020 年读。如果你喜欢阅读这份(以及之前的)清单,你可能会喜欢阅读第三份:
生成性对抗网络的阅读建议。
towardsdatascience.com](/gan-papers-to-read-in-2020-2c708af5c0a4)
欢迎评论或联系我。如果你刚接触媒体,我强烈推荐订阅。对于数据和 IT 专业人士来说,中型文章是 StackOverflow 的完美搭档,对于新手来说更是如此。注册时请考虑使用我的会员链接。你也可以直接支持我请我喝杯咖啡😃
感谢阅读:)
R 中你可能不知道的另外十个随机有用的东西
上次我努力保持在 10 分钟,所以再给你 10 分钟
我很惊讶于人们对我去年的文章的积极反应,这篇文章列举了十件人们可能不知道的事情。
我有一种感觉,R 作为一种语言已经发展到如此程度,以至于我们中的许多人现在都在以完全不同的方式使用它。这意味着可能有许多我们每个人都在使用的技巧、包、函数等等,但是其他人完全不知道,如果他们知道了,就会发现它们很有用。
老实说,我上次努力把它保持在 10 个,所以这里有 10 个关于 R 的东西,帮助我的工作更容易,你可能会发现有用。如果这些对你目前的工作有帮助,或者如果你对其他人应该知道的事情有进一步的建议,请在这里或 Twitter 上留言。
1.dbplyr
dbplyr
顾名思义。它允许您对数据库使用dplyr
。如果你从事数据库工作,并且你从未听说过dbplyr
,那么你很可能仍然在你的代码中使用 SQL 字符串,这迫使你在你真正想要思考整洁的时候去思考 SQL,并且当你想要抽象你的代码来生成函数之类的时候,这可能是一个真正的痛苦。
dbplyr
允许您使用dplyr
创建您的 SQL 查询。它通过建立一个可以使用dplyr
函数操作的数据库表,将这些函数转换成 SQL 来实现。例如,如果您有一个名为con
的数据库连接,并且您想要在CAT_SCHEMA
中操作一个名为CAT_DATA
的表,那么您可以将这个表设置为:
cat_table <- dplyr::tbl(
con,
dbplyr::in_schema("CAT_SCHEMA", "CAT_TABLE")
)
然后,您可以在cat_table
上执行常见的操作,如filter
、mutate
、group_by
、summarise
等,所有这些都将在后台翻译成 SQL 查询。非常有用的是,直到您使用dplyr::collect()
函数最终获取数据时,数据才真正下载到 R 会话中。这意味着您可以让 SQL 做所有的工作,并在最后收集您操作的数据,而不是一开始就必须拉整个数据库。
关于dbplyr
的更多信息,你可以查看我以前的文章这里和教程这里。
2.rvest 和 xml2
人们说 Python 更适合网络抓取。那可能是真的。但是对于我们这些喜欢在 tidyverse 中工作的人来说,rvest
和xml2
包可以通过使用magrittr
并允许我们使用管道命令来使简单的网页抓取变得非常容易。鉴于网页上的 HTML 和 XML 代码通常是大量嵌套的,我认为使用%>%
构建抓取代码是非常直观的。
通过最初读取感兴趣的页面的 HTML 代码,这些包将嵌套的 HTML 和 XML 节点分成列表,您可以逐步搜索和挖掘感兴趣的特定节点或属性。将它与 Chrome 的 inspect 功能结合使用,可以让你快速从网页中提取你需要的关键信息。
举个简单的例子,我最近编写了一个函数,可以从这个相当时髦的页面中抓取历史上任何时间点的基本 Billboard 音乐图表作为数据帧,代码如下:
get_chart <- function(date = Sys.Date(), positions = c(1:10), type = "hot-100") { # get url from input and read html
input <- paste0("https://www.billboard.com/charts/", type, "/", date) chart_page <- xml2::read_html(input) # scrape data
chart <- chart_page %>%
rvest::html_nodes('body') %>%
xml2::xml_find_all("//div[contains(@class, 'chart-list-item ')]") rank <- chart %>%
xml2::xml_attr('data-rank') artist <- chart %>%
xml2::xml_attr('data-artist') title <- chart %>%
xml2::xml_attr('data-title') # create dataframe, remove nas and return result
chart_df <- data.frame(rank, artist, title)
chart_df <- chart_df %>%
dplyr::filter(!is.na(rank), rank %in% positions) chart_df
}
更多关于这个例子这里,更多关于rvest
这里,更多关于xml2
这里。
3.长数据上的 k-均值
k-means 是一种越来越受欢迎的统计方法,用于对数据中的观察值进行聚类,通常是为了将大量的数据点简化为少量的聚类或原型。kml
包现在允许对纵向数据进行 k 均值聚类,其中的“数据点”实际上是数据序列。
这在你研究的数据点实际上是一段时间的读数时非常有用。这可能是对医院病人体重增加或减少的临床观察,或者是雇员的补偿轨迹。
kml
首先通过使用cld
函数将数据转换成ClusterLongData
类的对象。然后,它使用“爬山”算法对数据进行划分,对几个k
值分别测试 20 次。最后,choice()
函数允许您以图形方式查看每个k
的算法结果,并决定您认为的最佳聚类。
4.RStudio 中的连接窗口
最新版本的 RStudio 中的 connections 窗口允许您浏览任何远程数据库,而不必进入像 SQL developer 这样的独立环境。这种便利现在提供了完全在 RStudio IDE 中完成开发项目的机会。
通过在“连接”窗口中设置与远程数据库的连接,您可以浏览嵌套模式、表、数据类型,甚至可以直接查看表以了解数据的摘录。
RStudio 最新版本中的“连接”窗口
更多关于连接窗口的信息,请点击。
5.tidyr::完成()
R 数据帧中的默认行为是,如果某个特定观察没有数据,则该观察的行不会出现在数据帧中。当您需要将该数据帧用作某个东西的输入时,这可能会导致问题,该数据帧期望看到所有可能的观察值。
通常情况下,当您将数据发送到某个图形函数中,而该函数预期在没有观察值的情况下会看到零值,并且无法理解丢失的行意味着该行中的零值时,就会出现这种问题。当您进行未来预测并且起点缺少行时,这也可能是一个问题。
tidyr
中的complete()
函数允许您填补所有没有数据的观察值的空白。它允许您定义想要完成的观察,然后声明使用什么值来填补缺口。例如,如果您正在对不同品种的公狗和母狗进行计数,并且您有一些组合在样本中没有狗,您可以使用下面的方法来处理它:
dogdata %>%
tidyr::complete(SEX, BREED, fill = list(COUNT = 0))
这将扩展您的数据框架以确保包含所有可能的SEX
和BREED
的组合,并且它将用零填充COUNT
的缺失值。
6.gganimate
目前动画图形非常流行,gganimate
包允许那些使用ggplot2
的人(我会说是大多数 R 用户)非常简单地扩展他们的代码来创建动画图形。
gganimate
的工作原理是获取存在于一系列“过渡状态”的数据,通常是几年或其他类型的时间序列数据。您可以绘制每个转换状态中的数据,就好像它是一个简单的静态ggplot2
图表,然后使用ease_aes()
函数创建一个在转换状态之间移动的动画。对于如何过渡有许多选项,并且animate()
功能允许图形以多种形式呈现,例如动画 gif 或 mpeg。
这里有一个例子,我使用一个管道命令生成了汉斯·罗斯林著名的气泡图:
代码见这里和一个关于gganimate
的很好的一步一步的教程,我发现真的很有帮助见这里。
7.网络 3
D3 是一个非常强大的 javascript 数据可视化库。越来越多的软件包开始变得可用,允许 R 用户在 D3 中构建 viz,例如R2D3
,这很棒,尤其是因为它允许我们欣赏有史以来最好的十六进制贴纸之一(见这里)。
我最喜欢的 R 的 D3 包是networkD3
。它已经存在了一段时间,非常适合以一种反应灵敏或美观的方式绘制图形或网络数据。特别是,它可以使用forceNetwork()
绘制力定向网络,使用sankeyNetwork()
绘制桑基图,使用chordNetwork()
绘制弦图。这是我创建的一个简单的 sankey 网络的例子,它显示了英国退出欧盟公投中各地区的投票流量。
英国退出欧盟全民投票中使用网络的投票流量 3
8.使用 DT 的 RMarkdown 或 Shiny 中的数据表
DT
包是从 R 到 DataTables javascript 库的接口。这允许在一个闪亮的应用程序或 R Markdown 文档中非常容易地显示表格,这些表格具有许多内置功能和响应能力。这使您不必编写单独的数据下载函数,为用户提供了数据显示和排序的灵活性,并具有内置的数据搜索功能。
例如,一个简单的命令,如:
DT::datatable(
head(iris),
caption = 'Table 1: This is a simple caption for the table.'
)
能做出像这样好的东西:
更多关于 DT 这里,包括如何设置各种选项自定义布局和添加数据下载、复制和打印按钮。
9.用 prettydoc 给你的博客拉皮条
prettydoc
是 Qiu 开发的一个软件包,它提供了一组简单的主题来为您的 RMarkdown 文档创建一个不同的、更漂亮的外观。当你只是想让你的文档变得更有趣一些,但是没有时间自己设计它们的样式时,这是非常有用的。
真的很好用。对文档的 YAML 标题进行简单的编辑,就可以在整个文档中调用一个特定的样式主题,有许多主题可用。例如,这将在标题、表格、嵌入式代码和图形中调用可爱的干净蓝色和样式:
---
title: "My doc"
author: "Me"
date: June 3, 2019
output:
prettydoc::html_pretty:
theme: architect
highlight: github
---
更多关于prettydoc
这里。
10.可以选择用 code_folding 隐藏 RMarkdown 中的代码
RMarkdown 是记录您的工作的一种很好的方式,它允许您编写一个叙述性的内容,并在一个地方捕获您的代码。但是有时你的代码可能会让人不知所措,对于那些试图阅读你的工作的叙述,而对你如何进行分析的复杂性不感兴趣的非编码人员来说不是特别令人愉快。
以前,我们仅有的选择是在我们的knitr
选项中设置echo = TRUE
或echo = FALSE
,要么在文档中显示我们的代码,要么不显示。但是现在我们可以在 YAML 头球中设置一个选项,给我们两个世界最好的东西。默认情况下,在 YAML 标题中设置code_folding: hide
将隐藏代码块,但是在文档中提供了小的下拉框,以便读者可以随时查看所有代码或特定的代码块,就像这样:
R Markdown 中的代码折叠下拉
这就是我接下来的十个随机 R 技巧。我希望这些能有所帮助,请随意在评论中添加你自己的建议,让其他用户阅读。
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在 LinkedIn 或Twitter上找我。
用 StaticFrame 代替熊猫的十大理由
在处理数据帧时,创建更易维护、更不易出错的 Python
作者图片
如果你用 Python 处理数据,你可能会用熊猫。Pandas 提供了近乎即时的满足:复杂的数据处理例程可以用几行代码实现。然而,如果你在大型项目中使用熊猫多年,你可能会遇到一些挑战。复杂的 Pandas 应用程序会产生难以维护且容易出错的 Python 代码。发生这种情况是因为 Pandas 提供了许多方法来做同样的事情,具有不一致的接口,并且广泛支持就地突变。对于那些来自熊猫的人来说,StaticFrame 提供了一个更加一致的界面,减少了出错的机会。这篇文章展示了使用 StaticFrame 代替 Pandas 的十个理由。
为什么是静态框架
在使用 Pandas 开发后端财务系统多年后,我清楚地认识到 Pandas 并不是这项工作的合适工具。Pandas 对标记数据和缺失值的处理,性能接近 NumPy,确实提高了我的生产率。然而,熊猫 API 中的大量不一致导致代码难以维护。此外,熊猫对原位突变的支持导致了严重的出错机会。因此,在 2017 年 5 月,我开始实施一个更适合关键生产系统的库。
现在,经过多年的发展和完善,我们看到了用 StaticFrame 代替 Pandas 在我们的生产系统中取得的优异成绩。用 StaticFrame 编写的库和应用程序更容易维护和测试。我们经常看到 StaticFrame 在大规模、真实世界的用例中表现优于 panda,尽管对于许多独立的操作,StaticFrame 还没有 panda 快。
以下是支持使用 StaticFrame 而不是 Pandas 的十个理由。作为 StaticFrame 的第一作者,我当然对这个演示有偏见。然而,从 2013 年开始与熊猫一起工作,我希望有一些观点可以分享。
所有示例都使用 Pandas 1.0.3 和 StaticFrame 0.6.20。导入使用以下惯例:
>>> import pandas as pd
>>> import static_frame as sf
№ 1:一致且可发现的界面
应用程序编程接口(API)可以在函数的位置、函数的命名方式以及这些函数接受的参数的名称和类型方面保持一致。StaticFrame 偏离了 Pandas 的 API,以在所有这些领域支持更大的一致性。
要创建一个sf.Series
或sf.Frame
,你需要构造函数。Pandas 将其pd.DataFrame
构造函数放在两个地方:根名称空间(pd
,通常是导入的)和pd.DataFrame
类。
例如,JSON 数据是从pd
名称空间上的函数加载的,而记录数据(Python 序列的一个 iterable)是从pd.DataFrame
类加载的。
>>> pd.read_json('[{"name":"muon", "mass":0.106},{"name":"tau", "mass":1.777}]')
name mass
0 muon 0.106
1 tau 1.777>>> pd.DataFrame.from_records([{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}])
name mass
0 muon 0.106
1 tau 1.777
尽管 Pandas 有专门的构造函数,默认的pd.DataFrame
构造函数接受多种多样的输入,包括许多与pd.DataFrame.from_records()
相同的输入。
>>> pd.DataFrame([{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}])
name mass
0 muon 0.106
1 tau 1.777
对于用户来说,这种多样性和冗余性没有什么好处。StaticFrame 将所有的构造函数放在它们构造的类上,并尽可能地集中它们的功能。因为显式的、专用的构造函数更容易维护,所以它们在 StaticFrame 中很常见。比如sf.Frame.from_json()
和sf.Frame.from_dict_records()
:
>>> sf.Frame.from_json('[{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}]')
<Frame>
<Index> name mass <<U4>
<Index>
0 muon 0.106
1 tau 1.777
<int64> <<U4> <float64>>>> sf.Frame.from_dict_records([{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}])
<Frame>
<Index> name mass <<U4>
<Index>
0 muon 0.106
1 tau 1.777
<int64> <<U4> <float64>
显式导致大量的构造函数。为了帮助您找到您正在寻找的东西,StaticFrame 容器公开了一个interface
属性,该属性将调用类或实例的整个公共接口作为一个sf.Frame
提供。我们可以通过使用一个sf.Frame.loc[]
选择来过滤这个表,只显示构造函数。
>>> sf.Frame.interface.loc[sf.Frame.interface['group'] == 'Constructor', 'doc']
<Series: doc>
<Index: signature>
__init__(data, *, index, columns,... Initializer. Args...
from_arrow(value, *, index_depth,... Realize a Frame f...
from_clipboard(*, delimiter, inde... Create a Frame fr...
from_concat(frames, *, axis, unio... Concatenate multi...
from_concat_items(items, *, axis,... Produce a Frame w...
from_csv(fp, *, index_depth, inde... Specialized versi...
from_delimited(fp, *, delimiter, ... Create a Frame fr...
from_dict(mapping, *, index, fill... Create a Frame fr...
from_dict_records(records, *, ind... Frame constructor...
from_dict_records_items(items, *,... Frame constructor...
from_element(element, *, index, c... Create a Frame fr...
from_element_iloc_items(items, *,... Given an iterable...
from_element_loc_items(items, *, ... This function is ...
from_elements(elements, *, index,... Create a Frame fr...
from_hdf5(fp, *, label, index_dep... Load Frame from t...
from_items(pairs, *, index, fill_... Frame constructor...
from_json(json_data, *, dtypes, n... Frame constructor...
from_json_url(url, *, dtypes, nam... Frame constructor...
from_overlay(containers, *, union...
from_pandas(value, *, index_const... Given a Pandas Da...
from_parquet(fp, *, index_depth, ... Realize a Frame f...
from_records(records, *, index, c... Construct a Frame...
from_records_items(items, *, colu... Frame constructor...
from_series(series, *, name, colu... Frame constructor...
from_sql(query, *, connection, in... Frame constructor...
from_sqlite(fp, *, label, index_d... Load Frame from t...
from_structured_array(array, *, i... Convert a NumPy s...
from_tsv(fp, *, index_depth, inde... Specialized versi...
from_xlsx(fp, *, label, index_dep... Load Frame from t...
<<U94> <<U83>
№ 2:一致且丰富多彩的显示屏
熊猫以不同的方式展示它的容器。例如,pd.Series
显示了它的名称和类型,而pd.DataFrame
没有显示这两个属性。如果你显示一个pd.Index
或pd.MultiIndex
,你会得到第三种方法:一个适合eval()
的字符串,当它很大时是不可理解的。
>>> df = pd.DataFrame.from_records([{'symbol':'c', 'mass':1.3}, {'symbol':'s', 'mass':0.1}], index=('charm', 'strange'))>>> df
symbol mass
charm c 1.3
strange s 0.1>>> df['mass']
charm 1.3
strange 0.1
Name: mass, dtype: float64>>> df.index
Index(['charm', 'strange'], dtype='object')
StaticFrame 为所有容器提供了一致的、可配置的显示。sf.Series
、sf.Frame
、sf.Index
和sf.IndexHierarchy
的显示都共享一个公共的实现和设计。这种设计的一个优先考虑的问题是总是显式的容器类和底层数组类型。
>>> f = sf.Frame.from_dict_records_items((('charm', {'symbol':'c', 'mass':1.3}), ('strange', {'symbol':'s', 'mass':0.1})))>>> f
<Frame>
<Index> symbol mass <<U6>
<Index>
charm c 1.3
strange s 0.1
<<U7> <<U1> <float64>>>> f['mass']
<Series: mass>
<Index>
charm 1.3
strange 0.1
<<U7> <float64>>>> f.columns
<Index>
symbol
mass
<<U6>
由于大量的时间花费在可视化地探索这些容器的内容上,StaticFrame 提供了许多显示配置选项,所有这些都通过sf.DisplayConfig
类公开。对于持久的变更,sf.DisplayConfig
实例可以传递给sf.DisplayActive.set()
;对于一次性的更改,sf.DisplayConfig
实例可以传递给容器的display()
方法。
虽然pd.set_option()
可以类似地用于设置熊猫显示特征,但 StaticFrame 提供了更广泛的选项来使类型可被发现。如这个终端动画所示,特定类型可以被着色或者类型注释可以被完全移除。
№ 3:不可变数据:无需防御性副本的高效内存管理
Pandas 在数据输入和从容器中公开的数据的所有权方面表现出不一致的行为。在某些情况下,有可能在熊猫的“背后”变异 NumPy 阵列,暴露出不良副作用和编码错误的机会。
例如,如果我们向一个pd.DataFrame
提供一个 2D 数组,数组的原始引用可以用来“远程”改变pd.DataFrame
中的值。在这种情况下,pd.DataFrame
不保护对其数据的访问,只作为一个共享的可变数组的包装器。
>>> a1 = np.array([[0.106, -1], [1.777, -1]])>>> df = pd.DataFrame(a1, index=('muon', 'tau'), columns=('mass', 'charge'))>>> df
mass charge
muon 0.106 -1.0
tau 1.777 -1.0>>> a1[0, 0] = np.nan *# Mutating the original array.*>>> df *# Mutation reflected in the DataFrame created from that array.*
mass charge
muon NaN -1.0
tau 1.777 -1.0
类似地,有时从pd.Series
或pd.DataFrame
的values
属性中暴露出来的 NumPy 数组可能会发生变异,从而改变pd.DataFrame
中的值。
>>> a2 = df['charge'].values>>> a2
array([-1., -1.])>>> a2[1] = np.nan *# Mutating the array from .values.*>>> df *# Mutation is reflected in the DataFrame.*
mass charge
muon NaN -1.0
tau 1.777 NaN
有了 StaticFrame,就没有了“幕后”变异的漏洞:因为 StaticFrame 管理不可变的 NumPy 数组,所以引用只保存到不可变的数组。如果在初始化时给定了一个可变数组,将会产生一个不可变的副本。不可变数组不能从容器或对底层数组的直接访问中变异。
>>> a1 = np.array([[0.106, -1], [1.777, -1]])>>> f = sf.Frame(a1, index=('muon', 'tau'), columns=('mass', 'charge'))>>> a1[0, 0] = np.nan *# Mutating the original array has no affect on the Frame*>>> f
<Frame>
<Index> mass charge <<U6>
<Index>
muon 0.106 -1.0
tau 1.777 -1.0
<<U4> <float64> <float64>>>> f['charge'].values[1] = np.nan *# An immutable array cannot be mutated*
Traceback (most recent call last):
File "<console>", line 1, in <module>
ValueError: assignment destination is read-only
虽然不可变数据减少了出错的机会,但它也提供了性能优势。例如,当用sf.Frame.relabel()
替换列标签时,底层数据不会被复制。相反,对相同不可变数组的引用在新旧容器之间共享。这样的“无拷贝”操作因此是快速和轻量级的。这与在 Pandas 中做同样的事情时发生的情况形成了对比:相应的 Pandas 方法df.DataFrame.rename()
被强制对所有底层数据进行防御性复制。
>>> f.relabel(columns=lambda x: x.upper()) *# Underlying arrays are not copied*
<Frame>
<Index> MASS CHARGE <<U6>
<Index>
muon 0.106 -1.0
tau 1.777 -1.0
<<U4> <float64> <float64>
№ 4:赋值是一个函数
虽然 Pandas 允许就地赋值,但有时这种操作不能提供适当的派生类型,从而导致不良行为。例如,一个赋给整数pd.Series
的浮点数将在没有警告或错误的情况下截断它的浮点部分。
>>> s = pd.Series((-1, -1), index=('tau', 'down'))
>>> s
tau -1
down -1
dtype: int64>>> s['down'] = -0.333 *# Assigning a float.*>>> s *# The -0.333 value was truncated to 0*
tau -1
down 0
dtype: int64
使用 StaticFrame 的不可变数据模型,赋值是一个返回新容器的函数。这允许评估类型以确保结果数组可以完全包含赋值。
>>> s = sf.Series((-1, -1), index=('tau', 'down'))
>>> s
<Series>
<Index>
tau -1
down -1
<<U4> <int64>>>> s.assign['down'](-0.333) *# The float is assigned without truncation*
<Series>
<Index>
tau -1.0
down -0.333
<<U4> <float64>
StaticFrame 使用一个特殊的assign
接口来执行赋值函数调用。在一个sf.Frame
上,这个接口公开了一个sf.Frame.assign.loc[]
接口,可以用来选择赋值的目标。选择之后,要分配的值通过函数调用传递。
>>> f = sf.Frame.from_dict_records_items((('charm', {'charge':0.666, 'mass':1.3}), ('strange', {'charge':-0.333, 'mass':0.1})))>>> f
<Frame>
<Index> charge mass <<U6>
<Index>
charm 0.666 1.3
strange -0.333 0.1
<<U7> <float64> <float64>>>> f.assign.loc['charm', 'charge'](Fraction(2, 3)) *# Assigning to a loc-style selection*
<Frame>
<Index> charge mass <<U6>
<Index>
charm 2/3 1.3
strange -0.333 0.1
<<U7> <object> <float64>
№ 5:迭代器用于迭代和函数应用
Pandas 具有独立的迭代和函数应用功能。对于pd.DataFrame
上的迭代,有pd.DataFrame.iteritems()
、pd.DataFrame.iterrows()
、pd.DataFrame.itertuples()
和pd.DataFrame.groupby()
;对于pd.DataFrame
上的功能应用,有pd.DataFrame.apply()
和pd.DataFrame.applymap()
。
但是由于函数应用需要迭代,因此将函数应用建立在迭代之上是明智的。StaticFrame 通过提供一系列迭代器(如Frame.iter_array()
或Frame.iter_group_items()
)来组织迭代和函数应用,通过对apply()
的链式调用,这些迭代器也可用于函数应用。迭代器上也有应用映射类型的函数(比如map_any()
和map_fill()
)。这意味着一旦你知道你想如何迭代,函数应用只是一个方法。
例如,我们可以用sf.Frame.from_records()
创建一个sf.Frame
:
>>> f = sf.Frame.from_records(((0.106, -1.0, 'lepton'), (1.777, -1.0, 'lepton'), (1.3, 0.666, 'quark'), (0.1, -0.333, 'quark')), columns=('mass', 'charge', 'type'), index=('muon', 'tau', 'charm', 'strange'))>>> f
<Frame>
<Index> mass charge type <<U6>
<Index>
muon 0.106 -1.0 lepton
tau 1.777 -1.0 lepton
charm 1.3 0.666 quark
strange 0.1 -0.333 quark
我们可以用sf.Series.iter_element()
遍历一列值。我们可以通过使用在从sf.Series.iter_element()
返回的对象上找到的apply()
方法,使用同一个迭代器做函数应用。在sf.Series
和sf.Frame
上都可以找到相同的界面。
>>> tuple(f['type'].iter_element())
('lepton', 'lepton', 'quark', 'quark')>>> f['type'].iter_element().apply(lambda e: e.upper())
<Series>
<Index>
muon LEPTON
tau LEPTON
charm QUARK
strange QUARK
<<U7> <<U6>>>> f[['mass', 'charge']].iter_element().apply(lambda e: format(e, '.2e'))
<Frame>
<Index> mass charge <<U6>
<Index>
muon 1.06e-01 -1.00e+00
tau 1.78e+00 -1.00e+00
charm 1.30e+00 6.66e-01
strange 1.00e-01 -3.33e-01
<<U7> <object> <object>
对于sf.Frame
上的行或列迭代,一系列方法允许指定用于迭代的行或列的容器类型,即,用数组、用NamedTuple
或用sf.Series
(分别为iter_array()
、iter_tuple()
、iter_series()
)。这些方法采用一个轴参数来确定迭代是按行还是按列,并且类似地为函数应用程序公开一个apply()
方法。要对列应用函数,我们可以执行以下操作。
>>> f[['mass', 'charge']].iter_array(axis=0).apply(np.sum)
<Series>
<Index>
mass 3.283
charge -1.667
<<U6> <float64>
将函数应用于行而不是列只需要更改轴参数。
>>> f.iter_series(axis=1).apply(lambda s: s['mass'] > 1 and s['type'] == 'quark')
<Series>
<Index>
muon False
tau False
charm True
strange False
<<U7> <bool>
Group-by 操作只是另一种形式的迭代,具有相同的迭代和函数应用接口。
>>> f.iter_group('type').apply(lambda f: f['mass'].mean())
<Series>
<Index>
lepton 0.9415
quark 0.7000000000000001
<<U6> <float64>
№ 6:严格的仅增长框架
pd.DataFrame
的一个有效用途是加载初始数据,然后通过添加额外的列来生成派生数据。这种方法利用了类型和基础数组的列组织:添加新列不需要重新分配旧列。
StaticFrame 通过提供一个称为sf.FrameGO
的sf.Frame
的严格的、只增长的版本,使得这种方法不容易出错。例如,一旦创建了sf.FrameGO
,就可以添加新的列,而现有的列不能被覆盖或就地改变。
>>> f = sf.FrameGO.from_records(((0.106, -1.0, 'lepton'), (1.777, -1.0, 'lepton'), (1.3, 0.666, 'quark'), (0.1, -0.333, 'quark')), columns=('mass', 'charge', 'type'), index=('muon', 'tau', 'charm', 'strange'))>>> f['positive'] = f['charge'] > 0>>> f
<FrameGO>
<IndexGO> mass charge type positive <<U8>
<Index>
muon 0.106 -1.0 lepton False
tau 1.777 -1.0 lepton False
charm 1.3 0.666 quark True
strange 0.1 -0.333 quark False
这种有限形式的突变满足了实际需要。此外,从一个sf.Frame
到一个sf.FrameGO
的来回转换(使用Frame.to_frame_go()
和FrameGO.to_frame()
)是一个非复制操作:底层的不可变数组可以在两个容器之间共享。
№ 7:日期不是纳秒
Pandas 将所有日期或时间戳值建模为 NumPy datetime64[ns]
(纳秒)数组,而不管纳秒级的分辨率是否实用或合适。这给熊猫制造了一个“Y2262 问题”:超过 2262-04-11 的日期不能被表达。虽然我可以创建一个最长为 2262–04–11 的pd.DatetimeIndex
,但再过一天,Pandas 就会引发一个错误。
>>> pd.date_range('1980', '2262-04-11')
DatetimeIndex(['1980-01-01', '1980-01-02', '1980-01-03', '1980-01-04',
'1980-01-05', '1980-01-06', '1980-01-07', '1980-01-08',
'1980-01-09', '1980-01-10',
...
'2262-04-02', '2262-04-03', '2262-04-04', '2262-04-05',
'2262-04-06', '2262-04-07', '2262-04-08', '2262-04-09',
'2262-04-10', '2262-04-11'],
dtype='datetime64[ns]', length=103100, freq='D')>>> pd.date_range('1980', '2262-04-12')
Traceback (most recent call last):
pandas._libs.tslibs.np_datetime.OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 2262-04-12 00:00:00
由于索引通常用于粒度远小于纳秒的日期时间值(如日期、月份或年份),StaticFrame 提供了所有 NumPy 类型的datetime64
索引。这允许精确的日期-时间类型规范,并避免了基于纳秒的单位的限制。
虽然用 Pandas 不可能,但用 StaticFrame 创建一个扩展到 3000 年的年份或日期的索引是很简单的。
>>> sf.IndexYear.from_year_range(1980, 3000).tail()
<IndexYear>
2996
2997
2998
2999
3000
<datetime64[Y]>>>> sf.IndexDate.from_year_range(1980, 3000).tail()
<IndexDate>
3000-12-27
3000-12-28
3000-12-29
3000-12-30
3000-12-31
<datetime64[D]>
№ 8:层次索引的一致接口
分级索引允许将多个维度放入一个维度中。使用分级索引, n 维数据可以被编码到单个sf.Series
或sf.Frame
中。
分级索引的一个关键特征是在任意深度的部分选择,由此选择可以由每个深度级别的选择的交集组成。熊猫提供了许多方式来表达那些内在的深度选择。
一种方法是超载pd.DataFrame.loc[]
。当使用 Pandas 的层次索引(pd.MultiIndex
)时,pd.DataFrame.loc[]
选择中的位置参数的含义变成了动态的。正是这一点使得 Pandas 代码很难使用层次索引来维护。我们可以通过创建一个pd.DataFrame
并设置一个pd.MultiIndex
来看到这一点。
>>> df = pd.DataFrame.from_records([('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')], columns=('name', 'mass', 'charge', 'type'))>>> df.set_index(['type', 'name'], inplace=True)>>> df
mass charge
type name
lepton muon 0.106 -1.000
tau 1.777 -1.000
quark charm 1.300 0.666
strange 0.100 -0.333
类似于 NumPy 中的 2D 数组,当给pd.DataFrame.loc[]
两个参数时,第一个参数是行选择器,第二个参数是列选择器。
>>> df.loc['lepton', 'mass'] *# Selects "lepton" from row, "mass" from columns*
name
muon 0.106
tau 1.777
Name: mass, dtype: float64
然而,与这种期望相反,有时 Pandas 不会将第二个参数用作列选择,而是用作pd.MultiIndex
内部深度的行选择。
>>> df.loc['lepton', 'tau'] *# Selects lepton and tau from rows*
mass 1.777
charge -1.000
Name: (lepton, tau), dtype: float64
为了解决这种不确定性,Pandas 提供了两种选择。如果需要行和列选择,可以通过将分层行选择包装在pd.IndexSlice[]
选择修饰符中来恢复预期的行为。或者,如果不使用pd.IndexSlice[]
需要内部深度选择,可以使用pd.DataFrame.xs()
方法。
>>> df.loc[pd.IndexSlice['lepton', 'tau'], 'charge']
-1.0>>> df.xs(level=1, key='tau')
mass charge
type
lepton 1.777 -1.0
给予pd.DataFrame.loc[]
的位置参数的含义不一致是不必要的,这使得熊猫代码更难维护:使用pd.DataFrame.loc[]
的意图在没有pd.IndexSlice[]
的情况下变得不明确。此外,提供多种方法来解决这个问题也是一个缺点,因为在 Python 中最好有一种显而易见的方法来做事。
StaticFrame 的sf.IndexHierarchy
提供了更加一致的行为。我们将创建一个等价的sf.Frame
并设置一个sf.IndexHierarchy
。
>>> f = sf.Frame.from_records((('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')), columns=('name', 'mass', 'charge', 'type'))>>> f = f.set_index_hierarchy(('type', 'name'), drop=True)>>> f
<Frame>
<Index> mass charge <<U6>
<IndexHierarchy: ('type', 'name')>
lepton muon 0.106 -1.0
lepton tau 1.777 -1.0
quark charm 1.3 0.666
quark strange 0.1 -0.333
<<U6> <<U7> <float64> <float64>
与 Pandas 不同,StaticFrame 在位置参数的含义上是一致的:第一个参数总是行选择器,第二个参数总是列选择器。对于在sf.IndexHierarchy
中的选择,需要sf.HLoc[]
选择修改器来指定层次中任意深度的选择。有一个显而易见的方法来选择内心深处。这种方法使得 StaticFrame 代码更容易理解和维护。
>>> f.loc[sf.HLoc['lepton']]
<Frame>
<Index> mass charge <<U6>
<IndexHierarchy: ('type', 'name')>
lepton muon 0.106 -1.0
lepton tau 1.777 -1.0
<<U6> <<U4> <float64> <float64>>>> f.loc[sf.HLoc[:, ['muon', 'strange']], 'mass']
<Series: mass>
<IndexHierarchy: ('type', 'name')>
lepton muon 0.106
quark strange 0.1
<<U6> <<U7> <float64>
№ 9:索引总是唯一的
很自然地认为pd.DataFrame
上的索引和列标签是惟一标识符:它们的接口表明它们就像 Python 字典,其中的键总是惟一的。然而,熊猫指数并不局限于唯一值。在带有重复项的pd.DataFrame
上创建索引意味着,对于一些单标签选择,将返回一个pd.Series
,但是对于其他单标签选择,将返回一个pd.DataFrame
。
>>> df = pd.DataFrame.from_records([('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')], columns=('name', 'mass', 'charge', 'type'))>>> df.set_index('charge', inplace=True) # Creating an index with duplicated labels>>> df
name mass type
charge
-1.000 muon 0.106 lepton
-1.000 tau 1.777 lepton
0.666 charm 1.300 quark
-0.333 strange 0.100 quark>>> df.loc[-1.0] # Selecting a non-unique label results in a pd.DataFrame
name mass type
charge
-1.0 muon 0.106 lepton
-1.0 tau 1.777 lepton>>> df.loc[0.666] # Selecting a unique label results in a pd.Series
name charm
mass 1.3
type quark
Name: 0.666, dtype: object
Pandas 对非唯一索引的支持使得客户端代码变得更加复杂,因为它必须处理有时返回一个pd.Series
而有时返回一个pd.DataFrame
的选择。此外,索引的唯一性通常是对数据一致性的简单而有效的检查。
一些 Pandas 接口,比如pd.concat()
和pd.DataFrame.set_index()
,提供了一个名为verify_integrity
的可选惟一性检查参数。令人惊讶的是,熊猫默认禁用了verify_integrity
。
>>> df.set_index('type', verify_integrity=True)
Traceback (most recent call last):
ValueError: Index has duplicate keys: Index(['lepton', 'quark'], dtype='object', name='type')
在 StaticFrame 中,索引总是唯一的。试图设置非唯一索引将引发异常。这个约束消除了在索引中错误引入重复的机会。
>>> f = sf.Frame.from_records((('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')), columns=('name', 'mass', 'charge', 'type'))>>> f
<Frame>
<Index> name mass charge type <<U6>
<Index>
0 muon 0.106 -1.0 lepton
1 tau 1.777 -1.0 lepton
2 charm 1.3 0.666 quark
3 strange 0.1 -0.333 quark
<int64> <<U7> <float64> <float64> <<U6>>>> f.set_index('type')
Traceback (most recent call last):
static_frame.core.exception.ErrorInitIndex: labels (4) have non-unique values (2)
№ 10:去了又回来看熊猫
StaticFrame 旨在与熊猫并肩工作。通过专门的构造器和导出器,例如Frame.from_pandas()
或Series.to_pandas()
,来回切换是可能的。
>>> df = pd.DataFrame.from_records([('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')], columns=('name', 'mass', 'charge', 'type'))>>> df
name mass charge type
0 muon 0.106 -1.000 lepton
1 tau 1.777 -1.000 lepton
2 charm 1.300 0.666 quark
3 strange 0.100 -0.333 quark>>> sf.Frame.from_pandas(df)
<Frame>
<Index> name mass charge type <object>
<Index>
0 muon 0.106 -1.0 lepton
1 tau 1.777 -1.0 lepton
2 charm 1.3 0.666 quark
3 strange 0.1 -0.333 quark
<int64> <object> <float64> <float64> <object>
结论
“数据框”对象的概念在 2009 年 Pandas 0.1 发布之前很久就出现了:数据框的第一个实现可能早在 1991 年就出现在 r 的前身 S 语言中。今天,数据框在各种语言和实现中都有实现。熊猫将继续为广大用户提供优秀的资源。然而,对于正确性和代码可维护性至关重要的情况,StaticFrame 提供了一种替代方案,旨在更加一致并减少出错的机会。
有关 StaticFrame 的更多信息,请参见文档或项目现场。
数据科学面试中你应该知道的十个 SQL 概念
学习聪明,不努力。
由宏向量创建的设计向量—www.freepik.com
SQL 非常强大,有很多功能。然而,当谈到数据科学面试时,大多数公司测试的核心概念真的很少。这 10 个概念出现的频率最高,因为它们在现实生活中应用最多。
在这篇文章中,我将回顾我认为的 10 个最重要的 SQL 概念,这些概念是你在准备面试时应该花大部分时间关注的。
说到这里,我们开始吧!
1。案例当
您很可能会看到许多问题需要使用 CASE WHEN 语句,这仅仅是因为它是一个如此通用的概念。
如果您想根据其他变量分配某个值或类,它允许您编写复杂的条件语句。
鲜为人知的是,它还允许您透视数据。例如,如果您有一个月列,并且希望为每个月创建一个单独的列,则可以使用 CASE WHEN 语句透视数据。
示例问题:编写一个 SQL 查询来重新格式化该表,以便每个月都有一个收入列。
Initial table:
+------+---------+-------+
| id | revenue | month |
+------+---------+-------+
| 1 | 8000 | Jan |
| 2 | 9000 | Jan |
| 3 | 10000 | Feb |
| 1 | 7000 | Feb |
| 1 | 6000 | Mar |
+------+---------+-------+
Result table:
+------+-------------+-------------+-------------+-----+-----------+
| id | Jan_Revenue | Feb_Revenue | Mar_Revenue | ... | Dec_Revenue |
+------+-------------+-------------+-------------+-----+-----------+
| 1 | 8000 | 7000 | 6000 | ... | null |
| 2 | 9000 | null | null | ... | null |
| 3 | null | 10000 | null | ... | null |
+------+-------------+-------------+-------------+-----+-----------+
更多类似问题, 查看 StrataScratch100 个 SQL 问题。
2.选择不同
选择独特是你应该永远记住的。在聚合函数中使用 SELECT DISTINCT 语句非常常见(这是第三点)。
例如,如果您有一个显示客户订单的表,可能会要求您计算每个客户的平均订单数。在这种情况下,您可能希望计算订单总数,而不是客户总数。它可能看起来像这样:
SELECT
COUNT(order_id) / COUNT(DISTINCT customer_id) as orders_per_cust
FROM
customer_orders
3.聚合函数
关于第二点,你应该对 min、max、sum、count 等聚合函数有很深的理解。这也意味着你应该对 GROUP BY 和 HAVING 子句有很深的理解。我强烈建议您花时间来完成练习题,因为有一些创造性的方法可以使用聚合函数。
示例问题:编写一个 SQL 查询,在一个名为 *Person*
的表中查找所有重复的电子邮件。
+----+---------+
| Id | Email |
+----+---------+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
+----+---------+**ANSWER:**
SELECT
Email
FROM
Person
GROUP BY
Email
HAVING
count(Email) > 1
4.左连接与内连接
对于那些对 SQL 比较陌生或者已经有一段时间没有使用它的人来说,很容易混淆左连接和内连接。请确保您清楚地了解每个连接如何产生不同的结果。在许多面试问题中,你会被要求做一些连接,在某些情况下,选择一个对另一个是正确和错误答案之间的区别。
5.自连接
现在我们开始更有趣的东西了!SQL 自联接将表与其自身联接起来。你可能认为这没有用,但是你会惊讶于这是多么的普遍。在许多实际设置中,数据存储在一个大表中,而不是许多较小的表中。在这种情况下,可能需要自联接来解决独特的问题。
让我们看一个例子。
示例问题:给定下面的 *Employee*
表,编写一个 SQL 查询,找出收入高于其经理的雇员。在上表中,Joe 是唯一一个收入高于其经理的员工。
+----+-------+--------+-----------+
| Id | Name | Salary | ManagerId |
+----+-------+--------+-----------+
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
+----+-------+--------+-----------+**Answer:**
SELECT
a.Name as Employee
FROM
Employee as a
JOIN Employee as b on a.ManagerID = b.Id
WHERE a.Salary > b.Salary
6.子查询
子查询也称为内部查询或嵌套查询,是查询中的查询,嵌入在 WHERE 子句中。这是解决需要按顺序进行多次查询才能产生给定结果的独特问题的好方法。查询时,子查询和 WITH AS 语句都非常常用,所以您应该绝对确保知道如何使用它们。
例题:假设一个网站包含两个表,Customers
表和Orders
表。编写一个 SQL 查询来查找从不订购任何东西的所有客户。
Table: Customers.+----+-------+
| Id | Name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+Table: Orders.
+----+------------+
| Id | CustomerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+**Answer:**
SELECT
Name as Customers
FROM
Customers
WHERE
Id NOT IN (
SELECT
CustomerId
FROM Orders
)
7.字符串格式
字符串函数非常重要,尤其是在处理不干净的数据时。因此,公司可能会测试你的字符串格式和操作,以确保你知道如何操作数据。
字符串格式包括以下内容:
- 左,右
- 整齐
- 位置
- SUBSTR
- 串联
- 上、下
- 联合
如果你对这些不确定,可以查看一下模式关于清理数据的字符串函数教程。
8.日期时间操作
您肯定会遇到一些涉及日期时间数据的 SQL 问题。例如,您可能需要按月对数据进行分组,或者将变量格式从 DD-MM-YYYY 转换为月份。
您应该知道的一些功能有:
- 提取
- DATEDIFF
例题:给定一个 *Weather*
表,编写一个 SQL 查询,找出所有与前一个(昨天的)日期相比温度更高的日期的 id。
+---------+------------------+------------------+
| Id(INT) | RecordDate(DATE) | Temperature(INT) |
+---------+------------------+------------------+
| 1 | 2015-01-01 | 10 |
| 2 | 2015-01-02 | 25 |
| 3 | 2015-01-03 | 20 |
| 4 | 2015-01-04 | 30 |
+---------+------------------+------------------+**Answer:**
SELECT
a.Id
FROM
Weather a,
Weather b
WHERE
a.Temperature > b.Temperature
AND DATEDIFF(a.RecordDate, b.RecordDate) = 1
9.窗口功能
窗口函数允许您对所有行执行聚合值,而不是只返回一行(这是 GROUP BY 语句所做的)。如果您想对行进行排序、计算累积和等等,这是非常有用的。
例题:写一个查询得到工资最高的 *empno*
。确保您的解决方案能够处理领带!
depname | empno | salary |
-----------+-------+--------+
develop | 11 | 5200 |
develop | 7 | 4200 |
develop | 9 | 4500 |
develop | 8 | 6000 |
develop | 10 | 5200 |
personnel | 5 | 3500 |
personnel | 2 | 3900 |
sales | 3 | 4800 |
sales | 1 | 5000 |
sales | 4 | 4800 |**Answer:**
WITH sal_rank AS
(SELECT
empno,
RANK() OVER(ORDER BY salary DESC) rnk
FROM
salaries)
SELECT
empno
FROM
sal_rank
WHERE
rnk = 1;
10.联盟
作为奖励,#10 是工会!虽然这个问题不经常出现,但偶尔会有人问你这个问题,大体了解一下是有好处的。如果有两个列相同的表,并且想要合并它们,这时就应该使用 UNION。
同样,如果你不是 100%确定它是如何工作的,我会做一些快速的谷歌搜索来了解它。😃
感谢阅读!
仅此而已!我希望这对你的面试准备有所帮助,并祝你在未来的努力中好运。我确信,如果你对这 10 个概念了如指掌,那么在处理大多数 SQL 问题时,你会做得很好。
特伦斯·申
- 如果你喜欢这个, 在 Medium 上关注我 了解更多
- 查看更多 SQL 练习题
编程时节省时间和减少挫折的十个技巧⏳
另外还有一个奖金计划,可以更快地解决错误😀
来源:https://pixabay.com/
在我坚持下去之前,我开始和退出编程至少有四次。我对我的数据科学学生的调查显示,这个故事并不罕见。
许多错误的开始部分是由于学习编码的缓慢而乏味的过程以及伴随而来的挫折。因此,我热衷于消除那些阻碍人们成为合格程序员的不必要的障碍。障碍总是会有的,但是没有理由去设置不必要的障碍。😁
这里有 10 个技巧可以帮助你更快地跨越障碍,并获得一种能力感。如果你已经编码多年,这些技巧中的许多对你来说可能是次要的或显而易见的,但是当开始时,没有什么是显而易见的。即使你从手机被称为车载电话的时候就开始编写代码,也可能有一两个小技巧可以节省你的时间。🎉
10 多条提示。来源:https://pixabay.com/
十条建议
- **分割你的屏幕。**大多数时候,当你学习编码时,我建议你把屏幕分成两个并排的面板——一个用于你在 Jupyter 笔记本或代码编辑器中编写的代码,另一个用于你的网络浏览器。您将使用 web 浏览器查看文档、堆栈溢出和教程。如果你的屏幕太小,我建议你买一个更大的——如果你负担得起的话。在美国,你可以在亚马逊上花 100 多美元买到一台 25 英寸的显示器。随着你越来越熟练,你会经常想要专注于一件作品。使用键盘快捷键可以在分屏和全屏之间切换。对于 Mac,我喜欢 2.99 美元的 BetterSnapTool ,一些 Windows 选项在本文中讨论。🖥
- 排除杂念。电脑和手机上的静音通知。如果可以的话,把你的手机拿开。如果你在嘈杂的地方,播放一些舒缓的器乐或使用降噪耳机。你越能集中注意力,你就能学得越快。🎻
- 键入示例代码。你不能只看一个教程就希望记住它。复制粘贴价值不大。你得把它打出来。那就延伸一下。然后凭记忆打出来。“从理论上讲,理论和实践之间没有区别。实际上是有的。”——本雅明·布鲁斯特原本——不是约吉·贝拉。⚾️
- **多日学习。**学习一个新的代码概念,并在几天内对自己进行测试。研究表明,在长期记忆方面,间隔重复比死记硬背有效得多。🚚
- 寓教于乐。向他人清楚地解释一个概念——大声地或者以书面形式——迫使你理解这个概念的本质,建立关系,并进行类比。你加强了大脑中的联系。这个技巧是优秀的费曼学习技巧的一部分。📚
- 语言前库。在你尝试使用一个编程语言的库之前,要充分理解它。变量类型不是最令人兴奋的话题。具有属性和方法的类可能看起来很高级。但是一旦你真正理解了一门语言,这个库就更容易使用了。你会犯很少的错误,节省很多时间。🎉
- 一次学一件事。不要试图同时学习两件事。你不是学得慢两倍,而是慢 10 倍。😉相关地,不要被闪亮的东西分散注意力。有一个地方来保存看起来像诱人的兔子洞跳下来的网址。把与你现在正在学习的东西不相关的网址放在安全的地方。一个标签是一个方便的 Chrome 扩展,用于保存标签以备后用。
- **睡眠,锻炼,保持活力。**😴🏃🏾♀️Put 你的大脑处于一个强大的位置,形成和加强神经连接。🧠在我这本令人难忘的 Python 书籍中学习了更多帮助你保持敏锐的技巧。
- 学会更快地键入代码。 ⌨️你输入不寻常的符号越多,你就越快。然而,一点点的意向性可以显著加快这个过程。投资学习良好的触摸打字形式。这里有一个来自 SpeedCoder 的小练习。
- **成为有键盘快捷键的高手。**以下是 Chrome 、 Mac 和 [Windows](https://support.microsoft.com/en-us/help/12445/windows- keyboard-shortcuts) 的快捷方式链接。如果你是数据科学家或数据分析师,我在这里做了一个 GitHub Gist 的 Jupyter 实验室快捷方式。我为 Mac 或 Linux 终端创建了快捷方式,在
~/.bash_profile
:alias gs="git status"
向 Bash 概要文件添加了如下代码行。然后重启你的终端,输入两个键而不是两个单词。😉
额外收获:学会快速解决错误
这是一个如此大的话题,却没有得到足够的关注,以至于我正在考虑就此写一整篇文章。相反,这里有一个额外的部分——一个更快解决错误的 5 步计划。
翻译:当 shape 不存在时,你调用它作为方法,你想得到一个属性。
每个程序员都会看到很多错误。一旦你有了经验,大多数问题只会耸耸肩,因为你可以在一分钟内解决它们。
然而,当你开始时,一个错误可能需要 20 倍的时间来解决。对于一个初学者来说,错误会让你感觉像是在齐踝深的泥泞中跋涉。
模糊的错误。来源:https://pixabay.com/
这里有一个快速解决 95%错误的策略。
所以你有一个错误,你应该怎么做?
- 查找错别字——丢失的括号,或者拼写错误的变量或函数。语法突出显示在这里会有所帮助。一定要在提供代码高亮的软件中打字,以提高避免或快速修复缩进、不对称括号和类似错误的机会。有很多好的代码编辑器可供选择。如果你刚刚开始,我建议vs code——它拥有最大的市场份额,它是免费的,并且有许多方便的功能。
- **首先读取错误消息堆栈跟踪的顶部和底部。**中间的代码一般帮助不大。然后,根据错误消息中的线索,查看您的代码,看能否找出问题所在。如果不能快速解码错误消息,将消息的最后一行复制粘贴到 Google(如果使用 Python。其他语言可能会在堆栈跟踪之前显示错误消息)。一个新的程序员最大的错误是没有足够快地向谷歌求助。
来源:https://pixabay.com/
3.筛选网上资源去粗取精。🌾这里有一些启发,可以帮助你更快地找到更高质量的资源。😀
栈溢出、中、 Reddit 、黑客新闻、 Dev 等网络社区都有 upvotes 或 claps 等反馈指标,以显示哪些内容是针对目标的。这些度量标准并不完美,但是它们通常工作得很好。栈溢出、数据科学媒体出版物、关于回购的 GitHub 问题以及语言或库的官方文档是我找到最多解决方案的网站。
瞄准目标。来源:https://pixabay.com/
谷歌搜索结果中的博客质量时好时坏。如果你正在使用 Python,我可以根据我的经验推荐一些不错的网站:
4.避免旧的资源。对于大多数问题,您需要的是不超过几年的资源。当在谷歌上搜索错误的解决方案时,过滤掉旧的结果——要么通过对搜索参数进行时间限制,要么在查看结果片段时非正式地过滤。
使用谷歌搜索工具限制搜索结果的时间
如果您刚开始学习 Python,2009 年关于 Python 2 的内容可能对您没有帮助。🐍
API 在不断发展,而事情是如何完成的通常不是今天推荐的方法。你更有可能在相对最近的结果中找到一个合适的答案。📆
如果你第一次尝试不成功,那么下一次浏览旧帖子和低质量网站是值得的。
5.了解错误代码的含义。 这里是Python 的常见错误代码和解释列表。这里有一个非常好的流程图来帮助你处理常见的 Python 错误。
如果使用 Python pandas 库进行数据操作,会出现一些常见错误。下面是我用 Jupyter 笔记本写的 GitHub 要点以及如何解决它们:
常见熊猫错误:https://gist . github . com/disc diver/2 F8 df 1c 3 f1 c 66 f 47129568 a82c 0666 e 5
如果 Python 和 pandas 能提供真正有用的错误消息,包括对错误可能原因的解释,那就太好了。如果有人想解决它,那听起来像是一个有价值的项目!👍
这就是我更快处理错误的计划。如果你有其他建议,请告诉我。😀
包装
希望这 10 个减少挫败感和提高速度的建议能节省你的时间。希望解决错误信息的 5 步计划能帮助你或你认识的人更快地学习。🚀
有了正确的心态,一点指导和大量的实践,任何人都可以学习编码。这可能不会很快或很容易,但希望这篇文章会让它不那么令人沮丧。
如果你觉得这很有帮助,请在你最喜欢的社交媒体上分享,这样其他人也可以找到它。
我写关于 Python、Docker、数据科学等等的文章。如果你对此感兴趣,请在这里阅读更多和关注我。😄
如果你对帮助你提高工作效率的技巧感兴趣,我在我每月的 Data Awesome 时事通讯中分享了很多。
快乐学习!🚀
提高 Python 代码速度的十个技巧
君的轻音乐
每一步的微小进步,整体的巨大飞跃
Python 很慢。
我打赌你可能会多次遇到这种关于使用 Python 的反驳,尤其是来自C
或C++
或Java
世界的人。在许多情况下都是这样,例如,循环或排序 Python 数组、列表或字典有时会很慢。毕竟,开发 Python 是为了让编程变得有趣和简单。因此,Python 代码在简洁性和可读性方面的改进必须以性能为代价。
话虽如此,近年来已经做了许多努力来提高 Python 的性能。我们现在可以通过使用numpy
、scipy
、pandas
和[numba](http://numba.pydata.org/)
以高效的方式处理大型数据集,因为所有这些库都在C/C++
中实现了它们的关键代码路径。还有另一个令人兴奋的项目, Pypy 项目,与 Cpython (最初的 python 实现)相比,它将 Python 代码的速度提高了 4.4 倍。
Pypy 的缺点是它对一些流行的科学模块(如 Matplotlib、Scipy)的覆盖有限或不存在,这意味着您不能在 Pypy 的代码中使用这些模块。
除了这些外部资源,在日常编码实践中,我们还能做些什么来加速 Python 代码呢?今天和大家分享一下我在学习 Python 过程中经常用到的 10 招。
像往常一样,如果你想自己重新运行这篇文章中的代码,可以从 my Github 访问所有需要的数据和笔记本。
1。熟悉内置函数
图 1 |Python 3 中的内置函数
Python 自带了许多在C
中实现的内置函数,这些函数非常快并且维护良好(图 1)。我们至少应该熟悉这些函数名,并且知道在哪里可以找到它们(一些常用的与计算相关的函数有abs()
、len()
、max()
、min()
、set()
、sum()
)。因此,每当我们需要进行一个简单的计算时,我们可以走正确的捷径,而不是笨拙地编写自己的版本。
让我们以内置函数set()
和sum()
为例。如图 2 所示,使用set()
和sum()
分别比我们自己编写的函数快 36.1 倍和 20.9 倍。
图 2 | set()和 sum()函数的示例
2。**sort()**
vs**sorted()**
这两个函数都可以对列表进行排序。
如果我们只想获得一个已排序的列表,而不关心原始列表,那么无论是对于基本排序还是在使用 **key**
参数(key
参数指定了在进行比较之前要在每个列表元素上调用的函数)时,**sort()**
都比 **sorted()**
要快一点,如图 3 所示。
这是因为sort()
方法就地修改列表,而sorted()
构建一个新的排序列表并保持原始列表不变。换句话说,a_long_list
本身的值的顺序实际上已经改变了。
图 3| sort()和 sorted()
然而,sorted()
比sort()
更通用。这是因为sorted()
接受任何 iterable,而sort()
只为列表定义。因此,如果我们想对列表之外的东西进行排序,sorted()
是可以使用的正确函数。例如,我们可以根据字典的keys
或values
对其进行快速排序(图 4)。
图 4 |已排序的()字典
3。用符号代替它们的名字
如图 5 所示,当我们需要一个空的字典或列表对象时,不用使用dict()
或list()
,直接调用{}
(至于空集,需要使用set()
本身)和[]
。这个技巧不一定会加速代码,但是会让代码更复杂。
图 5 |直接使用 list()和 dict()符号
4。列表理解
通常,当我们需要根据某些规则从一个旧列表创建一个新列表时,我们使用一个for
循环来遍历旧列表,并根据规则转换它的值,然后保存在一个新列表中。例如,假设我们想从another_long_list
中找到所有偶数,我们可以使用以下代码:
even_num = []
for number in another_long_list:
if number % 2 == 0:
even_num.append(number)
但是,有一种更简洁、更优雅的方法可以实现这一点。如图 6 所示,我们把原来的 **for**
循环放在仅仅一行代码中。而且,速度提高了差不多 2 倍。
图 6 |列表理解
结合规则 3 ,我们也可以将列表变成字典或集合,只需将[]
改为{}
。让我们重写图 5 中的代码,我们可以省略赋值的步骤,在符号内部完成迭代,就像这个sorted_dict3 = {key: value for key, value in sorted(a_dict.items(), key=lambda item: item[1])}
。
要分解这个,从最后开始。函数“sorted(a_dict.items(), key=lambda item: item[1])
”返回给我们一个元组列表(图 4)。这里,我们使用多重赋值来解包元组,对于列表中的每个元组,我们将key
赋给它的第一项,将value
赋给它的第二项(因为我们知道在这种情况下每个元组中有两项)。最后,每一对key
和value
都保存在一本字典中。
5。使用 **enumerate()**
作为数值和索引
有时,当我们遍历一个列表时,我们希望在表达式中同时使用它的值和索引。如图 7 所示,我们应该使用enumerate()
,它将列表的值转换成索引和值对。这也将我们的代码速度提高了大约 2 倍。
图 7 | enumerate()示例
6。使用 **zip()**
打包和解包多个迭代
在某些情况下,我们需要遍历两个或更多的列表。然后我们可以使用zip()
函数,它将多个列表转换成一个元组列表(图 8)。注意,列表最好长度相同,否则,zip()
会在较短的列表结束后立即停止。
图 8 | zip()示例
相反,要访问列表中每个元组的条目,我们也可以通过添加星号(*)和使用多重赋值来解压缩元组列表,就像这样,letters1, numbers1 = zip(*pairs_list)
。
7。组合 **set()**
和 **in**
当我们想检查一个值是否存在于一个列表中时,一个笨拙的方法是构造这样一个函数:
**# Construct a function for membership test**
def check_membership(n):
for element in another_long_list:
if element == n:
return True
return False
然后调用check_membership(value)
看another_long_list
里面的value
是否。然而,一种 pythonic 式的方法是通过调用value in another_long_list
来使用in
,如图 9 所示。这就像你直接问 python“嘿 Python,你能告诉我value
是否在another_long_list
里面吗”。
图 9 |使用 in 和 set()检查成员资格
为了更有效,我们应该首先使用set()
从列表中删除重复项,然后测试 set 对象中的成员资格。通过这样做,我们减少了需要检查的元素的数量。此外,in
是一个非常快速的集合操作。
从图 9 中可以看出,尽管构建 set 对象花费了 20 毫秒,但这只是一次性投资,检查步骤本身仅用了 5.2 秒,这是 1962 倍的改进。
8。检查变量是否为真
不可避免地,我们会使用大量的if
语句来检查空变量、空列表、空字典等等。我们也可以从这里节省一点时间。
如图 10 所示,我们不需要在if
语句中显式地声明== True
或is True
,取而代之的是我们只使用变量名。这节省了魔法函数[__eq__](https://stackoverflow.com/questions/3588776/how-is-eq-handled-in-python-and-in-what-order)
用于比较两边值的资源。
图 10 |只需检查变量
同样,如果我们需要检查变量是否为空,我们只需要说if not string_returned_from_function:
。
9。使用计数唯一值**Counters()**
假设我们试图计算在规则 1 、a_long_list
中生成的列表中的唯一值。一种方法是创建一个字典,其中的键是数字,值是计数。当我们迭代列表时,如果它已经在字典中,我们可以增加它的计数,如果它不在字典中,我们可以将它添加到字典中。
num_counts = {}
for num in a_long_list:
if num in num_counts:
num_counts[num] += 1
else:
num_counts[num] = 1
然而,更有效的方法是在一行代码num_counts2 = Counter(a_long_list)
中使用 集合 中的Counter()
。是的,就是这么简单。如图 11 所示,它比我们编写的函数大约快 10 倍。
如果我们想知道 10 个最常见的数字,Counter()
实例还有一个非常方便的most_common
方法。
图 11 |计数器()示例
总之,收藏是一个很神奇的模块,我们应该把它保存到我们的日常工具箱中,随时使用。
10。将 **for**
循环放入函数中
可能有一段时间,我们构建了一个函数,并且需要重复这个函数给定的次数。一个显而易见的方法是我们构建一个函数,然后将这个函数放入一个for
循环中。
但是,如图 12 所示,我们没有重复执行函数 100 万次(a_long_list 的长度为 1,000,000),而是在函数内部集成了for
循环。这为我们节省了大约 22%的运行时间。这是因为函数调用是昂贵的,通过将函数写入列表理解来避免它是更好的选择。
图 12 |函数内部的 put for 循环
仅此而已!感谢你阅读这篇文章。希望有些小技巧能对你有用。此外,您还使用了哪些其他方法来加速 Python 代码?如果你能留下评论来分享它们,我将不胜感激。
以下是您可能感兴趣的链接:
- 如何使用
[sort()](https://docs.python.org/3/howto/sorting.html)
和[sorted()](https://docs.python.org/3/howto/sorting.html)
对列表进行排序 - 在 Python 中何时使用列表理解
- 将代码转换成漂亮、地道的 Python 语言
- Python 编程语言的优缺点
和往常一样,我欢迎反馈、建设性的批评以及听到关于您的数据科学项目的信息。可以通过 Linkedin 和我的网站找到我。
在 R 中执行常见数据任务的十种最新方法
用可爱企鹅的数据集来了解这十个简单的例子
无论您是否是 tidyverse 的粉丝,毫无疑问,这个 R 包集合提供了一些整洁而有吸引力的方式来处理数据,这些方式对用户来说通常非常直观。在 tidyverse 包的早期版本中,用户控制输出的一些元素被牺牲掉了,以利于新手能够掌握和容易使用的更简单的功能。在最近对dplyr
和tidyr
的更新中,在恢复这种控制方面有了重大进展。
这意味着 tidyverse 中有一些您可能不知道的新函数和方法。它们允许您更好地按照自己的意愿转换数据,并更灵活地执行操作。它们还提供了新的替代方法来执行任务,如嵌套、建模或绘图,使您的代码更具可读性和可理解性。事实上,我确信用户仅仅是对这个重要的软件包的最新更新的皮毛。
任何程序员都有责任跟上方法的发展。下面是最新的 tidyverse 更新提供的处理常见数据任务的新方法的十个示例。对于这些例子,我将使用新的帕尔默企鹅数据集,这是对有争议的虹膜数据集的替代,众所周知,Fischer 在他围绕优生学的工作中使用了该数据集。正如我们将看到的,它实际上是一个更好的教学和演示数据争论的全面数据集,我鼓励你使用和探索它。
首先,让我们加载 tidyverse 包和帕尔默企鹅数据集,并快速浏览一下。我鼓励您在尝试复制本文中的工作之前安装这些包的最新版本。
我们可以看到,该数据集提供了不同物种、性别和原产地的企鹅的各种解剖特征的几种测量方法,以及采取这些测量方法的年份。
1.选择数据中的列
tidyselect
现在内置了助手函数,允许您根据常见条件使用dplyr::select()
选择列,从而节省时间。在这种情况下,如果我想将数据集简化为仅包含账单测量值,我可以使用这个(注意,所有测量值列都包含一个下划线):
一整套tidyselect
助手函数可以在文档这里找到。
2.重新排序数据中的列
dplyr::relocate()
允许以一种新的方式对特定的列或列集进行重新排序。例如,如果我想确保我的所有测量列都在数据集的末尾,我可以使用这个(注意我的最后一列是year
):
类似于.after
你也可以在这里用.before
作为论元。
3.控制突变的列位置
你会注意到在penguins
数据集中,每只企鹅都没有唯一的标识符。当数据集中有多个相同物种、岛屿、性别和年份的企鹅时,这可能会有问题。为了解决这个问题并为后面的例子做准备,让我们使用dplyr::mutate()
添加一个惟一的标识符,这里我们可以说明mutate()
现在如何允许您以类似于relocate()
的方式定位新列:
4.从宽到长的转变
penguins
数据集显然是一种广泛的形式——它给出了跨列的多个观察值。出于多种原因,我们可能希望将数据从宽型转换为长型。在长数据中,每个观察值都有自己的行。在tidyr
中旧的函数gather()
在这类任务中很流行,但是它的新版本pivot_longer()
更加强大。在这种情况下,我们在这些列名中有不同的正文部分、度量和单位,但是我们可以像这样非常简单地将它们分开:
5.由长变宽
从长移回宽也一样容易。pivot_wider()
比旧的spread()
更加灵活:
6.跨多个列运行组统计信息
dplyr
如何使用across
副词将多个汇总函数应用于分组数据,帮助您提高效率。如果我们想总结所有企鹅的喙和鳍状肢的尺寸,我们会这样做:
7.控制跨多列汇总时输出列的命名方式
您将在上面看到penguin_stats
中的多个列是如何被赋予默认名称的,这并不直观。如果您命名了您的汇总函数,那么您可以使用.names
参数来精确地控制您想要如何命名这些列。这使用了glue
符号。例如,这里我想构造新的列名,方法是获取现有的列名,删除任何下划线或“mm”度量,并使用下划线粘贴到汇总函数名:
8.跨数据子集运行模型
summarise()
的输出现在可以是任何东西,因为dplyr
现在允许不同的列类型。您可以生成汇总向量、数据帧或其他对象,如模型或图形。
如果你想为每个物种运行一个模型,你可以这样做:
将模型对象保存在数据帧中通常不是很有用,但是您可以使用其他面向整洁的包来总结模型的统计数据,并将它们作为完美集成的数据帧返回:
9.嵌套数据
我们经常需要处理数据的子集,按子集对数据进行分组会很有用,这样我们就可以在所有数据子集上应用通用的函数或操作。例如,也许我们想看看我们不同种类的企鹅,并为它们制作一些不同的图表。以前,基于子集的分组可以通过以下有些笨拙的 tidyverse 函数组合来实现。
新功能nest_by()
提供了一种更直观、更快捷的方式来做同样的事情:
请注意,嵌套数据将存储在名为data
的列中,除非您使用.key
参数指定其他方式。
10.跨子集绘图
有了nest_by()
和我们现在可以总结或变异几乎任何类型的对象的事实,这允许我们跨子集生成图形,并将它们存储在数据帧中以备后用。让我们为我们的三个企鹅种类散点图比尔长度和深度:
现在我们可以很容易地显示不同的散点图,例如,我们的企鹅体现了辛普森悖论:
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。也可以看看我在【drkeithmcnulty.com】上的博客。
帕尔默企鹅作品由 @allison_horst (经许可使用)
tensor board——tensor flow 模型的可视化套件
学习使用 TensorBoard 可视化指标、图表、权重直方图和 Tensorflow 模型的偏差
在本文中,您将学习使用 TensorBoard 显示在 TensorFlow 中创建的深度学习模型在不同时期的指标、图表、图像以及权重和偏差直方图,以及您可能会遇到的常见问题。
什么是张量板?
TensorBoard 是 Tensorflow 的一个可视化工具包,用于显示不同的指标、参数和其他可视化内容,帮助调试、跟踪、微调、优化和共享您的深度学习实验结果。
通过 TensorBoard 可以追踪到哪些不同的东西?
TensorBoard 允许您跟踪
- 度量 : 损失和准确度随着每个时期而变化。跟踪训练周期中的损失和准确性将有助于您了解模型是否过度拟合。
- 模型图:可视化深度学习模型,了解其构建是否正确
- 分布直方图:可视化各个时期的权重和偏差直方图
- 图像:在 TensorBoard 中为你的数据集可视化任意图像
为什么要用 TensorBoard?
你正在开发一个图像分类深度神经网络,希望拥有 98%以上的准确率。
您希望跟踪模型在每个时期的准确性和损失;另外您可能希望使用不同的超参数值(如学习率、优化器、退出等)来跟踪和分析您的模型的准确性和损失。超参数不同值的跟踪精度将帮助您微调模型。
深度学习模型在计算上非常昂贵,使用不同的超参数值可视化我们的实验结果,并跟踪几个时期的准确性和损失,将有助于快速微调模型。
您可以基于不同的数据流图跟踪模型的准确性和损失,这些数据流图具有不同的隐藏层数和隐藏层中的单元数。
TensorBoard 通过跟踪精度和损失,帮助可视化模型中的张量流,以便进行调试和优化。
TensorBoard 将计算图形、训练参数、度量和超参数可视化,这将有助于跟踪模型的实验结果,从而更快地对模型进行微调。
有了 TensorBoard.dev ,你就可以托管你的深度学习模型实验结果,分享给你的团队。任何人都可以通过链接查看数据,所以不要上传敏感数据。
如何使用 TensorBoard?
我们将使用 TensorBoard 使用猫狗数据集可视化标量、图形和分布。
导入所需的库
**import tensorflow as tf
import datetime
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img**
我用过 TensorFlow 2.0.0 版本。
加载 TensorBoard 笔记本扩展
# Load the TensorBoard notebook extension
**%load_ext tensorboard**
创建图像分类深度学习模型
设置培训的关键参数
**BASE_PATH = 'Data\\dogs-vs-cats\\train\\'
TRAIN_PATH='Data\\dogs-vs-cats\\train_data\\'
VAL_PATH='Data\\dogs-vs-cats\\validation_data\\'batch_size = 32
epochs = 60
IMG_HEIGHT = 150
IMG_WIDTH = 150**
重新缩放并对训练图像应用不同的增强
**train_image_generator = ImageDataGenerator( rescale=1./255, rotation_range=45, width_shift_range=.15, height_shift_range=.15, horizontal_flip=True, zoom_range=0.3)**
重新调整验证数据
**validation_image_generator = ImageDataGenerator(rescale=1./255)**
为训练和验证数据集生成批量归一化数据
**train_data_gen = train_image_generator.flow_from_directory(batch_size = batch_size, directory=TRAIN_PATH, shuffle=True, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')val_data_gen = validation_image_generator.flow_from_directory(batch_size = batch_size, directory=VAL_PATH, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')**
创建并编译模型
**def create_model():
model = Sequential([
Conv2D(16, 3, padding='same', activation='relu',
input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
MaxPooling2D(),
Dropout(0.2),
Conv2D(32, 3, padding='same', activation='relu'),
MaxPooling2D(),
Conv2D(64, 3, padding='same', activation='relu'),
MaxPooling2D(),
Conv2D(128, 3, padding='same', activation='relu'),
MaxPooling2D(),
Dropout(0.2),
Flatten(),
Dense(512, activation='relu'),
Dense(2, activation='softmax')])
return model**# Create and Compile the model
**model= create_model()
model.compile(optimizer='adam', loss='categorical_crossentropy',
metrics=['accuracy'])**
使用张量板拟合模型
为了可视化损失和准确性、模型图以及权重和偏差的直方图,您需要创建日志并存储详细信息。TensorBoard 将使用这些日志文件来显示详细信息。
要创建日志文件,在拟合模型时使用TF . keras . callbacks . tensor board。
**log_dir=r'\Tensorboard_ex\logs_1\fit\' + datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
tensorboard_callback= tf.keras.callbacks.TensorBoard(log_dir=log_dir,
histogram_freq=1)**
为了对每个层和每个时期的权重和偏差进行直方图计算,我们将 histogram_freq=1 设置为默认关闭。
拟合模型
**model.fit_generator(
train_data_gen,
steps_per_epoch=1000,
epochs=epochs,
validation_data=val_data_gen,
validation_steps=1000,
callbacks=[tensorboard_callback]
)**
可视化张量板仪表盘
您可以使用 Jupyter 笔记本中的不同命令查看 TensorBoard 仪表板
%tensorboard --logdir="\TEnsorBoard_ex\logs_1\fit"
或者
**%tensorboard — logdir log_dir**
或者
通过指定存储事件日志的目录来启动 TensorBoard。这些事件日志是在我们拟合模型时创建的。
python -m tensorboard.main — logdir=”TensorBoard_ex\logs_1\fit”
在 Chrome 地址栏输入 http://localhost:6006/ ,你现在可以看到 Tensorboard
标量信息,包含 20 个时期内训练和验证数据集的准确性和损失
带有图像分类模型图形的张量板显示模型是否构建正确
你也可以查看非标量张量在多次迭代中的分布
直方图在多次迭代中显示相同的张量非标量张量变量,但显示为三维直方图
TensorBoard 常见问题
当前数据集没有活动的仪表板
杀死所有当前运行的 Tensorboard 任务。在命令提示符下键入以下命令
**taskkill /im tensorboard.exe /f**
**del /q %TMP%\.tensorboard-info\***
现在,通过指定存储事件日志的目录来启动 TensorBoard。这些事件日志是在我们拟合模型时创建的。
python -m tensorboard.main — logdir=”TensorBoard_ex\logs_1\fit”
有时,您可能需要多次尝试这些语句才能查看 Tensorboard 仪表板。
结论:
针对模型的不同元素的 TensorBoard 可视化使得更容易调试和优化深度学习模型实验,以获得更好的准确性。
下一步是什么?
在下一篇文章中,您将学习使用 TensorBoard 使用超参数调整来微调深度学习模型。
参考资料和灵感:
[## TensorBoard | TensorFlow 入门
在机器学习中,要改进某样东西,你通常需要能够测量它。TensorBoard 是一款提供…
www.tensorflow.org](https://www.tensorflow.org/tensorboard/get_started) [## Jupyter 中的 TensorBoard“本地主机拒绝连接”问题(Windows 问题#2481 …
解散 GitHub 是超过 5000 万开发者的家园,他们一起工作来托管和审查代码,管理项目,以及…
github.com](https://github.com/tensorflow/tensorboard/issues/2481) [## [Windows]tensor board-需要从与 logdir 问题#7856 相同的驱动器启动…
解散 GitHub 是超过 5000 万开发者的家园,他们一起工作来托管和审查代码,管理项目,以及…
github.com](https://github.com/tensorflow/tensorflow/issues/7856)
张量板:超参数优化
了解如何使用 TensorBoard 的 HParamas 仪表板为深度学习模型找到最佳超参数。
先决条件:
tensor board——tensor flow 模型的可视化套件
在本文中,您将学习超参数优化,然后使用 TensorBoard 显示超参数优化的结果。
深度神经网络背景下的超参数是什么?
你在深度学习神经网络中的目标是找到节点的权重,这将帮助我们理解图像、任何文本或语音中的数据模式。
为此,您可以使用为模型提供最佳准确度和精度的值来设计神经网络参数。
那么,这些被称为超参数的参数是什么呢?
用于训练神经网络模型的不同参数称为超参数。这些超参数像旋钮一样被调整,以提高神经网络的性能,从而产生优化的模型。
神经网络中的一些超参数是
- 隐藏层数
- 隐藏层中单元或节点的数量
- 学习率
- 辍学率
- 历元或迭代
- 像 SGD,Adam,AdaGrad,Rmsprop 等优化者。
- 激活功能,如 ReLU、sigmoid、leaky ReLU 等。
- 批量大小
如何实现超参数优化?
超参数优化是找到超参数值的过程,如优化器、学习率、辍学率等。深度学习算法的一部分,它将提供最好的模型性能。
您可以使用以下技术执行超参数优化。
- 手动搜索
- 网格搜索 : 对产生笛卡尔积的指定超参数的所有可能组合进行彻底搜索。
- 随机搜索 : 随机选择超参数,并不是每个超参数组合都尝试。随着超参数数量的增加,随机搜索是更好的选择,因为它可以更快地获得超参数的良好组合。
- 贝叶斯优化:In 合并关于超参数的先验数据,包括模型的精度或损失。先验信息有助于确定模型超参数选择的更好近似。
为了可视化 TensorBoard 上模型的超参数调整,我们将使用网格搜索技术,其中我们将使用一些超参数,如节点数量、不同的优化器或学习率以及不同的退出率,并查看模型的准确性和损失。
为什么要用 TensorBoard 进行超参数优化?
一张图胜过千言万语,这也适用于复杂的深度学习模型。深度学习模型被认为是一个黑匣子,你发送一些输入数据,模型进行一些复杂的计算,瞧,你现在有你的结果了!!!
TensorBoard 是 Tensorflow 的一个可视化工具包,用于显示不同的指标、参数和其他可视化内容,帮助调试、跟踪、微调、优化和共享您的深度学习实验结果
有了 TensorBoard,可以在每个历元跟踪模型的精度和损耗;并且还具有不同的超参数值。超参数不同值的跟踪精度将帮助您更快地微调模型。
最后,下面是用 Python 实现的代码……
我们将使用 TensorBoard 使用猫狗数据集可视化标量、图形和分布。
导入所需的库
导入 TensorFlow 和 TensorBoard HParams 插件以及 Keras 库,用于预处理图像和创建模型。
**import tensorflow as tf
from tensorboard.plugins.hparams import api as hp
import datetime
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img
import numpy as np**
我用过 TensorFlow 2.0.0 版本。
加载 TensorBoard 笔记本扩展
# Load the TensorBoard notebook extension
**%load_ext tensorboard**
创建图像分类深度学习模型
设置培训的关键参数
**BASE_PATH = 'Data\\dogs-vs-cats\\train\\'
TRAIN_PATH='Data\\dogs-vs-cats\\train_data\\'
VAL_PATH='Data\\dogs-vs-cats\\validation_data\\'batch_size = 32
epochs = 5
IMG_HEIGHT = 150
IMG_WIDTH = 150**
重新缩放并对训练图像应用不同的增强
**train_image_generator = ImageDataGenerator( rescale=1./255, rotation_range=45, width_shift_range=.15, height_shift_range=.15, horizontal_flip=True, zoom_range=0.3)**
重新调整验证数据
**validation_image_generator = ImageDataGenerator(rescale=1./255)**
为训练和验证数据集生成批量归一化数据
**train_data_gen = train_image_generator.flow_from_directory(batch_size = batch_size, directory=TRAIN_PATH, shuffle=True, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')val_data_gen = validation_image_generator.flow_from_directory(batch_size = batch_size, directory=VAL_PATH, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')**
为网格搜索设置超参数
我们通过列出超参数的不同值或值范围,使用四个超参数来运行我们的实验。
对于离散的超参数,会尝试所有可能的参数组合,对于实值参数,只会使用上下界。
- 第一密集层单元数:256 和 512
- 辍学率:范围在 0.1 到 0.2 之间。因此将使用 0.1 和 0.2 的辍学率。
- 优化者:亚当、SGD 和 rmsprop
- 优化器的学习率:0.001,0.0001,0.0005,
我们还将指标设置为显示在 TensorBoard 上的精确度
## Create hyperparameters
**HP_NUM_UNITS=hp.HParam('num_units', hp.Discrete([ 256, 512]))
HP_DROPOUT=hp.HParam('dropout', hp.RealInterval(0.1, 0.2))
HP_LEARNING_RATE= hp.HParam('learning_rate', hp.Discrete([0.001, 0.0005, 0.0001]))
HP_OPTIMIZER=hp.HParam('optimizer', hp.Discrete(['adam', 'sgd', 'rmsprop']))****METRIC_ACCURACY='accuracy'**
创建和配置日志文件
**log_dir ='\\logs\\fit\\' + datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
with tf.summary.create_file_writer(log_dir).as_default():
hp.hparams_config(
hparams=
[HP_NUM_UNITS, HP_DROPOUT, HP_OPTIMIZER, HP_LEARNING_RATE],
metrics=[hp.Metric(METRIC_ACCURACY, display_name='Accuracy')],
)**
创建、编译和拟合模型
超参数不是硬编码的,而是取自 hparams 字典的不同参数:**HP _ dropout 对于 dropout,HP_NUM_UNITS 对于第一个密集层中的单元数,HP_OPTIMIZER 设置不同的优化器。**我们采用使用的优化器,并根据 HP_LEARNING_RATE 设置学习率。
该函数返回最后一个纪元的验证精度。
**def create_model(hparams):
model = Sequential([
Conv2D(64, 3, padding='same', activation='relu',
input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
MaxPooling2D(),**
#setting the Drop out value based on HParam
**Dropout(hparams[HP_DROPOUT]),
Conv2D(128, 3, padding='same', activation='relu'),
MaxPooling2D(),
Dropout(hparams[HP_DROPOUT]),
Flatten(),
Dense(hparams[HP_NUM_UNITS], activation='relu'),
Dense(2, activation='softmax')])**
#setting the optimizer and learning rate
**optimizer = hparams[HP_OPTIMIZER]
learning_rate = hparams[HP_LEARNING_RATE]
if optimizer == "adam":
optimizer = tf.optimizers.Adam(learning_rate=learning_rate)
elif optimizer == "sgd":
optimizer = tf.optimizers.SGD(learning_rate=learning_rate)
elif optimizer=='rmsprop':
optimizer = tf.optimizers.RMSprop(learning_rate=learning_rate)
else:
raise ValueError("unexpected optimizer name: %r" % (optimizer_name,))**
# Comiple the mode with the optimizer and learninf rate specified in hparams
**model.compile(optimizer=optimizer,
loss='categorical_crossentropy',
metrics=['accuracy'])**
#Fit the model
**history=model.fit_generator(
train_data_gen,
steps_per_epoch=1000,
epochs=epochs,
validation_data=val_data_gen,
validation_steps=1000,
callbacks=[
tf.keras.callbacks.TensorBoard(log_dir), # log metrics
hp.KerasCallback(log_dir, hparams),# log hparams
])
return history.history['val_accuracy'][-1]**
对于模型的每次运行,记录带有超参数和最终历元精度的 hparams 摘要。我们需要将最后一个时期的验证精度转换为标量值。
**def run(run_dir, hparams):
with tf.summary.create_file_writer(run_dir).as_default():
hp.hparams(hparams)** # record the values used in this trial
**accuracy = create_model(hparams)**
#converting to tf scalar
**accuracy= tf.reshape(tf.convert_to_tensor(accuracy), []).numpy()
tf.summary.scalar(METRIC_ACCURACY, accuracy, step=1)**
使用不同的超参数值运行模型
这里的实验使用网格搜索,并测试第一层的单元数、辍学率、优化器及其学习率的超参数的所有可能组合,精确度用于精确度。
**session_num = 0****for num_units in HP_NUM_UNITS.domain.values:
for dropout_rate in (HP_DROPOUT.domain.min_value, HP_DROPOUT.domain.max_value):
for optimizer in HP_OPTIMIZER.domain.values:
for learning_rate in HP_LEARNING_RATE.domain.values:
hparams = {
HP_NUM_UNITS: num_units,
HP_DROPOUT: dropout_rate,
HP_OPTIMIZER: optimizer,
HP_LEARNING_RATE: learning_rate,
}
run_name = "run-%d" % session_num
print('--- Starting trial: %s' % run_name)
print({h.name: hparams[h] for h in hparams})
run('logs/hparam_tuning/' + run_name, hparams)
session_num += 1**
HParams 仪表板中结果的可视化
您可以使用不同的命令查看 HParams TensorBoard 仪表板:在 Jupyter notebook 中或使用 cmd
使用 cmd
您将通过使用以下命令提供存储不同运行日志的目录路径来显示 Hparam 仪表板
python -m tensorboard.main --logdir="logs/hparam_tuning"
当按降序对准确度排序时,可以看到优化最多的模型是 256 个单元,辍学率为 0.2,rmsprop 优化器的学习率为 0.0005。
使用 Jupyter 笔记本
%tensorboard --logdir='\logs\hparam_tuning'
您也可以查看平行坐标视图,显示每个超参数的单次运行并显示精确度
Tensorboard Hparams 仪表板有助于找到最佳的超参数,以获得最佳的模型精度
结论:
张量板超参数调整提供了一种直观的方式来了解哪些超参数可用于微调深度学习模型以获得最佳精度
参考资料:
https://github.com/tensorflow/tensorboard/issues/3688
https://www . tensor flow . org/tensor board/hyperparameter _ tuning _ with _ hparams
TensorFlow 2.0: tf.function 和亲笔签名
随着 TensorFlow (TF) 2.0 的出现,tf.function
的引入为 TF 1.0 带来了一些有用的改进,最显著的是 AutoGraph 的引入。
TensorFlow 是如何工作的?
基本上,TensorFlow 通过计算图运行,即节点图用于表示一系列 TensorFlow 操作。
然而,编译张量流图可能是一个繁琐的过程,因为语法结构与 Python 明显不同,并且图代码比简单的 Python 结构使用更多的资源。
AutoGraph 的目的是通过把用 Python 的经典语法结构编写的代码转换成 TensorFlow 图兼容代码来图形化代码。这使得使用 Python 和 TensorFlow 更加直观,因为它简化了从 Python 代码到图形代码的转换。
更重要的是,tf.function
允许直观地使用急切执行和自动签名,从而可以使用 Python 语法运行一个函数,然后转换成等价的图形代码。
我们举个例子。
急切执行:循环函数的区域
这是一个计算圆面积的 Python 函数。在 TF 2.0 中默认开启急切执行,值 r (圆的半径)被定义为tf.Variable
。
>>> import tensorflow as tf
>>> tf.executing_eagerly()
>>> r = tf.Variable(10.0, name="r")>>> def area(r):
>>> circle=3.14*(r**2.00)
>>> return circle>>> area(10)314.0>>> print('Areas of respective circles: %2.2f, %2.2f, %2.2f' % (area(tf.constant(50.0)), area(tf.constant(100.0)), area(tf.constant(150.0))))Areas of respective circles: 7850.00, 31400.00, 70650.00
用 tf 签名。会议
现在,假设有人希望使用 AutoGraph 实现这个代码。使用 TF 1.0 约定,这可能是使用tf.Session
实现的。以前,图形必须在会话中显式启动才能运行。
>>> r = tf.Variable(10.0, name="r")>>> def area(r):
>>> circle=3.14*(r**2.00)
>>> return circle>>> print(tf.autograph.to_code(area))def tf__area(r):
do_return = False
retval_ = ag__.UndefinedReturnValue()
circle = 3.14 * r ** 2.0
do_return = True
retval_ = circle
cond = ag__.is_undefined_return(retval_) def get_state():
return () def set_state(_):
pass def if_true():
retval_ = None
return retval_ def if_false():
return retval_
retval_ = ag__.if_stmt(cond, if_true, if_false, get_state, set_state)
return retval_
这是一个图形格式的代码示例。
>>> tf_area = tf.autograph.to_graph(area)
>>> tf_area<function __main__.create_converted_entity_factory.<locals>.create_converted_entity.<locals>.tf__area(r)>>>> print(tf_area)<function create_converted_entity_factory.<locals>.create_converted_entity.<locals>.tf__area at 0x7f7b57f8a620>>>> with tf.Graph().as_default():
a1 = tf_area(tf.constant(50.0))
a2 = tf_area(tf.constant(100.0))
a3 = tf_area(tf.constant(150.0))
with tf.compat.v1.Session() as sess:
print('Areas of respective circles (graph results): %2.2f, %2.2f, %2.2f\n' % (sess.run(a1), sess.run(a2), sess.run(a3)))Areas of respective circles (graph results): 7850.00, 31400.00, 70650.00
使用会话意味着上面计算的圆的面积值将只在会话本身内计算。
使用 tf.function 自动签名
让我们看看如何使用tf.function
来实现这一点。
>>> r = tf.Variable(10.0, name="r")>>> @tf.function
>>> def area(r):
>>> circle=3.14*(r**2.00)
>>> return circle>>> print(tf.autograph.to_code(area.python_function))def tf__area(r):
do_return = False
retval_ = ag__.UndefinedReturnValue()
circle = 3.14 * r ** 2.0
do_return = True
retval_ = circle
cond = ag__.is_undefined_return(retval_) def get_state():
return () def set_state(_):
pass def if_true():
retval_ = None
return retval_ def if_false():
return retval_
retval_ = ag__.if_stmt(cond, if_true, if_false, get_state, set_state)
return retval_>>> area(60)<tf.Tensor: id=13, shape=(), dtype=float32, numpy=11304.0>>>> print(area(tf.constant(50.00)), area(tf.constant(100.0)), area(tf.constant(150.0)))tf.Tensor(7850.0005, shape=(), dtype=float32) tf.Tensor(31400.002, shape=(), dtype=float32) tf.Tensor(70650.0, shape=(), dtype=float32)
从上面可以看出,计算面积值不需要会话。而是用tf.function
指定可以直接转换为 AutoGraph 的函数,然后面积值计算为张量。
结论
在这个例子中,我们看到了 TF 2.0 如何通过使用tf.function
和 AutoGraph 来简化 TensorFlow 中的函数实现。非常感谢您的时间,您还可以在 michael-grogan.com找到更多使用 TensorFlow 和 Keras 的机器学习示例。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。
TensorFlow 2.1:操作指南
我们深入吧!—Dids的惊人画面
keras 模式、渴望模式和图形模式
如果你和我一样,是一个正常人,你可能已经经历过有时你是如何陷入你的应用程序的开发中,以至于很难找到一个时刻停下来思考我们是否以最有效的方式做事:我们是否使用了正确的工具?哪个框架最适合我的用例?这种方法可扩展吗?我们考虑到可伸缩性了吗?
在 AI 领域更是如此。我们都知道人工智能是一个快速发展的领域。每天都有新的研究发表。正在高速开发的主要人工智能框架之间存在巨大的竞争。新的硬件架构、芯片和优化被发布以支持日益增长的人工智能应用的部署…然而,尽管有所有的华而不实,有时你需要停下来重新考虑。
什么时候是停下来重新考虑的好时机?只有你会知道。对我来说,这一刻是最近才到来的。自从我进入这个领域以来,我一直在工作和个人项目中使用 Keras 和 Tensorflow 1.x (TF1)。我完全喜欢 Keras 库的高级方法和 Tensorlfow 的低级方法,当您需要更多定制时,它们可以让您在引擎盖下进行更改。
尽管我是 Keras-Tensorflow 婚姻的超级粉丝,但总有一个非常具体的负面因素让这对夫妇远离田园生活:调试功能。正如你已经知道的,在 Tensorflow 中,有一个先定义计算图,然后编译它(或者移动到 GPU)然后非常高效地运行它的范例。这种范式非常好,从技术上讲也很有意义,但是,一旦你在 GPU 中有了模型,就几乎不可能调试它。
这就是为什么在 TensorFlow 2.0 的 alpha 版本发布大约一年后,我决定尝试 TensorFlow 2.1(我可以从 TF2.0 开始,但我们都知道我们喜欢新软件),并与您分享它的进展。
张量流 2.1
令人难过的事实是,我很难弄清楚我应该如何使用这个新的 TensorFlow 版本,即著名的 2.1 稳定版本。我知道,这里有大量的教程,笔记本和代码手册……然而,我发现困难并不在于编程,因为归根结底,这只是 Python,而是范式的转变。简而言之:TensorFlow 2 编程不同于 TensorFlow 1,就像面向对象编程不同于函数式编程一样。
在做了一些实验后,我发现在 TensorFlow 2.1 中有 3 种建模方法:
- Keras 模式(
tf.keras
):基于图形定义,稍后运行图形。 - 急切模式:基于定义一个迭代地执行定义一个图的所有操作。
- 图表模式(
tf.function
):之前两种方法的混合。
无聊到此为止。给我看看代码!
Keras 模式
这是我们都习惯的标准用法。只使用普通的 Keras 和自定义损失函数,以平方误差损失为特征。该网络是一个 3 密集层深度网络。
# The network
x = Input(shape=[20])
h = Dense(units=20, activation='relu')(x)
h = Dense(units=10, activation='relu')(h)
y = Dense(units=1)(h)
这里的目标是教一个网络学习如何对一个 20 个元素的向量求和。因此,我们向网络输入一个数据集[10000 x 20]
,即 10000 个样本,每个样本有 20 个特征(要求和的元素)。那是在:
# Training samples
train_samples = tf.random.normal(shape=(10000, 20))
train_targets = tf.reduce_sum(train_samples, axis=-1)
test_samples = tf.random.normal(shape=(100, 20))
test_targets = tf.reduce_sum(test_samples, axis=-1)
我们可以运行这个例子,得到通常漂亮的 Keras 输出:
Epoch 1/10
10000/10000 [==============================] - 10s 1ms/sample - loss: 1.6754 - val_loss: 0.0481
Epoch 2/10
10000/10000 [==============================] - 10s 981us/sample - loss: 0.0227 - val_loss: 0.0116
Epoch 3/10
10000/10000 [==============================] - 10s 971us/sample - loss: 0.0101 - val_loss: 0.0070
这里发生了什么?嗯,没什么,只是一个 Keras 玩具的例子训练在 10 秒每时代(在一个英伟达 GTX 1080 Ti)。编程范式呢?和之前一样,就像在 TF1.x 中,你定义了图形,然后你通过调用keras.models.Model.fit
来运行它。调试功能呢?和以前一样…没有。你甚至不能在损失函数中设置一个简单的断点。
运行完这段代码后,您可能会疑惑一个非常明显的问题:TensorFlow 2 版本承诺的所有优秀特性都到哪里去了?你是对的。如果与 Keras 包的集成意味着不需要安装额外的包,那么优势是什么?
除此之外,还有一个更重要的问题:众所周知的调试特性在哪里?幸运的是,这是急切模式来拯救。
渴望模式
如果我告诉你有一种方法可以交互地构建你的模型,并且可以访问运行时的所有操作,那会怎么样?— 如果你激动得发抖,这意味着你已经经历了 10 个纪元后随机批次运行时错误的深刻痛苦……是的,我知道,我也去过那里,我们可以在那些战斗后开始称自己为战友。
嗯,是的,这就是你要找的操作模式。在这种模式下,所有的张量操作都是交互式的,你可以设置一个断点并访问任何中间张量变量。然而,这种灵活性是有代价的:更显式的代码。让我们来看看:
阅读完代码后,你脑海中出现的第一件事可能是:许多代码只是为了做一个model.compile
和一个model.fit
。是的,没错。但另一方面,你可以控制之前发生的一切。引擎盖下发生了什么?训练循环。
所以现在情况变了。在这种方法中,你可以从头开始设计事情如何进行。以下是您现在可以指定的内容:
- 度量:曾经想要测量每个样本、批次或任何其他自定义统计的结果吗?没问题,我们掩护你。现在,您可以使用传统的移动平均线或任何其他自定义指标。
- 损失函数:曾经想做疯狂的多参数依赖损失函数?嗯,这也解决了,你可以在损失函数定义中得到所有你想要的技巧,而不用 Keras 用它的
_standarize_user_data
( 链接)来抱怨它 - 梯度:您可以访问梯度,并定义向前和向后传递的细节。是的,最后,请和我一起欢呼吧!
这些指标是用新的tf.keras.metrics
API 指定的。您只需获取想要的指标,定义它并像这样使用它:
# Getting metric instanced
metric = tf.keras.metrics.Mean() # Run your model to get the loss and update the metric
loss = [...]
metric(loss)# Print the metric
print('Training Loss: %.3f' % metric.result().numpy())
损失函数和梯度分别在正向和反向过程中计算。在这种方法中,向前传球必须由tf.GradientTape
记录。tf.GradientTape
将跟踪(或记录)前向传递中完成的所有张量操作,因此它可以计算后向传递中的梯度。换句话说:为了向后跑,你必须记住你向前走过的路。
# Forward pass: needs to be recorded by gradient tape
with tf.GradientTape() as tape:
y_pred = model(x)
loss = loss_compute(y_true, y_pred)# Backward pass:
gradients = tape.gradient(loss, model.trainable_weights)
optimizer.apply_gradients(zip(gradients, model.trainable_weights))
这非常简单,在向前传递中,你运行你的预测,并通过计算损失来看你做得有多好。在反向过程中,通过计算梯度来检查权重如何影响损失,然后通过更新权重(在优化器的帮助下)来尝试最小化损失。
您还可以在代码中注意到,在每个时期结束时,会计算验证损失(通过只运行正向传递而不更新权重)。
让我们看看这与之前的方法相比如何(我将输出减少了一点,这样它就可以放在这里):
Epoch 1:
Loss: 1.310: 100%|███████████| 10000/10000 [00:41<00:00, 239.70it/s]
Epoch 2:
Loss: 0.018: 100%|███████████| 10000/10000 [00:41<00:00, 240.21it/s]
Epoch 3:
Loss: 0.010: 100%|███████████| 10000/10000 [00:41<00:00, 239.28it/s]
发生了什么事?你注意到了吗?在同一台机器上,每个时期花费了 41 秒,即 4 倍的时间增量…这只是一个虚拟模型。你能想象这对一个真实的用例模型,如 RetinaNet、YOLO 或 MaskRCNN,会有多大的影响吗?
幸运的是,优秀的 TensorFlow 人员意识到了这一点,并实现了图形模式。
图表模式
图表模式(来自 AutoGraph 或tf.function
)是前两种模式的混合模式。这里的这里的和这里的这里的你可以大致了解一下这是什么。但是我发现那些指南有点混乱,所以我用我自己的话来解释。
如果 Keras 模式是关于定义图形并稍后在 GPU 中运行它,而 eager 模式是关于交互式地执行每个步骤,那么 graph 模式允许您像在 eager 模式中一样编码,但运行训练的速度几乎与在 Keras 模式中一样快(所以是的,在 GPU 中)。
关于 eager 模式的唯一变化是,在 graph 模式中,您将代码分解成小函数,并用@tf.function
对这些函数进行注释。让我们来看看事情是如何变化的:
现在你可以看到前向和后向传递计算是如何被重构为两个用@tf.function
装饰器注释的函数的。
那么这里到底发生了什么?简单。每当你用@tf.function
decorator 注释一个函数时,你就像 Keras 一样将这些操作“编译”到 GPU 中。因此,通过注释您的函数,您可以告诉 TensorFlow 在 GPU 中的优化图形中运行这些操作。
在引擎盖下,真正发生的是函数正在被亲笔、tf.autograph
解析。AutoGraph 将获取函数输入和输出,并从它们生成一个张量流图,这意味着,它将解析操作,以将输入的输出转换为张量流图。这个生成的图形将非常高效地运行到 GPU 中。
这就是为什么它是一种混合模式,因为除了用@tf.function
装饰器注释的操作之外,所有的操作都是交互运行的。
这也意味着你可以访问所有的变量和张量,除了用@tf.function
修饰的函数中的变量和张量,你只能访问它的输入和输出。这种方法建立了一种非常清晰的调试方式,在这种方式下,您可以在渴望模式下开始交互式开发,然后,当您的模型准备就绪时,使用@tf.function
将其推向生产性能。听起来不错吧?让我们看看进展如何:
Epoch 1:
Loss: 1.438: 100%|████████████| 10000/10000 [00:16<00:00, 612.3it/s]
Epoch 2:
Loss: 0.015: 100%|████████████| 10000/10000 [00:16<00:00, 615.0it/s]
Epoch 3:
Loss: 0.009: 72%|████████████| 7219/10000 [00:11<00:04, 635.1it/s]
嗯,一个惊人的 16s/纪元。您可能认为它不如 Keras 模式快,但另一方面,您可以获得所有的调试功能和非常接近的性能。
结论
如果您一直在关注这篇文章,那么您将不会感到惊讶,所有这些最终都归结为一个非常古老的软件问题:灵活性还是效率?渴望模式还是 Keras 模式?为什么要和解?使用图形模式!
在我看来,TensorFlow 的工作人员在为我们这些开发人员提供更多灵活性方面做得非常出色,而且没有在效率方面做出太大的牺牲。所以从我的立场来看,我只能为他们说 bravo 。
TensorFlow 2.2+和自定义培训逻辑
最近的 TensorFlow 版本引入了 tf.keras.Model 的新方法 train_step 和 test_step ,极大地改进了我们处理自定义训练循环的方式。我会告诉你怎么做。
来源:pexels.com
大家都熟悉标准模型——一个输入、一个输出、一个损失函数。也许还有几个指标。你可以用顺序或功能的方式定义模型,编译并训练它。
有时候,会有多个输入。或多路输出。这仍然可以用这些 API 来实现。假设输出是“独立的”——损失函数(一个用于所有输出或一个用于每个输出)分别对每个输出起作用。
有时候,我们需要更进一步。也许损失是从更多的输出中计算出来的。或者有一些用于计算损失的附加信息。这是必须实现定制训练循环的时候。
在这篇博文中,我不会涉及这样的具体用例。相反,我将向您展示一个简化的示例,说明如何训练它的三种方式。稍后,我将继续一个更复杂的例子。敬请关注!
示例问题
即使在教程里,我也喜欢可运行的代码,甚至现实世界的问题。因此,让我们以 TensorFlow 数据集之一—bean为例。
Beans 是一个使用智能手机相机在田间拍摄的豆子图像数据集。它包括 3 个类别:2 个疾病类别和健康类别。描述的疾病包括角斑病和豆锈病。乌干达国家作物资源研究所的专家对数据进行了注释,数据由 Makerere AI 研究实验室收集。
它相当小(171.63 MiB,1295 张图片),使我们能够快速训练我们的模型。即使经过少量的时期,结果看起来也是有希望的。
来自 beans 数据集的示例图像和标签,来源tensorflow.org
这里是后面三章的通用代码,主要包含数据集的准备和训练设置。这与我们的主题没有直接联系,我不想让文章太长,所以如果你愿意,请查看随附的要点。
标准编译和拟合方法
这就是如何定义和训练一个简单的转移模型。没什么神秘的,但为了完整起见我会在这里展示一下。
像往常一样训练。关于导入、参数和数据集定义,参见该代码。
我使用了 MobileNet 和几个附加层。globalaveragepool2d将要素转换为单个矢量,该矢量可用于最终的密集图层。中间我用了辍学。请记住,数据集非常小,我们必须小心过度拟合。剩下的就简单了。
用老方法定制训练逻辑
自定义训练循环的详细描述可在官方文档中找到。如果我们将这一点转化为上一段中的问题,我们会得到以下代码:
自定义训练循环。关于导入、参数和数据集定义,参见该代码。
你看到所有的代码,只是关心跟踪损失和准确性?现在假设您有多个指标和几个回调,例如包括 TensorBoard。你想要更好的输出。代码开始越来越多的扩展。
未来:train_step()和 test_step()
从 TensorFlow 2.2 开始,不再需要所有这些锅炉板代码。你只需要告诉 TensorFlow 每个单独的训练步骤(可能还有测试步骤)将会是什么样子。其余的在 tf.keras.Model 类中完成。我们就不拐弯抹角了,下面是代码:
使用 train_step()和 test step()的示例。关于导入、参数和数据集定义,参见该代码。
我想指出两件事:
- 输入的格式通常是 x,y 和可选的样品重量。您不需要在子类中遵守这一点。不过要小心,这是有后果的。很明显,您不能再使用 fit 方法的
class_weight
参数。即使您只向这三个参数添加了额外参数,也是如此。 - 您可以使用已编译的 loss(损失)、metrics 和 optimizer。我建议你尽可能经常这样做。但是通常,当这些还不够时,您会覆盖这个方法。所以不要害怕从一个
compile
方法中去掉一些,定义你自己的或者完全离开compile()
。
没有编译的优化器、损失和指标
那么当你不考虑compile()
的时候会发生什么呢?您仍然需要添加一些代码。但是请注意与完全定制的训练循环的巨大差异。您现在正在指定一个步骤的行为。因此,父模型类可以负责所有的支持功能。例如打印漂亮的进度条或者调用所有的回调函数。(它们可以以通常的方式作为参数提供给fit
方法。)
我试图将所有重要的注释写入代码本身。因此,我在此仅补充一点。可以省略metric()
属性,直接返回train_step()
和test_step()
中的 name: (float) value 对。进度输出将是正常的,你会看到一个平均值。但不是在你的试镜中。在那里,您将获得与您返回的值完全相同的值。
最后的想法
在这篇博文中,我向您介绍了在 TensorFlow 中训练模型的不同方法,包括覆盖 Keras 模型的新方法train_step
和test_step
。这个例子使用起来太简单了。但我希望它能达到演示不同方法的目的,而不需要解释复杂的模型。我想做的比一些即将到来的文章。
如果你觉得这有用,请给我留言。如果你有任何问题,请随时这样做。我很乐意回答你。
张量流和图像增强
是的,我知道。你已经听说过很多图像增强。但是你猜怎么着!无论如何我都想给你一个惊喜。让我们开始吧!
来源:Pexels.com
A 增强是一种众所周知的防止深度神经网络过度拟合的技术。如果你做得好,你可以增加你的数据集,而不需要收集和注释新数据的高成本。Python 中有很多这方面的库,但我想主要谈谈 TensorFlow。
快速概览
为了获得最佳性能,应该考虑使用 tf.data API。第一眼看上去很好看。使用简单,有效。过了一会儿,你开始想知道图像放大。我如何把它们放进去?
如果你习惯了像*imgaug,albuminations*甚至 OpenCV ,你需要一个TF . py _ function的帮助。但是一些纯张量流解呢?有些可以编译成图表运行。
tf .图像
这个 是你可能会看的第一个地方。至少我做到了。有一些非常有用的函数,例如:
- 图像调整—亮度、对比度、饱和度、JPEG 质量等。
- 裁剪。
- 翻转、旋转和换位—向左/向右翻转、向上/向下翻转、旋转 90 度。不幸的是不是完整的旋转。
tfa .图像
图片 包来自tensor flow Addons是另一个你应该定期检查的包。不仅仅是增强,还有附加层、损耗、优化器等等。但是现在回到赛道上。我们有什么?主要是旋转、剪切和一般的平移和变换。
图像检测?
不幸的是,这两个人都对图像检测不感兴趣。是的,你可以翻转图像。但是选择的边界框呢?!我猜你不想失去他们!
来源:Pexels.com
我们的图书馆— tf-image
**如果你渴望自己查库,请访问我们的 GitHub 资源库 。全部是开源的。我们只是刚刚开始,所以我们欢迎任何关于你喜欢(不喜欢)或你需要什么的反馈。如果你觉得它有用,我们将非常感谢你的代码贡献!
不同的工作流程
这篇博文旨在介绍这个库,而不是完整的手册或对每个功能的描述。因此,我将仅限于举两个例子。
多合一
为了使增强尽可能简单,有 random_augmentations 函数。您提供图像、增强设置和可选的边界框。剩下的就是函数本身了。此外,增强是以随机顺序执行的,以使该过程更加强大。
很酷,但在某些方面有局限性。主要是这个设置太简单了。每个选项都有许多参数。将来我们会推出更先进的设置。但是现在,您可以使用我们库的所有其他部分轻松创建自己的增强功能。
使用核心功能
使用核心函数也很容易。为了向您展示 random _ function(random _ function _ bbox es),我在下面演示了一个例子。如果你不想每次都应用你的增强,这是一个值得一看的地方。
结论
今天到此为止。如果你已经走了这么远,谢谢你。请,评论,批评,分享,发送合并请求!
我要感谢 Michal Lukac 与我在这个项目上的合作,感谢 Ximilar 给了我这个机会和一个积极的方法来开源我们的一些工作。
Tensorflow 最佳实践:命名输入和输出
根据位置索引和输入值排序退出。开始依赖指定的输入和输出。避免数据布线错误
命名输入和输出本质上是带有字符串关键字和张量值的字典。
利益
- 防止特征重新排序
- 提供签名和元数据的自给自足模型
- 重命名和缺少功能保护
大多数机器学习管道从结构化源(数据库、CSV 文件/ Pandas 数据帧、TF 记录)读取数据,执行特征选择、清理(以及可能的)预处理,将原始多维数组(张量)与表示每个输入样本的正确预测的另一个张量一起传递给模型。
在生产中重新排序或重命名输入特征? → 无效结果或客户端生产中断
缺项特征?缺失数据?产值解读不好?误混整数索引? → 无效结果或客户端生产中断
想知道训练时使用了哪些特征列,以便为推理提供相同的特征列? → 你不能——曲解错误
想知道输出值代表什么值? → 你不能——曲解错误
不要在模型输入层上删除列名。
默认情况下,tf.data.Dataset
已经允许您这样做,将输入视为一个字典。
这些年来,上述问题变得更容易处理了。以下是 Tensorflow 2.x
生态系统中可用解决方案的简要概述。
-
tfRecords 和 TF。示例无疑是用于任何规模深度学习项目的最佳数据格式。默认情况下,每个功能都以命名。
-
Tensorflow Transform 使用指定的输入并产生指定的输出,鼓励你对你的模型做同样的事情。
-
Keras 支持图层字典作为输入和输出
添加多种尺寸的特征很简单:只需为 window_size 添加另一个参数,或者将特征形状与特征名称一起传递。
- TensorSpec 和服务签名定义默认支持命名 IOs。
通过使用这个serving_raw
签名定义,您可以通过 JSON 有效负载直接调用 Tensorflow 服务端点,而无需序列化到tf.Example
。
看看 TF 服务上的metadata
签名,我目前正在做一个比特币预测模式的例子:
最后,如果您正在使用 TFX 或获得了用于输入的协议缓冲模式,您应该使用它来发送用于推断的数据,因为这样会更有效率,并且错误会更快地出现在客户端,而不是服务器端。即使在这种情况下,也要为您的模型使用命名的输入和输出。
感谢一路看完!
还想学习如何正确构建您的下一个机器学习项目吗?
- 查看我的[结构化 ML 管道项目文章](http://Want to create) 。