TowardsDataScience 博客中文翻译 2021(六百四十一)
卡兰·泰勒是一门让事物看起来自然而精致的艺术。在上图中,Karan 小心翼翼地让羊角面包看起来很自然。同样,将机器学习模型转化为少量规则也需要努力。相反,数据科学家建立了 107 个模型的复杂集合,以赢得网飞推荐内容。我们看到深度学习模型拥有 100%的准确率,当仔细检查时,它们充满了方法上的缺陷。这篇文章试图将 sprezzatura 的艺术应用于机器学习。除了获得风格,您的模型将更容易理解,被
学习数据科学的艺术
由 Chanin Nantasenamat 使用 alexacrib 在 envato elements 上的图形创建
入门;数据科学
如何学习数据科学
回到 2020 年 1 月,我发布了一个名为2020 年学习数据科学的策略的视频,分享了我关于如何开始学习数据科学的一些技巧。一年过去了,我认为这将是一个很好的机会来重温这个话题,我写了这篇文章学习数据科学的艺术,分享我关于如何在 2021 年学习数据科学的最佳技巧(以及几位杰出的数据科学家的技巧)。
我最喜欢的一句话是*“学习数据科学的最好方法是做数据科学。”我总是在我的 YouTube 上的每个视频结束时说这句话*但是为了做到这一点,你还需要知道。所以棘手的问题是:
要了解多少才能做数据科学?
让我们在这篇文章中了解一下吧!
**注意:**当你在看的时候,也一定要看看下面我之前创作的同名视频, 学习数据科学的艺术 。
☝️My 关于开始数据科学学习之旅的最佳提示。
1.本文的动机
在 2020 年的过程中,我在解释数据科学的概念时尝试了很多教育技术。其中包括制作 YouTube 视频、写博客、手绘信息图以及创造迷因。除此之外,我很幸运有机会认识其他数据科学家、数据科学 YouTubers(肯·吉、克里斯·纳伊克、马得和丹尼尔·伯克)以及与人工智能相关的公司。此外,我有机会阅读和参与 Twitter 或 LinkedIn 等各种平台上来自观众、其他数据科学家以及数据科学 YouTubers 的数千条评论。
这些评论中最受欢迎的一个问题是:
如何开始学习数据科学?
为了做数据科学,一个人应该知道多少?
正如我之前提到的,在学习数据科学和从事数据科学之间似乎有一个平衡。因此,在你能做数据科学之前,你应该学习多少?
YouTube 上已经有几个视频在谈论如何学习数据科学。著名的数据科学 YouTubers 发布了这样的视频,例如 Ken Jee,他有几个视频与你分享他的最佳技巧,告诉你如果他必须从头开始,他将如何学习数据科学。在 Ken Jee 和 Andrew Mo 长达一小时的合作视频中,他们讨论了如果他们必须重新开始,他们将如何学习数据科学。到 2020 年底,Ken Jee 还发布了一个视频,讲述他将如何在 2021 年重新开始学习数据科学。
今年 2021 年初,Tina Huang 发布了一个病毒视频,讲述了她学习数据科学的最小输入最大输出方法。克里斯·纳伊克和达瓦尔·帕特尔也制作了他们自己的视频,谈论如何聪明地学习数据科学,以及如何免费学习数据科学。此外,Daniel Bourke 分享了机器学习和数据科学中关键主题的思维导图。
在写这篇文章之前,我花了几个月的时间从这些如何学习数据科学视频中收集要点,结合我自己的观点和想法,我总结了如何学习数据科学的所有关键概念。应该注意的是,这适用于有抱负的数据科学家以及希望提高技能或重新提高技能的实践数据科学家,以及希望获得新技术技能以提升其数据分析/数据工程师/数据科学技能的数据分析师或数据工程师。
我的目的是让这篇文章尽可能保持最新,所以请在这篇文章中随意评论这篇文章中缺少的任何学习数据科学的资源。
让我们开始吧!
2.学习数据科学艺术的 4 个步骤
由于一张图片胜过千言万语,因此我想出了这张信息图或卡通插图,总结了您可以在学习数据科学和数据技能时使用的概念框架。这个如何学习数据科学的例子叫做学习数据科学的艺术。
它主要由四个主要步骤组成:
- **第一步。**计划
- **第二步。**学习
- **第三步。**构建
- **第四步。**解释
我们这里有很多内容要讲,所以让我们开始吧!
**图解学习数据科学的艺术。**由 Chanin Nantasenamat(又名数据教授)手绘
3.第一步——计划
3.1.设定学习目标
在开始你的学习之旅之前,设定你的学习目标是非常必要的。考虑以下问题:
你希望从学习数据科学中获得什么?
你打算用这些知识去找工作吗?
你只是出于爱好而使用这些知识吗?
你是否将这些知识应用到你现有的日常工作中。
你有多少时间学习?
通过考虑以上问题,你会对自己的目标有更好的把握,对学习数据科学的期望也会更加现实。
通过意识到你的时间限制,你将能够留出一个固定的时间段来适应你的时间表。
通过开始这个学习之旅,这表明你有一个成长的心态。你相信如果你投入必要的时间和精力,你可以获得新的数据技能。
3.2.制作你自己的学习课程
创建自己的个人学习课程是非常必要的。
为什么?你可能会问。
正如一种尺寸不能适合所有人一样,学习课程也是如此。一般的课程可能是一个很好的起点,但如果课程变得个性化并符合你自己的兴趣,这将有助于使学习之旅成为一次更有价值的经历。因为在一天结束的时候,如果你正在学习的主题或你正在从事的项目有趣、吸引人,那么你就更有可能被激励去学习它或从事它。弄清楚是什么激发了你的好奇心,然后利用它来制作你自己的定制学习课程。
在这一部分,我将讨论一些主题(如数学、统计学、机器学习等。)帮助您迈出成为数据科学家的第一步。
3.3.贸易工具
行业工具插图。作者手绘。
3.4.数据科学中需要学习的主题
3.4.1。数据处理
在上机器学习之前,先说一些你应该了解的数据技巧。所以你应该知道的一些数据技能包括如何处理数据?如何清理数据?如何设计特征、将特征组合在一起(加、乘、减、除或对数变换)以改变或变换它们的值?
3.4.2。数据可视化
另一个重要的部分是如何可视化数据?如何选择合适的数据可视化图?你应该把它显示成散点图吗?是否应该显示为条形图?饼状图?一片森林?PCA 图?直方图?那要看你想展示什么了?您希望在数据中显示哪些趋势?
3.4.3。型号说明
另一个重要的数据技能是模型解释。所以有一些工具会帮到你。例如,随机森林具有内置的要素重要性功能,您可以查看基尼指数,然后合理调整要素对预测的重要性或贡献。在简单的线性回归中,您还可以利用回归系数来推断哪些特征是重要的,以及在哪个方向上对感兴趣的变量的预测贡献最大(正或负)。除此之外,黑盒算法很难搞清楚,它们对模型预测有什么贡献,或者如何贡献。你可以看看一些优秀的库,Python 中的 Shap 库可以让你检查黑盒算法的特性重要性。因此,Shapley 值将告诉您哪些特征是重要的,并有助于预测,还有其他库,如 Lime 和最近的 Dalex。所以你一定要去看看。
3.4.4。学习类型
好了,现在来看看机器学习。但是在谈论机器学习之前,让我们来看看一些流行的或一般的学习类别。我们有监督学习和非监督学习,还有强化学习。因此,对于数据科学课程来说,了解一些监督或非监督学习方法是非常必要的。
在监督学习中,你有一个 X 变量和一个 Y 变量的矩阵,你将使用 X 与 Y 相关联。因此你有回溯数据,你可以使用它来训练你的预测模型,它可以是回归模型或分类模型。所以给定 X,你可以预测 Y。所以在一个简单的 Y 等于 f 的 X 中,你将应用一个函数来计算给定 X 的 Y。所以 X 可以有多个 X 变量,在无监督学习中,你将在没有 Y 的情况下学习,你将只有 X 特征(自变量),所以 X 变量也被称为自变量,Y 也被称为因变量。所以在无人监管的情况下,你只能访问 x。
这些算法的一个例子是聚类,因为通过聚类,您将能够看到每个数据样本之间的相对距离,在强化学习中,学习将会发生并得到改善。如果它正确地完成了任务,它将得到奖励,因此这将以迭代的方式发生。(机器学习)被分解为机器学习中的一些常见任务,包括分类、回归、聚类、关联规则、自然语言处理以及时间序列分析。所以对你来说,弄清楚你想深入了解哪些细节是非常重要的。因此,如果你一开始就知道你想学习自然语言处理,那么你可以自由地投入到自然语言处理中去。
3.5。从哪里开始?
但是,如果你开始学习数据科学,但不知道要从哪里开始,我建议你从分类、回归和聚类开始。一旦你掌握了这三项,你也可以着手处理这里提到的另一项任务。我推荐大家看一下一些传统的机器学习算法,包括线性回归、逻辑回归、决策树、随机森林、支持向量机以及人工神经网络。因此,除了传统的反向传播神经网络,还有其他基于深度学习的神经网络。这包括卷积神经网络,一些常见的应用是计算机视觉。递归神经网络以及用于序列处理的长短期记忆。编码器解码器以及变压器,这是自然语言处理。自动编码器和生成对抗网络,这是用于生成学习的。因此,另一个需要考虑的重要话题是您将用于实施数据科学学习和数据科学项目的工具。
3.6。数据科学工作环境
所以一个常见的问题是你应该买什么电脑,所以我的建议是使用你现在可以使用的任何电脑。没有必要购买任何计算机,因此您可以使用 Google Colab 来实施数据科学项目以及学习数据科学的一些最佳资源。Google Colab 是一个免费的资源,你可以用它来创建 Jupyter 笔记本,这些笔记本可以用于运行机器学习和深度学习的计算,因为它们也为你提供了一些免费的 GPU 资源。除了这些云计算,您可能还想在自己的计算机上安装一个本地版本,因此我建议安装 conda,您可以使用它为每个项目设置数据科学环境,或者您也可以使用 Python 安装虚拟环境。这里包含了一些您可以使用的文本编辑器或 IDE(集成开发环境)。一些流行的包括 Visual Studio Code(或 VS Code)、PyCharm、Atom、Jupyter、Spyder 和 Vim。有些甚至可能暗示崇高。
3.7。编码
在您的数据科学之旅中,非常重要并且可能是您应该开始学习的核心技能之一是学习如何编码。如果您正在处理大量数据,那么您肯定应该学习 SQL。
您可以了解的一些库和包就在这里。因此,如果您使用 Python 来操作数据或数据框,您应该使用 Pandas。在 R 中,你可以使用 tidyverse,tidyverse 有很多包,比如 tidyr,dplyr。如果你正在使用 Python,如果你正在使用数组,你肯定应该使用 NumPy,R 有它自己的内置数组系统,对于科学计算,SciPy 也值得一看。对于数据可视化,如果你正在使用 Python,你肯定应该学习 Matplotlib、Seaborn 和许多其他类似 Plotly、Bokeh 和 Altair 的工具。在 R 中,你一定要使用 R 的基本图和 ggplot2。ggplot2 有一些惊人的可视化功能,您可以利用这些数据。在 Python 中,要实现机器学习计算,scikit-learn 必不可少。如果你想实现深度学习模型,你肯定应该看看 TensorFlow 和 PyTorch,还有 fast.ai。在 r 中,你有 tidymodels,你有 parsnip,你有 caret,你也有 TensorFlow。
我提到过,除了使用自己的本地计算机来计算机器学习模型,如果你觉得有必要扩展你的计算资源,并且如果你是一名学生,你肯定可以查看这些云基础设施中的一些,它们将为你提供一些免费积分来帮助你入门。即使你不是学生,其中一些基础设施也会为你提供一些免费学分,帮助你开始你的数据科学项目。
最重要的事情之一也是 GitHub,因为你正在进行你的机器学习或数据科学项目,所以在 GitHub 上分享这些是非常必要的。所以做一个投资组合。
3.8。数据来源
我马上会在学习数据科学的概念框架的第四部分中谈到这一点,有许多广泛的数据来源可供您在数据科学项目中使用。这包括 UCI 机器学习数据集,TidyTuesday。实际上我制作了一个关于这个的视频。所以我也会把这个包含在视频的描述里。Kaggle 也是一个非常好的数据集资源,你也可以参加他们的一些有趣的比赛,同时你可以从其他人的代码中学习,你可以用这些代码作为你自己项目的起点。现在有几个数据集是公开的,所以在谷歌上搜索,你可以输入“公开数据”。谷歌也发布了自己的谷歌数据集搜索,所以你一定要去看看,有一个杂志出版商叫 Nature-Springer,他们有一个杂志叫 Scientific Data,他们在那里分享(顾名思义)科学家发表的科学数据,供其他人在他们自己的项目中使用。所以你一定要检查一下,或者你也可以收集你自己的数据。你可以做一个调查来收集数据。你可以创建一些谷歌表单来收集数据(用户可以输入他们的数据到谷歌表单)。或者你也可以通过网络搜集来收集数据。所以有一些很好的工具供你使用。特别是在 Python 中,你可以使用漂亮的 Soup 来抓取数据。如果你期待部署你的机器学习模型,你绝对应该检查一下你可以用来实现一些有用功能的 API。因此,互联网上有各种各样的 API 可供使用,这些 API 将允许您访问独特的功能,例如,执行 web 抓取,这方面有一些 API。所以有些 API 是免费的,而有些 API 是你必须付费的。另一个例子是在生物信息学中,有一些 API 可以让你从生物活性数据库中获取数据。
3.9。模型部署
通过部署您的模型,这意味着您正在将您的机器学习模型公之于众,在那里您可以共享它,它有一个图形用户界面。如果它没有图形用户界面,它被称为 API。但是如果它有一个图形用户界面,你可以称之为 web 应用,一些流行的方法是实现 Flask 和 Django。然而,这两者都需要大量的技术技能。另一方面,Dash 和 Streamlit 等低代码解决方案比 Flask 或 Django 更容易掌握和实现,我说的更容易是指花费的时间更少。我还有一个完整的视频系列,讲述如何使用 Streamlit 实施几个数据科学项目,使用 Streamlit,您可以部署机器学习模型,部署深度学习模型,还可以创建交互式数据应用。
我还创建了一个包含 30 多个视频的 Streamlit 视频播放列表,介绍如何使用 Streamlit 构建各种网络应用。
3.10。数据科学流程
所以一定要去看看!数据科学课程的另一个重要部分是“数据科学过程”,有时也可以称之为“数据科学生命周期”简而言之,它包括了你在做数据科学时通常会遇到的一些任务。因此,您可以将此视为数据科学的蓝图,您可以参考数据科学家在实施数据科学项目时使用的一些最佳实践。这通常包括数据收集、数据清理、数据建模、数据探索、数据部署以及解释。所以我也制作了一个视频,你一定要看看这些视频。并且链接也将在描述中提供。好了,现在我们已经提到了数据科学课程的核心要素,让我们回到大背景。
3.11.一致性和问责制
好了,现在我们已经有了数据科学课程,下一步是制定学习数据科学的时间表。是的,我们知道学习数据科学可能是一个非常持久的过程。如果没有承诺和责任,学习数据科学是很难成功的。
由 Ken Jee 发起的名为#66daysofdata 的计划是启动您的数据科学学习之旅的一个很好的方式。参加#66daysofdata 很简单,你需要在 66 天内每天至少花 5 分钟学习或研究数据科学,然后在 Twitter 或 LinkedIn 等社交平台上分享你的进展。公开分享你的学习历程有利于增加你的责任感。不要相信我的话,发表在 转化行为医学 杂志上的一项研究显示,在推特上公开分享减肥进展的参与者比那些没有分享的参与者能够减掉更多体重。但这还不是全部,下一个问题是为什么是 66 天?在《原子习惯》一书中,作者詹姆斯·克利尔讨论了一些科学发现,这些发现揭示了一个新习惯的形成大约需要 66 天。
因此,通过持续地公开分享你的学习进度,你将有效地对自己的学习进度负责,并见证社区中其他志同道合的学习者的意图和责任。因此,关于 66 天的数据的伟大之处在于,它允许你对你的学习之旅有一个一致的时间表。
试图在尽可能短的时间内学到尽可能多的东西可能是可取的,但重要的是要注意到,并不是所有的日子都是富有成效的。但是不要因为没有效率而责备自己。知道非生产性的日子可能会被其他生产性的日子所弥补,可能会让人安心。
3.12.时间管理
好的,当你学到足够的东西,然后你能够在数据科学项目中实现它, 另一个非常有用的建议是将某种形式的时间管理应用到您的学习中,一种流行的方法是使用番茄工作法,您工作 25 分钟,并专注于工作,然后每过 25 分钟,您将休息五分钟,这将构成一个循环,因此您可以在数据科学学习过程中实施几个循环。 你也可以设置一个具体的时间,比如你可以花一轮番茄大战来学习如何使用随机森林分类器,当这一轮结束时,你可以转到另一个主题学习,这样可以帮助你避免陷入某个特定的主题。防止你掉进兔子洞,你就能进入下一个话题。好吧,如果你被困住了,你可以继续前进,稍后再回来。
4.第 2 部分—学习
有大量的学习资源可供学习数据科学。这里有一些根据不同认知类型分类的资源(观看、倾听、项目工作等)。).
4.1.通过观看视频来学习
学习数据科学的第一资源是通过观看 YouTube 视频上的视频,或者你也可以观看各种学习平台上的视频,有 Udemy 、DataCamp、365 Data Science、Dataquest、Skillshare 等。
4.2.通过听播客来学习
你也可以在 YouTube、Spotify 和 SoundCloud 上听播客。还有几个关于人工智能和数据科学的很棒的播客。所以你可以跑步,慢跑,然后听数据科学。你可以开车去上班听音乐,你可以通勤去上班听播客。
以下是我听过的一些很棒的播客:
4.3.通过读书来学习
因此,像 Abhishek 的百页机器学习书、数据科学家实用统计、处理几乎任何机器学习问题之类的书,他还有一个很棒的 YouTube 频道,在那里他详细介绍了各种机器学习项目和深度学习项目的实施,所以你绝对应该看看他的频道。你绝对不应该错过这本书用 scikit-learn、Keras 和 TensorFlow 实践机器学习:构建智能系统的概念、工具和技术,如果你开始学习机器学习和数据科学,这是一本必不可少的书,所以它有很多很好的例子,它将涵盖重要的机器学习和深度学习库,如 Scikit-Learn 以及 Keras 和 TensorFlow。至于 R 用户,也有一些用 R 组成深度学习的深度学习书籍。
或者,如果你想使用 Python,你想从头开始实现深度学习,那么你肯定应该看看这本书,你在这里看到的背景中,我也有几本书,我期待着学习机器学习和深度学习。正如你在这里看到的,学习是一个终生的过程,数据科学是一个非常大的领域,不可能什么都知道。一旦你认为你知道很多主题,那么总会有一些新的主题被发布,被出版,所以学习永远不会停止。
机器学习背景下的数学好书是 机器学习的数学 。作者还非常好心地在他们位于 https://mml-book.com/的网站上免费提供这本书的 PDF 版本,而印刷版由剑桥大学出版社出版。
有几本很好的统计学书籍,我手头的这本是 数据科学家实用统计学 。这本书的伟大之处在于,它有许多代码示例,当您在数据科学的背景下学习统计的关键概念时,可以参考这些示例。
至于学习机器学习,我有一本百页的机器学习书籍https://amzn.to/3fkfnB6,它很好地解释了常见机器学习(ML)算法的本质。当我偶尔需要快速浏览 ML 算法的高级解释时,这本书就派上了用场。
4.4.通过阅读博客来学习
除了书籍之外的其他资源是阅读 Medium 博客文章,我实际上在《走向数据科学》中发表了几篇文章,我在这些文章中一步一步地详细介绍了如何实施数据科学项目,因此 Medium 也是您学习数据科学的一个非常好的资源,所以您一定要去看看,有几个平台可以让您学习数据科学。
4.5.学习如何学习
因此,作为一名数据科学家,最重要的事情之一就是热爱学习,热爱学习新工具,学习新的机器学习算法,学习新的框架,学习新的库,所以学习永远不会停止!这是我的一些学习建议,第一个也是其他 YouTubers 用户提到的,比如 Ken Jee 和 Tina Huang 在她的“最小输入最大输出”视频中提到的,就是学习刚刚开始。
我之前提到过在你能做之前你应该知道多少。当接近一个我想在项目中使用的新机器学习库时,我会使用的一个技巧是首先浏览 API 文档。准备好,看看函数的名字,看看输入参数,看看函数做什么,用什么输入,输出是什么样子。浏览他们的入门部分也很有帮助,他们很可能会有一些教程或分步 Jupyter 笔记本,其中他们使用 Iris 数据集或其他数据集示例实施了一个解决方案。如果你能理解这一点,你几乎可以开始为你的项目实现任何新的机器学习库或工具。
尝试找出什么是输入数据,什么是结果输出数据,以及在将输入转换为输出时执行了什么转换。实现了什么类型的计算来将输入数据转换成输出数据。好的,让我们以随机森林分类器为例。所以你要做的第一件事就是通过from sklearn.ensemble import RandomForestClassifier
从scikit-learn
导入RandomForestClassifier()
函数。特别是,您可以将随机森林分类器函数分配给名为clf
的变量。应该注意,您可以将输入参数留空,它将使用默认参数。但是,如果您想要指定参数,您应该查看 API 文档,您将会看到哪些输入参数是可用的,以及它们的用法示例。例如,你可以设置树的数量,你可以设置叶子节点的数量,最大叶子节点,等等。然后,您将通过运行命令clf.fit(X_train, y_train)
来执行模型构建。特别地,指定的输入参数对应于来自用于建立机器学习模型的训练集的 X 和 Y 变量。一旦模型定型,您就可以应用该模型进行预测。
现在我们已经完成了第 2 步,下一步将是构建。
5.步骤 3 —构建
现在,您将运用所学的知识来构建数据科学项目。这是我的一些建设项目的建议。
如果你正在为现有的工作获取数据科学技能,我建议你从事与工作相关的项目。因此,要做的第一件事就是弄清楚你想从事哪个项目。和你的老板谈谈,想出如何应用数据科学来解决业务/研究问题,这样做可以让你在工作中获得数据科学技能。
应该注意的是,这并不容易,因为它需要你自学。但是不要担心,我们为您提供了保障,您可以参考我们之前讨论过的上述资源。
或者,如果您无法在工作中学习数据科学,您也可以考虑在周末项目或业余时间进行项目。因此,在你朝九晚五的工作中,你将会工作,而对于你朝五晚九的工作,你肯定可以找到一些可以让你实施数据科学项目的副业,以推进你的学习之旅。我已经提到了一些你可以使用的公共数据集。此外,对你来说,编译你自己的数据集会更有趣、更吸引人。或许你可以从智能手表导出你的健身数据,从网飞导出观看历史,从 Spotify 导出收听历史。
另一种选择是,使用公开的数据集在 Kaggle 上竞争。这样做的好处是可以很好地融入社区,你肯定可以从 Kaggle 平台上共享的大量现有笔记本中学到很多东西。
如果你正在接受挑战,我最近启动了 开放生物信息学研究项目 倡议,我们可以在我编译的生物信息学数据集上工作。更多详情请见下面的视频:
呼吁参与开放生物信息学研究项目 @ 数据教授 YouTube 频道
6.第 4 步——解释
学习数据科学框架艺术的最后一部分是解释。一旦你已经建立了数据科学项目,你需要向某人解释,向某人解释可以有多种形式(例如,博客、文章、谈话、讨论、视频、音频等。).
解释的实际过程将帮助你将你所获得的所有知识和实践技能具体化,成为一种已经内化并铭刻在你心中的具体形式。有几种解释的方法,我在这里列出了它们。
首先,告诉别人你是如何构建和解释机器学习模型的,这是一种强化过程细节的好方法。这被称为费曼技术。
其次,指导一个想学习数据科学的朋友也是巩固知识的好方法。
第三,写作也是另一个很好的方式,也许是写博客,
第四,你可以建立一个文档齐全的 GitHub portfolio 网站,展示和描述你的项目。
第五,你还可以制作关于数据科学特定主题的 YouTube 视频,以及谈论你的学习之旅,这可以激励其他人的学习之旅。
此外,你可以在聚会和会议上演讲,也可以参与播客。说到播客,我特别喜欢柴数据科学播客,肯最近的邻居和数据科学的艺术家(Harpreet Sahota)。至少对我来说最重要的是画出信息图来解释数据科学的概念。所以我也喜欢这样做,所以这是我绘制的信息图,总结了我关于如何开始学习数据科学的一些最佳技巧,所以我称之为学习数据科学的艺术。
结论
总之,踏上数据科学的学习之旅是一次有益的经历,尽管有时会很困难。在这篇文章中,我总结了一些最佳的结构化技巧,以及如何让学习之旅尽可能顺利和容易。
如果你想分享你学习数据科学的技巧和诀窍,请在评论区分享!我期待任何意见和建议。
公开
- 这篇文章中可能有附属链接,我可能会从合格的购买中获得,这将有助于未来内容的创建。
接下来读这些
- 数据科学如何掌握 Python
下面是数据科学需要的必备 Python - 如何掌握数据科学的熊猫
下面是数据科学需要的必备熊猫 - 如何用 Python 构建 AutoML 应用
使用 Streamlit 库的分步教程 - 学习数据科学的策略
打入数据科学的实用建议 - 如何免费搭建一个简单的作品集网站
不到 10 分钟从零开始的循序渐进教程
✉️ 订阅我的邮件列表,获取我在数据科学方面的最佳更新(偶尔还有免费赠品)!
关于我
我是泰国一所研究型大学的生物信息学副教授和数据挖掘和生物医学信息学负责人。在我下班后的时间里,我是一名 YouTuber(又名数据教授)制作关于数据科学的在线视频。在我做的所有教程视频中,我也在 GitHub 上分享 Jupyter 笔记本(数据教授 GitHub 页面)。
*https://www.youtube.com/dataprofessor
在社交网络上与我联系
✅YouTube:http://youtube.com/dataprofessor/
♇网站:http://dataprofessor.org/(在建)
♇LinkedIn:https://www.linkedin.com/company/dataprofessor/
♇Twitter:https://twitter.com/thedataprof
♇Facebook:http://facebook.com/dataprofessor/
♇github:https://github.com/dataprofessor/
♇Instagram:)*
解决问题的艺术
提高技能的建议
问题从提出问题,对未知现象的兴趣,或者仅仅是好奇开始。它们起初是的一般和的包容,但当提问者加上自己的细节和约束时,就变成了的排他,形成了的特例。
例题
图像分类是一个一般性问题,考虑到不同的情况,在许多子问题中实例化。在其最简单的形式中,例如人脸识别,有许多不相交的标签,并且每个图像只能接受其中的一个。然而,在像场景理解这样的情况下,图像可以被标记许多标签,称为多标签分类。此外,如果我们想要将产品图像分类到亚马逊层级中,标签集非常大,并且是以层级结构构建的;在这种情况下,我们称问题为极端分类。现实世界的问题可能会有更多的挑战;数据集可能不平衡,或者我们可能有丢失的值。因此,在一般问题上增加约束会导致不同的个别情况。
图一。子问题。照片由你好我是 Unsplash 上的 Nik 。
解决一个具体问题首先要通过研究以前的作品和文献,了解它所衍生的一般问题、它的不同方面以及其他人迄今为止所做的工作。接下来应该花足够的时间去思考,让你的大脑处理你发现的不同想法及其利弊。最后,你可能会对自己的具体问题有新的想法。
例子 最近我们公司决定开发一个自动描述生成器,它将产品标题、图片和标签作为输入,并编写描述,描述产品。对于没有文案写作技能的店主来说,这可能是一个有用的工具。那一次,我知道自然语言生成(NLG)进展很快,但我不知道 NLG 模型是如何工作的。所以我决定从文献开始,阅读 GPT 的论文,在那里我发现了如何在新的数据集上微调预训练的模型。由于我们有处理电子商务数据的经验,我们很容易地收集了一个大型数据集,用标准的 NLP 模型清理它,并使用它进行微调。我们还训练了一个多标签图像分类器来预测图像的一些标签,并将它们与标题和标签一起馈送到 NLG 模型,这在我们没有文本数据时极大地提高了我们的结果。
图二。解决问题(阅读、思考和写作)。扬尼克·普尔弗在 Unsplash 上拍摄的照片。
艺术
虽然潜在的文献存在于精确的正式语言的书籍和学术研究中,但思想和想法存在于你的头脑中,在那里你应该修剪细节并建立直觉以发现新的解决方案。真正的艺术是在这两者之间切换。
图三。左边的圆圈显示了书籍和学术研究的正式领域,语言是正式的和通用的,你可以找到大量的定理和细节。在右边你可以看到思维领域,直觉和想法生活在那里,语言是简单和个性化的。(图片由作者提供。)
作为一名优秀的问题解决者,你必须能够应对学术写作的复杂性,不要迷失在复杂的注释中,并找到导致主要思想的意图。阅读文学是一把双刃剑:如果你读了很多,你可能会偏向于以前的作品,失去你的创造力;另一方面,仅仅依靠自己的想法而不研究以前的作品,可能会导致另起炉灶。因此,如果你能在阅读和思考之间建立正确的平衡,那将是最好的。在找到一个新的解决方案后,用正式的语言写下来并与其他人讨论是一个好主意,这样你就可以发现可能的缺陷。这些是我发现在解决问题时很重要的一些技巧,下面是一些提高这些技巧的建议。
- 要主导正式空间,参考主要学习,做一个自学者。课程和博文不够正规。还有,不要让别人代替你发现一切。
- 主要思路简单,数量少。它们只是被复杂的形式语言包裹着,你要努力抓住它们背后简单的直觉。
- 为了避免对现有的想法和解决方案产生偏见,首先要思考,然后参考文献。此外,使用你自己的个性化语言和符号,因为语言是基础,思想是以语言为基础的。
科学的艺术
数据科学
创造有洞察力和美丽的生物信息学可视化
有一种误解,认为科学是枯燥的,没有幽默感的,过于技术化的,但事实并非如此!有效的可视化可以将原始 DNA 转化为清晰传达大量信息的惊人图形。
Biopython 是 python 的开源生物信息学或计算分子生物学包。它提供自定义的类和方法,解析标准的生物文件格式,并与其他生物信息学程序接口,如 BLAST 、 EMBOSS 或 Clustalw 。就本文的目的而言,它可以使用生物数据创建惊人的视觉效果。
我们将从 Biopython 处理 DNA、RNA 和蛋白质序列的方法开始。然后,我们可以使用这些方法来提高整个基因组的复杂性,以及这些基因组如何通过系统进化树可视化随时间变化。这一过程将允许你探索任何你感兴趣的基因组,并开始从原始数据中获得洞察力!
顺序
Biopython 有一个 Seq (Sequence)基类,类似于基本的 Python string 类,但是增加了一些方法来方便相关的生物学用例。例如,我们可以载入 DNA 序列。
>>> my_sequence = Seq('AGTCC')
我们可以对这个对象使用传统的字符串方法,比如迭代字符串中的每个字母,或者使用 len()获得字符串的长度。
# Print length
>>> print(len(my_sequence))
5# Print each basepair in the sequence
>>> for basepair in my_sequence:
>>> print(basepair)
A
G
T
C
C
我们也可以通过它们的索引来选择碱基对,并正常地对序列进行切片。切片会返回一个新的 Seq()对象,可以存储在一个新的变量中,也可以覆盖原来的序列。
# Select basepair by index
>>> my_sequence[2]
'T'# Slice sequence (returns a new Seq() object)
>>> my_sequence[1:4]
Seq('GTC')
现在我们将看看 Seq()类中一些更有趣和生物学上有用的方法。我们可以很容易地生成 DNA 序列的互补和反向互补,并计算出序列中 G 和 C 碱基对的比例。
补码
当 DNA 被复制时,每条链都被分离出来,用来构建另一条链。腺嘌呤(A)和胸腺嘧啶(T)是互补的,鸟嘌呤(G)和胞嘧啶©是互补的。所以一个“ACTG”序列会有一个互补的“TGAC”序列。在 RNA 中,碱基尿嘧啶(U)被用来代替胸腺嘧啶(T ),因此在转录过程中读取的“ACTG”DNA 序列将产生“UGAC”的 RNA 序列。
>>> my_sequence = Seq('AGTCC')# Generate complement sequence
>>> my_sequence.complement()
Seq('TCAGG')
反向补码
在转录过程中,DNA 实际上是从运行 3’→5’的模板链中读取的,反向互补给出了 mRNA 序列。然而,我们通常从运行 5’→3’的编码链中分析 DNA。
虽然生物过程实际上是两步走的,首先是模板链的反向互补,然后转录成 RNA。我们可以简化这个过程,从编码链开始,用 T 碱基对代替 u 碱基对。
# Generate reverse complement sequence
>>> my_sequence.reverse_complement()
Seq('GGACT')# Two step solution from template strand
>>> coding_strand = Seq('ATGTAG')
>>> template_strand = coding_strand.reverse_complement()
>>> template_strand.reverse_complement().transcribe()
Seq('AUGUAG')# From coding strand
>>> coding_strand = Seq('ATGTAG')
>>> coding_strand.transcribe()
Seq('AUGUAG')
气相色谱含量
Biopython 还能够计算 DNA 或 RNA 序列中 GC 碱基与 AT(或 U)碱基的比例。这在生物学上是相关的,因为 GC 含量在物种之间是不同的,因此它可以用于根据已知物种的 GC 含量来鉴定未知基因组。
在基因组中,编码区(通常是基因)通常比非编码区具有更高的 GC 含量。因此,GC 含量可用于鉴定未知基因组中的基因。
# Calculate GC proportion
>>> from Bio.SeqUtils import GC
>>> GC(my_sequence)
60.0
翻译
既然我们已经从 DNA 中生成了 mRNA,我们需要翻译成真正的蛋白质序列!与转录非常相似,Biopython 提供了几种方法来实现这一点,要么是生物学上正确的方法,要么是从编码链 DNA 中更有效地实现。
# In the biologically correct way after creating the mRNA sequence
>>> mRNA = Seq('AUGGCCAUUGUA')
>>> mRNA.translate()
Seq('MAIV')# From the coding strand directly
>>> coding_strand = Seq('ATGGCCATTGTA')
>>> coding_strand.translate()
Seq('MAIV')
值得注意的是,不同的分类组需要不同的表来正确地从 mRNA 合成蛋白质序列。点击这里查看 NCBI 遗传密码资源。关键字参数可以在。translate()方法-。例如,translate(table = “脊椎动物线粒体”)。
基因组数据
既然我们已经了解了序列如何在 Biopython 中工作,让我们进入一个更高的层次,并尝试可视化整个基因组。有一个叫做 GenomeDiagram 的模块,专门用于在线性或圆形图中可视化基因组(对于大多数原核生物)。
Biopython 有几个示例基因组,可以从 GitHub 下载。感兴趣的基因组也可以从 NIH 基因序列数据库 GenBank 下载,有多种文件格式(如。gbk 或者。fasta)。
# Import Libraries
from reportlab.lib import colors
from reportlab.lib.units import cm
from Bio.Graphics import GenomeDiagram
from Bio import SeqIO
from Bio.SeqFeature import SeqFeature, FeatureLocation# Read in our genome
record = SeqIO.read("NC_005816.gb", "genbank")gd_diagram = GenomeDiagram.Diagram(record.id)
gd_track_for_features = gd_diagram.new_track(1, name="Annotated Features")
gd_feature_set = gd_track_for_features.new_set()# We can color code every other gene for clarity
for feature in record.features:
if feature.type != "gene":
# Exclude this feature
continue
if len(gd_feature_set) % 2 == 0:
color = colors.blue
else:
color = colors.lightblue
gd_feature_set.add_feature(
feature, sigil="ARROW", color=color, label=True, label_size=14, label_angle=0)# We can add in restriction sites for some popular enzymes
for site, name, color in [
("GAATTC", "EcoRI", colors.green),
("CCCGGG", "SmaI", colors.orange),
("AAGCTT", "HindIII", colors.red),
("GGATCC", "BamHI", colors.purple)]:
index = 0
while True:
index = record.seq.find(site, start=index)
if index == -1:
break
feature = SeqFeature(FeatureLocation(index, index + len(site)))
gd_feature_set.add_feature(
feature,
color=color,
name=name,
label=True,
label_size=10,
label_color=color)
index += len(site)# Create our diagram!
gd_diagram.draw(
format="circular",
circular=True,
pagesize=(20 * cm, 20 * cm),
start=0,
end=len(record),
circle_core=0.5)# Can save to any standard photo format
gd_diagram.write("plasmid_circular_nice1.png", "PNG")
现在我们可以开始制作一些漂亮的可视化效果了!这里我们看到了鼠疫耶尔森氏菌生物变种田鼠的基因组,这是一种作为疫苗开发的淋巴腺鼠疫无毒菌株。基因以蓝色和浅蓝色显示,限制性酶位点用彩色线标出。
作者图片
可视化一个更复杂的基因组,一个水芹(拟南芥)叶绿体,显示出更多的基因和几十个 EcoRI 限制性位点。这个基因组有 154478 个碱基对,而上面只有 9609 个碱基对。
图片由 Brona 在维基百科上以 CC BY-SA 3.0 授权提供。
这是一种水芹。它是遗传学中的模式生物。因此,我们可以使用与上述相同的基本代码从 GenBank 中指定基因组,以生成环状 DNA 序列。
# Specify the thale cress chloroplast genome
record = SeqIO.read("NC_000932.gb", "genbank")...# Only including the EcoRI sites (green)
for site, name, color in [("GAATTC", "EcoRI", colors.green)]:
...
作者图片
这大概是一个基因组能够以任何清晰的方式呈现信息的最复杂程度了。但是,它也很有视觉冲击力。事实上,如果我们回去添加更多的限制性酶位点,就像我们在第一个基因组中所做的那样,我们会得到一个更加美丽的图像。
...for site, name, color in [
("GAATTC", "EcoRI", colors.green),
("CCCGGG", "SmaI", colors.orange),
("AAGCTT", "HindIII", colors.red),
("GGATCC", "BamHI", colors.purple)]:...
作者图片
发展史
现在我们已经从原始 DNA 序列到完整的基因组,我们知道基因组并不像我们希望的那样稳定。突变很频繁,尤其是在病毒中。开发病毒疫苗的一个主要困难是它们频繁变化的基因组。
我们可以通过比较新序列和基本序列来追踪变异是如何随着时间的推移而产生的。对于新型冠状病毒(新冠肺炎)病毒,碱基序列是来自中国武汉的初始毒株。
使用另一种可视化系统发育的工具, Nextstrain 有几个软件包,允许公共卫生官员和研究人员分析当地社区的新冠肺炎新菌株。然后,您可以为各种特征构建动画视觉效果,例如突变数量、原产国、基因型、分支等。
你可以遵循这个指南来创造你自己的新冠肺炎发展史。您将需要为 Nextstrain 创建一个新的 conda 环境(在指南中有介绍),因为您将下载许多依赖项。
1-5 月 SARS-cov-2 的系统发育。用 Nextstrain 的 Auspice 可视化工具生成。作者 GIF。
结论
Biopython 是 python 中生物信息学的基础包。我希望这篇文章已经让我们领略了它在分析序列、转录和翻译 DNA → mRNA →蛋白质序列、可视化整个线性和环形基因组以及可视化染色体方面的作用。将来我可能会发布更多利用 Biopython 的文章。
Biopython 拥有大量的文档,并能够通过 FASTA 文件利用 NCBI 资源,如完整基因组、变异菌株基因组和染色体。因此,玩玩感兴趣的基因组,看看你能做什么!你可能最终会得到一个有效的、令人惊叹的可视化效果。
连接
我一直在寻找连接和探索其他项目!你可以在 GitHub 或 LinkedIn 上关注我,并在媒体上查看我的其他故事。我也有一个推特!
机器学习的 Sprezzatura 艺术
模型可解释性
构建可解释模型概述
Sprezzatura 是一门让事物看起来自然而精致的艺术。在上图中,Karan 小心翼翼地让羊角面包看起来很自然。同样,将机器学习模型转化为少量规则也需要努力。相反,数据科学家建立了 107 个模型的复杂集合,以赢得网飞推荐内容。我们看到深度学习模型拥有 100%的准确率,当仔细检查时,它们充满了方法上的缺陷。
这篇文章试图将 sprezzatura 的艺术应用于机器学习。除了获得风格,您的模型将更容易理解,被广泛采用,并且易于部署。
这篇文章的大部分评估了可解释模型的四种通用方法,从最灵活和精确的方法开始,到最简单的方法。其中一些接近黑盒 AutoML 集合模型的性能!这些方法包括 rulefit 算法、GA2M(广义加法模型加交互)算法、规则列表算法和记分卡方法。成人数据集被用作基准。最终结果的一个潜在峰值显示在该图中。
作者图片
**剧透警告:**其中两种可解释的方法与由 20 个独立模型组成的叠加系综 AutoML 模型的结果相同或略低!
该职位的结构:
- 为什么要使用可解释的模型
- 构建基线模型
- 规则拟合算法
- GA2M 算法
- 规则集模型
- 记分卡模型
我写这篇文章的动机是作为一名长期关注模型可解释性和可解释性的数据科学家。你也可以在圣路易斯机器学习和数据科学会议上找到这个演讲的视频。您还可以访问笔记本,在 rulez Github 中可以找到运行这些可解释模型的完整代码。
为什么要使用可解释的模型
大多数数据科学家都不熟悉可解释的模型。它们被定义为建模方法的子集,强调特征的稀疏性和理解这些特征如何相互作用以产生预测的能力。因此,这些模型更容易调试,非技术业务利益相关者和监管者也更容易信任它们。另一个意想不到的好处是,更简单的模型通常更容易操作!
构建模型涉及几个步骤,包括问题设置、标注数据、特征工程、特征选择、算法选择和实施。这篇文章(以及许多数据科学文献)关注的是算法选择。实际上,构建可解释的模型和解决方案需要这里提到的所有步骤,而不仅仅是选择正确的算法。例如,多重共线性对模型的可解释性有很大的影响。因此,虽然这篇文章关注的是算法选择,但让我们指出模型选择只是解决方案的部分。
阅读学术文章,你会认为算法选择是数据科学家工作的主要焦点。这种工作流强调通过预测准确性衡量的增量改进。类似地,Kaggle 风格的模型比赛强调通过提高准确性来获胜,然而,轻微的改进。虽然准确性无疑是重要的,但其他问题,如可解释性,也会受到影响。
首先,最精确的模型通常在企业环境中不可用。赢得 T4 100 万美元网飞竞赛的模特是其他 107 个模特的组合。虽然这个模型在准确性上是最好的,但对网飞来说太复杂了,难以实现。
其次,这些复杂的模型很难解释。实际上,这意味着受模型影响的涉众不信任他们不理解的模型。在受监管的企业中,模型风险管理团队的重点是管理机器学习模型的风险,他们拒绝批准复杂的模型。太多的失败可以归咎于黑盒模型,这些模型后来被发现有简单的缺陷,如目标泄漏。最后,在数据移动的情况下,复杂的模型可能是脆弱的和不可用的。当数据转移成为一个问题时,可理解的模型更容易诊断和修改。
另一种选择是从一个可解释的模型开始。让我们考虑一个实际问题,然后应用可解释的模型。
问题:预测收入
让我们使用来自 UCI 的成人数据集直接比较复杂和简单的方法。这是一个公共领域数据集,用于根据人口统计和税收信息预测收入。该数据集包含超过 30,000 个观察值,并且包含连续特征和分类特征的混合。这些特征具有共线性和交互作用。其大小和复杂性类似于企业环境中的许多表格数据集。
基线模型
第一步是建立几个基线模型。这是建模时的一个重要步骤(有时会被忽略)。基线让你更好地理解你在一个模型中有多少信号,什么特征是重要的,以及其他算法提供了多少提升。所有这些都是有用的数据点,可以帮助您理解应该投入多少精力来改进您的模型。
当您开始计算您的基线时,您应该已经对最终模型的需求有了一些期望。您的业务合作伙伴应该帮助您了解为组织带来价值所需的预测性能、围绕部署的要求以及模型审查过程的严格程度。
首先,我将使用三种不同的算法作为基线。是的,三个!都有不同的用途,都用好就好。它们是逻辑回归、自动堆叠集成和 OneR 基于规则的模型。
逻辑回归
用成人数据集预测收入是一个分类问题。我建议基线模型的合适起点是逻辑回归模型。逻辑回归的加法性质很容易理解,并为我们提供了一个众所周知的与其他算法进行比较的基准。
对于这个数据集,我使用开源 H2O 包用几个命令构建了一个逻辑回归(LR)模型:
glm_model = H2OGeneralizedLinearEstimator(family= "binomial", lambda_ = 0, compute_p_values = True, remove_collinear_columns = True)
glm_model.train(x, y, training_frame= train, validation_frame=valid)
我构建了两个模型,一个是仅使用数字特征的非常简单的逻辑回归,另一个是使用分类和数字特征的更复杂的逻辑回归。在这种情况下,添加分类特征确实明显改善了模型。
模型结果:
- LR (6 个数字特征):AUC 为 0.83 的逻辑回归模型。
- LR (6 个数字和 6 个分类特征):分类特征的一个热编码导致 100 个逻辑回归模型的预测值,AUC 为 0.91。
逻辑回归模型通常被认为是容易理解的,并且被广泛认为是非常容易解释的。这些系数可以一起用于计算预测。这种透明度是 LR 在受监管行业中被广泛接受的一个重要原因。
逻辑回归模型的透明性经常导致人们认为它是一个容易理解的模型。然而,情况并非总是如此。添加大量要素,尤其是存在多重共线性时,会使理解逻辑回归模型变得非常困难。Poursabzi-Sangdeh 的研究发现,从 2 个特征增加到 8 个特征对模型的可理解性有显著影响。因此,虽然系数是有帮助的,但当目标是一个容易理解的模型时,它们不是灵丹妙药。
自动堆叠系综
AutoML 允许我们快速比较不同类型的特征工程和算法。我用 H2O 的汽车制造了一个挑战者。
aml = H2OAutoML(max_models=20, seed=1)
aml.train(x=x, y=y, training_frame=train)
模型结果:
- AutoML H2O:最佳模型是所有 20 个模型的叠加组合,AUC 为 0.93。
这是一个非常精确但复杂的结果。一个由 20 个模型组成的堆栈集合包含了每一个预测的数万个决策点。虽然解释方法可以让您很好地理解模型可能会做什么,但大多数模型风险评估都无法通过这种方法。这简直太复杂了。然而,这给了我们一些预测性能上限的概念。
无比的人
OneR 使用一个特征建立一个模型。它非常容易理解,也非常简单。它基本上是只有一个分支的决策树。该模型通过查看所有特征并为最终模型选择一个特征来工作。你可以找到 R 和 python 的 OneR 的多种实现。
from imodels import OneRClassifier,clf = Pipeline(steps=[('preprocessor', preprocessor),
('classifier', OneRClassifier())])
clf.fit(X_train, y_train)
在成人数据集上运行这个给了我们一个简单的规则:
如果资本收益大于 5178 美元,那么这个人极有可能是高收入人群。
该模型比随机模型好,但比我们的其他基线模型差,AUC 为 0.60。OneR 是一个很好的检查基准,可以防止目标泄漏。在某些情况下,您可能会从一个特性中发现足够的启发,部署一个简单的规则比部署一个更复杂的模型更有用。
基线摘要
在特征数量、模型复杂性和预测性能之间的权衡应该是相当明显的。
作者图片
这里的复杂性是一个说明性的数字,近似于模型中参数的数量。它旨在强调 OneR 与堆叠 AutoML 系综在复杂性上的差异。这使我们能够更好地理解提高预测性能和最终模型的复杂性之间的矛盾。
OneR 结果确定了不止一个特征是必要的。分别运行仅具有数字和分类特征的模型,很明显数字和分类数据都提供了有用的信号。当我们考虑用不同的方法来表示这些特性以及什么样的模型类型工作得最好时,这变得很重要。最后,我们看到复杂的算法和集成确实增加了一些提升。这暗示了数据中的一些复杂性,这些复杂性是我们当前的特征集所没有捕捉到的。这些线索可能会本能地促使数据科学家使用复杂的方法来解决问题。然而,让我们考虑一些更容易理解、部署和维护的替代方法。
规则匹配
Friedman 和 Popescu (2008) 的 RuleFit 算法是一个可解释的模型,能够提供高预测性能以及可理解的规则。它非常容易使用,并且有许多可用的实现。rulefit 算法创建一组重叠的规则。
H2O 最近增加了一个 rulefit 的实现,它很快成为我的最爱。让我们从使用 5 条规则构建一个简单的模型开始。
from h2o.estimators import H2ORuleFitEstimator# Build and train the model:
rfit = H2ORuleFitEstimator(max_num_rules=5,seed=1,
model_type='rules')
rfit.train(training_frame=train, x=x, y=y,validation_frame=valid)
接下来让我们向您展示模型创建的规则:
与逻辑回归模型类似,对于给定的预测,您需要确定与该预测相关的系数/规则。例如,当受教育少于 13 岁,资本收益少于 7031 美元,并且关系不是家庭内,其他亲属,自己的孩子,未婚时,上面的规则适用。这是在成人数据集中预测某人低收入的最强规则。
这个型号的性能不是很好。具有 5 个规则的 rulefit 在测试集上获得了 0.80 的 AUC 性能。可能的问题是,rulefit 是一种决策树方法,这意味着它会在连续数据中创建拆分。看看这些规则,你会发现其中既有绝对规则,如关系规则,也有连续规则,如资本收益规则。用五条规则,真的很难捕捉到那些连续的特征。
H2O 实现的一个很好的额外特性是将线性模型与决策树结合起来构建规则。这结合了两种模型的优势,使用了随着决策树不断变化的线性关系,决策树在宁滨和交互方面非常有效。通过加入线性成分,这是一个具有 5 条规则的规则拟合模型,实现了 0.88 的 AUC。
规则匹配结果,按作者排序的图像
线性规则很容易理解。例如,随着受教育程度和年龄的增加,预测高收入的概率也会增加。
这加强了这个数据集有很强的线性效应。通过添加线性效果,性能从 0.80 跃升至 0.88。任何只依赖少数决策树的方法都将在性能上受到限制。接下来,让我们改变规则的数量,并评估它们如何影响模型的性能。以下是一些结果:
作者图片
为了说明 Rulefit 的灵活性,我绘制了一系列具有 AUC 和复杂性的模型。OneR 的复杂度为 2,而具有 100 个预测器的 LR 的复杂度为 100。对于 Rulefit 模型,复杂性是规则数乘以 3(因为每个规则允许三个特征)。
作者图片
总之,H2O 的规则拟合通过使用决策树和线性关系提供了很大的灵活性。虽然使用大量规则可以让您获得非常高的预测性能,但是重叠的规则使得理解任何单个预测变得更加困难。这使得跟踪预测或诊断模型问题变得更加复杂。让我们考虑另一种开发稀疏模型的方法,这种模型仍然提供良好的预测性能。
GA2M
广义加性模型(GAM) 是一个可解释的模型,与逻辑回归模型一样透明,但可以更精确。一个 GA2M 模型是一个包含了交互项的 GAM。这个相互作用项的加入通常提高了预测性能。
我在 DataRobot 工作时了解到 GA2M 模型,它有一个优秀的实现。GA2M 的开源实现可以在微软的 interpretML 包中获得。让我们用成人数据集来探索这个问题。拟合模型很简单:
ebm = ExplainableBoostingClassifier(random_state=42)
查看带有成人数据集的模型对象,您将看到以下功能列表:
ebm.feature_names[‘age’, ‘workclass’, ‘fnlwgt’, ‘education’, ‘education_num’, ‘marital_status’, ‘occupation’, ‘relationship’, ‘race’, ‘sex’, ‘capital_gain’, ‘capital_loss’, ‘hours_per_week’, ‘native_country’, ‘relationship x hours_per_week’, ‘age x relationship’, ‘marital_status x hours_per_week’, ‘occupation x hours_per_week’, ‘education_num x occupation’, ‘age x capital_loss’, ‘fnlwgt x education_num’, ‘education x capital_loss’, ‘age x fnlwgt’, ‘occupation x relationship’]
GA2M 创建并选择一些交互特征用于模型中,例如“年龄 x 关系”。这个选择很有价值。我经常检查 GA2M 用来理解我的数据集的交互方式。即使我的最终模型不会是 GA2M,它仍然对识别交互非常有用。
对于成人数据集,这些交互特征提供了性能提升,AUC 为 0.93。**这个真好!**这与堆叠系综模型不相上下。
结果模型是透明的,所有系数都可用。甚至可以把一个等级表放在一起,列出一个因素的每个级别的系数因子。InterpretML 包还包括可视化,因此您可以自己看到这一点。这里我们看到婚姻状况的不同系数。
作者图片
总之,GA2M 模型从最好的线性模型开始,但也混合了结合最佳交互的能力。找到这些互动并不容易(你们都应该利用这些互动来传达自己对问题的理解)。通过添加这些交互作用,GA2M 通常可以提供非常好的预测性能。GA2M 使用系数和评级表是众所周知的做法,在模型风险管理实践和监管要求中被广泛接受。
规则列表
rulefit 算法依赖于重叠的规则集。这意味着几个规则可以影响一个预测。相比之下,规则列表被约束为没有任何重叠的规则。非重叠规则的优点是每个预测都可以追踪到一个规则。这使得结果非常容易理解。
钱丹·辛格—伊莫德尔斯
构建规则列表模型在计算上可能是昂贵的。规则列表还绑定了连续的特征,因此线性关系可能很难合并到规则中。最后,规则列表的当前实现限于分类问题。
实现规则列表的一个很好的起点是可解释的机器学习模型包(imodels)。我从一个众所周知的方法开始,certified Optimal RulE ListS(CORELS),但是在我的 Python 3.7+环境中安装时遇到了问题,而且它太慢了,只能在成人数据集上构建非常基本的规则列表。
我发现可扩展贝叶斯规则列表方法工作得更快。这只是一个过时的 R 包。我的测试使用成人数据集及其分类特征,以及 15,000 行的训练数据集。
sbrl_model <- sbrl(data_train, iters=30000, pos_sign="1",
neg_sign="0", rule_minlen=1, rule_maxlen=1,
minsupport_pos=0.003, minsupport_neg=0.003,
lambda=10.0, eta=1.0, nchain=10)
print(sbrl_model)
使用 rule_maxlen=1 运行这段代码只需要几秒钟。结果如下:
结果是按顺序读取的非重叠规则。这意味着任何人都可以识别哪个规则适用于他们的预测。理解任何预测的结果都会变得容易得多。
规则列表模型的 AUC 为 0.86,比 rulefit 发现的 5 个规则的 AUC 0.80 有了显著提高。然而,为了找到这个更好的解决方案,计算这个最佳规则集需要额外的计算。可以通过将 SBRL 模型的最大规则长度增加到 2 来提高性能。这将性能提高到 0.87 AUC。这确实会导致更复杂的规则:
如果{教育人数=(4.75,8.5),婚姻状况=已婚公民配偶},则正概率= 0.14173228
增加最大规则长度会增加搜索空间。移动到 2 的嵌线长度需要更长的计算时间。在我的笔记本电脑上无法计算规则长度 3。
这些规则的非重叠性对于部署期间的互操作性、错误检测和问题诊断来说是一个巨大的优势。但是请记住,计算这些规则是复杂的。然而,这些规则的简单性意味着它们在预测性能上往往落后于现代算法。
记分卡
一种常见的可解释的决策方法是使用记分卡。记分卡允许许多功能,预测分数基于简单的求和。它们非常容易使用,常见于医疗保健和刑事司法等领域。
作为记分卡的累犯预测的可解释模型。"优化的评分系统:医疗保健和刑事司法领域对机器学习的信任"接口 48,5 号(2018):449–466。
从机器学习中开发最佳记分卡在计算上具有挑战性。一个众所周知的技术是使用超解析线性整数模型(SLIM) 。在这篇文章中,我使用了 OptBinning 包中的记分卡函数。记分卡的分数范围从最低收入概率的 0 到最高收入概率的 100。
作者图片
相当简单的记分卡得出的 AUC 为 0.89。该模型非常接近使用所有特征(100 个预测值)的逻辑回归。然而,记分卡比逻辑回归系数更容易解释和部署。如果可解释性非常重要,记分卡方法非常有效。
后续步骤
为了提供一个总体视图,下面是预测性能与复杂性的结果和图表。重要的是要理解每种技术,rulefit、GA2M、规则列表和记分卡,都有其独特的权衡。我在 rulez githu b repo 上分享了代码,所以你可以全部尝试并做出相应的选择。
我希望这篇文章能给你的机器学习增加一点活力。花点时间去理解你的问题,处理你的数据,看看它是否适合一个更简单的可解释的模型。
有比这篇文章更广泛的可解释模型。还有其他流行的可解释模型,如贝叶斯方法,提供了预测的不确定性。超越莫尔纳尔的可解释性论文的一个很好的起点是钱丹的 imodels 包,它包含了许多不同方法的样本。
我收到了许多人的反馈,包括乔纳森·达尔伯格、泰勒·拉金、钱丹·辛格、迈尔斯·阿德金斯、戴夫·海尼克和尼尚特·甘地。所以非常感谢他们所有人。
你也可以在推特或 LinkedIn 上关注我,并在我的项目网站上找到以前的博客文章。
编写高效代码注释的艺术
数据科学任务代码注释的最佳实践
如果我们在 Jupyter 笔记本上运行import this
,或者简单地打开这个链接,我们将得到Python 的禅,这是 Tim Peters 收集的 19 条 Python 设计指南的集合。其中一条说:“可读性很重要”,在这篇文章中,我们将讨论这个原则的一个方面:代码注释,它是包含在代码中的自然语言文本片段,用于它的文档。事实上,代码注释,加上干净的代码,变量和函数的正确命名,以及使代码尽可能清晰,对我们项目的可读性有很大的贡献。这不仅对将来阅读我们作品的人很有帮助,而且对我们自己也很有帮助。
在本文中,我们将关注注释应用于数据科学任务的 Python 代码的最佳实践。然而,这些准则中的大部分也适用于任何其他编程语言或领域。
语法和样式
在 Python 中,有两种类型的代码注释:块注释和内联注释。
根据 PEP 8 ,块注释以一个 hash ( #
)开头,后跟一个空格,由一个或多个句子组成,第一个单词大写,每句话尾加一个句号。如果有几个句子,用两个空格隔开。块注释通过一个空行与上面的代码分开。它们适用于直接跟在它们后面的代码(没有空行),并且具有相同的缩进。
一长行块注释应该分散在几行中,每一行都以一个散列开头,后跟一个空格。用 Python 编写多行注释的另一种方式是使用多行字符串——那些嵌入在三个单引号或双引号中的字符串。从技术上讲,它们最初是为另一个目的而设计的:将文档分配给函数、方法或类。然而,如果不在这种质量中使用或不赋给变量,多行字符串不会生成代码,因此它可以作为 Python 中代码注释的一种非常规方式。这种方法得到了 Python 创始人吉多·范·罗苏姆在他的推特上的认可。
✔️
# Isolate the outliers with at least $3,500 spent per month except
# for the cases when the respondent hadn't attended any bootcamp or
# had been learning programming for a maximum 3 months before the
# survey.
above_3500 = usa[(usa['MoneyPerMonth']>=3500)\
&(usa['AttendedBootcamp']!=0.0)\
&(usa['MonthsProgramming']>3.0)]✔️
'''Isolate the outliers with at least $3,500 spent per month except for the cases when the respondent hadn't attended any bootcamp or had been learning programming for a maximum 3 months before the survey.
'''
above_3500 = usa[(usa['MoneyPerMonth']>=3500)\
&(usa['AttendedBootcamp']!=0.0)\
&(usa['MonthsProgramming']>3.0)]✔️
"""Isolate the outliers with at least $3,500 spent per month except for the cases when the respondent hadn't attended any bootcamp or had been learning programming for a maximum 3 months before the survey.
"""
above_3500 = usa[(usa['MoneyPerMonth']>=3500)\
&(usa['AttendedBootcamp']!=0.0)\
&(usa['MonthsProgramming']>3.0)]
一个行内注释被放置在它所注释的代码段的同一行上,用至少两个空格、一个散列和一个空格隔开。行内注释通常较短,应谨慎使用,因为它们往往会在视觉上与上下的代码行混在一起,因此变得不可读,如下面这段代码所示:
❌
ax.set_title('Book Rating Comparison', fontsize=20)
ax.set_xlabel(None) # remove x label
ax.tick_params(axis='both', labelsize=15)
plt.show()
不过,有时行内注释可能是不可或缺的:
✔️
colors = [[213/255,94/255,0], # vermillion
[86/255,180/255,233/255], # sky blue
[230/255,159/255,0], # orange
[204/255,121/255,167/255]] # reddish purple
请注意,在上面的代码中,行内注释的对齐有助于提高它们的可读性。
尽管代码注释语法的指导方针是为了使代码更具可读性和一致性,但我们应该记住,语言本身是不断发展的,样式约定也是如此。此外,一个公司(或一个特定的项目)可以有自己的编码风格方法,如果与 PEP 8 不同,那么优先考虑那些特定情况的建议。因此,有时官方的指导方针可以被忽略。但是,有些做法几乎总是应该避免的:
- 在注释前使用多个哈希
- 在注释的结尾也使用一个或多个散列
- 省略哈希和注释之间的空格
- 使用全部大写字母(我们稍后会看到一个例外)
- 在注释上方的一段代码后省略一个空行
- 在注释及其相关代码之间插入空行
- 忽略注释代码的缩进
这些方法是不可取的,因为它们用不必要的元素使代码变得混乱,降低了代码的可读性,并使区分不同的代码块变得困难。比较这些注释样式不同的相同代码:
❌
###REMOVE ALL THE ENTRIES RELATED TO 2021###questions = questions[questions['Date'].dt.year < 2021]
###CREATE A LIST OF TAGS###tags_list = list(tags.keys())✔️
# Remove all the entries related to 2021.
questions = questions[questions['Date'].dt.year < 2021]# Create a list of tags.
tags_list = list(tags.keys())
写有意义的评论
如果代码本身告诉计算机要做什么,那么代码注释是写给人类的,向他们解释这段代码到底是做什么的,特别是为什么我们需要它。关于“什么”,有一种普遍的观点认为,如果我们的代码尽可能显式和简单,如果我们对变量和函数使用自解释的名称,那么我们几乎根本不需要代码注释。尽管我同意编写易于理解的代码和仔细选择变量/函数名非常重要,但我认为代码注释也相当有用。
- 它们有助于将代码分成几个逻辑部分,使其更容易导航。
- 它们有助于解释很少使用的或新的方法/功能的功能。此外,如果我们必须应用不寻常/不明显的方法来编码,添加代码注释是一个好主意,因为当试图遵循更明确的方法时,可能会检测到一些 bugs 技术问题/版本冲突。
- 编写代码时,我们应该记住将来可能会阅读我们代码的人(例如我们的同事)。对一个人来说很清楚的事,对另一个人来说可能一点也不明显。因此,我们应该考虑到不同人在经验、技术和背景知识方面的潜在差距,并通过使用注释来促进他们的任务。
- 代码注释对于添加关于作者、日期和修改状态的信息很有价值(例如
# Updated by Elena Kosourova, 22.04.2021\. Added an annotation of the current year on the graph.
)。
因此,代码注释是提高代码可读性和可访问性的快速而强大的方法,对于编写清晰而有意义的注释来说,仅仅成为一个好的编码员是不够的,成为一个好的作者也很重要,这本身几乎是一项需要学习的技能。
让我们考虑一些我们应该遵循的建议,以便有效地应用代码注释并防止过度使用。
避免明显的代码注释
我们的评论不应该脱离上下文陈述明显或清楚的事情。让我们再次记住,“证据”可能因人而异,我们工作的范围和目标受众也很重要(例如,如果我们正在编写一个关于数据科学的真实数据项目或一本学生手册)。我们的任务是找到平衡。
❌
# Import libraries.
import pandas as pd
import numpy as np
避免低效的代码注释
为了最大限度地发挥作用,我们的代码注释应该尽可能地精确和简洁,给出关于代码的所有必要细节,并排除任何不相关的信息,如对以前代码的观察、对未来的打算或括号。此外,由于代码通常暗示了一些动态(它做了,创建了,移除了,等等)。),一个好的做法是使用动词而不是名词:
❌ **Too vague**
# Select a specific type of columns.
characters = star_wars.iloc[:, 15:29]❌ **Too wordy**
# Now, let's select all the columns of a radio-button type from our dataframe.
characters = star_wars.iloc[:, 15:29]❌ **Observations from the previous code**
# Select radio-button type columns, as done earlier for checkbox type ones.
characters = star_wars.iloc[:, 15:29]❌ **Using a noun instead of a verb**
# Selection of radio-button type columns.
characters = star_wars.iloc[:, 15:29]✔️ **Good**
# Select radio-button type columns.
characters = star_wars.iloc[:, 15:29]
更新代码注释
这是一个棘手且非常常见的陷阱,但我们应该始终记住,当代码发生变化时,也要对代码注释进行所有必要的修改。正如 PEP 8 所说,“与代码相矛盾的注释比没有注释更糟糕”。更重要的是,有时我们不仅要检查与被修改的代码相关的代码注释,还要检查其他可能被这种修改间接影响的代码注释。
要使用的语言
一种常见的做法是始终用英语编写代码注释。然而,这是在某些情况下可以忽略的规则之一:如果我们绝对确定我们的代码永远不会被不讲我们的语言(或任何其他通用语言)的人阅读,那么我们最好完全用那种语言写注释。在这种情况下,对于我们特定的目标受众来说,代码变得更容易理解。
注释掉部分代码
有时,在一个项目中,我们发现为同一任务或调试代码测试几种方法是有用的,然后我们可以决定保留一些测试过的部分,即使我们在最后选择了另一个,以防万一。然而,这种方法只是暂时的,在项目的最终版本中,清除这样的碎片是很重要的。事实上,对于那些将来会阅读这些代码的人(包括我们自己)来说,这可能会让他们分心。读者可能会开始怀疑被注释掉的代码在任何一步是否有用,以及它是否应该保留在项目中。为了避免这种混乱,最好删除所有注释掉的代码。
像往常一样,这种最佳实践可能存在非常罕见的例外。比方说,我们的工作是展示获得相同结果的不同方法,一种方法比另一种方法更可取(仍然可行,但使用较少)。在这种情况下,我们可以关注 main 方法,并将第二个方法显示为注释掉的代码,并带有相应的注释。此外,这里我们可以使用前面提到的另一个例外,在代码注释中使用大写字母,以使其更加清晰可见:
✔️
# Create a stem plot.
plt.stem(months.index, months)# # ALTERNATIVE WAY TO CREATE A STEM PLOT.
# plt.vlines(x=months.index, ymin=0, ymax=months)
# plt.plot(months.index, months, 'o')
结论
总而言之,代码注释是一种方便的技术,可以向其他人传达我们用于编码的方法以及它们背后的原因。它提高了整体代码的可读性,并使在不同的逻辑块之间导航变得更加容易。为了在 Python 中创建有意义的代码注释,我们必须遵循简单明了的技术准则,牢记可能的本地公司或项目特定的约定,使我们的注释尽可能简洁和信息丰富,在代码修改时更新它们,最后但同样重要的是,避免过度使用它们。
感谢阅读!
如果你喜欢这篇文章,你也可以发现下面这些有趣的:
https://python.plainenglish.io/the-little-prince-on-a-word-cloud-8c912b9e587e https://medium.com/geekculture/creating-toyplots-in-python-49de0bb27ec1 https://medium.com/mlearning-ai/11-cool-names-in-data-science-2b64ceb3b882
你要找的关于 PCA 和 LDA 的文章!!
直觉、基础数学和更多…
由 Chandan Durgia 和 Prasun Biswas 撰写
来源:Unsplash 来自 Devin Avery
说实话,随着 AI/ML 世界的日益民主化,这个行业中的许多新手/有经验的人都操之过急,缺乏底层数学的一些细微差别。
这也很容易理解。AI/ML 世界对任何人来说都是势不可挡的,原因有很多:
a.一个人必须学习不断发展的编码语言(Python/R),大量的统计技术,并最终理解这个领域。
b.人工智能/人工智能技术的发展速度令人难以置信。最近在某处读到,每天都有大约 100 篇 AI/ML 研究论文发表。
c.如果你没有特定的背景,基础数学可能会很难。不幸的是,这不适用于像神经网络等复杂的主题。对于回归、分类问题、降维等基本概念也是如此。
通过这篇文章,我们打算至少一劳永逸地选择两个广泛使用的主题:
-主成分分析
-线性判别分析
这两个主题都是“降维技术”,并且有一些相似的数学基础。我们之前在一篇单独的文章中已经介绍过 t-SNE(https://prasunbiswas90.medium.com/improving-convolution-neural-networks-cnn-accuracy-using-t-sne-a4222d306d8)。
当人们想到降维技术时,有几个问题会出现:
a)为什么要降维?降维是什么意思?
b)线性代数与降维有什么关系?
c)为什么我们需要做线性变换?
d)特征值和特征向量与降维有什么关系?
e)根据变换的级别,是否可能有多个特征向量?
f)LDA 和 PCA 的目标有何不同,它如何导致不同的特征向量集?
g)除了我们讨论的内容之外,PCA 还有其他内容吗?我们要选择所有的主要成分吗?
h)除了使用散布矩阵之外,LDA 的计算类似吗?
I) PCA 与 LDA 的主要区别?什么时候该用什么?
还有更多…
我们试图用最简单的方式回答这些问题。如果您觉得任何特定的概念需要进一步简化,请随时回复这篇文章。
让我们开始吧。
A) 为什么要降维?降维是什么意思?
当数据科学家处理具有大量变量/特征的数据集时,有几个问题需要解决:
a)由于要执行的功能太多,代码的性能变得很差,特别是对于像 SVM 和神经网络这样需要很长时间训练的技术。
b)许多变量有时不会增加多少价值。
降维是一种用于减少独立变量或特征数量的方法。
下图描绘了我们练习的目标,其中 X1 和 X2 概括了 Xa、Xb、Xc 等的特征。
作者图片
B)线性代数与降维有什么关系?
人们可以把这些特征看作是坐标系的维度。
作者图片
*4 功能图是说明性的
虽然目标是减少特性的数量,但这不应该以降低模型的可解释性为代价。即自变量可以解释多少因变量。因此,应该通过以下约束来减少维度——“数据集中各种变量的关系不应受到显著影响。”
这就是线性代数介入的地方(深呼吸)。简而言之,线性代数是一种从各种角度看待坐标系中任何数据点/向量(或数据点集)的方法。
我们来解码一下。
考虑 A 点和 B 点为(0,1),(1,0)的坐标系。现在,为了从不同的透镜(坐标系)可视化该数据点,我们对坐标系进行以下修正:
a)旋转
b)拉伸或挤压
作者图片
正如你在上面看到的,新的坐标系被旋转了一定的角度并被拉伸。请注意,它仍然是相同的数据点,但我们改变了坐标系,在新的坐标系中,它位于(1,2),(3,0)。
如果仔细分析,两种坐标系都具有以下特征:
a)所有线路保持线路状态。即曲线中的线条没有变化。
b)原点(0,0)保持不变。
c)拉伸/挤压仍然保持网格线平行且间隔均匀。
这就是线性代数或线性变换的本质。
每当进行线性变换时,它只是将一个坐标系中的向量移动到一个新的坐标系中,该坐标系被拉伸/挤压和/或旋转。
其实以上三个特征就是一个线性变换的性质。值得注意的是,由于这三个特征,尽管我们正在移动到一个新的坐标系,一些特殊向量之间的关系不会改变,这是我们将利用的部分。
有趣的事实:当你将两个向量相乘时,它具有旋转和拉伸/挤压的相同效果。
C) 为什么我们需要做线性变换?
线性变换有助于我们实现以下两点:
a)从不同的角度看世界,这会给我们带来不同的见解。
b)在这两个不同的世界中,可能有某些数据点的特征相对位置不会改变。
对于上面的#b,考虑下面有 4 个向量 A、B、C、D 的图片,让我们仔细分析转换给这 4 个向量带来了什么变化。
作者图片
作者图片
所以,向量 C 和 d 发生了有趣的事情,即使有了新的坐标,这些向量的方向保持不变,只是长度变了。
为神奇的部分做好准备!!
如果我们能够设法将这个 2 维空间中的所有(大部分)向量(特征)与这些向量之一(C 或 D)对齐,我们将能够从 2 维空间移动到一维空间的直线。
瞧,降维成功!!
这个过程也可以从一个大维度的角度来思考。也就是说,如果我们的数据是 3 维的,那么我们可以将它简化为 2 维的“平面”(或一维的线),如果我们有 n 维的数据,我们可以将其简化为 n-1 维或更小的维。
请注意,在现实世界中,不可能所有矢量都在同一条直线上。因此,对于不在线上的点,取其在线上的“投影”(详情如下)。通过投射这些向量,虽然我们失去了一些可解释性,但这是我们需要为降维付出的代价。同样,可解释性是自变量可以解释因变量的程度。
D)特征值和特征向量与降维有什么关系?
旋转特性不变的这些矢量(C&D)称为本征矢量,这些矢量被缩放的量称为“本征值”。正如您从上面的描述中所看到的,这些是降维的基础,并将在本文中广泛使用。
C = 3 的特征值(向量增加了原来大小的 3 倍)
D = 2 的特征值(向量增加了原来大小的 2 倍)
关键是,如果我们能够定义一种方法来找到特征向量,然后将我们的数据元素投影到这个向量上,我们就能够降低维度。
特征向量的关键特征是,它保持在其跨度(线)上,不旋转,它只是改变大小。即,对于任何特征向量 v1,如果我们应用变换 A(旋转和拉伸),那么向量 v1 仅通过因子λ1 进行缩放。这里λ1 称为特征值。
E)根据变换的级别,可能有多个特征向量吗?
是的,根据变换的级别(旋转和拉伸/挤压),可能有不同的特征向量。因此,根据我们分析数据的目标,我们可以定义转换和相应的特征向量。请注意,练习的“目标很重要,这也是 LDA 和 PCA 存在差异的原因。
F)LDA 和 PCA 的目标有何不同,它们如何导致不同的特征向量集?
对于 PCA,目标是确保我们尽可能地捕捉我们的自变量的可变性。在下图中,我们可以看到数据在某个方向上的可变性。请注意,我们的原始数据有 6 个维度。这只是二维空间中的一个说明性的图形。
作者图片
使用协方差矩阵获取多个值的“可变性”度量。在我们的例子中,输入数据集有 6 维[a,f],覆盖矩阵的形状总是(d * d ),其中 d 是特征的数量。
作者图片
这里,协方差计算如下:
其中 N 是观察次数。
这就是我们计算特征向量的矩阵。
另一方面,线性判别分析(LDA)试图解决“监督”分类问题,其中目标不是理解数据的可变性,而是最大化已知类别的分离。在 LDA 中,协方差矩阵被散布矩阵“替代”,散布矩阵实质上捕捉了“类间”和“类内”散布的特征。
G)PCA 还有比我们讨论的更多的内容吗?我们要选择所有的主要成分吗?
还有一些额外的细节。因此,在这一节中,我们将在我们到目前为止讨论过的基础上进一步深入。
那么,让我们将它端到端地分解开来:
a.假设数据集包含 6 个要素。如前所述,这意味着数据集可以在 6 维空间中可视化(如果可能)。为了便于说明,我们假设这个空间看起来像:
作者图片
b.为了降低维数,我们必须找到这些点可以投影的特征向量。因为这里的目标是捕捉这些特征的“变化”,所以我们可以计算协方差矩阵,如上面#F 中所描述的。
c.现在,我们可以使用下面的公式来计算这个矩阵的特征向量(EV1 和 EV2)。请注意,PCA 是以第一主成分占数据中最大可能方差的方式构建的。然后,因为它们都是正交的,所以一切都是迭代的。
****
作者图片
为什么特征向量是垂直的?
如果使用的矩阵(协方差矩阵或散布矩阵)在对角线上对称,则特征向量是“实数”和“垂直的(正交)”。否则,特征向量将是“复数虚数”。
将任何矩阵转换成对称矩阵的方法是将其乘以其转置矩阵。在后面的部分中,在计算散布矩阵时,我们将使用它将一个矩阵转换成对称矩阵,然后再导出它的特征向量。
d.一旦我们从上面的等式中得到特征向量,我们就可以将数据点投影到这些向量上。为了简单起见,我们假设二维特征向量。
作者图片
注意,当把一个矢量投影到一条直线上时,它失去了一些可解释性。即,对于上图中的向量 a1,其在 EV2 上的投影是 0.8 a1。这就是主成分被写成单个向量/特征的某个比例的原因。
EV1 = PC1 = a1 * 0.8+a2 * 0.9+a3 * 0.78…
值得注意的一点是,计算的特征向量之一将自动成为数据的最佳拟合线,而另一个向量将与其垂直(正交)。
e.尽管在上面的例子中,为了简单起见,选择了 2 个主分量(EV1 和 EV2)。对于具有 n 个向量的情况,n-1 个或更低的特征向量是可能的。根据练习的目的,用户可以选择考虑多少个主成分。这取决于一个人想要获得多少可解释性。使用碎石图也可以得出同样的结论。
Scree plot 用于确定在数据的可解释性中有多少主成分提供了“真实值”。“真实值”意味着添加另一个主成分是否会有意义地提高可解释性。在碎石图上,曲线斜率变平的点(“弯头”)表示分析中应使用的因子数量。
作者图片
H)除了使用散布矩阵之外,LDA 的计算类似吗?
与 PCA 不同,LDA 是一种监督学习算法,其目的是对低维空间中的一组数据进行分类。换句话说,目标是创建一个新的线性轴,并将数据点投影到该轴上,以最小的“类内”方差最大化“类间”的类可分性。见图三十
这在数学上可以表示为:
a)最大化类别可分性,即最大化均值之间的距离。即最大化两类平均值的差的平方。((平均值(a) — Mean(b))^2)
b)尽量减少每个类别中的差异。即最小化数据的传播。即(利差(一)^2 +利差(b)^二)
作者图片
注意,对于 LDA,从#b 到#e 的其余过程与 PCA 相同,唯一的区别在于#b 使用了散布矩阵而不是协方差矩阵。两种散布矩阵的公式都非常直观:
类间离差由下式给出:
其中 m 是完整数据的组合平均值,mi 是各自的样本平均值。
类内离差由下式给出:
其中 x 是单个数据点,mi 是各个类别的平均值。
直观上,这找到了类内和类之间的距离,以最大化类的可分性。
请注意,对于这两种情况,散布矩阵乘以它的转置。如前所述,矩阵乘以其转置使其对称。这样做是为了使特征向量是“真实的”和“垂直的”。
I) PCA vs LDA 的关键差异领域?什么时候该用什么?
作者图片
作者图片
希望这将清除一些讨论的主题的基础,你会有一个不同的视角来看待矩阵和线性代数向前发展。
就像他们说的,任何基本的东西的伟大之处在于它不局限于它被阅读的上下文。这是真正意义上的基础,人们可以在此基础上实现飞跃。
快乐学习!!
免责声明:本文所表达的观点是作者以个人身份发表的观点,而非其各自雇主的观点。
增强工程师
思想和理论
一个几乎微不足道的训练技巧如何成为人工智能向自我监督学习转变的中心。
自从深度学习的早期,当我们仍然缺乏数据时,增强就已经存在了。这可能是书中最古老的把戏了。如果只需做一些改动,我就能把每张照片都变成一张“新”的,为什么还要凑合使用我数据集中的 20 张猫照片呢?我可以移动猫一点,缩放和裁剪,也许创建一个镜像,稍微旋转它,玩颜色直方图,瞧,我已经创建了一个新的猫图片!增强已经成为深度学习训练过程中不可或缺的一部分,以至于模型很少看到原始图片。事实上,模型很少两次看到相同的图片,因为每次需要向模型呈现图片时,它都会经历这样一种随机的转换组合,以至于我们使用相同的图片获得相同组合的机会实际上为零。为增强组合干杯!
猫的增强:旋转,缩放和裁剪,镜像,或者它们的组合。作者插图。
在所有这些使用中,我们假设即使我们改变了图像,我们所做的改变也不会影响它的本质。猫还是猫。或者,如果我们处理肺部的医学图像(CAT 扫描?),如果我们拍摄镜像,肿瘤仍然会在那里。通过使用同一张图片的多个增强来训练模型,并告诉它预测相同的结果,我们实际上是在教模型不受图片中技术变化的影响,这些变化不会改变其本质。
把增强向前推进一步,随之而来的突破是令人惊讶的。为什么只在训练期间增加?为什么不在球场上,在产品本身上这样做呢?如果该算法是为了识别肺部肿瘤,为什么只显示一张肺部照片?我们可以拍摄同一张 x 光照片(T7),稍微移动一下,缩放和裁剪,也许拍摄一张镜像,稍微旋转,改变颜色,并要求模特提供第二种意见。还有第三个。还有第四个。在这个过程的最后,我们将对所有的回答进行平均。事实证明,这种被称为测试时间增加的技巧,持续地将模型的性能提高了好几个百分点,为什么不呢?
标签已经够多了
深度学习的最新变化将增强放在了前沿和中心。这背后的原因是机器学习的圣杯——自我监督学习:没有任何标签的学习。事实上,这就是我们开始的地方,当时深度学习还处于起步阶段,数据匮乏。我们建立了自动编码器——将一张图片压缩成一个小的代表性向量,一个瓶颈,然后将其扩展回其完整大小的模型。我们会训练模型,使重建的图像尽可能与原始图像相似,并祈祷所有重要的图像信息都保留在瓶颈中,而不会指导模型保留什么是重要的。通过这种方式,我们能够在来自互联网各地的数百万张未标记图像上训练模型,这些模型只不过是将图像压缩到一个瓶颈向量(通常称为特征向量)中,然后将其提取回原始大小。
一个自动编码器架构。原始图像被压缩成瓶颈向量,然后被扩展回其原始大小。训练该模型,使得右边的黄色图片应该尽可能与左边的黄色图片相似。作者图。
直到第二阶段,我们才转向更小的带标签的数据集,它只包含几千张图像,其中只有几十张是猫,然后使用自动编码器将它们压缩成特征向量。相信这个向量也编码了图像是否包含猫的知识,并且它以一种比原始图像的像素更容易提取的方式被编码,这使我们认为我们能够训练一个不是特别渴望数据的非深度模型,也许只是一个普通的 SVM,将特征向量分类到不同的类别,然后知道图像是包含猫还是狗。
但是后来他们建造了金字塔。除了其他新世界奇迹,如谷歌的代码、亚马逊的仓库、苹果的 iPhone 和网飞的流媒体,还有一个名为 ImageNet 的项目,该项目拥有数百万张图像的数据集,这些图像被标记为一千种对象类型(包括数千张猫的图像!).这是亚马逊的在线贸易服务“土耳其机器人”在 2012 年制作的最大项目。因此,我们告别了自动编码器,开始训练深度模型,将图像直接映射到相应的类别,结果大大改善。
历年在 ImageNet 上训练和测试的不同模型的准确率。来源:paperswithcode.com
自 2012 年以来,大规模、公开、带标签的数据集的汇编已经将该领域的研究重新导向监督学习。鉴于那里是技术进步发生的地方,许多公司发起了收集和标记数据的大规模行动,有时是通过在第三世界国家雇用许多工人,这些工人几乎不做其他事情。例如,众所周知,Mobileye 的资产之一是从安装在汽车上的摄像头收集数据的机制,并使用位于斯里兰卡的指定团队标记数千万公里的道路行驶。然而,世界上大多数数据仍然是未标记的,只是坐在那里,等待一种知道如何使用它并击败标记数据算法的算法出现。
去年,2020 年,终于发生了。
这一领域的最新突破源于一种叫做对比损失的方法,在这种方法中,增强发挥了核心作用。这是一个两阶段的方法,工作方式类似于我们使用 autoencoder 的瓶颈向量。在第一阶段,我们训练一个模型,使用大量未标记的数据从每幅图像中生成高质量的特征向量,在第二阶段,我们使用有限的标记数据训练一个简单的(线性)模型来分类图像。
那么我们如何教会模型生成一个“高质量”的特征向量呢?首先,这样一个向量应该有什么特征?我们的一个见解是,我们希望有一个不受增强影响的矢量。我们之前说过,增强不会改变图像的本质,我们希望有一个不受图像中与其本质无关的技术变化影响的模型。与自动编码器不同,自动编码器必须在其瓶颈处对允许其使用与原始图像相同的像素来重建图像的一切进行编码,并且因此对任何可能的增强敏感,这里我们希望将同一原始图像的两个增强编码到同一特征向量中。
这导致了下面的训练方法:我们给模型两个图像, x 和x’,在它们以某种方式被增强之后。也许 x 是用原始图像旋转 90 度得到的,x’是通过改变颜色直方图得到的。该模型是一个神经网络(类似于自动编码器中的编码器),它从每幅图像中生成一个特征向量,比如说 y 和y’。我们不告诉模型在每个这样的特征向量中放入什么,但是训练损失函数强加了一个间接的要求——如果两个增强 x 和x’是从相同的原始图像生成的*,我们希望两个特征向量 y 和y’非常相似。相反,如果两个增强是从两个不同的图像创建的*,,我们希望它们的特征向量彼此不同。
SimCLR 插图。来源:谷歌 AI 博客
因此,每次训练迭代,我们给计算机一批不同的图像。我们对每张图像应用两种随机增强,创建图像对——在一些图像对中,两张图像来自同一张图像,模型学习吸引它们各自的特征向量,而在另一些图像对中,图像是使用不同的原始图像创建的,模型学习排斥它们的特征向量。本质上,模型学会忽略增强,并且仅将不受其影响的事物编码到特征向量中。与图像的本质有关的东西,而不是它的技术元素。
这种方法在过去的一年里获得了发展势头,如谷歌的sim clr【1】和BYOL【2】等作品带来了新的技巧,进一步改善了特征学习过程,每批图像中驻留的图像更少。最新的结果表明,如果我们将 ImageNet 数据集视为一组未标记的图像,并将其用于第一阶段的训练,那么在第二阶段,我们可以仅使用一小部分已标记的图像来达到与使用完全标记的 ImageNet 数据训练的模型几乎相同的性能。图表还显示,如果我们增加模型中参数的数量,对比学习方法会变得更好。我确信在接下来的几个月里,我们将会听到使用这种方法获得的新的 ImageNet 高度。
在 ImageNet 上获得的性能是标记的 ImageNet 图像比例的函数。BYOL 和 SimCLR 算法也以未标记的方式使用完整的 ImageNet 数据库。监督算法仅使用标记图像。来源: BYOL 论文。
找到增强(和异常)
那些在异常检测领域工作的人很早就意识到,没有 ImageNet 能够拯救他们,我们将永远不得不处理数据不足的问题,因为异常(例如,行李 x 光扫描中的枪支)就其定义而言,是一种很难在有机组织的数据集中发现的现象。为了解决这个问题,通常使用自动编码器。如果自动编码器在大量没有手枪的手提箱上被训练,并且突然需要将手枪的扫描编码到瓶颈向量中,那么它将很难做到这一点,因为模型从未被要求重建手枪的图像。因此,与“常规”图像不同,在图像包含手枪的情况下,重建图像和原始图像之间可能会有显著差异,如果是这种情况,我们可以向安全部门报警。这种方法在一定程度上起了作用,并在相当一段时间内成为探测异常的主要方法。
但是另一个令人惊讶的突破来自增强领域。对于大量未标记的数据,我们可以做的一件事是自动标记它,尽管是针对我们并不真正感兴趣的任务,例如“找到增强”。在这个游戏中,我们从(比方说)70 种可能性(例如,180 度旋转,90 度旋转,拍摄镜像,缩放和裁剪,或者什么都不做)的集合中通过单个随机增强传递图像;然后,我们将它呈现给模型,并要求它猜测哪个增强(在上述所有可能性中)应用于图像。这是一个虚构的分类任务,虽然它本身并不有趣,但它利用了未标记的数据,我们现在可以训练一个视觉模型来执行它。
被训练来玩这个游戏的模型不会总是设法检测到正确的增强。例如,如果图像是旋转对称的对象,例如从顶部观看的花,那么模型将很难检测到 90 度的旋转。如果这是一张猫的图像,那么相对容易检测出 90 度的旋转,但镜像图像仍然无法与普通图像区分开来。我们可以说,“发现增强”模型将具有某种混淆特征:一些增强将很容易被检测到,而另一些则不那么容易。
然而,在他们 2018 年的论文[4]中,Yitzhak Golan 和 Ran El-Yaniv 教授注意到,当一个掌握了“找到增强”的模型获得一个异常图像时,该模型会以一种完全不同于常规图像的方式感到困惑。换句话说,它的混淆签名是不同的。这就产生了一种检测异常的新方法:将可疑图像进行 70 次放大,看看模型能识别多少。如果可疑图像的混淆签名与平均混淆签名(从“常规”图像估计)相差很大,则发出警报并呼叫保安。令人惊讶的是,这种方法击败了以前使用的自动编码器。
增强工程师
深度学习的人感到自豪的是,他们不再为了告诉模型如何读取数据而进行特征工程,随着我们向自我监督学习过渡,我们也将使用越来越少的标签和标记。尽管如此,我们仍然在设计对解决这个问题至关重要的东西:增强本身。最终,如果我们的目标是区分不同类型的花,那么改变图像的颜色作为可能的增强将阻止特征向量包含关于花的颜色的关键信息。然而,如果我们的目标是区分“花”和“车”,那么这种增强工作得很好。如果我们的目标是发现肺部的恶性肿瘤,它也可能沿着它们的边界出现,那么裁剪掉图像的一部分将是错误的,因为这样我们也可能裁剪掉肿瘤,但是肺部的镜像图像将会很好地工作。我们需要明智地选择我们的增强集,以便不破坏我们希望执行的任务的图像的本质。
左:不同的增强类型。右:y 轴增强是否有助于执行沿 x 轴描述的分类任务。例如,如果我们想要建立一个花分类器,那么旋转是一个有用的增强,但是改变花的颜色就不那么有用了。如果我的任务是区分不同的动物,反过来也是正确的。来源:Tete Xiao 等人的论文《对比学习中哪些不该对比》[3]。
事实上,只要我们需要为对比学习编译正确的增强集,这仍然是监督学习的一种形式,不是吗?为了摆脱这一层,在训练算法中需要另一个阶段,它将自动决定哪一组增强适合于我的数据和期望的任务。在此之前,在去除了特征工程、标签和模型架构之后,我们仍然只剩下“增强工程师”的角色。
参考
[1]陈婷,西蒙·科恩布利斯,穆罕默德·诺鲁齐,杰弗里·辛顿。 SimCLR:视觉表征对比学习的简单框架。PMLR 2020arxiv
[2] Jean-Bastien Grill 等人(DeepMind)。引导你自己的潜能:自我监督学习的新方法。 NeurIPS 2020。[ arxiv ]
[3]永龙田,,本·普尔等.艾尔。(麻省理工,谷歌)。对比学习的好观点是由什么构成的? NeurIPS 2020 [ pdf
[4]伊扎克·戈兰,冉·亚尼夫。使用几何变换的深部异常检测。 NIPS 2018。[ arxiv
自动侍酒师——如何实现 HuggingFace Transformers 并构建搜索引擎
享受一些葡萄酒,现代自然语言处理,简单的代码,情节和破折号
贾斯汀·艾金在 Unsplash 上的照片
创建自动侍酒师
回到 2019 年 8 月,我将我的第一个自然语言处理(NLP)项目投入生产,并在我的网站上主持了自动侍酒师。使用 TensorFlow 1 和通用语句编码器,我允许用户描述他们理想的葡萄酒,并返回与查询相似的描述。该工具将葡萄酒评论和用户输入转换为向量,并计算用户输入和葡萄酒评论之间的余弦相似性,以找到最相似的结果。
余弦相似度是一种比较文档相似度的常用方法,因为它适用于词频等对分析非常重要的数据。它反映了单个向量维度的相对比较,而不是绝对比较。在本文中,我不会深入探究余弦相似性背后的数学,但我知道它是内积空间的两个非零向量之间相似性的度量。
自动侍酒师(RobotsDoDreams.com)
增强时间到了
虽然该模型仍然有效,但自 2019 年以来,自然语言处理取得了巨大进展。使用像 HuggingFace 的 Transformers 这样的工具,将句子或段落转换成可用于语义相似性等 NLP 任务的向量从未如此简单。使用最新的技术和语言模型重构我的代码将会使它更有性能。如果你是 NLP 新手,可以看看我的初学者教程。
在本教程中,我将解释如何使用拥抱脸变形金刚库、非度量空间库和 Dash 库来构建一个新的和改进的自动侍酒师。完整的代码和 GitHub 链接可以在文章的底部找到。
葡萄酒数据
葡萄酒数据来自在kaggle.com 上找到的葡萄酒评论数据集。原始数据包含大约 130,000 行数据,包括国家、描述、标题、品种、酒厂、价格和评级等列。
查看所有数据工程代码的原文。
将数据放入 dataframe 后,我删除了包含重复描述的行和价格为空的行。我还将数据限制在有超过 200 条评论的葡萄酒品种上。
通过排除少于 200 条评论的品种来减少数据,我得到了 54 种葡萄酒。在清除了空数据和重复数据之后,剩下 100,228 行。通过谷歌搜索剩下的品种,我可以添加一个颜色栏,这样用户就可以通过想要的葡萄酒颜色来限制他们的搜索。
导入依赖项和数据
很容易将数据连接并加载到 dataframe 中,因为它已经是一个 sqlite 文件。按照三个步骤加载库、数据和数据帧。
1.导入 pandas 和 sqlite3 库。
2。连接到 sqlite 文件。
3。将数据加载到熊猫数据框架中。
#*Import dependencies* import numpy as np
import pandas as pd
import sqlite3
from sqlite3 import Errorimport texthero as hero
from texthero import preprocessing
from sentence_transformers import SentenceTransformer, util
import nmslibimport time
import datetime *#Establish connection to sqlite database*
conn = sqlite3.connect("wine_data.sqlite")*#load the data into a pandas DataFrame*
df = pd.read_sql("select * from wine_data", conn)
注意,我还导入了将在教程中使用的其他库。我将更多地介绍他们。使用 pandas read_sql 函数生成一个使用原始 sql 的数据帧。请注意,数据集中有 16 列和 100228 行。
葡萄酒数据 df.head(3)
拥抱脸🤗变形金刚(电影名)
如果你在过去一年左右的时间里参与了自然语言处理(NLP)领域,你可能已经听说过 HuggingFace🤗。HuggingFace 是一个专注于 NLP 的人工智能和深度学习平台,目标是实现人工智能技术的民主化。他们精简和简化了应用和微调预先训练的语言模型。
Transformers 是一个开源库,具有一个模型中心,允许用户基于通用架构(如 BERT、XLM、DistilBert 等)实现最先进的深度学习模型…它构建在 PyTorch、TensorFlow 和 Jax 之上,并且已知在框架之间具有良好的互操作性。
他们刚刚发布了一个完整的课程,带你穿越拥抱脸生态系统。我强烈推荐它:
**https://huggingface.co/course/chapter1
使用 pip 安装库
$ pip install transformers
在这个例子中,我将使用distilBERT-base-un cased模型,因为它与我们的用例、语义相似性表现良好。它将文本转换成 768 维向量。如果你不想使用 distilBERT ,探索所有句子相似度的 HuggingFace 模型 。这个模型是不区分大小写的,这意味着它不区分英语和英语。查看官方文件,了解关于该型号的详细信息。
要实现该模型,请遵循以下步骤:
- 用distil Bert-base-un cased模型实例化 SentenceTransformer 。
- 调用编码并将葡萄酒描述传入其中。将参数convert _ to _ tensor=设置为 True。
#load the distilbert model
distilbert = SentenceTransformer('distilbert-base-uncased')#generate the embeddings for the wine reviews
embeddings = distilbert.encode(df['description'], convert_to_tensor=True)
注意 :如果您之前从未下载过该模型,您将看到它正在下载,并且可能会弹出一些消息。这很正常。
一旦该过程完成,文本描述将被转换成长度为 768 的向量。我们可以检查长度和第一次嵌入,以确保它看起来像预期的那样:
print(len(embeddings[0])
print(embeddings[0])
为了使向量更容易分析,使用 numpy 将数据从张量对象转换为列表对象,然后将列表附加到 pandas 数据帧。
#add embeddings to dataframe
df['distilbert'] = np.array(embeddings).tolist()#show the top row
df.head(1)
数据帧中的嵌入 df.head(1)**
创建搜索索引
当使用像谷歌或必应这样的搜索引擎时,用户希望很快得到结果。为了以闪电般的速度搜索我们的结果集,我们可以使用轻量级且高效的非度量空间库(NMSLIB) 。
使用 pip 安装它:
$ pip install nmslib
如前所述,我们希望使用余弦相似度作为我们比较用户输入和葡萄酒描述的度量。我们需要能够找到与我们的搜索向量最近的向量。使用蛮力技术搜索和排序数据既昂贵又缓慢。相反,应该为数据点创建一个索引。
创建搜索余弦相似性索引相当简单:
- 初始化一个新的索引,通过 hnsw 作为方法和余弦米尔作为空间**。**
- 使用 addDataPointBatch 方法将嵌入添加到索引中。
- 使用 createIndex 方法创建带有数据点的索引。
# initialize a new index, using a HNSW index on Cosine Similarity
distilbert_index = nmslib.init(method='hnsw', space='cosinesimil')
distilbert_index.addDataPointBatch(embeddings)
distilbert_index.createIndex({'post': 2}, print_progress=True)
如果您希望保存索引并在以后加载它(比如在生产服务器上),请使用以下代码:
#Save a meta index and the data
index.saveIndex('index.bin', save_data=True)#Re-intitialize the library, specify the space
newIndex = nmslib.init(method='hnsw', space='cosinesimil_sparse')*#Re-load the index and the data* newIndex.loadIndex('sparse_index.bin', load_data=**True**)
创建搜索功能
既然数据已经矢量化,搜索索引也已经填充,那么是时候创建接收用户查询并返回相似葡萄酒的函数了。
search_wine 函数将接受两个输入: DataFrame 和 UserQuery 。用户查询将使用编码转换成一个向量,就像我们对葡萄酒描述所做的那样。然后 NMSLIB 可以用来返回用户查询向量的 k 近邻 。我设置了 k=20 ,不过可以随意实验。
def search_wine(dataframe, userQuery):
if dataframe is not None and userQuery is not None:
df = dataframe.copy()
query = distilbert.encode([userQuery], convert_to_tensor=True)
ids, distances = distilbert_index.knnQuery(query, k=20) matches = [] for i, j in zip(ids, distances): matches.append({'country':df.country.values[i]
, 'winery' : df.winery.values[i]
, 'title' : df.title.values[i]
, 'variety': df.variety.values[i]
, 'color' : df.color.values[i]
, 'description': df.description.values[i]
, 'price': df.price.values[i]
, 'rating': df.rating.values[i]
, 'distance': j
}) return pd.DataFrame(matches)
注意结果被返回并作为字典附加到一个列表中。这使得将结果转换回数据帧变得容易。对于 距离 值,越小越好。例如,距离为 0 意味着向量相同。
测试功能:
搜索 _ 葡萄酒结果
创建可视化资源管理器
除了文本搜索之外,我们还可以使用降维技术在二维空间中绘制葡萄酒,从而提供一个可视化的浏览器。使用 Texthero 库,很容易应用 t-SNE 算法来降低向量的维数并将其可视化。在引擎盖下,Texthero 使用 Plotly 制作交互式图表。
t-SNE(t-distributed random neighbor embedding)是一种用于可视化高维数据的机器学习算法。t-SNE 技术应用了非线性降维。
将 t-SNE 应用于数据帧中的蒸馏向量列。
df['tsnedistilbert'] = hero.tsne(df['distilbert'])
使用 texthero 创建散点图。
#create scatter plot of wines using the
hero.scatterplot(df, col='tsnedistilbert'
, color='variety'
, title="Wine Explorer"
, hover_data = ['title','variety','price','description'])
葡萄酒浏览器可视化
数据中有如此多的变化,以至于散点图看起来像宇宙背景辐射,但这没关系。将鼠标悬停在一个点上会显示更多信息。用户可以点击一个品种,将其从图表中删除。例如,这里是关于葡萄牙红色和白色的混合:
葡萄牙红色和白色混合
有趣的是,我们可以看到一些品种是如何聚集在一起的,而另一些品种是如何分散在各处的。
创建用户界面
为了允许用户与搜索功能进行交互,我们可以使用 Dash by Plotly 构建一个简单的用户界面。 Dash 是一个基于 Flask、Plotly.js 和 React.js 编写的 Python 框架,如果你是 Dash 新手,想要掌握基础知识,请查看我的所有教程:
**https://medium.com/swlh/dashboards-in-python-for-beginners-and-everyone-else-using-dash-f0a045a86644
用户界面
如果您想在 jupyter 笔记本中构建 dash 应用程序,请安装 Dash、Dash 引导组件和 jupyter-dash。
pip install dash
pip install dash-bootstrap-components
pip install jupyter-dash #if you want to build in a jupyter notebook
Dash 应用程序由布局和回调组成:
布局
布局由组件树组成,描述了应用程序的外观以及用户如何体验内容。
复试
回调使 Dash 应用程序具有交互性。回调是 Python 函数,每当输入属性改变时,就会自动调用。
构建布局
布局超级简单,采取极简的手法。使用一张卡片来放置搜索框,让用户按一个按钮,然后返回结果。这个例子使用了引导组件。
import dash
from jupyter_dash import JupyterDash
import dash_bootstrap_components as dbc
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Outputexternal_stylesheets = [dbc.themes.BOOTSTRAP]app = JupyterDash(__name__, external_stylesheets = external_stylesheets)# Create server variable with Flask server object for use with gunicorn
server = app.server
添加 boiler-plate 代码以运行 Dash 应用程序后,创建将用作布局的组件树。我称第一个组件树为 search_card。
search_card 使用卡和卡体来包含文本区和按钮组件。在我看来,把它们放进卡片里会让它看起来更好一点。
search_card = dbc.Card(
dbc.CardBody(
[
html.H5("Auto-Sommelier", className="card-title")
, dbc.Textarea(id = 'user-input', bs_size="lg"
, placeholder="Describe Ideal Wine")
,dbc.Button('search', id = 'search', color="danger")
]
)
)
第二个组件树充当 search_card 和包含葡萄酒结果的卡片的容器。该组件树将被设置为 app.layout 。
app.layout = html.Div([html.H1("Find Your Wine!")
, html.Br()
, dbc.Row(dbc.Col(**search_card**,
width={"size": 6, "offset": 3}))#end row
, html.Br()
, dbc.Card(dbc.CardBody(html.Div(id = 'results')))
])
注意 search_card 被放到行和列组件中,这样更容易居中。在第二棵树的末尾,它包含另一个卡片和卡片主体,其中只包含一个 html。Div 与 id =结果 。该应用程序将依靠回调来用搜索结果填充 CardBody 的 Div 组件。
回调会将结果输出到结果 Div 。作为输入,回调将接受用户的查询和按钮点击。如果都不是,查询将被传递给 search_wine 函数,并以数据帧的形式返回结果。
[@app](http://twitter.com/app).callback(Output("results", "children")
, [Input("user-input", "value")
, Input("search", "n_clicks")
])
def return_wine(userquery, n):
if userquery is not None and n is not None:
dff = df.copy()
results = search_wine(dff, userquery)
table = dbc.Table.from_dataframe(results, striped=True, bordered=True, hover=True)
return table
请注意,dataframe 是使用 bootstrap Table 组件转换成 HTML 表格的。
要运行 Dash 应用程序,请使用以下命令:
app.run_server()
#Dash app running on [http://127.0.0.1:8050/](http://127.0.0.1:8050/)
自动侍酒师
恭喜你!你刚刚用拥抱脸变形、余弦相似度和破折号制作了你自己的葡萄酒搜索引擎!**
最后的想法和完整的代码
与我在 2019 年创建的原始自动侍酒师相比,这个版本的实现速度快得多,也简单得多。通过像 HuggingFace 这样的框架利用最先进的语言模型的能力为像我这样的机器学习爱好者打开了大门,让他们只用几行代码就可以构建一些令人敬畏的应用程序。现在是做一些分析的时候了,看看结果与原来的工具相比有什么改进!
比较同一查询的结果。新(顶部)
感谢阅读!以下是完整的代码:
**https://github.com/bendgame/MediumWineRecommend2
谢谢大家!
https://erickleppen.medium.com/the-ultimate-guide-to-erics-articles-d32df8241353 **
2021 年的自动化书写辅助前景
该领域的过去和未来的全景,从 Unix 作者的工作台到 Grammarly 以及其他
罗伯特·戴尔和杰特·维埃森
乔安娜·科辛斯卡在 Unsplash 上的照片
自动化写作辅助——包括各种基于计算机的帮助写作的工具——已经以这样或那样的形式存在了 60 年,尽管它一直是 NLP 领域中相对较小的一部分。但深度学习的最新进展极大地推动了这一类别。我们回顾一些历史,看看今天的情况,并考虑事情可能会走向何方。
这篇文章的 T4 版本出现在《自然语言工程杂志》上。
1.介绍
本专栏以前两次研究过商业上可用的写作辅助工具,更具体地说,是语法检查器和相关技术。早在 Dale (2004) 的时候,我们就认为微软将语法检查整合到几乎无处不在的文字处理平台 MS Word 中,扼杀了这个领域的创新。通过将当时最先进的语法检查方法与市场领先的桌面办公软件捆绑在一起,实际上是免费的,微软似乎为第三方设置了高得不可攀的准入门槛。
然后,五年前,我们调查了一些新进入者是如何设法在写作辅助市场站稳脚跟的( Dale 2016) 。我们特别关注成立于 2009 年的 Grammarly,尤其是因为它通过其有点无情的在线营销计划获得了显著的知名度。
今天,Grammarly 是主要功能是写作辅助的工具的市场领导者。2019 年,公司获得 9000 万美元资金,这对于一家 NLP 公司来说是相当大的一笔数目;而 Grammarly 现在声称每天有 3000 万用户。当然,与 Word 的市场渗透率相比,这算不了什么——即使在 2016 年,微软声称有 12 亿 MS Office 用户。但是很难找到有多少人真正使用 Word 的语法检查器的数字,而更容易找到的是许多人要么忽视它的建议,要么关闭它的轶事证据。你还会发现很多博客文章声称 Grammarly 的 checker 比微软的好,但很少有人提出相反的观点。
深度学习的最新进展似乎将再次重新安排自动化写作辅助空间,所以现在似乎是进行评估的好时机。下面,我们回顾一些历史,看看今天的情况,并考虑他们在未来可能去哪里。
2.传统景观
从自然语言处理的角度来看,自动写作辅助传统上包括三种不同的能力,其目的是帮助作者解决他们写作中的缺陷:拼写检查、语法检查和风格检查。当我们在这里使用这些术语时,拼写检查就是要确保你为记住的单词键入的字符序列与那些被普遍接受的组成该单词的字符序列相对应;语法检查是要确保你输入的单词序列符合你所用语言的公认语法规则;风格检查是为了确保除了拼写和语法的正确性之外,你在选择如何说你想说的话时所做的其他自由选择都适合你的预期交际目的和目标受众。
2.1 拼写检查
也许并不奇怪,拼写检查是历史最悠久的,最早的拼写检查器是在整整 60 年前由斯坦福大学的 Les Earnest 在 1961 年开发的( Earnest 2016 )。
早期的方法依赖于有效单词的列表,文本中不在该列表中的任何单词都被认为是潜在的拼写错误。除了长尾词汇的明显问题使得这种列表的构建似乎是一个回报递减的无止境的任务之外,还有“真实单词”错误的问题,其中一个单词被拼错为另一个有效单词。直到 20 世纪 80 年代末和 90 年代初,统计语言模型的工作才在这里提供了一个解决方案,用 n 克概率来检测上下文中似乎不太可能的单词。
今天,这些方法的变体现在被用于,例如,微软 Word 和谷歌文档。微软在 2007 年将上下文拼写检查添加到 Word 中:这最初在设置面板中被标记为“使用上下文拼写”,但后来被重命名为“经常混淆的单词”。2009 年,谷歌在现已倒闭的 Wave 产品中加入了一个上下文敏感的拼写检查器,它使用了一个大规模的网络衍生语言模型。
2.2 风格检查
20 世纪 70 年代末,贝尔实验室的 Lorinda Cherry 和 Nina McDonald 开发了 Unix Writer’s Workbench,这是一套旨在帮助解决各种写作问题的程序,其中许多都属于风格的范畴( Macdonald 1983) 。这些程序通常基于简单的字符串模式匹配,包括检测重复单词和分裂不定式的工具,以及识别行话、性别歧视语言和其他不推荐的单词和短语的工具。
由于与许多教育机构选择的操作系统捆绑在一起,Unix WWB 在学术界获得了强大的立足点。在学术部门之外,许多商业实体很快在 20 世纪 80 年代初进入市场的个人计算机平台上复制了 WWB 的关键功能。这些产品中最著名的是 Grammatik,它被誉为个人电脑上的第一个语法检查程序。
今天,我们可能不愿将这些模式匹配方法称为“语法检查”,但区分正在处理的现象的分类和用于识别或纠正这些现象的技术是很重要的。借助简单的正则表达式,可以捕获一系列常见的语法错误;如果句子结构太复杂,正则表达式无法发现潜在的问题,那么在简单的语法方法暴露其缺点之前,对过长句子的预先检查可能只会将问题返回给作者。
大约在同一时间出现并提供类似功能的其他软件包有 RightWriter、标点和风格、正确语法和 PowerEdit 多亏了互联网档案馆,你可以在 1991 年一期的 InfoWorld 上找到一篇关于这些软件包的有趣的比较评论。
有趣的是,即使使用这些简单的技术也能实现如此多的目标。你不能用简单的模式匹配解决所有的拼写、语法和文体问题,但你仍然可以做很多;因此,正如我们将在下面进一步看到的,这些技术在今天的写作辅助领域仍然占有突出的地位。
2.3 语法检查
正如刚才提到的,正则表达式可以帮助您识别一些语法错误。但面对一个包含双重嵌入关系从句的长主语名词短语,几乎可以肯定的是,一个简单的模式匹配方法在试图检查主语和动词数量一致时会发现错误的标记。20 世纪 80 年代和 90 年代初的主流观点是,要正确地进行语法检查,你需要一个全面的、覆盖面广的短语结构语法,以及一个处理该语法认为不正确的单词序列的机制。
有大量关于语法检查的研究文献,其中大部分来自 20 世纪 80 年代,当时语法和解析似乎吸收了 NLP 研究社区的大部分精力。然而,除了一个非常突出的例外,还不清楚这些研究中的大部分是否进入了商业产品。凯伦·詹森(Karen Jensen)和乔治·海多恩(George Heidorn)的工作使当时被认为是“真正的”语法检查脱颖而出,他们首先在 IBM 开发了 EPISTLE 系统(海多恩等人,1982 年),随后在微软,他们负责开发了第一个基于对被分析文本进行完全解析的广泛可用的语法检查器,于 1997 年作为微软 Word 的一个组件发布。
在我们接受从树库中学习语法的想法之前,开发这样一个覆盖广泛的语法资源是一项巨大的任务,很少有公司尝试过。Grammatik 的后来版本,最后一次出现在 WordPerfect 中,似乎包含了某种程度的语法分析;在 Jensen 和 Heidorn 的工作之前,Microsoft Word 使用了一款名为 CorrecText 的产品,据称该产品使用了全面的语法分析( Dobrin 1990 )。前面提到的语法似乎也使用了某种形式的句法分析。但是唯一公开的有良好记录的基于商业级语法的语法检查器仍然是 Word 中使用的系统( Heidorn 2000 )。
3.今日景观:旧与新的相遇
那么,当今市场上可买到的自动化写作辅助工具是什么样的呢?为了了解这一点,我们回顾了 50 多种目前可用的写作辅助商业工具,引导我们确定了三大类:
- 模式匹配风格检查器:虽然 80 年代开发的几乎所有程序都消失了,但它们的精神在许多成功的新应用程序中仍然存在。
- 语言模型驱动的重写工具:这一新的类别为传统的文本修改方法提供了另一种选择。
- 文本生成工具:能够根据用户提供的提示创建各种长度的文本,这代表了从计算机作为编辑者到计算机作为作者的范式转变。
我们在下面挑选出我们认为更有趣的应用。
3.1 模式匹配样式检查器
20 世纪 80 年代出现的特定样式检查应用程序几乎已经消失。RightWriter 仍然以为代表,这是一个网站,它很好地提醒人们上世纪 80 年代的网站是什么样子,但是如果你点击链接购买该软件,什么也不会发生;该网站唯一正常运行的外部链接是一个论文工厂,我们怀疑该网站被劫持只是为了托管该链接。
20 世纪 80 年代的其他工具现在都不在网上了,除了一个有趣的例外:1989 年首次出现的 StyleWriter,看起来仍然很流行。它的网站早于现代网站模板光滑简单的设计,其商业模式同样来自更早的时代:这是一个通过一次性购买(尽管有可选的年度升级和额外费用的支持)购买的应用程序,而不是通过现在无处不在的 SaaS 模式按月租用。
但是,在风格检查这个领域,年龄和经验可能会有所帮助。这些应用程序的核心是手动创建的规则,这些规则可以检测文本中非常具体的模式,提出同样具体的更正建议,并经常对有争议的问题提供详细的手动解释。在其他条件相同的情况下,你存在的时间越长,你的软件积累的规则就越多。StyleWriter 检查 50,000 个单词和短语,并利用按难度分级的 200,000 个单词的列表;该网站声称每年新增 1000 张支票。该应用程序还包括一个正则表达式语言,如果您最喜欢的 bugbear 还没有被覆盖,您可以使用它来添加新的模式匹配规则。
这里更有趣的是,同样的老派技术如何成为一批更新应用的基础。其中,海明威编辑器(成立于 2014 年)看起来是最简单的。这利用了一个简单明了的界面来推荐修改,使你的文本“醒目清晰”;它的范围仅限于检测副词和被动语态的使用,建议更简单的表达形式,反对难以阅读的句子。这个工具并没有假装做语法检查;重点是通过检测可以删除或替换的特定模式来简化文本。
这个领域的许多应用程序都面向专业作家,因此通常支持跨用户团队共享样式设置。 Lingofy 和 Linguix 都属于这一类;在每种情况下,您都可以使用现有的规则集进行样式检查,或者使用您自己的规则来扩充这些规则集。Linguix 还声称可以检查语法,但是这种能力似乎仅限于可以通过简单的模式匹配检测到的错误。
PerfectIt (成立于 2009 年)特别关注围绕家居风格的更多机械方面的使用一致性,如字母数字表达式的格式、内部符号标点和地理拼写偏好;它还为根据特定需求定制样式表提供了广泛的支持。 StyleGuard 似乎是美联社风格指南的一个实现,拥有‘超过 25000 条风格规则’;同样,提供的所有示例似乎都可以通过模式匹配来实现。
WordRake (成立于 2012 年),似乎特别针对法律行业,也注重编辑的清晰和简洁;它与许多其他应用程序的区别在于强调隐私承诺,因为它是一个桌面实现,不会向云发送任何数据或文档。该应用程序得到了许多非常详细的专利的支持,尽管这些专利似乎描述了与使用许多其他供应商提供的模式匹配语言捕获的规则非常相似的规则。
ProWritingAid (成立于 2012 年),我们也将其包括在下一节描述的应用程序类别中,它揭示了似乎是编写自己的规则的最复杂的语言,包括词性标签和屈折形态学;这使其与开源工具语言工具 ( Naber 2003 )和截止日期后 ( Mudge 2010 )的不相上下。
3.2 文本重写中的语言模型
各种各样的统计语言模型已经进入了写作辅助工具。2019 年,谷歌在 Google Docs 中推出了增强的语法纠正工具,采用了一种现在常见的方法,即把语法错误纠正视为机器翻译任务,并使用神经 MT 技术来检测和纠正文本中的错误。同样在 2019 年,Grammarly 发表了一项研究,描述了他们如何使用 Transformer 语言模型进行语法错误纠正。
但是大型语言模型的可用性也支持新的写作辅助任务,这在以前是不可行的。一旦我们允许一个语言模型对一个提供的文本提出一个替代方案,对错误或不恰当的检测和纠正就变成了更一般的文本重写任务的一个特例。提供现有句子的替代翻译的能力,不管它们是否包含错误,已经成为写作工具中新的必备功能;因此现在有许多应用程序将错误检测与语句级重写结合起来。
除了声称使用某种形式的人工智能所提供的方向性暗示之外,供应商很少明确他们使用的技术的确切性质。据我们所知,以下利用大型语言模型来纠正或转换文本:
- Ginger (成立于 2007 年)似乎利用了相当复杂的句法分析,但也利用了“基于人工智能的”句子重组器。
- Gramara 称自己是“人工智能驱动的语法检查器”;它让你指定其重写的语气,并提供翻译你的文本。
- Outwrite Pro (成立于 2015 年,名为 GradeProof)非常重视它的整句释义能力。
- Wordtune 以随意或正式的风格重写提供的文本,并且可以缩短或扩展最初提供的文本。
除了重写文本,这些应用程序通常还会检测特定的错误,提供一个与更传统的检查器非常相似的界面,从而将文本中发出的错误与弹出窗口相关联,这些弹出窗口对错误类型进行分类,提供潜在的替换,并允许忽略或关闭相应的规则。通常不清楚这些功能是通过简单的规则还是更复杂的规则来提供的,尽管后者肯定是一种可能性:根据其创造者的说法,前面提到的 ProWritingAid 结合了一种模式匹配方法来检测常见问题,以及一种使用深度学习模型来创建校正文本的方法,然后使用更传统的分类算法标记错误类型。
3.3 从编辑到作者
无论我们谈论的是拼写、语法还是风格,自动化写作帮助传统上一直关注于纠正或改进人类作者所写的文本。像 OpenAI 的 GPT-3 这样的大型语言模型令人印象深刻的能力,可以生成任何主题的可信和令人信服的文本,这为一系列新的能力打开了大门,在这些能力中,人类和机器共同承担文本内容的责任。
这种可能性的第一个迹象是 2015 年谷歌智能回复功能的发布,根据该功能,对于某些类型的收到的邮件,Gmail 将提供一组简单的一键回复消息,其内容根据收到的邮件确定。这实际上是一个更复杂的版本:谷歌 2018 年的智能撰写功能,当你键入电子邮件时,它会交互式地提供句子完成建议,不仅会考虑你已经键入的内容,还会考虑你正在回复的电子邮件和电子邮件的主题行。
这些是我们在其他地方称为“短皮带”一代的例子( Dale 2020 ):机器生成的文本不允许偏离太远,受到之前发生的事情的充分限制,很有可能它是对你可能会说的话的准确预测。
但是仅仅一年前开放人工智能的 GPT-3 API 的发布已经极大地改变了这里的可行性。由早期和较小的语言模型创建的文本的似真性不是很好,特别是当它们变得更长时;开始的几个句子可能看起来不错,但之后的输出会很快退化成废话。GPT-3 仍然很有能力产生废话,但总的来说,这是更合理的废话;通过适当的微调和提示,它生成的文本可以令人难以置信地令人信服。
因此,现在有几十种应用程序可以通过给你一个简短的提示来帮你写作。表 1 列出了一个示例;当你读到这里的时候,其中的一些可能已经消失,被其他的取代。其中大多数允许你注册免费试用,这是值得做的,以了解他们能做什么。输出往往很弱,尤其是对于较长的文本,但偶尔你会真正留下深刻印象;如果你有写作障碍,即使较弱的结果也可能提供灵感来源。
每个应用程序都支持一系列用例,有时称为模板或任务:最常见的是较短的文本,如标题生成、产品描述和广告文案,但也有相当多的应用程序支持较长的文本和不太常见的用例。这些应用程序的界面和对生成的输出的控制程度各不相同;每种情况下的结果质量在某种程度上取决于开发人员针对其特定用例进行的调整。
4.从这里去哪里?
关于我们之前描述为定义自动书写帮助范围的一组功能的一些观察:
- 拼写检查是一种商品。这并不是说这是一个已经解决的问题——没有一个拼写检查器能发现 100%的拼写错误——也不意味着你遇到的每个拼写检查器都能提供最先进的性能;但是现在几乎所有支持文本编辑的平台都集成了拼写检查器。对大多数作者来说,知道有某种拼写安全网就足够了,即使这个网中可能有漏洞。由于第一条红色曲线下划线的弹出,我们确信最常见或最严重的错误会被发现,并承担那些可能被忽略的错误发生概率较低的风险。
- 对语法检查的态度各不相同。对于一些供应商来说,战场已经转移到更普遍的“改进写作”的任务上:他们认为语法检查战已经被现有的厂商赢得了,并且不认为任何进一步的战斗值得努力。一旦通过 Word、Grammarly 或其他方式进行了语法检查,这些供应商就有效地将他们的解决方案推销为您使用的应用程序;这意味着语法检查实际上是例行公事,他们宣传他们的重点是解决语法以外的问题。其他供应商坦率地承认,他们不尝试语法检查,因为这太难了,也许暗示这根本不可能。然后还有一些声称使用人工智能来捕捉语法错误的产品,这些产品是更主流的产品所不能的。
- **时尚永远不会过时。**正如我们所见,使用 20 世纪 80 年代技术解决风格问题的应用程序仍然很强大。使用大型语言模型重写文本的新一波应用程序可以说都与风格有关:给定一个原始句子和一个传达相同内容的重写版本,你通常可以将这种差异描述为纯粹的风格差异。大型语言模型可能会鼓励我们从更全面的角度来思考风格,将其视为整个文本的属性;也许 20 世纪 80 年代根据特定词序的恰当与否来描述风格的方法是一种只见树木不见森林的情况。
面对这个领域如此多的新应用,为了保持自己的地位,Grammarly 长期以来一直不强调对语法检查的特别关注(名字真丢人,嗯?);在写作时,它目前的口号是“用 Grammarly 的人工智能写作助手撰写大胆、清晰、无错误的作品”。类似地,微软也将其语言校对功能重新打包并扩展为 Microsoft Editor,这是一个通过插件整合了文本预测和重写功能的工具,该插件还将其覆盖范围扩展到了标准的微软平台之外。
面对大型语言模型空间的快速发展,更传统的样式检查解决方案是否会继续蓬勃发展还有待观察。不要低估这些公司拥有的重要资产,这一点很重要:除了他们随着时间的推移积累的大量精心策划的规则集,以及迄今为止超出语言模型智慧的有时详细的解释,还有他们围绕自己的产品开发的生态系统,通过电子邮件简讯和博客帖子为写作任务提供广泛的额外支持,解决具体的写作问题或难题。这些辅助资源不仅提供了一些可能提高客户忠诚度的粘性,它们还提供了一种方便的途径来为软件本身不能或不能处理的写作问题提供支持,部分补偿了软件的局限性。但更有可能的是,为了生存,这些玩家将不得不适应,融入更新的技术以跟上竞争;ProWritingAid 的混合方法就是一个很好的例子。
但是最大的转变是从帮助编辑的工具到帮助创作的工具的转变。可以想象,在五年的时间里,没有一个自动化写作辅助工具会被认为是完整的,如果没有一个功能,它可以完成你的句子,在你打着键盘打瞌睡的时候,把你从狭窄的角落写出来,并填充背景段落。鉴于微软与 OpenAI 在 GPT-3 方面的独家许可协议,如果在不久的将来,我们看到这些功能成为微软 Word 功能菜单上的另一个项目,这并不奇怪。
如果你想了解自然语言处理领域的最新动态,请在 NLP 上注册免费订阅本周新闻。
Matplotlib 的最低指南
折线图、散点图、直方图以及一系列定制它们的方法
点击 这里 ,在我的个人博客上阅读这篇文章的更详细版本。
用于数据可视化的典型 Python 库是 Matplotlib。它易于使用、灵活,并且许多其他可视化库都是在 Matplotlib 的基础上构建的。这意味着学习 Matplotlib 将更容易理解和使用一些更花哨的可视化库。
入门指南
您需要安装 Matplotlib 库。假设您有一些终端,并且安装了 pip ,您可以使用以下命令安装 Matplotlib:pip install matplotlib
。你可以在 Matplotlib 的安装指南中阅读更多关于安装的信息。
面向对象的方法
我们将从制作一个简单的散点图开始。首先,我们必须导入 matplotlib。plt
框架是我们将用于 Python 绘图的。
import matplotlib.pyplot as plt
import numpy as np
我们还导入了 numpy,所以我们可以很容易地生成点来绘图!让我们在正弦函数上挑选一些点。我们选择一些 x 值,然后用np.sin
计算 y 值。
x = np.linspace(-3, 3, num=10)
y = np.sin(x)
现在我们已经生成了我们的点,我们可以制作我们的散点图!我们首先制作一个Figure
对象和一个Axes
对象。
fig = plt.figure()
ax = fig.add_subplot()
我们可以把Figure
对象看作是我们想要放入图形的框架,而Axes
对象是我们框架中的一个实际图形。然后,我们将散点图添加到Axes
对象,并使用plt.show()
来可视化图表。
ax.scatter(x, y)
plt.show()
这是它的要旨!
折线图
这里是我们可以使用的颜色的例子。我们可以用许多不同的方法来指定颜色;十六进制代码,RGB,普通旧名。
from scipy.stats import norm
x = np.linspace(-4, 4, num=100)fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.plot(x, norm.pdf(x, loc=-1, scale=1), color="magenta")
ax.plot(x, norm.pdf(x, loc=0, scale=1), color=(0.85, 0.64, 0.12))
ax.plot(x, norm.pdf(x, loc=1, scale=1), color="#228B22")plt.show()
我们还可以使用许多预定义的线型。注意,在没有定义颜色的情况下,Matplotlib 会自动为我们的线条选择一些不同的默认颜色。
x = np.linspace(-6, 6, num=100)fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.plot(x, norm.pdf(x, loc=-3, scale=1), linestyle="solid")
ax.plot(x, norm.pdf(x, loc=-1, scale=1), linestyle="dotted")
ax.plot(x, norm.pdf(x, loc=1, scale=1), linestyle="dashed")
ax.plot(x, norm.pdf(x, loc=3, scale=1), linestyle="dashdot")plt.show()
我们还可以调整线条的宽度!
x = np.linspace(-2, 9, num=100)fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()for i in range(1,7):
ax.plot(
x, norm.pdf(x, loc=i, scale=1), color="black", linewidth=i/2
)plt.show()
散点图
对于散点图,我们可以更改标记及其大小。这里有一个例子
x = np.linspace(-4, 4, num=20)
y1 = x
y2 = -y1
y3 = y1**2fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.scatter(x=x, y=y1, marker="v", s=1)
ax.scatter(x=x, y=y2, marker="X", s=5)
ax.scatter(x=x, y=y3, marker="s", s=10)plt.show()
我们还可以使用[ax.plot](https://matplotlib.org/3.3.4/api/_as_gen/matplotlib.pyplot.plot.html)
函数,通过改变fmt
参数来组合折线图和散点图。fmt
参数由标记、线条和颜色部分组成:fmt = [marker][line][color]
。如果fmt = "s--m"
,那么我们有正方形标记,一条虚线,它们将被染成洋红色。
x = np.linspace(-2, 2, num=20)
y = x ** 3 - xfig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.plot(x, y, 'H-g')plt.show()
直方图
我们可以使用ax.hist
功能轻松制作直方图。
x = np.random.randn(10000)fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.hist(x)plt.show()
我们可以改变直方图中的许多东西,使它变得更好——我们甚至可以添加多个!
x1 = np.random.randn(10000)-1
x2 = np.random.randn(10000)+1fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.hist(
x1,
color='turquoise',
edgecolor='none',
bins=50,
alpha=0.5,
density=True
)
ax.hist(
x2,
color='magenta',
edgecolor='none',
bins=200,
alpha=0.5,
density=True
)plt.show()
传说
很自然,我们会想在图表中添加一个图例。这可以通过[ax.legend](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.legend.html)
功能简单实现。
x = np.linspace(-2, 2, num=100)
y1 = x
y2 = x**2fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.plot(x, y1, color='turquoise', label='First')
ax.plot(x, y2, color='magenta', label='Second')ax.legend()plt.show()
Matplotlib 将自动尝试并找到图表上图例的最佳位置,但是我们可以通过为loc
参数提供一个参数来改变它。另外,一个常见的偏好是图例周围没有框架,我们可以通过将frameon
参数设置为False
来禁用它。此外,Matplotlib 在一列中列出了图例的元素,但是我们可以在ncol
参数中提供要使用的列数。
x = np.linspace(-2, 2, num=100)
y1 = x
y2 = np.sin(x)+np.cos(x)
y3 = x**2fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot()ax.plot(x, y1, color='turquoise', label='First')
ax.plot(x, y2, color='magenta', label='Second')
ax.plot(x, y3, color='forestgreen', label='Third')ax.legend(loc='lower center', frameon=False, ncol=3)plt.show()
最终提示
您可以使用 Matplotlib 做许多奇怪的事情和不同的事情,不幸的是,我无法在这里一一提供。但是,有一些指导方针可以帮助您开始:
- 使用
plt.savefig()
功能保存图表。 - 有许多构建在 Matplotlib 基础上的库可能对您试图创建的特定图表有益,例如 Seaborn 、 Bokeh 、 Plotly 等等。
- 看图库。拜托,拜托,看看图库!如果有人已经做好了,不要浪费 3 个小时在图表上。
优秀分析数据仓库的基础
将您的数据恰当地分层到 L1、L2 和 L3 层
作者图片
分析数据仓库存储的数据基本上与事务数据库生成的数据相同,不多也不少。但是,尽管事务数据库可能被设计得尽可能快,但分析数据库必须被设计得尽可能灵活并且与数据仓库中嵌入的所有业务逻辑保持一致。分析数据仓库的目标是使数据科学家(或可互换的分析师)能够快速混合数据以回答复杂的问题,并发现不明显的有用信息。然而,分析数据仓库还有另一个同样重要的目标:让两个不同的数据科学家面对同一个问题得出不同答案的可能性极小。如果使用事务数据库执行分析,这是很有可能的,因为业务定义和业务逻辑没有嵌入事务数据库中。如果你的公司倾向于召开会议,不同的部门对非常简单的问题给出不同的答案,那么你可能没有一个正确设计的分析数据仓库。设计合理的分析数据仓库将使数据科学家很难得出不同的结论;他们将不得不积极尝试得出不同的结论。想象一下,如果一致的答案成为标准,公司将节省多少时间。不要浪费时间争论哪个数字是“正确的”,你可以花时间决定采取什么行动(或者讨论得出结论的逻辑)。那段时间的美元价值是巨大的(更不用说因为不能在基本问题上达成一致而延迟行动的机会成本了)。
创建良好的分析数据仓库的关键是将业务分解成一系列事实、维度和用户表。
- 事实表:这正是天真、简单的英语解释。事实表是围绕单个实体的事实的集合。例如,如果您的企业使用互联网销售小部件,并且只在电视上做广告,那么您可能会有以下事实表:订单、客户、产品、网站和营销。orders 事实表将包含一个订单 ID、每个订单的值、下单日期、发货日期、订购该订单的客户的客户 ID 等等。customers 事实表有一个客户 ID、客户的姓名、客户的地址、一生的订单数、一生的销售额、他们的业务部门等等。营销事实表将具有电视商业广告 ID、电视商业广告的名称、它播出的电台的电台 ID、它播出的时间、商业广告的长度等等。将有额外的事实表来捕获关于电台的信息,例如观众的平均数量、地理位置等等;这些数据不会存储在营销事实表中,因为这些是关于电视台的事实,而不是企业经营的电视广告。这是创建适当的分析数据仓库的最困难的方面:通过识别数据必须分割成的正确的逻辑实体来正确地分离事实表。
- 维度表:您可以使用简单的英语语句来标识维度;单词“by”后面的任何内容都是维度。例如,“我们一周有多少订单?我们每月有多少订单?一周中的每一天我们有多少订单?”在本例中,维度是“日期”。因此,任何值得使用的分析数据库都会有一个“日期维度”表。该表将有一个主键(如 2015 年 1 月 10 日)和许多许多提供该日期信息的列。例如,它将包含一年中的某一天(10)、一年中的某一周(2)、一周中的某一天(周六)、一年中的某一月(1)、一周的开始日期(2015–01–05)、一周的结束日期(2015–01–11)、是否是周末(是)、是否是联邦政府假日(否)、是否是公司假日(否)等等。另一个例子是“按业务部门划分,我们有多少订单”?因此,业务部门是一个维度,我们将有一个“业务部门维度”表。还有一个例子是“根据邮政编码,我们有多少销售额?按城市?按州?”因此,地理也是一个维度,分析数据仓库将有一个“地理维度”表。
- 用户表:用户表通常通过将来自几个不同实体的事实与维度相结合来回答一个非常具体的问题。例如,“我们一周有多少未取消、未修复的订单?”或者“每月各业务部门的平均订单价值是多少?”大多数企业直接从他们的事务数据库中创建数十个甚至数百个用户表。直接从事务数据库创建用户表的问题是它们不灵活(如果不返回到源系统并从头开始,就不能重新组合数据来生成不同的视图),而且它们本质上是不一致的,因为每次创建用户表时,创建者都会决定应用什么逻辑。但是,从事实表和维度表派生的用户表非常有用,因为它们可以用来快速回答企业一直询问的基本问题。数据分析数据仓库可以自动创建一个用户表,在每天早上 5 点执行相同的步骤,并在早上 7 点到达时将结果通过电子邮件发送给数据科学家,供他或她审阅,而不是付钱给数据科学家,让他或她跨事务表运行一打 SQL 查询,并将数据拉入 Excel 电子表格并应用透视来回答问题。通过使用计算机自动化代替人工处理,这不仅节省了大量的时间,而且减少了错误。
尽管任何分析数据仓库都有一个很容易忽略但绝对重要的关键组件:维护良好、准确且有文档记录的数据模型。构建一个好的分析数据仓库不仅仅是写一堆 SQL 代码,它还包括创建和维护数据模型这一枯燥但重要的工作。您如何知道您是否在创建和维护数据模型文档方面做得很好?有一个任何企业都可以应用的简单测试:将数据模型打印在纸上,将所有的纸贴在墙上,并要求一名新的分析师快速写出 SQL 伪代码,说明他或她将如何提取一个基本问题的答案。如果分析师花了 2 分钟以上的时间快速查看墙上的文章并写出基本的伪代码(或者如果您知道伪代码是错误的,因为文档已经过时),那么您就没有一个好的数据模型或适当的文档。这对您的业务至关重要,因为这是您真正释放并注意到正确构建的分析数据仓库的力量的时候。过去需要几小时到几天才能回答问题,现在只需几分钟就能回答。想象一下,如果您能够比竞争对手反应更快,因为您的分析师不会浪费时间费力地通过您的事务数据库来发现关键发现,那么这将为您的企业带来什么样的竞争优势!
在以后的文章中,我们将进一步深入设计良好的分析数据仓库中应该存在的每一层。在上图中,这些图层应该是 L1(即原始图层)、L2(即上述数据模型)和 L3(即用户图层),在 L3 中,您可以像使用乐高积木一样重复使用 L2 中的组件。有时有一种误解,认为将数据分层会增加工作量;事实上,恰恰相反。将您的数据分层实际上会减少您的团队花费在获取他们需要的数据和维护这些数据上的工作量。我们将很快讨论如何!
哈希表的基础
哈希表基本上是一种提供快速插入、查找和删除的数据结构
我们这些活得够长的人一定知道或者至少看过黄页。没错。你说得对。它是一本厚厚的黄皮书,里面有企业名录和它们的电话号码。这使我们能够寻找销售我们所需商品的商家,并与他们联系。
(我不认为 Z 世代和之后的几代人会知道这本厚厚的黄皮书。😆)
电话号码簿通常是按字母顺序排列的,所以我们知道从哪里开始查找。一旦我们找到我们想要的企业名称,我们就可以拿到电话号码并给他们打电话。你明白了。💡
照片由 Foto Sushi 在 Unsplash 上拍摄
哈希表
如果我告诉你黄页或者电话簿是哈希表的一种实现呢?你打赌!🐴
哈希表本质上是一个与哈希函数耦合的数组。它通常用于以无序的方式存储键值数据,例如,企业及其电话号码、学生及其成绩、项目及其价格等等。
每个键必须是唯一的,并映射到数组中的特定索引,其值存储在该位置。🔑 ➡🚪注意,正因为如此,插入、搜索和删除操作都是⚡️速度。事实上,哈希表的插入、搜索和删除操作的平均时间复杂度是常数时间或O(1)
。
因此,当您需要一个提供快速插入、查找和删除的数据结构时,哈希表是首选之一。当您有大量的关系键值数据时,这非常有用,例如在数据科学和/或机器学习算法中。
散列函数
哈希函数使得哈希表成为一种强大而有用的数据结构。哈希函数接受一段数据,或者通常称为一个键,并返回一个哈希代码作为输出。这个散列码是一个整数,然后被映射到数组中的一个索引,该值将存储在数组中。
哈希代码不直接与数组中的索引相关联的原因之一是因为哈希代码的值可能非常大,例如10000000
,而我们想要存储的键值数据量(或者构成哈希表的数组的大小)可能不一定那么大。
将散列码映射到数组中的索引的一种简单方法是根据散列表的大小应用模运算。
index = hashCode(String key) % length(array)
让我们来看一个散列函数的例子。
*function int hashCode(String s) {
return s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
}*
这是 Java String
对象使用的哈希函数,其中s
是String
对象的字符数组,例如s[0]
是第一个字符,n
是String
对象的长度。让我们看看它的实际效果。
hashCode("Apple") // 63476538
hashCode("Costco") // 2024204569
hashCode("Ikea") // 2280798
hashCode("Snowflake") // 1973786418
从☝️的几个例子中可以注意到,散列函数为每个String
对象输出不同的散列码值。如前所述,每个键都应该是唯一的,因此哈希函数也应该为每个键生成唯一的哈希代码。这将为键值数据在哈希表中均匀分布提供更好的机会。
此外,让我们将散列码映射到数组中的索引。
function int mapToIndex(int hashCode, int arraySize) {
return abs(hashCode % arraySize)
}// Let's assume the size of our hash table or the length of the array is 500hashTable = Array(500)mapToIndex(hashCode("Apple"), size(hashTable)) // 38
mapToIndex(hashCode("Costco"), size(hashTable)) // 69
mapToIndex(hashCode("Ikea"), size(hashTable)) // 298
mapToIndex(hashCode("Snowflake"), size(hashTable)) // 418
按照☝️的说法,苹果的数据将位于索引 38,好事多的数据位于索引 69,宜家的数据位于索引 298,雪花的数据位于索引 418。
碰撞
在上一节中,我们看了一个如何使用 hash 函数来确定特定键值数据在哈希表中的位置的示例。现在,让我们看看另一个散列函数的例子。
*function int hashCode(int i) {
return i % 5
}*
这个哈希函数将一个整数作为输入,并应用模运算来输出哈希代码。现在,让我们用几个整数输入来测试一下。
hashCode(1) // 1
hashCode(5) // 0
hashCode(11) // 1
等一下。散列码不是应该对每个键都不同吗?在这种情况下,我们选择的散列函数对于我们可能拥有的可能的键来说可能并不理想。它为不同的输入值生成相同的哈希码,即输入值 1 和 11 将返回哈希码 1,这意味着它们将被映射到数组中的相同位置。这种现象叫做碰撞。
处理冲突的两种方法
有两种常见的方法用于处理哈希表中的冲突:
- 线性探测
- 单独链接
Clark Van Der Beken 在 Unsplash 上拍摄的照片
线性探测
在线性探测中,我们通过在由哈希函数确定的假定位置之后搜索数组中最近的可用空间来处理哈希表中的冲突。让我们使用上一节中的冲突示例来想象一下,输入值 1 和 11 会产生相同的哈希代码。
假设我们将 1-Apple 的键值数据插入到哈希表中。哈希函数返回哈希代码 1,我们假设它映射到数组中的索引 1。现在,数组中的索引 1 包含 1-Apple 的键值数据。
接下来,我们要将 11-Orange 的键值数据添加到哈希表中。哈希函数返回哈希代码 1,它也映射到索引 1,但是,此时,数组的索引 1 中的块已经被键值数据 1-Apple 占用。
在这种情况下,线性探测将在数组中寻找最近的空闲位置,并在那里存储键值数据 11-Orange。当我们希望从键等于 11 的哈希表中检索数据时,情况也是如此。线性探测将首先找到键值数据 1-Apple,然后继续在相邻位置搜索,直到在数组中找到与键 11 匹配的块或空块,这表明键 11 不存在。
单独链接
当使用分离链接方法时,哈希表将在链表中存储键值数据。这意味着数组中的每个块都包含一个链表,而不仅仅是一个键值数据。
因此,在发生冲突时,不是像线性探测那样寻找数组中的下一个空闲块,而是将键值数据添加到链表中。
同样,以上一节中的冲突示例为例,单独链接将在数组的索引 1 处的链表中存储 1-Apple 和 11-Orange 键-值对,其中链表的头将是 1-Apple,因为它被添加在 11-Orange 之前。
好的散列函数的特征
基于我们到目前为止所了解的,我们可以推断出一个好的散列函数应该具有以下特性。
利用数据中的每一条信息
好的散列函数应该利用数据的每个元素,以便增加可能的散列码的数量。
让我们看看下面的例子。
*function int hashCode(String k) {
String s = k.substring(start=0, end=2)* *return s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
}*
这个散列函数接受一个类型为String
的输入数据,取前 3 个字符并应用 Java String
对象使用的散列函数。您可以想象,对于前三个字符相同的数据,例如 apple 和 application,会发生冲突。
在这种情况下,并不是所有的信息都用来生成散列码。如您所见,冲突的可能性更大,因为可用的哈希代码更少。
非常快的计算
我们使用哈希表的原因之一是它的插入、查找和删除速度。每个操作都依赖哈希函数来获得哈希代码,从而获得数据在数组中的位置。
因此,我们要确保我们的哈希函数超级快,以便提供我们在哈希表中寻找的速度。
这意味着我们应该避免使用复杂或缓慢操作的散列函数,并致力于快速、简单而有效的操作。
在表中均匀分布数据
对于我们的哈希函数来说,最理想的情况是将数据均匀地分布在哈希表中。如果散列函数没有均匀地分布数据,那么发生冲突的可能性就更大。
摘要
我们已经学习了哈希表的基础知识,包括它的结构,哈希函数是什么和做什么,冲突以及处理冲突的方法。下次当你面临一个需要快速查找、插入和删除的问题时,哈希表可能是你解决它的最佳选择。😺
编写程序集的基础
想学汇编?从这里开始!
(src =https://pixabay.com/images/id-424812/
介绍
计算机是技术革新,在仅仅半个世纪的时间里,它彻底改变了整个世界和我们做任何事情的方式。计算技术对现代社会至关重要,由于国际互联网和计算机的普及,我们甚至将我们生活的时间称为“信息时代”。不用说,计算机理论是一门非常重要的学科,尤其是在数据科学的背景下。计算机理论的核心是硬件的一个非常重要的组成部分,中央处理器,或称 CPU。
中央处理器
CPU 通常可以比喻为人脑,因为就计算机而言,它本质上是操作的大脑。我认为更准确的比喻可能是大脑皮层本身,因为处理器不一定做大脑做的所有事情,例如存储记忆,但我跑题了,这个比喻仍然完成了工作。处理器主要是一个 I/O 设备,它可以在寄存器中临时存储要计算的位。CPU 内部唯一的其他组件是控制单元,它控制数据进出寄存器,最后是组合逻辑核心。组合逻辑内核用于非常快速地处理带有数据的命令,甚至可以用于存储在存储器中的位,而不仅仅是寄存器。换句话说,如果堆栈中有 8 位,处理器寄存器中有 8 位都是整数,我们想将这些数字相加,我们可以使用 add 命令。您现在可能会忽略它,它的程序集如下所示:
mov rsi, example_data1
add rsi, example_data2
当然,这是假设这两个部分都是堆栈的别名部分,是预先保留或分配的。当然,对于这种复杂的硬件组件,总是需要某种方式让软件与之接口,这就是汇编或机器码发挥作用的地方。
什么是组装?
汇编是一个由寄存器标号、段和命令组成的系统,处理器可以输入这些信息以便在硬件端执行某些操作。如果说 CPU 是计算机的大脑,那么汇编就是脊髓。汇编允许 CPU 在内存和内核之间传递信息,以使计算机真正做人类想让它做的事情。
为什么要学汇编?
汇编语言无疑已经失去了很多用户,因为 C 语言更容易使用,功能也差不多,但速度稍慢。然而,我认为至少追求最低限度的汇编语言教育的一个重要原因是,与编写 Python 或 C++,甚至 C 语言相比,它确实可以帮助您学习更多的计算机知识。即使您是初学者,我认为对汇编语言的基本理解也确实可以帮助您从非常低的水平掌握输入和输出的概念。
此外,有很多薪水很高的汇编编程工作,所以我认为它甚至对赚钱也很有用。不用说,如果你没有足够好地掌握其他语言来编写你想要的代码,汇编语言总是一个备用点。然而,我认为我想写一篇关于汇编编程的文章的主要原因是为了教育的利益,因为这可能是本文要完成的大部分内容。数据科学家确实有相当大一部分工作分配给了计算机编程,因此理解计算机对于编写更好的代码至关重要。
书写组件
现在我们对汇编有了一个基本的了解,实际上我们可以开始用这种语言编写我们的第一个程序了!当然,为了实际编写一些汇编,你需要编译一个汇编器。你可能有时会听到程序员把汇编语言称为汇编程序,这在技术上是不恰当的,因为汇编程序更类似于编译器,而不是语言本身。比如 Python 语言和 Python 编译器是不一样的。这是一件奇怪的事情,让我很恼火,所以我想我很乐意解释这两个词之间的区别。
设置装配
马上,我们将需要查看我们的系统,以便了解我们将需要什么样的汇编程序。您通常可以针对其他内核和处理器品牌进行汇编,但是如果没有针对您的操作系统和处理器的合适汇编程序,您将无法有效地进行调试。由于我在 Linux 上,目前流行!操作系统,Ubuntu 的衍生物,并有一个英特尔处理器,我将使用全网汇编,或 NASM。对于 Windows Intel 系统,你会希望获得微软宏汇编程序,或 MASM。如果你有其他系统,你可以谷歌一下
(OS) (CPU 制造商)汇编程序
还应该注意的是,一些系统调用可能与汇编程序之间的例子有所不同。在本文的例子中,不同的内核之间有巨大的差异,所以我不能完全解释所有的差异。考虑到这一点,即使您不理解,这篇文章可能仍然是一篇好文章,因为这里的主要目的是学习更多关于计算机的知识。然而,不管你用的是什么系统,我至少可以给你指出正确的搜索方向,以及你到底需要安装什么:
- Windows —点击 Windows 开始按钮,然后点击设置(齿轮图标)。在设置菜单中,点击系统。向下滚动并点击关于。
- OSX —点击苹果菜单>关于这台 Mac。
- 如果你不知道如何做到这一点,我将假设你在 Gnome 上。在大多数 DEs 上,这仍然有效。按下您的“活动”按钮(或 Windows 按钮),然后键入 about 并按 enter 键。
(图片由作者提供)
在 Linux 上,您可以通过您的软件包管理器直接安装 NASM。例如,Apt:
sudo apt-get install nasm
在 Windows 上,我相信你可以通过标准的安装向导安装 MASM。在 MacOS 上,我必须承认我完全不知道如何安装汇编程序。我认为它可能会通过 Brew 来完成。
我们的准则
对于我们今天的项目,我们将编写一个简单的 Hello " Name "程序。这本质上是 Hello World!这个应用程序还将展示如何为输入和诸如此类的事情保留字节。而我通常认为你好世界!对于第一个项目来说有点太简单了,因为在大多数高级语言中,它只是类似于print(“Hello world!”)
的东西,在汇编的例子中,我认为这是一个很好的学习例子!此外,这段代码将在 Github 上提供,因此您可以随意下载、汇编或在这里查看:
https://github.com/emmettgb/Assembly-Intro
部分
对于这个项目,我们需要讨论和理解的第一件事是节的概念。段用于定义处理器需要分配的数据,或在堆栈中保留的数据,或通过文本给处理器重要的指令。有几个部分,但现在我们将重点放在。数据部分。为了定义一个部分,您只需编写 section,后跟我们想要创建的部分。例如,在。数据部分:
section .data
此外,对于节,我们不需要像函数那样使用冒号。无论如何,我们还将在它下面定义另一个称为。bss 部分,然后是。文本部分。之后,我们的部分应该看起来像这样:
section .datasection .bsssection .text
别名堆栈
首先。数据段用于定义静态数据。这意味着我们将把这些数据直接放入堆栈中,并使用一个别名来调用它。这个命令叫做定义字节,简称 db。在 db 之前,我们需要提供这部分堆栈的别名。假设我们的堆栈从 0 开始,每当我们写这个别名时,它将只为我们保存字节的起点,0,在这个例子中我们将写 hello _____。这将是 6 个字节,一个用于 hello 中的每个字符,一个用于结尾的空格。我还将为结尾预留更多的内存,包括解释点和返回。虽然我们已经习惯了内核中带有内核的可爱而奇特的正则表达式,但是处理器没有这些,所以我们将使用数字 10 来代替\n。这只是一个数字,本质上类似于添加新行的正则表达式。
section .data
hello: db "Hello "
ending: db "!", 10
section .bsssection .text
所以现在,如果我们的堆栈从 0 开始,我们将有别名 hello,从 0 开始,到 6 结束,然后堆叠在它上面的是别名 end,从 7 开始,到 9 结束(为 10 保留了一个字节)。现在让我们转到。bss 部分,通常用于为应用程序内部将要使用的内容保留数据。当然,我们将为我们的用户输入保留字节,所以我们将我们的新别名命名为。长度超过 16 个字节的名字不多,我就分配这么多。我们用 resb 命令保留字节。我们按照我们希望保留的字节数:
section .bss
input: resb 16
。文本
我们今天要学习的最后一个部分是。文本部分。该部分用于为处理器提供重要信息,并保存我们汇编软件的所有代码。在这种情况下,_start 将成为处理器应该访问的汇编文件内部的入口点。当然,它也有其他的用途,但是在这个例子中,我们只需要使用那一部分。我们所需要做的就是用全局命令调用我们的启动函数(我们还没有写):
section .text
global _start
现在,我们代码的 section 部分将如下所示:
; Sections:
section .data
hello: db "Hello "
ending: db "!", 10section .bss
input: resb 16section .text
global _start; Functions:
您还可以用注释代码;比如 Lisp。
功能
为了编写一个函数,我们只需键入一个函数别名,后跟一个冒号,例如我们的 start 函数:
_start:
函数在汇编中工作就像在其他语言中一样。考虑到这一点,我们现在需要将命令添加到我们的函数中,在这之前会有很多解释,所以准备好吧。
寄存器和系统调用
在许多方面,汇编编程将是处理器和内核之间的通信。我们通过将数据移入寄存器来完成大部分通信,然后使用 syscalls 来完成内核端的操作,以向使用计算机的实际人员提供某种回报。系统调用总是以这样的方式工作,首先将数据移动到寄存器中的特定位置,然后进行系统调用,内核执行放入寄存器中的操作。我们可以把寄存器看作是处理器内部的临时数据存储器,它是不可思议地可变的。这些寄存器以位置命名,例如 1、2、3 …
这是每种架构的所有寄存器的映射。请注意,我找不到一个具有知识共享署名的表格,所以我承担了为您创建自己的表格的艰巨任务。
(图片由作者提供)(对了,知识共享(CREATIVE COMMONS),你可以保存这个图片并分享。)
大多数情况下,我们将使用这些寄存器处理系统调用或处理器命令。我们都可以从寄存器和内存中做到这一点。例如,我们想添加 rax 和 rbx 寄存器:
add rax, rbx
现在让我们来谈谈系统调用。为了对我们的内核进行系统调用,我们需要将数据放入我们的寄存器。通常,在位置 1,rax 寄存器中,我们会放入一个命令,让内核与其他寄存器一起执行。当然,您可以为您的特定内核查找一个完整的系统调用列表。
进行系统调用
当然,对我来说,与您一起检查每个系统调用的工作量将是非常大的,例如,在 Linux 中有 313 个系统调用,所以我将只展示我今天要处理的系统调用。您可以在这里查看 Linux 系统调用的完整列表:
无论如何,我们今天要看的 3 个调用是 sys_exit、sys_read 和 sys_write。我们的程序被设计为先读后写,因为它从来没有真正要求我们的名字(你能看出来,因为我们没有为此保留字节),所以我们需要调用的第一件事是 sys_write。我将在这里为 sys_read 和 sys_write 提供一个小表:
(图片由作者提供)
这些表格将为您提供几乎所有您需要了解的关于这些系统调用的信息。我们可以把系统调用看作是以寄存器作为参数的方法。我们将使用 MOV 命令将数据移入寄存器。
写 Hello World!
为了开始系统调用的演示,我觉得使用最简单的系统调用可能会有用,这当然是退出系统调用。这也有助于我们更好地理解如何只使用 to 寄存器进行系统调用。
出口
(图片由作者提供)
正如我们在表中看到的,这个系统调用的数字标签将是 60。当我们进行系统调用时,它几乎总是进入位置 1 寄存器,即 rax 寄存器。现在让我们从在 rax 上使用带无符号整数的 MOV 命令开始。这个无符号整数是一个退出代码,在这种情况下,我们希望它是 0。在编程的世界里,代码 0 意味着我们的程序没有出错。我们首先将 0 移入 rax 寄存器的 3 中:
_start:
mov rax, 60
请注意的语法
命令,(寄存器或数据)
按照相同的语法,让我们将 0 移入第二个寄存器位置 rdi:
_start:
mov rax, 60
mov rdi, 0
最后,我们将简单地在末尾添加 syscall 来进行 syscall:我们现在的最终结果应该是这样的:
; Sections:
section .data
hello: db "Hello "
ending: db "!", 10section .bss
input: resb 16section .text
global _start; Functions:_start:
mov rax, 60
mov rdi, 0
syscall
正在组装!
现在我们将组装我们的新应用程序。当然,这个应用程序除了打开和关闭之外什么也不做,但是它仍然很容易判断出是否有问题,因为我们将收到一个分段错误消息。现在,我们需要将终端带到包含汇编文件的目录,并使用 nasm 来汇编它:
nasm -f elf64 hello_world.asm
这将为我们提供一个. o 文件,我们现在可以将它转换成一个可执行的二进制文件:
ld hello_world.o -o hello
然后我们可以用。/:
./hello
(图片由作者提供)
恭喜你!
你的第一个汇编程序已经正式完成了!如果您最终得到“分段错误(核心转储)”,这基本上意味着您的代码中的某个地方有问题。找到这些 bug 可能相当棘手,因为核心转储实际上并没有描述太多关于异常发生位置的信息。
读取字节
接下来我们需要做的是处理一些简单的标准输入,并输出该输入以及我们的 hello 消息。让我们回头参考 syscall 表,该表包含我们为此所需的信息:
我们看到 sys_read 是第一个调用,它为零。记住这一点,我们将把它移到 rax 寄存器中:
mov rax, 0
接下来,我们将把 0 移入 rdi 寄存器。该寄存器用于描述我们正在使用的缓冲器。当然,对于这个例子,我们需要 STDIN,标准输入是 0,标准输出是 1。
mov rdi, 0
接下来,我们将把我们的保留字节放入 rsi 寄存器的位置 3:
mov rsi, input
当我们这样做时,我们基本上是在说
“在这里存储输入!”
这意味着我们指向堆栈中的一个位置,该位置被保留并作为输入别名。最后,我们需要这个缓冲区允许占用的字节数,16:
mov rax, 0
mov rdi, 0
mov rsi, input
mov rdx, 16
syscall
输出
最后,现在让我们把这个打印出来,和我们的其他信息一起显示在屏幕上。我们将从信息的“你好”部分开始。首先,查看上面的图表,我们将 1 移入 rax 寄存器。
mov rax, 1
因为这是标准输出,所以我们也需要将 1 移动到 rdi 中的描述符。
mov rdi, 1
现在我们需要将内存的别名移入 rsi,hello,然后将它需要输出的字节数移入 rdx。为了演示 stack 的工作原理,我将把这个数字改为 7,然后组装它,这样我们就可以看到发生了什么:
mov rdx, 7
现在我们的最终结果看起来有点像这样。
mov rax, 1
mov rdi, 1
mov rsi, hello
mov rdx, 7
syscall
让我们看看在 rdx 中多编译一个字节会发生什么:
(图片由作者提供)
忽略 f,那是我的输入,但是注意我们的。数据段没有用解释点定义我们的 hello 消息?
section .data
hello: db "Hello "
ending: db "!", 10
栈之所以叫栈是有原因的。它是内存中一系列相互堆叠的字节。每当我们分配这些内存部分时,我们都将“ending”放在“hello”的上面。hello 的长度是未知的,这就是为什么每当我们进行这个 syscall 时,我们必须将它移动到 rdx 寄存器中。无论如何,我现在将把它改回 6,我们将继续重复这段代码,但不是打印 hello,而是打印输入。之后,我们将打印结局。我还强烈推荐使用适当的格式和分离您的系统调用,因为这肯定会使所有的操作混为一谈。这是我们的最终产品:
; Sections:
section .data
hello: db "Hello "
ending: db "!", 10section .bss
input resb 16section .text
global _start
_start:
mov rax, 0
mov rdi, 0
mov rsi, input
mov rdx, 16
syscallmov rax, 1
mov rdi, 1
mov rsi, hello
mov rdx, 6
syscallmov rax, 1
mov rdi, 1
mov rsi, input
mov rdx, 16
syscallmov rax, 1
mov rdi, 1
mov rsi, ending
mov rdx, 2
syscall
mov rax, 60
mov rdi, 0
syscall
; Functions:
现在让我们组装并运行它!
结论
(图片由作者提供)
我们的感叹号出现在底部可能有点奇怪。发生这种情况的原因是因为我们为 STDOUT 保留了 16 个字节。换句话说,我们还不知道这个名字的长度。这当然可以很容易地通过比较、跳转和标记来解决,但这是我希望这篇文章所能达到的深度。
我觉得这种语言的技巧绝对可以应用到更高级的编程中!事实上,这几乎是我最初学习汇编语言的全部原因。除此之外,写起来很有趣,因为通常比写标准的高级语言更有挑战性。这真的让你不得不更多地考虑硬件。如果有一件事是我个人最喜欢的,那就是从软件的角度与硬件交互。这基本上是我们在不接触裸机的情况下最接近硬件的了(另一篇文章的想法?),我觉得很有意思。非常感谢大家的阅读,我将考虑做另一部分,我们将在这篇文章中投入更多的组装乐趣。到那时,快乐的发现,快乐的编程!
更多推荐
所有评论(0)