LDA 主题建模:一种解释
Photo by Patrick Tomasso on Unsplash
背景
主题建模是在一组文档中识别主题的过程。这对于搜索引擎、客户服务自动化以及任何其他了解文档主题很重要的情况都很有用。有多种方法可以做到这一点,但在这里我将解释一种:潜在狄利克雷分配(LDA)。
该算法
LDA 是一种无监督学习的形式,它将文档视为单词包(即顺序无关紧要)。LDA 的工作方式是首先做出一个关键的假设:生成文档的方式是挑选一组主题,然后为每个主题挑选一组单词。现在你可能会问“好吧,那么它是如何找到主题的?”答案很简单:它逆向工程这个过程。为此,它为每个文档 m 执行以下操作:
- 假设所有文档中都有 k 个主题
- 通过给每个单词分配一个主题,在文档 m 中分布这些 k 主题(这种分布称为α,可以是对称的,也可以是非对称的,稍后会详细介绍)。
- 对于文档 m 中的每个单词 w ,假设其主题是错误的,但是每隔一个单词被分配正确的主题。
- 基于以下两点从概率上给单词 w 分配一个主题:
-文档 m
- 有多少次单词 w 在所有文档中被分配了一个特定的主题(这种分布被称为 β ,稍后将详细介绍) - 对每个文档重复这个过程几次,你就完成了!
模型
Smoothed LDA from https://en.wikipedia.org/wiki/Latent_Dirichlet_allocation
上面是所谓的 LDA 模型的板块图,其中:
α是每个文档的主题分布,
β是每个主题的单词分布,
θ是文档 m、 的主题分布,φ是主题 k、 z 是文档 m 中第 n 个单词的主题,以及的主题
调整模型
在上面的板模型图中,可以看到 w 是灰色的。这是因为它是系统中唯一可观察的变量,而其他变量是潜在的。正因为如此,要调整模型,有一些事情你可以弄乱,下面我重点介绍两个。
α是一个矩阵,其中每行是一个文档,每列代表一个主题。行 i 和列 j 中的值表示文档 i 包含主题 j 的可能性。对称分布意味着每个主题均匀地分布在整个文档中,而非对称分布则倾向于某些主题。这将影响模型的起点,当您大致了解主题如何分布以改善结果时,可以使用它。
β是一个矩阵,其中每行代表一个主题,每列代表一个单词。行 i 和列 j 中的值表示主题 i 包含单词 j 的可能性。通常每个单词在整个主题中均匀分布,这样就不会有主题偏向某些单词。这可以被利用,虽然为了偏向某些主题,以有利于某些词。例如,如果你知道你有一个关于苹果产品的主题,那么在其中一个主题中偏向“iphone”和“ipad”这样的词会有所帮助,以便推动模型找到那个特定的主题。
结论
本文并不打算成为一个成熟的 LDA 教程,而是给出 LDA 模型如何工作以及如何使用它们的概述。有许多实现方式,例如 Gensim 易于使用且非常有效。关于使用 Gensim 库进行 LDA 建模的很好的教程可以在这里找到。
有什么想法或发现我错过了什么?让我知道!
快乐话题造型!
LDA2vec:主题模型中的词嵌入
了解有关 LDA2vec 的更多信息,LDA 2 vec 是一种结合狄利克雷分布的潜在文档级主题向量混合来学习密集单词向量的模型。
Originally published at https://www.datacamp.com/community/tutorials/lda2vec-topic-model.
这篇博文会给大家介绍一下 lda2vec,这是 Chris Moody 在 2016 年发表的一个话题模型。lda2vec 使用主题和文档向量扩展了 Mikolov 等人在 2013 年描述的 word2vec 模型,并结合了单词嵌入和主题模型的思想。
主题模型的一般目标是产生可解释的文档表示,这些表示可用于发现未标记文档集合中的主题或结构。这种可解释文档表示的一个例子是:文档 X 20%是主题 a,40%是主题 b,40%是主题 c。
今天的帖子将从介绍潜在的狄利克雷分配(LDA)开始。LDA 是一个概率主题模型,它将文档视为一个单词包,所以您将首先探索这种方法的优点和缺点。
另一方面,lda2vec 在单词嵌入的基础上构建文档表示。您将了解更多关于单词嵌入的知识,以及为什么单词嵌入目前是自然语言处理(NLP)模型中的首选构件。
最后,您将了解更多关于 lda2vec 背后的一般思想。
潜在狄利克雷分配:简介
主题模型接受一个未标记文档的集合,并试图在这个集合中找到结构或主题。注意,主题模型通常假设单词的使用与主题的出现相关。例如,您可以提供一个包含一组新闻文章的主题模型,该主题模型会根据单词的用法将文档分成若干个组。
主题模型是自动探索和构建大量文档的好方法:它们根据文档中出现的单词对文档进行分组或聚类。由于关于相似主题的文档倾向于使用相似的子词汇表,因此得到的文档簇可以被解释为讨论不同的“主题”。
潜在狄利克雷分配(LDA)是概率主题模型的一个例子。这到底意味着什么,您将在下面的章节中学习:您将首先理解 LDA 如何从一个单词包描述开始来表示不同的文档。然后,您将看到如何使用这些表示在文档集合中查找结构。
词汇袋
传统上,文本文档在 NLP 中被表示为一个单词包。
这意味着每个文档都被表示为一个长度等于词汇表大小的固定长度向量。这个向量的每个维度对应于一个单词在文档中的出现次数。能够将可变长度的文档简化为固定长度的向量使它们更适合用于各种机器学习(ML)模型和任务(聚类、分类等)。
The image above illustrates how a document is represented in a bag-of-word model: the word “document” has a count of 1, while the word “model” occurs twice in the text.
虽然单词袋导致稀疏和高维的文档表示,但是如果有大量数据可用,则在主题分类上通常会获得良好的结果。你可以随时阅读最近脸书关于主题分类的论文。
固定长度的文档表示意味着您可以轻松地将不同长度的文档输入到 ML 模型中(SVM 模型、k-NN 模型、随机森林模型等等)。这允许您对文档执行聚类或主题分类。文档的结构信息被移除,并且模型必须发现哪些向量维度在语义上是相似的。例如,在不同维度上映射“猫”和“猫”不太直观,因为模型被迫学习这些不同维度之间的相关性。
LDA 模型
当训练 LDA 模型时,您从一组文档开始,并且这些文档中的每一个都由一个固定长度的向量(单词包)来表示。LDA 是一种通用的机器学习(ML)技术,这意味着它也可以用于其他无监督的 ML 问题,其中输入是固定长度向量的集合,目标是探索这些数据的结构。
要实现 LDA 模型,首先要定义文档集合中的“主题”数量。这听起来很简单,但是如果您正在处理大量的文档,就没有听起来那么直观了。
在具有 M 个主题的 N 个文档上训练 LDA 模型对应于找到最佳解释数据的文档和主题向量。
请注意,本教程将不会详细涵盖 LDA 背后的全部理论(参见 Blei 等人的这篇论文),因为重点是更好地理解一般概念。
假设文档中的词汇由 V 个单词组成。
在 LDA 模型中, N 个文档中的每一个都将由长度为 M 的向量来表示,该向量详细描述了哪些主题出现在该文档中。一个文档可以由 75%的“主题 1”和 25%的“主题 2”组成。通常,LDA 会产生带有大量零的文档向量,这意味着每个文档中出现的主题数量有限。这与文档通常只讨论有限数量的主题的想法是一致的。这极大地提高了这些文档向量的可解释性。
每一个 M 主题都由一个长度为 V 的向量表示,该向量详细描述了给定一个关于该主题的文档中可能出现的单词。因此,对于主题 1,“学习”、“建模”和“统计”可能是一些最常见的词。这意味着你可以说这是“数据科学”的话题。对于主题 2,“GPU”、“计算”和“存储”可能是最常见的词。你可以把这解释为“计算”主题。
下图直观地展示了 LDA 模型。该模型的目标是找到解释不同文档的原始单词包表示的主题和文档向量。
需要注意的是,您依赖于主题向量是可解释的这一假设,否则模型的输出几乎是垃圾。本质上,你是在假设,给定足够的数据,该模型将找出哪些单词倾向于同现,并将它们聚类到不同的“主题”中。
LDA 是一个简单的概率模型,效果很好。文档向量通常是稀疏的、低维的和高度可解释的,突出了文档中的模式和结构。您必须确定文档集合中出现的主题数量的准确估计值。此外,您必须手动为不同的主题向量分配不同的命名者“主题”。由于单词袋模型被用于表示文档,LDA 可能遭受与单词袋模型相同的缺点。LDA 模型学习文档向量,该向量预测文档内部的单词,而不考虑任何结构或这些单词在局部级别上如何交互。
单词嵌入
单词袋表示的一个问题是,该模型负责计算文档向量中的哪些维度是语义相关的。有人可能会认为,利用单词在语义上如何相互关联的信息将会提高模型的性能,而这正是单词嵌入所承诺的。
对于单词嵌入,单词被表示为固定长度的向量或嵌入。存在几种不同的模型来构建嵌入,但是它们都基于分布假设。这意味着“一个词是由它的朋友来描述的”。
单词嵌入的目标是从大型无监督文档集中捕获语言中的语义和句法规则,如维基百科。出现在相同上下文中的单词由彼此非常接近的向量来表示。
Image taken from “Visualizing Word Embeddings with t-SNE”
上图是使用 t 分布随机邻域嵌入(t-SNE)将单词嵌入空间投影到 2D 空间。t-SNE 是一种降维方法,可以用来可视化高维数据。该方法将单词嵌入作为输入,并将它们投影到二维空间,该空间可以容易地在绘图中可视化。只调查单词空间的一个子部分,集中在接近“教师”的单词上。不是通过向量中的无信息维度来表示单词,而是可以使用单词嵌入通过语义相关的向量来表示单词。
当使用单词嵌入时,ML 模型可以通过将信息嵌入到向量表示中来利用来自大量文档(也称为“语料库”)的信息。这对于单词袋模型是不可能的,当没有大量数据可用时,单词袋模型会损害模型性能。单词嵌入导致文档表示不再是固定长度的。相反,文档由可变长度的单词向量嵌入序列表示。虽然一些深度学习技术,如长短期记忆(LSTM)、具有自适应池的卷积网络等,能够处理可变长度的序列,但通常需要大量数据来正确训练它们。
word2vec
正如您在简介中看到的,word2vec 是由 Mikolov 等人开发的非常流行的单词嵌入模型。注意,在分布式语义领域中还存在其他几种单词嵌入模型。虽然获得高质量的单词嵌入需要一些技巧,但是本教程将只关注 word2vec 背后的核心思想。
在 word2vec 中使用下面的训练过程来获得单词嵌入。
1.在文本中选择一个(枢纽)单词。当前中枢单词的上下文单词是出现在中枢单词周围的单词。这意味着你在一个固定长度的单词窗口内工作。中枢词和上下文词的组合构成了一组词-上下文对。下面的图片取自 lda2vec 上的克里斯·穆迪的博客。在这个文本片段中,“awesome”是中枢单词,它周围的单词被作为上下文单词,产生 7 个单词-上下文对。
Image taken from “Introducing our Hybrid lda2vec Algorithm”
2.存在 word2vec 模型的两个变体:a .在单词袋体系结构(CBOW)中,基于一组周围的上下文单词来预测中枢单词(即,给定‘谢谢’,‘这样’,‘你’,‘顶’该模型必须预测‘棒极了’)。这被称为单词包架构,因为上下文单词的顺序并不重要。b .在 skip-gram 架构中,中枢词用于预测周围的上下文词(即给定‘牛逼’预测‘谢谢’、‘这样’、‘你’、‘顶’)。下图描述了两种不同的 word2vec 架构。注意,使用了相对简单的(两层)神经模型(与计算机视觉中的深度神经模型相比)。
Image taken from “Efficient Estimation of Word Representations in Vector Space” (Mikolov et al., 2013)
通过在大型语料库上训练该模型,您将获得对语义信息进行编码的单词嵌入(投影层中的权重)以及一些有趣的属性:可以执行向量运算,例如国王-男人 + 女人 = 女王。
例如,与简单的独热编码表示相比,字向量是有用的表示。它们允许将大型语料库中的统计信息编码到其他模型中,如主题分类或对话系统。单词向量通常是密集的、高维的和不可解释的。考虑下面这个例子:[ -0.65,-1.223,…, -0.252,+3.2]。虽然在 LDA 中,维度大致对应于主题,但是对于词向量来说,情况通常不是这样。每个单词被分配一个上下文无关的单词向量。然而,单词的语义高度依赖于上下文。word2vec 模型学习单词向量,该向量预测不同文档中的上下文单词。结果,特定于文档的信息在单词嵌入中混合在一起。
lda2vec
受潜在狄利克雷分配(LDA)的启发,word2vec 模型被扩展为同时学习单词、文档和主题向量。
Lda2vec 是通过修改 skip-gram word2vec 变体获得的。在原始的 skip-gram 方法中,模型被训练为基于中枢单词来预测上下文单词。在 lda2vec 中,将主词向量和文档向量相加以获得上下文向量。该上下文向量然后被用于预测上下文单词。
在下一节中,您将看到这些文档向量是如何构造的,以及如何像 LDA 中的文档向量一样使用它们。
lda2vec 架构
在 word2vec 模型中集成上下文向量的想法并不新鲜。例如,段落向量也探索了这个想法,以便学习可变长度文本片段的固定长度表示。在他们的工作中,对于每个文本片段(一个段落的大小),学习一个密集的向量表示,类似于学习的单词向量。
这种方法的缺点是上下文/段落向量类似于典型的单词向量,使得它们不太容易被解释为例如 LDA 的输出。
lda2vec 模型通过处理文档大小的文本片段并将文档向量分解成两个不同的部分,比段落向量方法更进了一步。本着与 LDA 模型相同的精神,文档向量被分解成文档权重向量和主题矩阵。文档权重向量表示不同主题的百分比,而主题矩阵由不同的主题向量组成。因此,通过组合文档中出现的不同主题向量来构建上下文向量。
考虑下面的例子:在最初的 word2vec 模型中,如果中枢单词是“法语”,那么可能的上下文单词可能是“德语”、“荷兰语”、“英语”。在没有任何全局(文档相关)信息的情况下,这些将是最合理的猜测。
通过在 lda2vec 模型中提供额外的上下文向量,可以更好地猜测上下文单词。
如果文档向量是“食物”和“饮料”主题的组合,那么“长棍面包”、“奶酪”和“葡萄酒”可能更合适。如果文档向量类似于“城市”和“地理”主题,那么“巴黎”、“里昂”和“格勒诺布尔”可能更合适。
请注意,这些主题向量是在单词空间中学习的,这便于解释:您只需查看与主题向量最接近的单词向量。此外,对文档权重向量进行约束,以获得稀疏向量(类似于 LDA ),而不是密集向量。这使得不同文档的主题内容的解释变得容易。
简而言之,lda2vec 的最终结果是一组稀疏的文档权重向量,以及易于解释的主题向量。
虽然性能与传统的 LDA 相似,但是使用自动微分方法使得该方法可扩展到非常大的数据集。此外,通过结合上下文向量和词向量,您可以获得“专门的”词向量,这些词向量可以在其他模型中使用(并且可能优于更多的“通用的”词向量)。
lda2vec 库
Lda2vec 是一种相当新的专门的 NLP 技术。由于它建立在现有方法的基础上,任何 word2vec 实现都可以扩展到 lda2vec。Chris Moody 在 Chainer 中实现了该方法,但也可以使用其他自动微分框架(CNTK,Theano,…)。Tensorflow 实现也公开发布。
lda2vec Python 模块的概述可以在这里找到。由于训练 lda2vec 可能是计算密集型的,建议对较大的语料库使用 GPU 支持。此外,为了加速训练,不同的单词向量通常用预先训练的 word2vec 向量来初始化。
最后,讨论了作为主题模型的 lda2vec,但是也可以更一般地定义向 word2vec 模型添加上下文向量的想法。例如,考虑由来自不同地区的不同作者编写的文档。然后,作者和区域向量也可以被添加到上下文向量,从而产生一种无监督的方法来获得文档、区域和作者向量表示。
结论
这篇博文只提供了 LDA、word2vec 和 lda2vec 的快速概述。注意,原作者还发表了一篇关于 lda2vec 技术细节的很棒的博文。
传单 Javascript 库点坐标样式和设计
传单上高度可定制的地图标记
在我们软件开发旅程中的某个时刻,我们大多数参与地理空间技术的人都遇到过被称为“传单”的开源库。
这只是一个简单的文章来解释它的默认标记转换成你喜欢的图像,图标等。希望在这篇文章结束时,改变这一点:
Default leaflet marker
将触手可及。不要再谷歌**“如何定制传单标记”**只是为了得到一堆复杂但不可用的回复。
对于初学者来说,你需要包括在基本传单库,可以在这里找到:【http://leafletjs.com/download.html】T2,并初始化一个基本的地图和标记。在本例中,我将使用可从 http://cdn.leafletjs.com/leaflet/v1.2.0/leaflet.zip下载的传单 v1.2.0,并在一个简单的 html 页面上包含 fleet . CSS、fleet . js 和 images 文件夹:
在这种情况下,上面的代码转换成下面的新加坡地图视图:
Image by Author | A rendered map view of Singapore by leaflet.js
默认图标:
这行代码:
L.marker([1.2800945, 103.85094909999998]).addTo(map);
在地图上创建默认标记,如下所示:
I 型标记器。圆形标记器
要将其转换为活页中的简单圆,代码的变化如下:
Image by Author | Note: The method pointToLayer is used and for simplicity, the marker is given a white fill with a black border.
此外,你可以改变上述标记的半径、颜色或不透明度,但这不是本演示的重点(没有双关语)。
标记类型 II。图像图标
虽然圆形点作为标记在某些地图可视化中可能是理想的,但有时更具体的图标/图像可能更合适。在这种情况下,应该使用下面的方法,而不是上面提到的方法:
Image by Author | (left) The image icon is rendered in the map above | (right) Preview of image file “ERP.png” stated in the code snippet
标记类型 III。编码图像(base64)图标
更进一步,我尝试使用 base64 作为 iconUrl 参数的替代,猜猜会发生什么?它也工作得非常好!
Image by Author | (left) The image icon is rendered in the map above as a base64 encoded string | (right) Preview of encoded image file stated in the code snippet
就个人而言,当使用的标记是一次性的,并且开发人员发现没有必要在项目中包含实际的图像文件时,我建议使用 base64 字符串来替代图像路径。我推荐的一个 base64 图像编码器工具是:https://www.base64-image.de/
标记类型 IV。类名和字体图标
最后,如果你是使用字体图标的大多数开发者之一(例如,字体-awesome),那么在这种情况下使用 L.divIcon 将是最合适的:
Image by Author | Note that for the font icon above to render correctly, the class name “fa fa-star” in font-awesome.css has to be included
当你发现默认的传单图标不适合你的视觉化时,这些是你可以使用的一些图标定制风格。希望这篇文章对你正在进行的网络地理空间可视化之旅有所帮助!
感谢阅读。
获得李思欣·崔和其他作家在媒体上的所有帖子!😃您的会员费直接…
geek-cc.medium.com](https://geek-cc.medium.com/membership)
精益创业和机器学习
大约十年前,创造了这个术语。从那时起,它已经成为最有影响力的创业方法之一,尤其是那些基于网络的软件公司。精益在互联网革命中成熟。我们现在正处于一场不同革命的尖端——一场由机器学习算法引入的革命。可以有把握地认为,在不久的将来,大多数或所有的软件都将包含一些机器学习的元素。但是精益与机器学习在原理和实践上有多兼容呢?(有趣的视角见这里)
经验证的学习和机器学习
根据精益创业哲学,大多数创业公司的失败不是因为产品的质量,而是因为对客户的错误假设,即创业公司最终为一个不存在的客户制造了完美的产品。为了避免这种命运,创业公司应该首先通过运行实验来了解他们的客户,从而验证和完善他们的假设。这个过程被称为经验证的学习。在精益中,经验证的学习是通过执行重复的假设检验来完成的。
假设总是关于给定产品功能定义的顾客反应的断言。为了进行一个实验,首先需要从功能上定义产品(基本上是一个特性列表)——这定义了一个可行的产品。此外,还需要定义客户对产品反应的量化措施。功能性产品的定义仍然留下了许多(主要是技术性的)选择。这些选择应该以最小化创业中最有价值的商品(通常是时间)的支出的方式做出;这样的选择定义了最小可行产品(MVP)。然后,人们将着手建立 MVP,把它带给客户,记录他们的反应,并确定假设的有效性。实验通常设置为分割测试,将当前基线产品作为对照,将提议的变更作为处理。
请注意,精益中没有产品方面的不确定性——给定一个功能定义,人们就可以确定是否能制造出符合该定义的产品。对于传统软件来说确实如此。传统软件产品的行为完全由您提供的指令(即代码)决定。因此,给定其行为的完整规范,您可以“逆向”工作,并找出您需要提供什么指令来构建一个具有所需行为的软件。从某种意义上说,MVP 基本上是你为此目的所需的最小指令集。一个软件的行为可以基于上下文表现出不同的变化。然而,这种可能的变化的数量通常是相当小的,并且提供作为所有这种变化的总和的功能定义是有意义的。
然而,机器学习有着本质的不同。在机器学习中,产品是通过将一组指令(算法)与一些数据(训练数据)相结合而生成的模型。因此,模型的行为不仅由算法决定,还由训练数据决定。在机器学习中,不可能事先(在访问训练数据之前)对产品的行为做出精确的断言。此外,机器学习算法从海量数据中学习复杂的规则。基于上下文的行为的可能变化的集合是非常大的。因此,即使人们可以访问训练数据,通过手动列出其行为的所有可能变化来从功能上定义机器学习产品仍然是不可行或不明智的——如果你已经确切知道算法将从数据中学习什么,那么你就不需要首先训练算法。
当涉及到机器学习产品时,这为验证学习提出了一个基本问题。简而言之,如果不能保证假设的前提条件得到满足,就不能用实验来检验假设的有效性——人们无法判断测试失败是因为假设无效还是因为前提条件没有得到满足。在统计假设检验中,这被称为混杂。
就其原始形式而言,精益实验不适合在机器学习领域或任何其他具有显著产品端不确定性的领域中进行验证学习。正如我们上面所讨论的,这个难题的根本原因是由于精益提出通过重复的分割测试来验证假设,作为验证学习的手段。
分裂测试是有效学习的最有价值的部分
有效学习的目标实际上是从经验证据中了解客户。为此,分割测试是一个非常严格的工具。那么当初为什么会选择它呢?我相信,这是精益创业方法找到自己的 MVP 的一个例子。
当精益创业作为一种具体的方法被创建时,它的主要“客户”是基于网络的软件公司。在这个领域,分割测试是产品优化的普遍工具(在你非常了解你的客户的情况下,对产品做一些小的改变)。这个熟悉的工具只是为了有效的学习而改变用途。
当时,最明显的具有重大产品方面不确定性的领域大多是登月计划,如寻找癌症的治疗方法。就像现在一样,当时还不清楚精益原则对这种登月计划是否有用。因此,用不太知名的工具使形式主义复杂化是没有意义的。换句话说,分裂测试是验证学习的 MVP。
是时候转向了
机器学习改变一切——软件的首要目标是从执行指令转变为从数据中学习。对于精益创业方法来说,这代表了其最重要的客户群的巨大转变。分裂测试与机器学习产品的不兼容性意味着,很快它将不再是用于验证学习目的的“可行产品”。现在是寻找其他选择的时候了;是时候转向了。
关于灵感,让我们转向有效学习的原始灵感来源——科学研究。精益从科学方法中借鉴了很多通过实验学习的方法。然而,在科学上,实验是昂贵的。使用实验作为统计假设检验的手段只在少数情况下使用,例如当需要监管机构的批准时(如药物的临床试验)或当没有真正的潜在理论基础时。
在更成熟的科学领域,人们通常不会从一个实验到另一个实验去验证假设,而是试图建立理论。我将在下面的段落中对这个过程进行简单的描述。接下来的事情有点像漫画;不完全准确,但符合我们的目的。
理论基本上是对大量实验事实的解释。但是通常最简单的解释不是从直接可观察到的物体方面获得的,而是从某些概念物体方面获得的。例如,原子理论可以解释各种实验事实。然而,你在实验中实际观察到的不是原子本身,而是探测器上的一些闪烁。现在我们需要一种方法,一张地图,把原子和眨眼联系起来。地图是理论世界(由原子组成)和可观察世界(由眨眼组成)之间的桥梁。
该图将用于将理论结果转换成关于特定实验装置的可观察量的预测。如果理论的预测与所有现有的观察一致,并且有一些新的预测,那么根据新预测的重要性,可能会委托进行新的实验。如果发现理论的任何预测与现有的观察结果不一致,那么就需要对理论进行修改。
如上图所示的地图不是简单的表格,而是复杂的实体。它们是实验装置的精确模型;例如上述例子中的检测器。事实上,探测器物理本身就是一个子领域。
一个重要的问题是,我们为什么需要这样的地图?我们为什么不简单地从理论到实验呢?原因有很多,但最重要的一个,也是与我们的讨论最相关的一个原因是,这些地图提供了关注点的分离。理论的目标是提供尽可能简单的解释;人们应该能够用在上海、旧金山或圣保罗进行的实验来检验理论的有效性,而不必担心每个实验设置的细节。另一方面,实验的作用是能够比较试图解释同一现象的多种相互竞争的理论。如果包括每个实验设置的细节的负担被放在理论本身上,那么以上都不可能。在理论和实验之间放置一张地图,提供了理论世界和现实世界之间必要的分离。
现在,在上面的讨论中,如果我们用产品世界代替理论世界,用客户世界代替现实世界,我们将立即注意到经验证的学习的相似性。验证学习的目标是找到一个将产品配置与客户反应联系起来的地图。由于没有更好的术语,我将简单地称之为客户地图。找到产品/市场的契合度就相当于找到了客户地图的最佳第一近似值。在产品优化阶段,我们使用这个客户图为合适的客户制造合适的产品。在产品优化阶段,还会进一步完善客户地图。
一种新的有效学习 MVP
如何着手构建客户地图?一般来说,为每个产品配置进行实验是非常低效的,因为我们想知道客户的反应。这或多或少是重复分割测试采用的方法。相反,我们需要的是一种能够从数据中归纳的工具。碰巧的是,在精益创业引入以来的十年里,我们已经完善了一个工具,这个工具就是所谓的机器学习。
机器学习是精益创业中验证学习的完美工具。为了使这种说法更加具体,让我举一个例子来说明这在概念上是如何工作的。正如我们已经讨论过的,客户地图应该将产品配置与客户反应联系起来。推荐系统就是这样一种机器学习算法。推荐系统中映射的质量显然取决于用于训练它的数据的质量。因此,在第一阶段,应该选择产品配置以减少映射中的不确定性,而在后一阶段,我们使用映射来生成给定客户的最佳产品配置。这恰恰是语境土匪解决的问题。情境强盗是一种强化学习算法。强化学习是机器学习的一个子领域。在上下文 bandit 算法中,两个阶段被称为探索和利用。因此,推荐系统结合上下文强盗可以成为有效学习的工具。这种设置是重复分割测试的一个非常好的替代。
我刚才描述的验证学习方法与机器学习产品完全兼容,因为它不需要建立不受产品不确定性影响的假设。因此,它没有重复分割测试固有的混淆问题。
这种方法也不需要产品的功能定义。这种方法中的产品配置可以仅仅是技术规格。对于机器学习算法,这相当于选择算法类型、参数选择、训练数据集等。然而,有一个产品的功能定义对理解它与顾客的关系是有用的。事实上,在该方法中提供功能性产品定义是可能的。产品的功能完全由它在与当前客户地图一起使用时产生的响应来定义——本质上,这是我们对客户将如何对产品做出反应的最佳猜测。这不是传统产品定义中的功能列表。但它可能更有用,因为它可以使用行为分析工具进行分析,以深入了解客户对产品的看法。
总而言之,机器学习产品完全符合精益的原则。然而,为了在实践中实现这种兼容性,我们需要放弃一些用于验证学习的过时工具,并用现代机器学习工具来代替它们。在迁移过程中出现的中心实体是客户地图,它的确定成为经验证学习的主要目标,取代了通过重复分割测试进行的假设验证。
通过分享来学习
为什么我要离开图书馆去写一个数据科学博客
传统教育很简单:坐下,闭嘴,听老师讲课。课后去图书馆反复阅读同样的单词,试图理出我们日常生活中意义不大的抽象话题。即使作为一名研究生,我仍然被例行公事地讲课,并被期望在课外花大部分时间独自思考我的研究。虽然这可能对需要简单的考试信息回流的科目(查看你的历史)很有效,但它完全不适合现代技术科目,如数据科学。
考虑到这一点,这里有一个激进的建议:当你想理解一个概念时,不要去看书,你应该去你的博客,试着向别人解释清楚。这个想法很简单:如果你不能把一个话题教给别人,那么你自己也不会明白。
Well said
当我开始研究生课程时,我决定采取一种新的教育方法。我不再被动地坐在课堂上,而是打算每次演讲至少问一个问题。这个小小的调整对我的课堂参与产生了深远的影响。我把我的问题集中在如何实现我们所涉及的概念上,这些概念经常在没有任何实际例子的情况下出现。这种积极的参与让我在课堂上更容易集中注意力,也更容易将话题应用到我的研究和作业中的问题上。
课外,我花更少的时间独自学习,花更多的时间在实验室实施数据科学技术。我还努力与其他学生就课堂内容展开对话。在这个过程中,我不是通过死记硬背,而是通过向他人解释来理解这些主题。通过这些讨论,我的同事和我会尝试使用这些技术来解决我们的问题。无论我们是失败还是成功,我们都会回来进行更多的辩论,形成一个富有成效的反馈循环。幸运的是,我在一个实验室里,有比我聪明的学生和教授——永远不要成为房间里最聪明的人是一个好主意——每天我都通过看到实践来学习新的东西。
这学期有更多的数据科学和人工智能(AI)研究生课程,我需要加强我的分享游戏。我的目标是每周至少写一篇博文,解释课堂上涉及的一个话题。我没有太多的时间去开发一些很酷的应用程序,比如股票探索工具(T0)或 T2 体重追踪工具(T3)Python 工具,但是我可以把平时花在复习课程材料上的时间用来写我所学到的东西。这既是为了测试我是否真正理解了材料,也是为了让其他人受益!
当信息——至少是无害的信息——被自由分享时,社区得到了最好的服务。有些人认为,因为他们努力学习他们所知道的,其他人也必须这样做,他们拒绝透露任何会使其他人学习更容易的事情。我强烈反对这种观点:仅仅因为我们为教育支付了几万美元并不意味着我们应该保密。相反,我相信教育的民主化,相信帮助别人从我的(许多)错误和(有限但不断增长的)经验中吸取教训。在技术领域,特别是数据科学,互联网扩大了获取信息的渠道,现在任何人都有可能学习和实践尖端技术。正规机构不再垄断知识,我想在降低这些令人兴奋的新领域的壁垒方面发挥一小部分作用。
Libraries may own books, but nobody owns knowledge
试图解释概念有助于我们自己更好地理解它们。为普通观众翻译一个主题需要真正的理解,而不是死记硬背。我们都经历过这样的情况:我们详尽地研究了一个主题,并认为我们完全理解了这个想法,但当我们必须在一个基本的情况下应用它时,却完全空白了。此外,一个领域中最成功的人往往不是最聪明的,而是那些能够最好地交流发现并展示其相关性的人。尼尔·德格拉斯·泰森是世界上最著名的物理学家,不是因为他发表了最杰出的论文,而是因为他为广大读者解释了艰难的概念。清晰的书面和口头交流技能是课堂上无法教授的一大优势!
这些每周一次的帖子通常是关于数据科学和机器学习的,重点是现实世界的例子和隐喻。我的目标的一个很好的指示器是这个相关性对因果关系帖子。虽然隐喻可能会过度简化概念,但我的意图是为学习这些概念提供一个高层次的框架。在深入细节之前,先记下基本的想法是很有用的。具体的细节可以通过应用它们来解决问题来填充(或者如果你喜欢这种方式,可以写在一本书里)。如果你等不及我的帖子,我建议看看数据怀疑论播客,它为普通观众提供了大量数据科学话题。更好的是,开始你自己的博客!写作是思考你的想法的最好方式,分享知识对社区中的每个人都有好处。
一如既往,我欢迎反馈和建设性的批评。我可以在推特上通过 @koehrsen_will 联系到我。
学习数据科学—信息图
在 2012 年被《哈佛商业评论》评为“21 世纪最性感的工作”后,Glassdoor 将其评为 2016 年“年度最佳工作”。
然而,在这四年中,对数据科学家的态度发生了很大变化:2012 年,大多数文章都集中在试图解释什么是数据科学家以及他们具体做什么。当时,在谷歌上简短搜索“如何成为数据科学家”几个字,就会发现这个概念对不同的人有不同的含义。2016 年,这个搜索仍然给你各种各样的文章和广泛的观点。然而,尽管数据科学家曾经是一个实际存在的人,但现在越来越多的文章专注于解释为什么数据科学家是独角兽。
因为尽管数据科学家的定义并不固定,但还没有多少人达到设定的高期望值。招聘信息显示,公司正在寻找拥有沟通技能、创造力、聪明、好奇心、技术专长……这些能力有时被描述的方式让人们看起来不可能成为数据科学家。
随着需求取代供应,数据科学团队而非数据科学家的趋势正在上升,随之而来的是对数据科学是什么和如何的重新强烈关注。然而,就像数据科学家的定义一样,数据科学的定义是多方面的,对于那些想学习数据科学的人来说,有很多建议。然而,这些信息可能依赖于行业和环境,也可能是个人的。
数据科学 8 步指南
为了指导你通过这一信息和建议的丛林,DataCamp 修改了它的“8 步成为数据科学家”信息图:它现在提供了你学习数据科学需要通过的 8 个步骤的更新视图。这八个步骤中的一些对某些人来说会比其他人容易,这取决于背景和个人经历等因素。
然而,我们的目标仍然是让这成为对学习数据科学感兴趣的每个人或者已经成为数据科学家或数据科学团队成员但希望获得更多资源以进一步完善的每个人的可视化指南。
Learn Data Science
(点击这里下载完整信息图)
如果你正在考虑学习数据科学,或者刚刚开始学习,不要被信息图中呈现的八个步骤吓到。
数据科学是一场马拉松,而不是短跑。
学习数据科学需要时间和个人投资,但这个过程一点也不枯燥!
递归神经网络如何工作
你肯定遇到过翻译自然语言的软件(Google Translate)或者把你的语音转换成文本的软件(Apple Siri ),可能一开始你很好奇它是如何工作的。
在过去的几年里,这些系统背后的科学已经有了相当大的进步。例如,在 2016 年末,谷歌在他们的谷歌翻译背后引入了一个新系统,该系统使用了最先进的机器学习技术。改进是显著的,你可以亲自测试一下。
另一个惊人的例子是百度最近的文本转语音:
Credits to Dhruv Pathasarathy for the amazing demo.
那么以上都有什么共同点呢?他们处理序列数据来做出预测。好吧,但是这和众所周知的猫图像识别器有什么不同呢?
想象一下你想说如果照片里有只猫。你可以使用多张有猫和没有猫的照片来训练一个前馈神经网络(典型的是 CNN-卷积神经网络)。
在这个网络中,信息只在一个方向上移动,即从输入节点向前,通过隐藏节点(如果有的话)到达输出节点。网络中没有循环或环路。— 维基百科
这些网络主要用于模式识别,如下所示:
Feedforward neural network
相反,为了成功处理序列数据,你需要使用递归(反馈)神经网络。它能够“记忆”部分输入,并使用它们进行准确的预测。这些网络是语音识别、翻译等的核心。因此,让我们深入进行更详细的解释。
什么是递归神经网络?
训练典型的神经网络包括以下步骤:
- 从数据集中输入一个示例。
- 该网络将采用该示例,并使用随机初始化的变量(称为权重和偏差)对其进行一些复杂的计算。
- 将产生一个预测结果。
- 将该结果与预期值进行比较会给我们一个错误。
- 通过相同的路径将误差传播回来将调整变量。
- 重复步骤 1-5,直到我们有信心说我们的变量是定义良好的。
- 预测是通过将这些变量应用到一个新的看不见的输入中来实现的。
当然,这是对神经网络的一个非常天真的解释,但是,至少,给出了一个很好的概述,并且可能对这个领域的新手有用。
递归神经网络的工作方式类似,但为了清楚地了解这种差异,我们将通过最简单的模型,使用基于前一个单词预测序列中的下一个单词的任务。
首先,我们需要使用大型数据集来训练网络。出于目的,我们可以选择任何大型文本(列夫·托尔斯泰的《战争与和平》是一个不错的选择)。当完成训练后,我们可以输入句子“拿破仑是……的皇帝”,并期望根据书中的知识做出合理的预测。
那么,我们如何开始?如上所述,我们一次输入一个例子,产生一个结果,这两个结果都是单词。前馈网络的不同之处在于,在评估结果之前,我们还需要了解以前的输入。因此,您可以将 RNNs 视为多个前馈神经网络,从一个网络向另一个网络传递信息。
让我们检查以下模式:
Recurrent neural network
这里 x_1,x_2,x_3,…,x_t 表示来自文本的输入单词, y_1,y_2,y_3,…,y_t 表示预测的下一个单词, h_0,h_1,h_2,h_3,…,h_t 保存前一个输入单词的信息。
由于纯文本不能用于神经网络,我们需要将单词编码成向量。最好的方法是使用单词嵌入 ( word2vec 或 GloVe )但是出于本文的目的,我们将使用独热编码向量。这些是( V,1) 向量( V 是我们词汇表中的字数),其中所有的值都是 0,除了在第 i 个位置的那个。例如,如果我们的词汇是苹果、杏、香蕉、…、国王、…斑马,单词是香蕉,那么向量就是*【0,0,1,…,0,…,0】*。
通常,词汇表包含所有英语单词。这就是为什么有必要使用单词嵌入。
让我们定义训练所需的等式:
- 1)—保存序列中前面单词的信息。可以看到, h_t 是用之前的 h_(t-1) 向量和当前的词向量 x_t 计算出来的。我们还将非线性激活函数 f (通常为 tanh 或 sigmoid )应用于最终求和。假设 h_0 是一个零向量是可以接受的。
-
- —计算给定时间步长 t 的预测字向量。我们使用 softmax 函数产生一个 (V,1) 向量,所有元素的总和为 1。这种概率分布为我们提供了词汇表中最有可能的下一个单词的索引。
- 3)-在每个时间步 t 使用交叉熵损失函数来计算预测字和实际字之间的误差。
如果你想知道这些 W 的是什么,它们中的每一个都代表了网络在某一阶段的权重。如上所述,权重是用随机元素初始化的矩阵,使用来自损失函数的误差进行调整。我们使用更新权重的反向传播算法来进行这种调整。我将在以后的文章中解释这个过程,但是,如果你对它是如何工作的感到好奇,迈克尔·尼尔森的书是必读的。
一旦我们获得了正确的权重,预测句子“拿破仑是……的皇帝”中的下一个单词就非常简单了。在 RNN 的不同时间步长插入每个字将产生h1、H2、H3、H4。我们可以利用 h_4 和x _ 5(“of”这个词的向量)推导出 y_5 。如果我们的训练是成功的,我们应该期望在 y_5 中最大数字的索引与我们的词汇表中单词“France”的索引相同。
标准 RNN 的问题
不幸的是,如果你实现了上面的步骤,你不会对结果感到高兴。这是因为最简单的 RNN 模型有一个主要缺点,称为**消失梯度问题,**这使得它不精确。
简而言之,问题来自于这样一个事实,即在训练期间的每个时间步,我们都使用相同的权重来计算 y_t 。该乘法也在反向传播期间完成。我们越往后退,误差信号就变得越大或越小。这意味着网络在记忆序列中距离较远的单词时会遇到困难,并且只根据最近的单词做出预测。
这就是为什么更强大的型号像 T4 LSTM T5 和 T6 GRU T7 会出现。解决了上述问题,它们已经成为实现递归神经网络的公认方法。
奖金
最后,我想与所有让我更好地了解 RNNs 的资源分享我的列表:
热身:
更深入:
高级(抓住细节):
我希望这篇文章能让你对递归神经网络有一个很好的理解,并能对你激动人心的深度学习之旅有所贡献。
还有哪些 AI 内容?在 LinkedIn 上关注我获取每日更新。
感谢您的阅读。如果你喜欢这篇文章,给它一些掌声👏。希望你有一个伟大的一天!
用这个玩具数据集学习 RCNNs
这里有一个数据集,旨在帮助展示递归卷积神经网络(RCNN)何时会优于非递归卷积神经网络(CNN)。
一点入门知识
递归模型是专门设计用于使用一系列数据进行预测的模型(例如,使用过去 3 天的一系列数据点的股票市场预测器)。
卷积模型是专为处理图像数据而设计的模型。
因此,递归卷积模型是一种专门设计用于使用一系列图像(通常也称为视频)进行预测的模型。
数据集
The aptly named Sliding Square
toy dataset
从 Kaggle 这里下载,也可以在 my Github 上找到。
任务
预测方块的下一个位置!
输入
作为一个 CNN 你只能得到一个单一的帧作为输入:
Input to CNN (single image)
作为一个 RCNN 你得到多个帧作为输入:
Input to RCNN (multiple sequencial images)
直觉
为什么 CNN 会在这个数据集上表现不佳?
看看上面 CNN 的输入(单帧)。你能自信地预测滑动方块的下一个位置是什么吗?我假设你的回答是“不”。这是因为它是完全模糊的,也就是说,它可能向左或向右移动,没有办法根据一张图片就知道。
Ambiguity in trying to predict the next position based on a single frame.
为什么 RCNN 应该在这个数据集上表现良好?
尝试使用上面的 RCNN 输入(两帧)预测下一个位置。现在很容易了吧?正方形移动的方向现在不再模糊。
Showing how using multiple frames disambiguates the next position.
CNN 与 RCNN 的性能测试
查看这本笔记本,看看在这个数据集上构建和训练简单 CNN 和 RCNN (Keras 模型)的代码。
为了评估性能,我们使用来自数据集的随机帧样本,并让每个模型尝试预测正方形的下一个位置。
下面是每个模型预测误差的频率表。例如,你可以看到,对于 7 次测试,CNN 得到了完全正确的正方形位置(零误差)。
frequency chart of prediction errors for a sample of test data
CNN 错误分数是怎么回事?
你可能会注意到 CNN 犯了很多+/- 4 的错误。当我们考察其中一个预测时,一切都揭晓了:
CNN prediction (always predicts it to be the same position as the input)
CNN 似乎总是预测这个正方形与它在输入图像中的位置相同。你可能觉得这很奇怪,因为方块的下一个位置只能是左边或者右边。为什么它会预测它会在一个它根本不会在的地方!?一个字… **一致性!**一般来说,对于机器学习模型来说,最好总是有点错误,而不是有时非常错误。在大多数学习算法中,这种偏好是通过平方误差来实现的,如下所示:
How consistent errors are encouraged by squaring the original error. See how both models have the same Sum(Error) but Model B has a significantly higher Sum(Error x Error)
CNN 已经学会猜测与输入相同的位置,因为这样它总是离正确答案最多+/-5。如果它总是猜测方块向右移动:一半的时间它是完全正确的,但另一半的预测会相差 10 %!(这不太一致)
自然 RCNN 是在其元素
从误差图中可以看出,RCNN 的误差远不及 CNN。这是因为它在计算正方形移动的方向上没有问题。
RCNN not having any trouble in predicting the next position of the square.
希望这给了你更多关于何时使用 RCNNs 而不是标准 CNN 的直觉。
所有用于生成玩具数据集的代码以及测试模型的代码都可以在我的 Github 上找到:
理解-递归卷积神经网络-使用合成生成的视频数据来学习如何/何时…
github.com](https://github.com/ZackAkil/understanding-recurrent-convolutional-neural-networks)
在 15 分钟内了解大数据分析的火花!
我向你保证,这个简短的教程将为你节省大量阅读长篇文档的时间。准备好搭乘大数据列车了吗?我们开始吧!
基本概念
RDD : 火花的基本抽象。它将数据对象分布到多台机器上,并在内存中处理,这比在像 R 或 Python 这样的单台机器上处理要快。但是它没有组织在命名的列中。
Dataframe :构建在 rdd 之上,可以拥有一个包含列名和数据类型的模式。它比 rdd 更快,因为它为 Spark 提供了更多的数据信息。
惰性评估:当你使用转换时,Spark 实际上并不执行数据,直到你调用一个动作。
转换:选择、过滤/where、排序/排序依据、连接、分组依据、聚集
动作:显示、获取、计数、收集、描述、缓存、打印模式
基本语法
创建一个 Rdd
# 1\. Read external data in Spark as Rdd
rdd = sc.textFile("path")# 2\. Create rdd from a list
rdd = sc.parallelize(["id","name","3","5"])# 3\. Dataframe to rdd
rdd = df.rdd
创建数据框架
# 1\. Read in Spark as Dataframe directly
# header and schema are optionaldf = sqlContext.read.csv("path", header = True/False, schema=df_schema)# 2.1 rdd to dataframe with column names
df = spark.createDataFrame(rdd,["name","age"])# 2.2 rdd to dataframe with schemafrom pyspark.sql.types import *df_schema = StructType([
**... ** StructField("name", StringType(), True),
**... ** StructField("age", IntegerType(), True)])df = spark.createDataFrame(rdd,df_schema)
转换:
# Basic transformations:# 1\. select. Index column by df.column_name or use "column_name"
df.select(df.name)
df.select("name")# 2\. filter/where they are the same
df.filter(df.age>20)
df.filter("age>20")
df.where("age>20")
df.where(df.age>20)# 3\. sort/orderBy
df.sort("age",ascending=False)
df.sort(df.age.desct())# 4\. groupBy and agg
df.groupBy("gender").agg(count("name"),avg("age"))# 5\. join
df1.join(df.2, (df1.x1 == df2.x1) & (df1.x2 == df2.x2),'left')# 6\. create a new column from existing column
df.withColumn("first_name",split(name,'_')[0])
在 Spark 中编写 SQL
1\. Run sqlContext=sqlContext(sc) before create a dataframe
2\. Create a dataframe called df
3\. Run df.createOrReplaceTempView("table1") to create a temp table
4\. talbe2=sqlContext("select id, name from table1")If you are writing multiple lines, use """ like this:
talbe2=sqlContext.sql("""
select id, name
from table1
where age>20
""")
动作:
1\. Show: show first n rows of a dataframe (but not rdd) without cells being truncated
df.show(5, truncate=False)2\. Take: display a list of first few rows of dataframe or rdd
df.take(5)3\. collect: collect all the data of a dataframe or rdd
df.collect()4\. count: count number of rows
df.count()6\. printSchema: show column names, data types and whether they are nullable.
df.printSchema()7\. cache : cache the data in memory if it's going to be reused a lot. Use unpersist() to uncache data and free memory.
df.cache()
df.unpersist()# Transformations and actions can be connected and executed in sequence from left to right.
df1.filter("age>10").join(df2,df1.x==df2.y).sort("age").show()
外卖:
- 如果可能的话,使用并寻找在数据帧上使用的函数,而不是 rdd,因为在数据帧上速度更快
- 使用 collect()时要小心,除非你需要下载整个数据;使用显示/拍摄
- 如果您以后会经常用到这些数据,请将其缓存起来。
No spam, I’ll be mindful of your time and attention
给我几个掌声,如果你觉得有帮助,就跟我来吧!
您可能对如何通过使用数据科学节省租金感兴趣:
当我在寻找一个新的公寓出租时,我开始想知道:是否有一个数据驱动的决策策略…
zhenliu.org](https://zhenliu.org/2017/11/29/how-to-analyze-seasonality-and-trends-to-save-money-on-your-apartment-lease/)
学习构建机器学习服务,原型化真实的应用程序,并将您的工作部署给用户
在这篇文章中,我将向读者展示如何将他们的机器学习模型公开为 RESTful web 服务,原型真实的应用程序,方法是为这些服务附加一个前端,并使用容器部署他们的应用程序。
我认为这三个方面代表了数据科学家为了使他们的工作有意义和有影响力而应该具备的关键技能。
我将在本教程中介绍的 3 个工具是:
- Jupyter 笔记本(在 JupyterLab 中)带内核网关(把机器学习工作流变成 web 服务);
- Azle (快速前端原型库);
- Docker (在隔离的、可移植的容器内部署应用);
为什么数据科学家要把他们的机器学习变成 web 服务?
定期创建机器学习代码的端点迫使我们思考抽象。我们需要不断地向团队的其他成员公开我们的工作。这是让我们的工作流程对产品问题负责,并邀请非从业者参与机器学习对话的原因。
数据科学家为什么要做前端应用的原型?
我们不是为数据科学家做机器学习,而是为产品的最终用户做机器学习。这意味着我们需要认识到我们的模型将如何被消费,而不仅仅是被创造出来。将我们的数据管道连接到前端,可以让团队中的其他人了解机器学习的能力,并让我们的机器学习受益于非技术反馈。
为什么数据科学家要将他们的工作隔离在容器中?
容器化使得应用程序便携和可复制。可移植性对于任何应用程序都是一个挑战,但是机器学习软件特别容易受到依赖性问题的影响。多亏了一系列用定制代码拼凑起来的开源库,数据科学才成为可能。如果没有容器,当我们改变环境时,很难保证事情会一致地运行。这有助于我们更有效地协作,并允许我们更无缝地在组织内外共享应用程序。
让我们开始吧…
挑战:比方说我开了一家专门做鲜花的公司。我定期雇人进货,并在进货前给花贴上标签。虽然大多数鲜花都是从批发商那里贴上标签的,但这足以证明我需要培训员工如何识别鲜花。培训员工既费时又费钱。
****解决方案:我将创建一个应用程序,帮助快速培训我的员工。由于的研究显示竞争导致更好的学习,我的应用程序将允许新员工在基于照片和测量识别花朵方面与人工智能竞争。
****模型:低保真度模型帮助我们探索想法,而不会将自己局限于特定的工程实践或协议。在本教程中,我将使用虹膜数据集创建一个应用程序,帮助用户训练识别鸢尾花,并与做同样事情的“人工智能”竞争。
我们的应用程序将包含 3 个部分。我们的第一部分将允许用户在测量时看到随机的鸢尾花。这将是他们的培训区域,用于练习将花卉尺寸与他们的标签联系起来。
我们的第二部分将允许用户挑战我们的 AI,看谁(或什么)能得到更多正确的猜测。
我们的第三部分将显示当前的记分牌**,其中包含基于我的学员和 AI 表现的实时分数。我将与新员工一起审查这些报告,以评估他们的培训情况。**
构建整个应用程序超出了本文的范围。我们将专注于构建第一部分,剩下的代码留给您自己去探索。
到本文结束时,我们将已经构建了以下机器学习应用程序的前端和后端:
Machine Learning Application Built Using Azle andJupyterLab
后端对前端
我们既需要后端进行处理,也需要前端为用户提供交互。我们的后端将包括我们在 Jupyter 笔记本中原型化的机器学习,而前端将是使用 Azle 构建的 web 应用程序。
由于我们正在构建一个识别鸢尾花的应用程序,因此编写一个数据管道来收集和验证鸢尾花数据,训练和验证一个能够对鸢尾花进行分类的模型,并将该模型部署为前端消费的服务是有意义的。因为快速原型是我的目标,所以我将利用现有的机器学习管道来坐在我的 Jupyter 笔记本中。Google 的 Tensorflow 教程有一个使用深度学习对鸢尾花进行分类的端到端例子。由于虹膜数据集很小,而且我的应用程序不需要可解释性,深度学习应该可以正常工作。
安装 JupyterLab
我们将使用 JupyterLab 作为我们的开发环境。JupyterLab 允许我们在浏览器中使用 IDE,这在进行机器学习工作和 web 应用程序开发时非常方便。
安装完成后,在桌面(或者任何你喜欢的位置)创建一个新文件夹,命名为 flower_power。打开终端,进入新的 flower_power 目录。进入后,运行以下命令打开 JupyterLab:
jupyter lab
这将在您的默认浏览器中自动打开 JupyterLab 作为一个新选项卡:
在启动器中选择一个新的 Python 3 笔记本**。重命名笔记本 flower_power.ipynb**
如果你是机器学习的新手,请花时间跟随 Tensorflow 的教程,将它的代码添加到 flower_power 笔记本的连续单元格中。这是对机器学习中常见任务的一个很好的概述。如果您已经知道该练习,只需将代码添加到每个单元格中,并在笔记本上单击以确保满足所有依赖关系。或者你可以简单的 在 GitHub 这里下载Jupyter 的完整笔记本。只需使用 Jupyter 的上传工具来加载文件。
现在,我们的 JupyterLab 已经可以使用 flower_power.ipynb 笔记本了:
The flower_power.ipynb Jupyter Notebook
学习将 Jupyter 细胞作为 REST 终点
除了在浏览器中实现数据科学,Jupyter 还可以将我们的机器学习变成一种 web 服务。通过添加 Jupyter 内核网关,我们可以将笔记本中的任何单元格作为端点公开。请将端点视为笔记本电脑和任何需要使用其输出的应用程序之间的网关。创建端点意味着我们在机器学习中所做的工作可以向应用程序及其开发者公开。
安装 Jupyter 内核网关
在您的终端中运行以下命令:
pip install jupyter_kernel_gateway
我们将很快看到如何使用网关。此时,我们希望选择笔记本中的单元格作为端点**。这很简单,只需在单元格的开头添加以下注释:**
# GET /my_path
我们将用端点的名称替换“my_path”。首先,让我们创建一个函数**,它从 flower_power.ipynb 笔记本中的测试数据集中返回一朵随机的鸢尾花:**
这个函数从 3 个可能的选项中随机选择一个花的名称,选择随机选择的 URL,并检索与花相关的数据。
我们将使用一个端点调用这个函数,这样我们的应用程序就可以获取一个随机的花名称、它的图像 URL 和与它的 4 个测量值相关联的数据**。**
我通过进入维基百科的虹膜数据集页面并简单地在数据集部分右键单击适当的图像,将图像 URL 添加到上述函数中。我选择了 C opy 图像地址并将 url 粘贴到上面的函数中,分别为 url_1 、 url_2 和 url_3 。
我们希望将这个函数变成一个端点,使它可以在浏览器中从我们的 web 应用程序中调用。如上所示,我们通过在代码周围包装以下内容来使用内核网关**😗*
- 一个得到** 注释的单元格的第一行;**
- 输出 JSON 的打印** 命令。**
现在,当我们启动内核网关(稍后)时,这段代码将通过将浏览器指向以下 URL 来执行:
localhost:9090/get _ random _ flower
先不要担心 9090 端口,稍后我将展示如何设置它。这个端点将运行上面创建的 show_random_iris ()函数,并将结果添加到名为 res 的字典中(因为我们使用的是 JSON,所以请确保将 import json 添加到笔记本的顶部单元格中)。我们得到的是一个看起来像这样的 JSON 响应 :
您必须将 GET 注释添加到您想要作为端点的 Jupyter 单元格的第一行。如果您在 GET 行上放置任何注释或代码,该单元格将失败。
为了检查我们的端点,让设置 Jupyter 内核网关**。因为我们已经安装了 Jupyter gateway,所以我们只需要在包含 flower_power.ipynb 笔记本的 s ame 目录中的终端中输入以下命令:**
jupyter kernelgateway --KernelGatewayApp.api='kernel_gateway.notebook_http' --KernelGatewayApp.ip=0.0.0.0 --KernelGatewayApp.port=9090 --KernelGatewayApp.seed_uri=flower_power.ipynb --KernelGatewayApp.allow_origin='*'
该命令执行以下操作:
- 启动 Jupyter 网关;
- ****设置基础 URL 为 localhost(0 . 0 . 0 . 0);
- ****向浏览器公开端口 9090(你可以把这个改成任何可用的端口);
- ****将flower _ power . ipynb 笔记本分配给网关;
- ****允许来自任何地方的输入流量(记住,我们只是在做原型)
运行该命令将产生与 Tensorflow(忽略)和我们注册的资源(端点)相关的输出。最后一行应为:
Jupyter Kernel Gateway at [http://0.0.0.0:9090](http://0.0.0.0:9090)
在浏览器中打开一个新标签,指向:
localhost:9090/get _ random _ flower
您应该会看到返回的数据。持续点击刷新查看终端输出,每次返回不同的花朵。我们现在调用 python 代码,放在 Jupyter 笔记本里,全部通过我们的浏览器。您现在已经构建了一个服务。因为我们的机器学习将用 Python 编写,所以创建一个机器学习服务也没什么不同。我们只需通过浏览器选择我们希望调用的功能。
为了检查我们的端点,我们应该使用开发人员常用的工具来做同样的事情。一个这样的工具是 Swagger 。Swagger 是一款帮助开发人员设计、构建、记录和使用 RESTful Web 服务的软件。将服务端点传递给团队中的开发人员是一种常见的方式。Jupyter 内核网关的一个很棒的特性是它自动生成一个 swagger.json 端点,我们可以使用 SwaggerUI 来访问它。
这使得开发人员可以使用不同的 API 端点来“混搭”应用程序。作为数据科学家,我们希望我们的模型成为混搭的一部分,这样我们的组织就可以看到机器学习为他们的产品带来的价值。您组织内的开发人员可能会安装他们自己的 Swagger UI(或类似的东西),因此您可以简单地将您的 Swagger 端点交给他们。
对于我们自己的检查,让我们从 Dockerhub 下载 Swagger UI 并运行 UI 应用程序,检查我们刚刚用内核网关创建的端点。如果没有安装 Docker, 下载****先安装 Docker 。安装并运行 Docker 后,在终端中打开一个新标签,运行 Docker pull 命令下载 DockerUI:
docker pull swaggerapi/swagger-ui
在您的控制台中键入 docker images 以查看以下内容:
这显示了我们新图像的名称,swaggerapi/swagger-ui。我们现在可以使用这个名称来运行它的容器:
docker run -p 80:8080 swaggerapi/swagger-ui
…然后在我们的浏览器 80 端口上打开 swagger-ui** 。**
在浏览器的新选项卡中,导航到以下 URL:
[http://localhost](http://localhost):80
在屏幕的顶部是探索栏,在这里我们添加到我们的 swagger.json 文件的路径;我们的 Jupyter 内核网关暴露的那个。将以下内容添加到此栏中(确保 URL 中包含 http://):
[http://localhost:9090/_api/spec/swagger.json](http://localhost:9090/_api/spec/swagger.json)
因为我们只是点击我们创建的服务中的现有端点,所以端口仍然是 9090。按回车键,看看会发生什么。您应该会在屏幕上看到以下内容:
这是我们在 Swagger 内部公开的 GET 端点。点击获取按钮,然后在上试出** ,然后在上执行:
这些信息对你的开发者很有用。他们可以看到如何通过终端中的 curl 命令访问我们的 API,看到完整的请求 URL** ,看到响应主体,以及与该响应相关的任何头。我们还可以看到成功代码 200,显示我们的端点按预期工作。**
因为我们没有在端点上设置任何参数,所以没有什么可查询的。通常,开发人员会运行查询来决定如何最好地将我们的机器学习引入应用程序。我们将在稍后构建 Flower Power 应用程序时展示如何设置和传递参数。
我们现在对数据科学家如何在他们的 Jupyter 笔记本中为任何代码创建 web 服务有所了解。虽然我们在这里只使用了一个简单的例子,但这种方法向我们团队内外的利益相关者公开了所有类型的机器学习功能。
我们现在准备开始原型化一个真正的应用程序。虽然这通常属于开发人员的权限范围,但有充分的理由让数据科学家自己去做。真正的应用程序给了我们的机器学习“腿”,并消除了机器学习如何为组织的产品做出贡献的许多模糊性。
当然,数据科学家已经有足够多的工作要做,不用深入研究 HTML、CSS 和 JavaScript,更不用说为工程大规模 web 应用程序定制框架了。我们需要的是一个快速前端原型库,基于直观的语法,可以轻松连接到 API 端点。
一些机器学习从业者会争辩说他们的专业排除了学习前端开发的时间/需要;我发现这通常源于对学习如何开发软件的恐惧,而不是真正相信他们不需要这样做。重要的是要认识到原型软件和开发产品软件是不同的艺术形式。有了正确的高级库,原型软件对领域专家来说是非常有益的。在当今信息驱动的经济中,没有什么比快速将他们的愿景转化为人们可以使用的现实工具的能力更能赋予专业人员权力。
学习用 Azle 构建应用程序原型
太多时候,数据科学家孤立地致力于改进他们的机器学习模型,只在对模型满意后才把结果扔出围栏。这增加了项目投资回报率低的风险,因为真空中的机器学习永远不会受益于现实世界的反馈。应用程序的前端是真正的用户对学习算法产生的输出的唯一接触点。数据科学家需要根据人们对产品的触摸和感受来通知他们的管道。
Azle 是一个前端原型库,专门为快速制作应用程序而设计。它似乎在灵活性和易用性之间找到了合适的抽象层次。它使用函数来添加和样式 内容,绑定事件,并与 API 端点交互,从而更容易构建全功能、数据驱动的应用程序。
所有的 web 应用程序都是从一个 html 模板开始的,这个模板最终会包含使应用程序前端成为可能的元素。我们的第一步是使用 Azle 提供的模板。您可以将以下内容复制并粘贴到名为 index.html的文件中:
HTML template for Azle.
我们所有的 Azle 函数都可以添加到底部的
Azle 是一种直观的语言,我们根据*正在做的事情编写代码。Azle 预装了极简风格,所以我们不必花太多时间让我们的应用程序看起来更现代。它还使用了布局**,使得在屏幕上排列内容变得容易。***
我们来看看 Azle 中一些常见的函数**。点击任何一个按钮来玩实际代码:**
添加内容
az.**add_button**(target_class, target_instance, {
"this_class" : "my_button",
"text" : "CLICK ME"
})
az.**add_dropdown**(target_class, target_instance, {
"this_class" : "my_dropdown",
"options" : ["option A", "option B", "option C", "option D"],
"title" : "choose option..."
})
az.**add_slider**("target_class", 1, {
"this_class" : "my_slider",
"default_value" : 4,
"min_value" : 0,
"max_value" : 10
})
注意,每个函数使用一个目标类和目标实例作为前两个参数。这告诉 Azle 在哪里放置内容,或者我们希望样式化哪些内容。例如,在上面的滑块中,我们首先创建一个部分来存放我们的应用程序,然后将滑块添加到第一个部分:
az.add_slider(**"my_sections", 1,** {})
第三个参数是一个包含我们希望传递的属性的对象**。在我们的第一个 slider 示例中,我们传入了一个类名(“this_class”)和默认值。每个内容和样式都有其预期的属性,可以在 Azle 的文档中找到。**
样式内容
我们也可以样式我们添加的任何内容。如上所述,通过使用 target_class 和 target instance,并将样式属性作为第三个参数传递给一个样式对象来完成样式化。例如,上面的按钮样式:
az.**style_button**('my_button', 1, {
"background" : "hotpink",
"border-radius" : "6px",
"width" : "200px",
"color" : "black",
"cursor" : "wait"
})
请注意,Azle 样式遵循标准的 CSS 样式化文档的方法。
添加事件
我们可以将事件绑定到我们的 UI 元素上。事件是像点击、悬停和选择这样的事情。这里我们使用 Azle 的 add_event 函数向按钮添加一个点击事件:
az.**add_event**("my_button", 1, {
"type" : "click",
"function" : "alert('you clicked me')"
})
要查看 Azle 中所有可用的事件,请参见这里。
抓取值
通常,我们希望在调用事件之前从输入或下拉列表中获取一个值。这是使用 Azle 的 grab_value ()函数完成的:
az.**grab_value**("my_dropdown", 1)
例如,我们可以添加一个按钮,其文本来自下拉菜单中的选择:
az.**add_button**("my_sections", 1, {
"this_class" : "my_button",
"text" : **az.grab_value**("my_dropdown", 1)
})
呼叫端点
调用端点是我们从 REST APIs 发送和获取数据的方式。Azle 的 call_api ()函数要求提供基 url** ,我们需要传递的任何参数,以及一旦收到响应就调用的函数😗*
添加视觉效果
Azle 与 D3.js 配合得很好,正如我在这里讨论的。还可以查看一下 azle_d3 示例应用 。
下面是预览视频**,展示了如何使用 Azle 将 D3 视觉效果绑定到应用程序中的 UI 元素:**
我们对 Azle 的简短访问向我们展示了创建更完整的应用程序所需的部分,包括前端内容、样式、事件和视觉效果,以及调用端点的能力。请务必查看文档,其中包含入门教程。
让我们开始创建我们的应用程序。
创造花的力量
在开始时创建模拟的整个应用程序超出了本文的范围。我们将只构建应用程序的第一部分然后看看其他部分需要考虑什么。GitHub 项目包含完整的代码。
下面是我们在开头看到的视频,展示了完整的应用程序:
为了开始原型化我们的应用程序,我们像往常一样从前面显示的 html 模板开始,在底部的
最快的开始方式是简单地将 html 模板保存到您的桌面并右键单击该文件,选择打开方式,然后选择您的浏览器,如下所示:
然而,将所有文件放在我们的 ide 中会更方便,对我们来说 IDE 就是 JupyterLab。这样我们可以根据需要打开终端,安装库,对我们的 flower_power.ipynb 进行修改,重启我们的机器学习服务,原型化我们的前端;所有这些都在一个浏览器屏幕上。当构建真实世界的软件时,这是非常有益的。我们将使用这种方法。
如果您从一开始就已经在名为 flower power 的文件夹中启动并运行了 JupyterLab,并且将 flower_power.ipynb 选项卡作为您的第一个 JupyterLab 文件打开。
让我们打开其他选项卡,这样我们就有了一个完整的原型开发环境**。点击新启动器图标,选择其他选项下的终端,让打开一个终端😗*
这样做两次,这样我们就有 2 个终端打开。让我们也为我们的 Azle 应用程序创建一个 index.html 文件。点击其他下的文本文件…**
…并将文件重命名为index.html。将此文件作为 JupyterLab 环境中的第 4 个选项卡打开。
让我们在主目录中创建两个文件夹**,一个叫做 d3_visuals ,另一个叫做 scripts 。我们在 JupyterLab 中通过单击“新建文件夹”按钮来创建文件夹:**
…然后右键单击新创建的文件夹,并将其重命名。您现在应该有一个如下所示的目录结构**😗*
**flower_power** ├── **scripts** ├── **d3_visuals**
├── flower_power.ipynb
├── index.html
您还应该为您的原型开发环境打开 4 个选项卡:
A prototyping environment for building machine learning applications.
我们稍后将添加 2 个额外的文件到 d3_visuals 和 scripts 文件夹中,但是现在让我们开始构建我们的应用程序。
我们需要在另一个浏览器选项卡中查看我们的应用程序,这意味着我们需要“启动 web 服务器”通过在 JupyterLab 中打开我们的第一个终端选项卡并运行以下命令,我们可以很容易地做到这一点:
python3 -m http.server
这将显示…
Serving HTTP on 0.0.0.0 port 8000 …
由于我们在与 index.html 文件相同的目录中运行这个命令,这意味着我们可以通过打开一个新的浏览器选项卡并导航到 http://localhost:8000/ 来查看我们的应用程序。
注:关于使用不同于 8000 的端口,请参见最后的附加部分。
当然,我们还没有添加任何东西到我们的 index.html 文件中,所以没有什么可展示的。让我们添加一些 Azle 函数。
我们从应用程序主体的基本样式开始。我们将添加一个极简的字体**(任何来自谷歌字体),一个“滚动到顶部”按钮,并设置我们的应用程序的宽度😗*
Azle 应用程序也需要部分**。章节就像它们听起来一样;显示特定上下文的应用程序的不同部分。让我们为“花的力量”创建 4 个部分;1 个用于顶部导航/标题栏,另外 3 个用于我们在开始时创建的 3 个样机设计。**
由于我们的应用程序中有一些部分我们现在有东西要看,所以我们可以在新的浏览器标签中打开**http://localhost:8000/来查看我们的应用程序。我们目前有一个由 4 部分组成的应用程序,如下所示:**
让我们设置我们的部分的高度,以符合我们的样机。我们将把第一部分的高度设置得更短,这样它就可以作为我们的导航/标题栏:
花之力量的顶端现在看起来像这样:
在我们的导航栏中,我们将需要一个徽标**,一个标题,以及几个按钮,允许用户滚动到特定部分。每当我们想在屏幕上安排内容时,我们就使用 Azle 的布局。让我们在第一部分中添加一个布局,然后添加图像、文本和按钮:**
我不会在本文中详细介绍每个 Azle 函数,但是,鉴于 Azle 的高度直观的语法,您应该能够理解哪个函数在做什么。记住,你使用 Azle 越多,你就能越快地对如何快速组装应用程序有一个直觉。
让我们也添加我们的按钮到我们布局中的第三个单元格:
虽然上面的可能看起来有点吓人,但它非常简单。我们首先将按钮标题存储在 Azle 的 hold_value 对象中。然后我们使用 Azle 的 call_multiple 函数在一组函数上迭代 3 次。反勾号内的函数(在 JavaScript 中称为“模板字符串”)是我们添加按钮、样式化按钮和添加滚动事件的地方。
让我们将第三个单元格的内容向右对齐:
最后,每当我们完成向布局添加内容时,我们应该将布局的边框设置为 0** 以移除边框。返回之前在“banner_layout”目标类上使用的 style_layout 函数,并删除边框:**
我们的顶部横幅现在看起来像这样:
对于剩余的部分,我不会遍历所有代码,而是简单地指出重要的 Azle 函数,因为它们与调用我们的机器学习服务有关。完整的源代码可以看 GitHub 项目。你可以复制并粘贴必要的代码到你的 index.html 文件中,或者简单的克隆整个项目并继续这样做。
为了构建 section 2,我们添加并样式化一个布局,添加文本、按钮、图像和可视化。第 2 部分如下所示:
注意:我们的应用程序部分不需要精确地跟随模型(这就是为什么它们被称为低保真度模型)。快速原型制作的美妙之处在于,我们可以快速调整设计以适应不断变化的需求。
如果你刷新你的 index.html 页面,你将看不到任何花或可视化。为此,我们需要调用我们之前创建的第一个端点**,因为该端点返回了 Iris URL 及其数据。我们还需要添加一个新的脚本到脚本文件夹,和一个 D3 视觉到 d3 _ 视觉文件夹。**
如果您从 GitHub 克隆了 flower_power 项目,那么您已经拥有了所有必需的文件夹和文件。如果没有,查看 GitHub 上的项目文件,并将必要的文件夹和文件添加到您的 JupyterLab 项目中。您的完整目录结构应该如下所示:
**flower_power** ├── **scripts** └── wrapped_functions.js
├── **d3_visuals** └── barchart.html
├── flower_power.ipynb
├── index.html
现在我们只需要启动并运行我们的 REST 服务。在第二个终端选项卡中,运行我们之前看到的用于启动内核网关的命令:
jupyter kernelgateway --KernelGatewayApp.api='kernel_gateway.notebook_http' --KernelGatewayApp.ip=0.0.0.0 --KernelGatewayApp.port=9090 --KernelGatewayApp.seed_uri=flower_power.ipynb --KernelGatewayApp.allow_origin='*'
现在点击 index.html 页面上的刷新**。第一部分应该返回一个随机虹膜图像及其数据可视化成一个有角度的条形图。**
至此,JupyterLab 中的原型环境已经完成并正在运行。你有:
- 终端 1 :运行网络服务器托管 index.html
- 终端 2 :运行 Jupyter 内核网关,将笔记本电池暴露为 REST 端点
- flower_power.ipynb :一个 Jupyter 笔记本,里面装着你的 Python 或者 R 代码
- index.html:用 Azle 构建的 web 应用
你可以使用同样的设置将你的机器学习变成真正的软件原型。由于大多数数据科学家和其他机器学习实践者使用 Jupyter 环境,这种设置使您能够快速将全功能前端附加到您的 Python 和/或 R 代码。
第 2 部分中的一个重要功能是当用户单击 SHOW IRIS 按钮时调用 REST 端点的能力。我们已经知道如何添加事件,因为我们之前在回顾 Azle 函数时已经看过了。我们添加按钮,然后附加一个点击事件:
此时,我们需要添加当用户单击按钮时调用的函数(上面只显示了一个空字符串)。之前我们看了 Azle 的 call_api ()函数。我们用它来调用我们创建的任何端点。您可以在 wrapped_functions.js 的 scripts 文件夹中找到这个函数。
在 Azle 中,每当我们想要将许多函数组合成一个单独的命名函数时,我们就使用包装函数**。例如,如果我们想创建一个名为“fetch_random_iris”的包装函数,它应该是这样的:**
通过这种方式,我们可以将多个函数组合在上面的花括号中,并通过调用fetch_random_iris()
来调用它们。
首先,让我们看看当用户点击我们的 SHOW IRIS 按钮时我们想要调用的函数组,然后我们将这些函数添加到我们的包装函数中。
下面这组函数做 3 件事:
- 调用我们当前由 Jupyter 内核网关公开的第一个端点;
- 当数据返回时,调用“call_function ”;
- 用户点击后旋转按钮。
让我们将这些添加到我们的包装函数 fetch_random_iris 中:
注意,我们在 fetch_random_iris 包装函数中添加了一个“call_function”参数。您可以将任意数量和类型的参数传递到包装的函数中。这里,我们传递一个“回调”,这是一个一旦 API 返回数据就会被调用的函数。
在 Azle 中,我们这样调用包装函数:
az.call_wrapped_function.fetch_random_iris()
因此,我们可以将它添加到上面的 click 事件中,这样当用户单击 SHOW IRIS 按钮时,它将运行 fetch_random_iris()。
注意,我们仍然需要在上面的 fetch_random_iris 调用中添加回调函数作为参数。我们还有一个 D3 可视化的条形图,一旦数据返回,就会绘制出来。这由回调函数处理,回调函数是另一个包装函数。如前所述,我不会遍历本文中的所有代码。我鼓励你浏览一下 GitHub 项目,看看完整的代码。
上面我也提到了 Azle 和 D3。看看我的文章关于如何使用 Azle 给你的应用程序添加 D3 视觉效果。
应用程序的其余部分遵循相同的方法。我们检查低保真度模型,添加布局和 UI 元素,根据需要绑定事件,将函数分组为包装函数,并使用 call_api 函数发送和接收来自我们的机器学习服务的数据。
下面是第三节**😗*
和第 4 节**😗*
正如我们在开始时看到的,这里是最终结果**😗*
有了真正的应用在手,就该出货了!
学习对你的机器学习应用进行分类
当我们“发布”一个应用程序时,我们将所有东西打包并放在服务器上。这允许其他人访问该应用程序,而不是让它只驻留在我们的本地机器上。将一个应用程序安装到远程服务器上有其自身的技术挑战。仅仅因为我们的应用程序在我们的笔记本电脑上运行良好并不意味着它在其他地方也能运行良好。人们使用不同的运行时环境、不同版本的库、不同版本的编程语言等。
为了让这变得更容易,Docker 把一种叫做集装箱化的技术变成了一种易于使用的产品。Docker 允许我们获取本地环境的快照,并将其精确配置传送到任何服务器上。这意味着我们可以使用 Docker 来封装我们的 Flower Power 应用程序所依赖的不同东西:
- Python 3
- 像熊猫和 Numpy 这样的图书馆
- 张量流
- Jupyter 内核网关
决定使用本文前面描述的 SwaggerUI 的读者已经接触过 Docker。如果您还没有, 下载****安装 Docker 然后再进行下一步。
安装 Docker 后,我们准备好编写** 一个 Docker 文件。Docker 文件是一个告诉 Docker 如何构建“映像”的文件图像指定如何构建容器。图像的实例是一个容器。Docker 中的一个关键概念是所谓的基础图像。基础映像允许我们使用已经打包好的语言和库来启动我们的容器。换句话说,如果它们包含在我们的基本映像中,我们不需要在 docerkfile 中明确列出所有的依赖项。我们将使用 jupyter/datascience-notebook 作为我们的基础映像,因为许多机器学习库都包含在这个映像中。**
让我们写下我们的 Dockerfile :
你可以参考 Docker 的教程来了解为什么 Docker 文件看起来是这样的。这里我们将只指出几个要点:
- 从允许我们带来我们的基本形象;
- RUN 允许我们安装额外的依赖项;
- CMD 是我们可以运行终端命令的地方。在这里,我们只是运行我们之前运行的用于启动内核网关的相同命令。
有了 Dockerfile 在手,我们现在可以建立我们的形象**😗*
docker build -t flower_power .
我们只是运行 docker build 命令,并将我们的图像命名为 flower power。
我们确保通过运行以下命令构建了我们的映像:
docker images
这应该会在终端中显示:
然后我们运行集装箱**😗*
docker run -p 9090:8888 flower_power
在这里,我们将端口 9090 暴露给外部世界。虽然终端会发出一条消息说“Jupyter Kernel Gateway athttp://0 . 0 . 0 . 0:8888”,但这是来自容器内部的。我们的访问实际上是通过端口 9090。
我们可以通过在终端中键入以下命令来确保我们的映像正在运行:
docker ps
我们现在有了一个运行的容器。部署可以非常简单,只需将 Dockerfile 转移到另一个服务器上,并在那里运行构建和运行命令。Docker 将提取所有必要的依赖项,并通过我们指定的端点公开我们的机器学习。我们的前端应用程序可以在我们部署机器学习的同一台服务器上,也可以在更高的另一台服务器上。这就是创建一个机器学习服务的意义,该服务可以部署到服务器上,并由其他应用程序使用。想想这些可能性。
设置远程服务器非常简单。您可以使用像 DigitalOcean 这样的服务来创建一个 droplet,然后使用传输协议(例如 scp 或 sftp)将您的 docker 文件传输到该 droplet。
需要注意的一点是,我们需要更改 Azle 的 call_api() 函数使用的 URL,因为它将在一个新的 IP 地址上调用我们部署的机器学习服务,而不是本地主机。
当然,部署可以比这复杂得多,但基本思想是相同的。无论是持续集成和部署、集成测试、微服务,还是任何其他相关概念,都可以归结为将打包的代码推送到服务器上并消费端点。
就是这样!在本文中,我们设置了一个具有机器学习管道的 Jupyter 笔记本,使用 Jupyter 的内核网关公开了端点,使用 SwaggerUI 查看了这些端点,使用 Azle 创建了一个前端应用程序来使用我们的端点,并将我们的机器学习打包到一个可以部署到服务器的容器中。这是对数据科学在现实项目中如何发生的更现实的审视。
因此,去构建和部署你的机器学习服务,并创建有趣的应用程序来使用它们。这些技能造就了全面发展的数据科学家,他们知道如何将自己的工作带入生活。
附言
如果你在这些步骤中有任何问题,请在评论区提问。
奖金票据
使用 Azle 实现原生 Python/R 可视化
数据科学家经常依赖 Python 的 Matplotlib 或 R 的 ggplot2 产生的视觉效果。当这些视觉效果被暴露为数据 URIs 时,Azle 可以使用它们。数据 URIs 是图像嵌入 web 应用程序的方式,而不是指向源 URL。例如,下面是我们通常如何用 matplotlib 生成一个图:
import numpy as np
import matplotlib.pyplot as pltt = np.arange(0., 5., 0.2)plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.savefig('lines.png')
产生了这个:
为了让 Azle 使用这个图,我们只需将我们的 matplotlib 图转换成数据 URI** ,并使用内核网关公开序列化的输出。**
下面是一个例子,我们使用 Azle 的 call_api 函数获取我们的编码图,然后使用 add_image 函数绘制该图:
为了在 Docker 容器中工作,我们需要通过挂载一个卷来创建一个数据文件夹。为此,我们可以在主文件夹中创建数据目录,并将这一行添加到 Docker run 命令中:
sudo docker run -p 9191:9191 -it -v /path_to_dockerfile/data:/data testing
**同样的方法也可以用在 **R 中。下面是我们如何使用 knitr 库在 R 中对图形进行编码:
library(knitr)file_name = 'lines.png'
uri=image_uri(file_name)
cat(uri)
构建更完整的应用程序
我们只展示了笔记本使用 GET 响应来自 Azle 的请求的基本方式。根据应用程序的复杂程度,Jupyter 的内核网关支持所有的 CRUD 操作。查看这里关于如何更全面地定义你的 web api 的更多细节。
C 构建 URL
- 作为 URL 路径的一部分(即
/api/resource/parametervalue
) - 作为查询参数(即
/api/resource?parameter=value
)
与基于 SOAP 的 web 服务不同,RESTful web APIs 没有“官方”标准。[14]这是因为 REST 是一种 的架构风格 ,而不像 SOAP 是一种 的协议 。
A 使冲突端口无效
- 如果你同时运行 Jupyter 笔记本和内核网关,你可以指定你的笔记本端口以避免冲突(或者只是设置你的内核网关端口为 Jupyter 默认的 8888 以外的端口)。例如,您可以使用以下命令将任何笔记本设置为端口 8889:
jupyter notebook --port=8889
M 取端点在 R
如果在 Jupyter 中使用 R 内核而不是 Python,可以如下处理请求:
# GET /my_pathreq <- fromJSON(REQUEST)
args <- req$argsparameter_name = args$parameter_name# do something with parameter_nameprint(toJSON(parameter_name))
确保将 jsonlite 库添加到笔记本的顶部。
library(jsonlite)
快乐大厦!
如果你喜欢这篇文章,你可能也会喜欢:
我们经常孤立地学习技术和方法,与数据科学的真正目标脱节;至…
towardsdatascience.com](/combining-d3-with-kedion-graduating-from-toy-visuals-to-real-applications-92bf7c3cc713) [## 机器学习工作流的 GUI 化:快速发现可行的流水线
前言
towardsdatascience.com](/gui-fying-the-machine-learning-workflow-towards-rapid-discovery-of-viable-pipelines-cab2552c909f) [## 创建 R 和 Python 库的分步指南(在 JupyterLab 中)
r 和 Python 是当今机器学习语言的支柱。r 提供了强大的统计数据和快速…
towardsdatascience.com](/step-by-step-guide-to-creating-r-and-python-libraries-e81bbea87911)**
通过在 tensorflow 中实现来学习 Word2Vec
嗨!
我觉得理解一个算法最好的方法就是实现它。因此,在这篇文章中,我将通过在张量流中实现来教你单词嵌入。
这篇文章背后的想法是避免所有的介绍和通常与 word embeddeds/word 2 vec 相关的闲聊,直接进入事情的实质。因此,许多国王-男人-女人-王后的例子将被跳过。
我们如何进行这些嵌入?
有许多获得单词嵌入的技术,我们将讨论一种已经很出名的技术,唯一的一种,word2vec。与普遍的看法相反,word2vec 不是一个深的网络,它只有 3 层!
注意:word2vec 有很多技术细节,为了更容易理解,我将跳过这些细节。
word2vec 的工作原理:
word2vec 背后的想法是:
- 以一个 3 层神经网络为例。(1 个输入层+ 1 个隐藏层+ 1 个输出层)
- 给它一个单词,训练它预测它的相邻单词。
- 移除最后一层(输出层),保留输入层和隐藏层。
- 现在,从词汇表中输入一个单词。隐藏层给出的输出是输入单词的*‘单词嵌入’*。
就是这样!仅仅做这个简单的任务就能让我们的网络学习有趣的单词表达。
让我们开始实现它来充实这种理解。
(完整代码可在这里获得。我建议您理解这篇文章中的内容,然后使用其中的代码。)
这是我们将要处理的原始文本:
(为了简化,我故意把句号隔开,并在最后避开了它们。一旦你理解了,就可以随意使用记号赋予器
import numpy as np
import tensorflow as tfcorpus_raw = 'He is the king . The king is royal . She is the royal queen '# convert to lower case
corpus_raw = corpus_raw.lower()
我们需要将它转换成一个输入输出对,这样如果我们输入一个单词,它应该预测相邻的单词:它前后的 n 个单词,其中 n 是参数,这里有一个例子,来自 Chris McCormick 在 word2vec 上发表的一篇令人惊叹的文章。
A training sample generation with a window size of 2.
注意:如果单词在句子的开头或结尾,窗口将忽略外面的单词。
在此之前,我们将创建一个字典,将单词翻译成整数,将整数翻译成单词。这个以后会派上用场的。
words = []for word in corpus_raw.split():
if word != '.': # because we don't want to treat . as a word
words.append(word)words = set(words) # so that all duplicate words are removedword2int = {}
int2word = {}vocab_size = len(words) # gives the total number of unique wordsfor i,word in enumerate(words):
word2int[word] = i
int2word[i] = word
这些字典允许我们做的是:
print(word2int['queen'])
-> 42 (say)print(int2word[42])
-> 'queen'
现在,我们想要一个句子列表作为单词列表:
# raw sentences is a list of sentences.
raw_sentences = corpus_raw.split('.')sentences = []for sentence in raw_sentences:
sentences.append(sentence.split())
这会给我们一个句子列表,每个句子都是一个单词列表。
print(sentences)
-> [['he', 'is', 'the', 'king'], ['the', 'king', 'is', 'royal'], ['she', 'is', 'the', 'royal', 'queen']]
现在,我们将生成我们的训练数据:
(这可能会变得难以阅读。参考代码链接)
data = []WINDOW_SIZE = 2for sentence in sentences:
for word_index, word in enumerate(sentence):
for nb_word in sentence[max(word_index - WINDOW_SIZE, 0) : min(word_index + WINDOW_SIZE, len(sentence)) + 1] :
if nb_word != word:
data.append([word, nb_word])
这基本上给出了一个单词列表,单词对。(我们考虑窗口大小为 2)
print(data)[['he', 'is'],
['he', 'the'], ['is', 'he'],
['is', 'the'],
['is', 'king'], ['the', 'he'],
['the', 'is'],
['the', 'king'],.
.
.
]
我们有训练数据。但是它需要用计算机能理解的方式来表达,比如用数字。这就是我们的格言派上用场的地方。
让我们更进一步,把这些数字转换成一个热矢量。
i.e.,
say we have a vocabulary of 3 words : pen, pineapple, apple
where
word2int['pen'] -> 0 -> [1 0 0]
word2int['pineapple'] -> 1 -> [0 1 0]
word2int['apple'] -> 2 -> [0 0 1]
为什么是一个热点载体?:稍后告知
# function to convert numbers to one hot vectors
def to_one_hot(data_point_index, vocab_size):
temp = np.zeros(vocab_size)
temp[data_point_index] = 1
return tempx_train = [] # input word
y_train = [] # output wordfor data_word in data:
x_train.append(to_one_hot(word2int[ data_word[0] ], vocab_size))
y_train.append(to_one_hot(word2int[ data_word[1] ], vocab_size))# convert them to numpy arrays
x_train = np.asarray(x_train)
y_train = np.asarray(y_train)
所以现在我们有 x_train 和 y_train:
print(x_train)
->
[[ 0\. 0\. 0\. 0\. 0\. 0\. 1.]
[ 0\. 0\. 0\. 0\. 0\. 0\. 1.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 1\. 0\. 0\. 0.]
[ 0\. 0\. 0\. 1\. 0\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 1\. 0\. 0\. 0.]
[ 0\. 0\. 0\. 1\. 0\. 0\. 0.]
[ 0\. 0\. 0\. 1\. 0\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 1\. 0\. 0\. 0\. 0\. 0.]
[ 0\. 1\. 0\. 0\. 0\. 0\. 0.]
[ 0\. 0\. 1\. 0\. 0\. 0\. 0.]
[ 0\. 0\. 1\. 0\. 0\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 0\. 1\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 0\. 0\. 0\. 1\. 0\. 0.]
[ 0\. 1\. 0\. 0\. 0\. 0\. 0.]
[ 0\. 1\. 0\. 0\. 0\. 0\. 0.]
[ 0\. 1\. 0\. 0\. 0\. 0\. 0.]
[ 1\. 0\. 0\. 0\. 0\. 0\. 0.]
[ 1\. 0\. 0\. 0\. 0\. 0\. 0.]]
两者都有形状:
print(x_train.shape, y_train.shape)
->
(34, 7) (34, 7)# meaning 34 training points, where each point has 7 dimensions
制作张量流模型
# making placeholders for x_train and y_trainx = tf.placeholder(tf.float32, shape=(None, vocab_size))
y_label = tf.placeholder(tf.float32, shape=(None, vocab_size))
从上图中可以看出,我们将训练数据转换成嵌入式表示。
EMBEDDING_DIM = 5 # you can choose your own numberW1 = tf.Variable(tf.random_normal([vocab_size, EMBEDDING_DIM]))b1 = tf.Variable(tf.random_normal([EMBEDDING_DIM])) #biashidden_representation = tf.add(tf.matmul(x,W1), b1)
接下来,我们利用嵌入维度中的信息,对邻居进行预测。为了进行预测,我们使用 softmax。
W2 = tf.Variable(tf.random_normal([EMBEDDING_DIM, vocab_size]))b2 = tf.Variable(tf.random_normal([vocab_size]))prediction = tf.nn.softmax(tf.add( tf.matmul(hidden_representation, W2), b2))
总结一下:
input_one_hot ---> embedded repr. ---> predicted_neighbour_probpredicted_prob will be compared against a one hot vector to correct it.
现在,剩下的就是训练它:
sess = tf.Session()init = tf.global_variables_initializer()sess.run(init) #make sure you do this!# define the loss function:
cross_entropy_loss = tf.reduce_mean(-tf.reduce_sum(y_label * tf.log(prediction), reduction_indices=[1]))# define the training step:
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy_loss)n_iters = 10000# train for n_iter iterationsfor _ in range(n_iters):
sess.run(train_step, feed_dict={x: x_train, y_label: y_train}) print('loss is : ', sess.run(cross_entropy_loss, feed_dict={x: x_train, y_label: y_train}))
在训练中,你会得到损失的显示:
loss is : 2.73213
loss is : 2.30519
loss is : 2.11106
loss is : 1.9916
loss is : 1.90923
loss is : 1.84837
loss is : 1.80133
loss is : 1.76381
loss is : 1.73312
loss is : 1.70745
loss is : 1.68556
loss is : 1.66654
loss is : 1.64975
loss is : 1.63472
loss is : 1.62112
loss is : 1.6087
loss is : 1.59725
loss is : 1.58664
loss is : 1.57676
loss is : 1.56751
loss is : 1.55882
loss is : 1.55064
loss is : 1.54291
loss is : 1.53559
loss is : 1.52865
loss is : 1.52206
loss is : 1.51578
loss is : 1.50979
loss is : 1.50408
loss is : 1.49861
.
.
.
它最终在持续亏损的基础上稳定下来。即使不能得到高精度,我们也不在乎。我们感兴趣的只是 W1 和 b1,也就是隐藏的表示。
让我们来看看它们:
print(sess.run(W1))
print('----------')
print(sess.run(b1))
print('----------')->[[-0.85421133 1.70487809 0.481848 -0.40843448 -0.02236851]
[-0.47163373 0.34260952 -2.06743765 -1.43854153 -0.14699034]
[-1.06858993 -1.10739779 0.52600187 0.24079895 -0.46390489]
[ 0.84426647 0.16476244 -0.72731972 -0.31994426 -0.33553854]
[ 0.21508843 -1.21030915 -0.13006891 -0.24056002 -0.30445012]
[ 0.17842589 2.08979321 -0.34172744 -1.8842833 -1.14538431]
[ 1.61166084 -1.17404735 -0.26805425 0.74437028 -0.81183684]]
----------[ 0.57727528 -0.83760375 0.19156453 -0.42394346 1.45631313]----------
为什么是一个热点载体?
again from Chris McCormick’s article (do read it)
当我们将 one hot vectors 乘以W1
时,我们基本上可以访问W1
的行,它实际上是由输入 one hot vector 表示的单词的嵌入式表示。所以W1
本质上是作为一个查找表。
在我们的例子中,我们还包括了一个偏见术语b1
,所以你必须添加它。
vectors = sess.run(W1 + b1) # if you work it out, you will see that it has the same effect as running the node hidden representationprint(vectors)
->
[[-0.74829113 -0.48964909 0.54267412 2.34831429 -2.03110814]
[-0.92472583 -1.50792813 -1.61014366 -0.88273793 -2.12359881]
[-0.69424796 -1.67628145 3.07313657 -1.14802659 -1.2207377 ]
[-1.7077738 -0.60641652 2.25586247 1.34536338 -0.83848488]
[-0.10080346 -0.90931684 2.8825531 -0.58769202 -1.19922316]
[ 1.49428082 -2.55578995 2.01545811 0.31536022 1.52662396]
[-1.02735448 0.72176981 -0.03772151 -0.60208392 1.53156447]]
如果我们想要“女王”的代表,我们要做的就是:
print(vectors[ word2int['queen'] ])# say here word2int['queen'] is 2->
[-0.69424796 -1.67628145 3.07313657 -1.14802659 -1.2207377 ]
那么我们能用这些美丽的载体做什么呢?
这里有一个快速函数,可以找到与给定向量最近的向量。请注意,这是一个肮脏的实现。
def euclidean_dist(vec1, vec2):
return np.sqrt(np.sum((vec1-vec2)**2)) def find_closest(word_index, vectors):
min_dist = 10000 # to act like positive infinity
min_index = -1 query_vector = vectors[word_index] for index, vector in enumerate(vectors): if euclidean_dist(vector, query_vector) < min_dist and not np.array_equal(vector, query_vector): min_dist = euclidean_dist(vector, query_vector)
min_index = index return min_index
我们现在将使用“king”、“queen”和“royal”查询这些向量
print(int2word[find_closest(word2int['king'], vectors)])
print(int2word[find_closest(word2int['queen'], vectors)])
print(int2word[find_closest(word2int['royal'], vectors)])->queen
king
he
有趣的是,我们的嵌入了解到
king is closest to queen
queen is closest to king
royal is closest to he
第三个是由于我们的语料库(还是蛮不错的)。更大的语料库将导致更好的结果。(注意:由于权重的随机初始化,您可能会得到不同的结果。如果需要,运行几次
让我们画出它们的向量!
首先,让我们用我们最喜欢的降维技术将维数从 5 减少到 2:tSNE(tee SNE!)
from sklearn.manifold import TSNEmodel = TSNE(n_components=2, random_state=0)
np.set_printoptions(suppress=True)
vectors = model.fit_transform(vectors)
然后,我们需要对结果进行规范化,以便在 matplotlib 中更方便地查看它们
from sklearn import preprocessingnormalizer = preprocessing.Normalizer()
vectors = normalizer.fit_transform(vectors, 'l2')
最后,我们将绘制 2D 归一化向量
import matplotlib.pyplot as pltfig, ax = plt.subplots()for word in words:
print(word, vectors[word2int[word]][1])
ax.annotate(word, (vectors[word2int[word]][0],vectors[word2int[word]][1] ))plt.show()
哇!she
离queen
近而king
离royal
和queen
等距离我们需要一个更大的语料库来看一些更复杂的关系。
注意:在发表这篇文章后,我意识到这个例子是不正确的,因为要得到有意义的向量收敛,我们需要一个非常大的语料库。数据量小,容易受到突然的“冲击”。然而,出于教学目的,我将保持这种写作的完整性。为了有效地实现 word2vec,请尝试使用一些类似于text 8的语料库 gensim 。
为什么会这样?
我们给了神经网络预测邻居的任务。但是我们还没有具体说明网络应该如何预测它。因此,神经网络计算出单词的隐藏表示形式,以帮助它完成预测相邻单词的任务。预测相邻单词本身并不是一项有趣的任务。我们关心这个隐藏的表示。
为了形成这些表示,网络使用上下文/邻居。在我们的语料库中,king
和royal
作为邻居出现,royal
和queen
作为邻居出现。
为什么预测邻居是一项任务?
嗯,其他任务也设法形成一个好的表示。预测这个单词是否是一个有效的 n-gram,如这里的所示也可以得到好的向量!
我们试图预测给定单词的相邻单词。这就是所谓的跳过克模型。我们可以使用中间词的相邻词作为输入,并要求网络预测中间词。这就是所谓的连续词汇袋模型。
延伸阅读:
这绝不是对 word2vec 的完整理解。w2v 的部分魅力在于它对我刚刚谈到的内容做了两处修改。这些是:
- 负采样
- 分级 Softmax
**负采样:**它表明,不是反向传播正确输出向量中的所有 0(对于 10 密耳的 vocab 大小,有 10 密耳减去 1 个零),而是我们只反向传播它们中的一些(比如 14 个)
分级 Softmax: 计算 10mill 的 vocab 的 Softmax 非常耗时且计算量大。分级 Softmax 提出了一种使用霍夫曼树的更快的计算方法
为了保持这篇文章的简洁,我避免了过多的探究。我绝对推荐多读点进去。
结束语:
- 单词向量超级酷
- 不要把这个 tensorflow 代码用于实际使用。只是为了理解。使用像 gensim 这样的库
我希望这能帮助人们更好地理解这些美景。如果是的话,让我知道!
如果我犯了错误,请告诉我。
我很乐意通过推特、 linkedin 或/和电子邮件与你联系。
再见!
学习新的数据科学语言
Source: Wikimedia Commons
在不断变化的数据科学工具生态系统中,您经常发现自己需要学习一门新语言,以便跟上最新的方法或更有效地与同事协作。我做 R 程序员已经有几年了,但是想要过渡到 Python 以便充分利用深度学习库和工具,比如 PySpark。此外,我加入了 Zynga 的数据科学团队,在那里 Python 是首选语言。虽然只有几个星期,但是我已经开始掌握使用这种新语言进行探索性数据分析和预测性建模的诀窍了。这不是我第一次尝试快速掌握一门新的数据科学语言,但却是最成功的一次。我想分享一些我作为一名数据科学家开始使用新编程语言的指导方针。
关注结果,而不是语义 虽然在深入研究编程之前了解编程语言的基础很有用,但我发现在掌握这门语言之前只需要简单介绍一下。在用 Python 编码之前,我从头开始通读了数据科学的第二章,它提供了 Python 的速成课程。我的下一步是整理出我希望能够用 Python 完成的任务列表,包括:
- 读取和写入 CSV 文件中的数据
- 对数据帧执行基本操作,例如显示数据类型
- 绘制直方图和折线图
- 连接到数据库并将数据拉入数据帧
- 创建逻辑回归模型
- 评估模型的度量,例如准确性和提升
我不再关注语言的语义,比如理解 Python 中列表和元组的区别,而是开始着手执行日常的数据科学任务。我知道如何在 R 中完成这些任务,并且需要学习如何将这些技能翻译成一种新的语言。比如我了解到 R 中的 summary() 类似于熊猫 dataframes 的 describe() 。
我很快发现,在 Python 中执行数据科学任务通常需要利用许多不同的库。当使用 Pandas 和 SciKit-Learn 库时,上述任务更容易执行。这引出了我在学习 Python 时学到的下一课。
学习生态系统,而不是语言 像 Tidyverse 这样的工具让 R 比基础语言多得多,其他数据科学语言也有类似的库,为语言提供了有用的扩展。我没有只专注于学习基础语言,而是将学习库作为我最初学习 Python 过程的一部分。作为起点,我探索了下面的库。
- 熊猫 :提供类似 r 的 dataframe 功能。
- 框架查询 : 支持在数据框架上使用 SQL。
- SciKit-Learn :为 ML 模型提供标准接口。
- Pandas-gbq:Python 的 BigQuery 接口。
使用这些库可以更容易地完成我已经熟悉的其他语言的任务。像 framequery 这样的库在学习一门新语言时很有用,因为在熟悉 Pandas 处理数据帧的方式之前,您可以使用 SQL 处理数据帧。我发现它很容易使用,因为它类似于我已经在 r 中使用的 sqldf 库。
使用跨语言库通过在一种新的语言中重新应用你已经知道的库来引导你的学习过程是很棒的。我在学习过程的早期探索的一个库是 Keras deep learning 库,我以前在 R 中使用过这个库。下面是用这些语言建立 Keras 模型的一个例子:
**# Creating a Keras model in R** model <- keras_model_sequential() %>%
layer_dense(units = 64, activation = "relu",
input_shape = 10) %>%
layer_dense(units = 64, activation = "relu") %>%
layer_dense(units = 1)**# Creating the same Keras model in Python** model = models.Sequential()
model.add(layers.Dense(64, activation='relu',
input_shape=10))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(1))
能够在我已经熟悉的库的基础上构建有助于加快我的学习过程。 Plotly 是另一个我以前在 R 中使用过的库,现在我正在 Python 中使用它。
使用真实世界的数据 样本数据集,例如 sklearn 中的糖尿病数据集,对于开始使用一门新语言或一个新的库来说是非常棒的,但是直到你需要将这些方法应用于一个新问题时,你才会真正了解你正在做的事情的细节。例如,您可能需要执行多类分类,而不是从示例数据集中重新应用二元分类。在学习一门新语言时,尽早应用来自组织的数据集是很有帮助的。
使用真实世界的数据非常有用,原因有很多,例如:
- **规模:**训练数据集通常很小,使用真实世界的数据往往涉及大得多的数据集,需要采样等方法。
- **表示:**使用来自您组织的数据意味着您需要定义如何为建模标记实例和编码特征。
- **管理:**您需要将数据放入内存并执行数据管理任务,比如处理缺失值。
- **评估:**使用来自您组织的现有数据集意味着您可以将新语言中的结果与其他实现中的先前结果进行比较,例如将 R 的 glm 与 sklearn.linear_model 进行比较。
这条指导方针与第一条相似,它有助于获得真实世界的数据并专注于产生结果。
如果可能的话,从本地开始 学习分布式计算语言时,比如 Spark 的 scala,让一个环境启动并运行起来通常是很困难的。我的建议是,如果没有必要,不要把分布式系统或虚拟化作为出发点。例如,使用 Spark,您可以在本地设置一个机器实例,并使用类似于 Zeppelin 的工具来提供一个交互式前端。当你学习一门新语言时,你不应该担心部署问题,比如将代码推送到服务器。在本地运行代码通常意味着有更多的调试工具可用。
对于 Python 入门,我也有同样的建议。Jupyter 笔记本使使用 Python 变得容易,但是一旦你熟悉了这种语言,它会更有用。首先,我推荐使用 IDE,比如 PyCharm 。一旦你准备好开始与队友合作,像 JupyterLab 这样的工具会提供很好的环境。
早动手 学习一门新语言的时候,不去尝试,你不会知道自己不懂的东西。这与我的第一条指导方针有关,它有助于及早发现你在知识方面的差距。在学习 Python 时,我发现将阅读和编程结合起来会更有效,而不是将一堆阅读内容放在前面。一旦我进入代码,我就确定了我需要学习的新领域,比如发现 Pandas 是我需要使用的第一个库之一。
在学习一门新的语言或库时,有几种不同的方法可以让你亲自动手。我和 R 在读深度学习的时候,用提供的笔记本开始做样题。此外,一些公司提供 Jupyter 笔记本电脑,新员工可以在入职过程中使用。我通常采用的方法是使用现有的数据集,同时用一种新语言重新实现一个模型。
推动您的知识
一旦您学习了该语言的一些基础知识,并了解了如何执行常见的数据科学任务,尝试该语言的一些新功能将非常有用。例如,当我第一次学习 R 时,我编写了使用函数作为参数和在数据帧上使用 apply 的基本示例。我对函数式编程比较陌生,这些例子帮助我了解了新的编程习惯。应用是一个非常有用的功能,我现在可以在熊猫上利用它了。
这一点我会留到以后的学习过程中去做,因为这对于掌握一门新的语言来说并不是必需的,但对于掌握一门语言来说却是必不可少的。如果你已经熟悉了几种编程语言,这是你可以更早开始的一步。
寻求反馈 拥有反馈渠道非常好,无论是您组织中的另一位数据科学家还是在线论坛。你可能会学到你还没有发现的新的库或语言特性。当你在一家小公司或从事独立项目时,这更具挑战性。在我的上一份工作中,当我在一家初创公司担任唯一的数据科学家时,我发现 rstats subreddit 是一个征求反馈的有用地方。也有不同语言的编程社区,这是学习的好地方。我参加了用户!2016 年的会议,这对于使用 R. Python 与科学家和实践者交流来说是一件非常棒的事情。Python 也有类似的会议,但我还没有参加过。
结论 学习一门新的数据科学语言需要时间,但它有助于你制定一个如何利用时间的计划。我的总体建议是深入研究,用真实数据对应用问题进行编码。你会发现自己的差距,并以此来指导你的学习过程。
本·韦伯是 Zynga 的首席数据科学家。我们正在招聘!
学习学习的算法
当我第一次听说元学习时,它的前提让我很兴奋:建造不仅能够学习,而且能够学习如何学习的机器。元学习的梦想愿望是算法能够根据性能信号修改其架构和参数空间的基本方面,算法能够在面对新环境时利用积累的经验。简而言之:当未来学家为我们编织普遍胜任的人工智能的梦想时,符合这一描述的组件是这些愿景不可或缺的组成部分。
这篇博文的目标是从这些崇高的高度,从我们想象的一些抽象的自我修改代理可以做的事情,下降到该领域今天的实际位置:它的成功,它的局限性,以及我们离强大的多任务智能还有多远。
为什么人类可以做我们做的事情?
具体来说:在许多强化学习任务中,相对于人类需要的时间,算法需要惊人长的时间来学习任务;目前玩 Atari 游戏的技术水平需要大约 83 小时(或 1800 万帧)的游戏时间才能达到人类的中等表现,大多数人在接触游戏几个小时后就可以达到这一水平。
A figure from the recent Rainbow RL paper
这种差异导致机器学习研究人员将问题框定为:人脑为这样的任务带来了什么工具和能力,以及我们如何以统计和信息论的方式构想这些工具?具体来说,元学习研究者似乎有两种主要策略,大致对应于关于这些工具是什么的两种理论。
- 习得先验:在这个镜头中,人类可以快速学习新任务,因为我们可以重复使用我们在过去任务中已经学到的信息,比如物体如何在空间中移动的直观物理学,或者在视频游戏中失去一条生命会导致奖励降低的元知识。
- **习得策略:**这是一种想法,即在我们的一生中(也许是在进化的时间尺度上),我们不仅收集关于世界的客体层面的知识,而且还发展了一种神经结构,这种结构在接受输入并将其转化为输出或策略方面更有效,即使在非常新颖的环境中也是如此。
现在,很明显,这两个想法并不相互排斥,它们之间甚至没有硬性的界限:我们与世界互动的一些硬编码策略可能基于关于世界的深刻先验,比如这样的事实(至少对于与这篇博客帖子相关的所有目的而言)世界有一个因果结构。也就是说,我发现这些想法非常不同,值得将它们分为这两个标签,并将其视为相关轴的两极。
不要放弃我的(一次)机会
在深入元学习之前,了解一下一次性学习相关领域的概念基础是很有用的。其中元学习的问题是“我如何才能建立一个快速学习新任务的模型”,而单镜头学习的问题是“我如何才能在只看到一个类的例子后,建立一个可以学习如何对该类进行分类的模型”。
让我们在概念层面上思考一下,是什么让一次性学习变得困难。如果我们试图只在相关类的一个例子上训练一个普通模型,它几乎肯定会过拟合。如果一个模型只看到一幅画,比如说,字母 3,它不会理解一幅图像可以经历什么样的像素变化而仍然保持本质上的 3。例如,如果模型只显示了这个序列中的前 3 个,那么如何先验地知道第二个 3 是同一物种的一个例子呢?理论上,我们对网络学习感兴趣的类别标签与构成字母的线条粗细有关系,这难道不是可能的吗?这对我们来说似乎很傻,但只有一个三的例子,对网络来说这不是一个微不足道的推论。
拥有更多 3s 的例子有助于解决这个问题,因为我们可以了解图像的哪些特征定义了它的基本三维性——两个凸起形状的存在,主要是垂直方向——以及哪些类型的修改是不相关的——线条的粗细,角度的锐度。为了一次性学习成功,我们必须激励网络学习什么样的属性通常将一个数字与另一个数字区分开,而不需要每个数字的具体允许变化的例子。
一次学习中的常见技术是学习嵌入空间,其中计算该空间中两个示例的表示之间的欧几里德相似性是计算这两个示例是否属于同一类的良好代理。直觉上,这需要学习在这个分布(在我的例子中:数字上的分布)中,阶级分化最强的内部维度**,并学习如何压缩和转换输入到那些最相关的维度。**
我发现把这个问题记在心里是一个有用的基础,尽管不是试图学习如何总结存在于类分布中的共享信息和模式,而是试图学习存在于任务分布中的规律,每个任务都有其自己的内部结构或目标。
如果要求构建一个神经网络元参数的排序,从最少到最抽象,它会是这样的:
- 通过使用超参数化梯度下降,网络学习表示在任务的全部分布中是有用的。 MAML 和爬虫就是很好的直接例子,而共享层次的元学习是一种有趣的方法,它将表示学习为主策略控制的显式子策略。
- 网络学习优化其自身梯度下降操作的参数。这些参数是:学习率、动量和自适应学习率算法的权重。在这里,我们开始走上修改学习算法本身的轨道,但以有限的、参数化的方式。这就是学习通过梯度下降学习通过梯度下降所做的。是的,那是报纸的真正标题。
- 学习内环优化器的网络本身就是网络。也就是说:其中梯度下降用于更新神经优化器网络参数,使得它们跨任务表现良好,但是其中在每个单个任务内从输入数据到输出预测的映射完全由网络进行,而没有任何损失或梯度的显式计算。这就是一个简单的神经注意力元学习者的工作方式。
为了让这篇文章不那么庞大,我将主要关注 1 和 3,来说明这个范围的两个概念。
任何其他名字的任务
另一个简短的旁白——我保证是最后一个——我希望它能澄清一个可能会令人困惑的话题。通常,在元学习的讨论中,你会看到提到“任务分配”的想法。你可能会注意到这是一个很难定义的概念,你是对的。当一个问题是一个任务,或者分布在多个任务上时,似乎没有一个明确的标准。例如:我们是否应该将 ImageNet 视为一项任务——物体识别——或多项任务——区分狗是一项任务,区分猫是另一项任务?为什么玩一个雅达利游戏是一个任务,而不是几个任务组成的游戏的个人水平?
我能从所有这些中提取的是:
- “任务”的概念与已经建立的数据集是非常复杂的,因为把在一个数据集上学习看作一个单一的任务是很自然的
- 对于任何给定的任务分布,这些任务彼此之间的差异可能非常显著(即,每个任务学习不同振幅的正弦波,而每个任务玩不同的 Atari 游戏)
- 因此,不要马上说“啊,这个方法可以推广到,所以这是一个很好的指标,它通常可以在一些任意不同的任务分布上表现良好”。这当然不是该方法有效的方向上的坏的 T2 证据,但是它确实需要批判性的思考来考虑网络为了在所有任务中表现良好实际上需要展示多大的灵活性。
那些莫名其妙地以动物命名的
2017 年初,切尔西·芬恩和一个来自伯克利的团队发布了一项名为 MAML 的技术:模型不可知元学习。
In case you didn’t think the joke was intentional, turn to the “Species of MAML” section of the paper
在习得策略和习得先验之间,这种方法倾向于后者。该网络的目标是训练一个模型,如果给定一个新任务的梯度步长更新,该模型可以很好地概括该任务。这个的伪代码算法是这样的
- 随机初始化网络的参数θ
- 从任务 t 的分布中选择某个任务 t。使用训练集中的 k 个示例(通常为 10 个),在当前参数集指示的位置执行一个梯度步骤,从而得到最终的参数集。
- 在测试数据集上评估这些最终参数的性能
- 然后,根据您的初始参数 theta 计算 task-t 测试集性能的梯度。然后基于该梯度更新那些参数。回到第一步,用你刚刚更新的θ作为这一步的初始θ
这是在做什么?在一个非常抽象的层面上,它是在参数空间中找到一个点,这个点在期望中最接近于它的分布中的许多任务的一个好的概括点。您可以认为这是在迫使模型在探索参数空间时保持某种程度的不确定性和谨慎。简而言之:当一个网络认为其梯度完全代表人口分布时,它可能会陷入一个损失特别低的区域,MAML 将更有动力在多个山谷的顶点附近找到一个区域,每个山谷在预期的所有任务中都包含合理的低损失。正是这种对谨慎的激励,帮助 MAML 避免了在新任务中只给出少量例子时,模型可能会出现的过度拟合。
2018 年初,该文献又增加了一个新版本,名为《爬行动物》。正如你可能从它的名字中猜到的那样——早期 MAML 的一个游戏——爬行动物从 MAML 的前提开始,但找到了一种计算它的初始参数更新循环的方法,这种方法在计算上更有效。在 MAML 显式地采用测试集损失相对于初始参数θ的梯度的情况下,爬虫代之以在每个任务上执行 SGD 更新的几个步骤,然后使用更新结束时的权重和初始权重之间的差,作为用于更新初始权重的“梯度”。
g1 here represents the gradient update you get from only performing one gradient descent step per task
从直觉上来说,这种做法有点奇怪,因为,天真地说,这似乎和把所有任务混合在一起进行训练没有什么不同。然而,作者认为,由于对每个任务采取 SGD 的多个步骤,每个任务的损失函数的二阶导数受到影响。为此,他们将其更新分解为两部分:
- 一个将结果推向“联合训练损失”的术语,即如果你只是在混合任务上训练,你会得到的结果,以及
- 将初始化推向一个点的术语,在该点上,后续 SGD 小批次的梯度彼此接近:即,小批次之间的梯度方差较低。作者推测,这一术语导致快速学习时间,因为它鼓励在每项任务中处于更稳定和低方差的训练区域。
我选择 MAML/爬行动物组作为事物“习得先验”的代表,因为理论上,网络通过学习内部表征而成功,这些表征要么对任务的全部分布进行分类有用,要么在参数空间中接近广泛有用的表征。
为了阐明这一点,看一下上图。它将 MAML 与一个刚刚经过预训练的网络进行了比较,当时两者都经过了一系列由不同相位和振幅的正弦波组成的回归任务的训练。在这一点上,两者都“微调”到一个新的特定任务:红色显示的曲线。紫色三角形表示少量渐变步骤中使用的数据点。例如,与训练前的网络相比,MAML 了解到正弦波具有周期性结构:在 K=5 时,它能够更快地将左手峰移动到正确的位置,而无需实际观察该空间区域的数据。虽然很难说我们的(有些生硬的)解释是否是引擎盖下发生的事情的完美机械匹配,但我们可以推断,MAML 在弄清楚正弦波彼此不同的两个相关方面——相位和振幅——以及如何从给定的数据中学习这些表示方面做得更好。
一路向下的网络
对于一些人来说,甚至使用已知的算法如梯度下降来学习全局先验的想法。谁说我们设计的学习算法是最有效的?我们能不能学一个更好的?
这是 RL(快速强化学习通过慢速强化学习)采用的方法。这个模型的基本结构是一个递归神经网络(技术上:一个 LTSM 网络)。由于 rnn 具有存储状态信息的能力,并根据该状态给出不同的输出,因此理论上它们有可能学习任意的可计算算法:换句话说,它们有可能是图灵完全的。以此为基础,RL 的作者构建了一个 RNN,使得 RNN 接受训练的每个“序列”实际上是一系列给定 MDP 的经历(MDP =马尔可夫决策过程)。对于这个解释,你可以把每个 MDP 看作是定义了一组可能的行动和这些行动在环境中产生的潜在回报。然后,在许多序列上训练 RNNs 如 RNN 通常所做的那样,在这种情况下,这些序列对应于许多不同的 MDP,并且优化 RNN 的参数,以在所有序列/试验的总和上产生低遗憾。后悔是一个衡量你在一系列事件中的总回报的指标,所以除了激励网络在试验结束时达到一个好的政策,它还激励更快的学习,这样你在坏的低回报政策下采取的探索行动就更少了。
A diagram showing the internal workings of the RNN over multiple trials, corresponding to multiple different MDPs.
在试验中的每一点,网络采取的动作是由在多个任务中学习的权重矩阵和隐藏状态的内容参数化的函数,隐藏状态的内容作为数据的函数更新,并充当一种动态参数集。因此,RNN 正在多个任务中学习如何更新其隐藏状态的权重,以及控制如何利用它的权重。然后,在一个给定的任务中,隐藏状态可以捕获关于网络有多确定的信息,是否是时候探索或利用,等等,作为它在特定任务中看到的数据的函数。从这个意义上来说,RNN 正在学习一种算法,这种算法可以确定如何最好地探索空间,并更新其最佳策略的概念,并且学习这种算法,以便在任务分配上表现良好。作者将 RL 架构与对他们尝试的任务来说渐近最优的算法进行了比较,RL 的表现相当。
我们能放大这个吗?
这只是一个非常压缩的领域介绍,我敢肯定,有我错过的想法,或我说错了概念。如果你想要一个额外的(更好的)视角,我强烈推荐切尔西·芬恩的这篇博客文章,它是 MAML 论文的第一作者。
我花了几个星期的时间,试图从概念上压缩这些论文的分布,并产生适用于所有论文的广泛理解,这给我留下了一系列一般性的问题:
- **这些方法在高度多样化的任务中表现如何?**这些论文中的大部分都针对具有相对较低多样性水平的任务分布进行了概念验证测试:具有不同参数的正弦曲线、具有不同参数的多股武装匪徒、来自不同语言的字符识别。对我来说,在这些任务分配上表现良好并不一定能概括为,例如,不同复杂程度和不同形式的任务,比如图像识别结合问题回答结合逻辑谜题。
然而,人类大脑确实从这些高度多样化的任务集中形成了它的先验,在它们之间来回传递关于世界的信息。我在这里的主要问题是:只要你投入更多的单元和计算,这些方法会像宣传的那样对这些更多样化的任务起作用吗?或者在任务多样性曲线的某个点上是否存在非线性效应,使得在这些低多样性下有效的方法在高多样性下根本无效。 - **这些方法对大量计算的依赖程度如何?**这些论文大部分是在小而简单的数据集上运行的部分原因是,当你的每一次训练运行都涉及(有效地)将模型训练到关于元参数功效的数据点的内部循环时,测试在时间和计算上可能非常昂贵。鉴于摩尔定律最近似乎在放缓,谷歌以外的任何地方有多大可能研究这些算法的有用规模版本,其中一个困难问题的每个内循环迭代可能需要数百小时的 GPU 时间?
- 这些方法与寻找明确编码世界先验的方法相比如何?语言是人类武器库中一个非常有价值的工具。用机器学习的术语来说,这基本上是高度压缩的信息,嵌入在我们知道如何从概念上操纵的空间中,我们可以从一个人转移到另一个人。没有人能够独自从经验中提炼出所有的知识,所以我怀疑我们是否能够真正解决整合世界知识的模型问题,除非我们找到了学习算法的类似方法。
了解数据科学—构建图像分类器(第 2 部分)
注意:这篇文章也可以叫做“我在哪里做得很快。ai 的第三课并意识到有很多东西我可以添加到我的模型中”。
第一部分,我建立了一个 VGG 模型:
嗨!我叫 Gabi,正在学习数据科学。我计划写下我正在学习的东西,以帮助我组织…
medium.com](https://medium.com/@gabrieltseng/learning-about-data-science-building-an-image-classifier-3f8252952329)
我在这篇文章中创建的模型概要:
I trained the ResNet and Inception V3 models, and used their outputs (and the output of the VGG model trained in part 1) as inputs for a classifier, which I then used to combine the three models and output my final predictions.
内容:
- 我对自己创建的第一个 VGG 网络实施了丢弃和批量归一化,但这最终对网络的改进作用不大
- 训练 ResNet50 和 Inception V3 我使用 Keras 的应用程序来创建和加载预训练的 ResNet 50 和 Inception V3 网络,并对它们进行微调。
- 优化 sklearn 算法 把 3 个模型结合起来,我用的是 sklearn。
我注意到我的验证准确性高于我的训练准确性:
A comparison of the training and validation accuracies for the first 5 epochs when training my model. This is the final model I trained, including the data augmentation and training all the way to the first convolutional layer.
这表明拟合不足,即模型过于受限,无法正确拟合训练数据。这可能是因为在完全连接的块中添加了一个层:
From part 1: A fully connected block, adding a fully connected layer followed by a dropout layer
丢弃层通过将一半的权重随机设置为 0 来防止过度拟合(在训练时,这不会发生在验证集上)。一种矫正方法是去除脱落层。
- 去除漏层和批量标准化
定义和动机:
Dropout 随机将某个权重比率设置为 0。这样做的后果是,它会导致欠拟合,因为我的模型正在训练的一半重量被忽略了。
批处理规范化类似于我在第 1 部分中添加的均值预处理层,除了批处理规范化不只是在开始时规范化数据集,而是在层之间进行。这很有用,因为在模型中,值可能会变得非常大,从而扭曲权重。批量标准化可以避免这种情况,从而防止过度拟合。
我编写了以下方法来实现丢弃和批处理规范化:
vgg_bn returns a VGG model with a defined dropout, and (optionally) with batch normalization. Lines 24–26 adjust the weights, multiplying them by 0.5/(1-dropout). Adding batch normalization doesn’t increase the number of layers in the model, so the weights can be directly copied over by enumerating over both models’ layers using zip()
我使用它能得到的最好结果是批量标准化和 0.5 的丢弃率(事实证明,删除丢弃率会导致显著的过度拟合,并显著降低验证的准确性)。
这就是说,改进是微不足道的(99%对 98.5%的验证准确率只代表多了一张正确分类的图像),并且没有反映在 Kaggle 中,表明该模型没有显著改进。
The best result so far!
幸运的是,我还有一招!
2。与 ResNet 组装
如果我训练不同的模型,它们可能会拾取稍微不同的东西,因此在它们之间,可能所有的图像都被正确分类。因此,我可以训练多个模型,然后组合它们的输出;这被称为集合。
具体来说,我要训练 ResNet50,它在 2015 年赢得了 ImageNet 比赛(VGG 在 2014 年获胜)。
2.a.i .实施 ResNet
ResNet-50 在映像网 2015 年的比赛中胜出(VGG 在 2014 年的比赛中胜出)。我的希望是,这两个模型中的差异将允许它们相互补偿,并最终产生更强的结果。
ResNet50 是一个 res idual net 的作品,它远比卷积网络(107 层到 VGG 的 38 层)更深入。形象化 ResNet 的一个好方法是卷积块的集合(回想一下,集合是不同模型的输出组合)。
Keras 实际上已经预装了一些模型(包括重量),作为其应用的一部分;我想构建自己的 ResNet 模型,这样我就可以包含一个预处理层(使用 fast.ai weights ),但我也将从这里开始绘制。
The definition for the Lambda layer is a little different than for the VGG model, see why here.
2.a.ii 培训结果网
为了训练 ResNet,我使用了与 VGG 相同的图像数据生成器。我分两个阶段训练 ResNet,首先训练(随机初始化的)最后一层,然后也训练最后一个卷积块。
The vertical red line marks where I allow more layers to be trained.
在学习率下降的情况下,对更多的时期(另外 40 个时期)训练 ResNet,将验证准确率提高到 97%。
2.b .盗梦空间 V3
在尝试让 ResNet 模型工作了很长时间之后,我意识到 lambda 层对它的性能没有什么帮助。因此,我添加了 Inception V3 模型,它也是由 Keras 预加载的,并根据 ImageNet 数据进行了训练:
然后,我将这个 V3 模型与数据进行拟合:
A plot of accuracy against epochs when training the Inception V3 model. The red line marks where I allowed more layers to be trained.
2.b .组合模型
我打算使用 Keras 合并层来合并这两个模型。然而,有一个问题:VGG 模型是顺序模型,而 ResNet 和 V3 模型是功能模型。我在 ResNet 和 VGG 中引入的 lambda 层可以自动预处理图像,这意味着 Keras 不想将模型从顺序模型更改为功能模型(否则,它应该能够),所以我要做的是使用 Scikit-learn 来组合模型。
为此,我将使用 VGG 和雷斯内特的输出作为我的线性回归的输入:
This method takes the outputs of the models in the input array ‘model’, and turns them into X data- features- with which a scikit learn linear regression can be trained. The classes of the batches — the true Y values — is also returned. The shape of X is printed to make sure everything is going okay; the number of rows should increase by 1 for every model trained, and the number of columns should stay constant.
我使用 make_xy 制作了以下几对特征:
X_train 和 Y_train,基于训练数据。
基于验证数据的 X_valid 和 Y_valid。
然后,我用 k 近邻、支持向量机和逻辑回归算法进行了实验。
具有 10 个邻居的 k 个最近邻居产生最高的 AUC-ROC 得分(0.9866)。
The AUC-ROC of the knn algorithm when ensembled from the X and Y data.
结论,我学到了什么
我从集合中最大的收获是,许多差的模型不能弥补一个强的模型;当 ResNet 和 V3 达到大约 90%的准确率时,我开始尝试组合这些模型,这降低了我的最终分数,而不是提高了它。
我的第二个观点是模特需要很长的训练时间!我在训练我的 VGG 模型时使用了提前停止回调,但没有在 Inception V3 的 ResNet 中使用它,因为这些模型并不总是具有不断增加的准确性,提前停止通常会缩短它们的训练。不使用早期停止意味着我没有为大约 20 个时期训练我的模型,而是最终为接近 50 或 60 个时期训练它们。
我也有过快降低学习速度的倾向,并假设模型已经收敛;实验(和耐心)让我大大提高了模型的能力。
最后,我认识到这个过程可能会令人沮丧!我花了很多时间在最终没有改善我的最终结果的路径上,但是最终得到一个更强的模型是非常棒的。
在现实世界中学习和表演
又名强化学习
为了与复杂的环境互动,生物实体需要产生一个“正确”的动作序列,以实现延迟的未来回报。这些生命实体,或者说角色,能够感知环境,并对环境和代理先前历史的一系列状态做出反应。见图:
The reinforcement learning loop: an agent performs actions in an environment, and gets rewards. Learning has to occur through sparse rewards
我们想要学习执行一些任务的场景是:
- 环境中的演员
- 演员有他/她想要实现的目标
- 循环:演员感知自身和环境
- 循环:演员动作影响自身和环境
- 延迟奖励或惩罚——我们达到目标了吗?
- 学习:如何最大化奖励和最小化惩罚
这些动作促使演员更接近多个目标,即使实现一个目标也可能需要不确定的步骤。代理需要学习模型状态和行动的正确顺序,通过与世界互动和执行在线学习来获得延迟的奖励。
人工智能(AI)的最新工作使用强化学习算法,利用稀疏和零星的奖励对代理的行为进行建模。该领域的最新进展允许训练智能体在复杂的人工环境中执行,甚至在相同的背景下超越人类的能力[Mnih2013,-2016]。该领域一些令人兴奋的最新成果是在执行强化学习任务的同时学习语言——见这里。
形式主义
为了学习在现实世界中表演,给定一系列状态 s 我们想要获得一系列动作 a,以最大化奖励或获胜概率。
基本需求(假设我们不知道游戏规则,只知道我们可以执行的一系列可能的操作):
- 需要能够评估一个状态——我们是赢了,还是输了,离奖励有多远?
- 需要能够从状态、行动 {s,a} 对预测结果——假设我们决定采取行动 a ,我们期望什么回报或结果?玩心理游戏
- 需要记住一系列可以延续很长时间的配对
在棋盘游戏中,如国际象棋、围棋等,整个游戏是完全可观察的,而在现实世界中,如自动驾驶机器人汽车,只有一部分环境是可见的,这两种游戏有很大的不同。
完全可观测的
AlphaZero 的论文展示了一种完全可观察游戏的优雅方法。
alpha zero😗***
One (AZ1): a 函数 f (神经网络)基于参数θ输出动作概率 a 和动作值 v (对出招值的预测)。**
a,v = f(θ,s)
二(AZ2): 一个预测模型,可以评估不同走法和自我玩法(脑子里的玩法场景)的结果。AlphaZero 中的蒙特卡罗树搜索。这些游戏结果、移动概率 a 和值 v 然后被用于更新函数 f 。
这两种成分为学习玩这些桌上游戏提供了一种非常简单和优雅的方式,并且在相对较短的时间内在没有人类支持的情况下学习。
部分可观察的
但是非完全可观察博弈呢?比如第一人称射击游戏(Doom)?还是学习驾驶的自动驾驶汽车?
只有环境的一部分是可见的,因此预测行动的结果和下一个状态是一个困难得多的问题。
有太多可能的选项需要搜索。搜索树太大了。我们可以使用 AZ2,但我们需要聪明和快速地评估哪些选项,因为有太多的选项,而我们只有有限的时间来决定下一步的行动!
可能没有其他玩家可以与之竞争。这里的代理人必须与自己竞争,或者与自己的预测竞争,从其预见事件的能力或不预见事件的能力中获得中间报酬。
我们如何做到这一点?
建议
不幸的是,尽管近年来做出了努力,RL 算法仅在小的人工场景中工作,而没有扩展到更复杂或真实生活的环境中。原因是,目前这些系统的大部分参数都是基于过于稀疏的奖励,用耗时的强化学习来训练的。在现实世界的场景中,模型变得非常大(有许多参数),几乎不可能在短时间内进行训练。
我们需要的是能够训练一个模型大脑熟悉环境,并且能够预测行动和事件组合。有了这个预先训练好的模型大脑,用于实现特定目标的强化学习分类器,以及一小组可训练的参数,使用稀疏奖励来训练要容易得多。我们需要一个预先训练好的神经网络,至少可以处理视觉输入。它需要在视频序列上进行训练,并且需要能够提供输入的未来表示的预测。
为了超越这些限制,我们正在研究一种具有以下特征的合成模型大脑:
- 活跃在环境中,能够感知和行动
- 感觉序列、动作和奖励的记忆
- 注意:只关注重要的数据
- 预测:预测演员和环境的未来状态,这样我们就可以进行在线学习——通过预测,我们可以了解我们知道或不知道什么,并在我们需要学习时有一个“惊喜”信号(见这个和这个)。预测也不需要监督信号,因为它们可以针对实际的未来事件及其结果进行误差测试。
关键是能够预测结果——函数 f (AZ1)需要有预测能力,换句话说,它需要有预见未来的能力。
A proposal for a neural network that can understand the world and act in it. This network uses video and can predict future representation based on a combination of the state s and its own associative memory. A multi-head attention can recall memories and combine them with state s to predict the best action to take.
在左边,你可以看到一个可以理解世界并在其中行动的神经网络的提案。该网络使用视频,并且可以基于状态 s 和它自己的关联存储器的组合来预测未来的表示。多头注意力可以回忆起记忆,并将它们与状态结合起来,以预测要采取的最佳行动。
该模型能够在多种条件下运行良好:
- 持续学习:我们需要这个模型能够同时学习多项任务,并且学习时不会忘记,所以旧的任务不会被忘记
- 一次性学习和迁移学习:我们需要这个模型能够从真实的和合成的例子中学习
- 虚拟重放:该模型是可预测的,即使在事件被目睹后也能预测不同的结果。它可以在脑海中播放和回放可能的动作,并选择最好的一个。关联存储器充当搜索树。
我们如何训练这个合成大脑?
- 大部分是无人监督的,或者自我监督的
- 这里或那里几乎没有监督,但不能保证什么时候
但是设计和训练预测神经网络是人工智能领域当前的挑战。
我们在过去争论过一种的新型神经网络,它已经被证明在学习 RL 任务中更加有效。
我们还喜欢胶囊的预测能力,它不需要地面实况表示,但能够根据前一层预测下一层输出。
注 1: 这是一篇[的好文章](http://A great summary of DL work and success or lack thereof https://www.alexirpan.com/2018/02/14/rl-hard.html),讲述了为什么 RL 工作得好或者不好,以及与之相关的问题。我同意现在很多学习效率很低,也没有迁移学习的成功案例。这就是为什么我们应该推行预训练网络、课程学习和将任务分解成许多更简单的任务,每个任务都有简单的奖励的方法。如果你的搜索空间如此之大,一个解决所有问题的方法是很难的!
关于作者
我在硬件和软件方面都有将近 20 年的神经网络经验(一个罕见的组合)。在这里看关于我:传媒、网页、学者、 LinkedIn 等等…
从图像中学习艺术风格
Neural Style Transfer with my face and various other styles.
目前,科技领域正在进行一场军备竞赛,深度学习和人工智能已经成为下一个行业级术语。每个人都希望通过人工智能的成功和创新应用,取得下一个巨大的商业成功。
一个这样的突破是使用深度学习神经网络从数学上分离图像的内容和风格。很自然地,我们会想到将一幅图像的内容和另一幅图像的风格融合到一幅图像中。这个想法在 2015 年被 Gatys 成功实施。等人在他们的论文“一种艺术风格的神经算法”。
从那以后,对基本思想有了许多见解和改进。该算法的现代迭代现在被称为神经风格转移,自 2015 年问世以来取得了很大进展。你可以在本文这里阅读更多关于改进的内容。
神经风格转移?
Image A provides the content. Image B is the final result, taking the semantic contents from Image A and the style from the smaller image.
转换风格背后的核心思想是拍摄两幅图像,比如一张人的照片和一幅画,并合成一幅同时匹配照片的语义内容 表示和相应艺术作品的风格表示的图像
如上所述——我们将对图像的风格和内容表示进行数学量化。使用一个空白的或者随机生成的图像(也称为模仿品),我们逐步地将它与期望的样式和内容表示相匹配。
深度学习脱颖而出!
那么深度学习到底是如何在这里发挥作用的呢?能够将风格和内容从图像中分离出来是怎么回事——这是人们有时很难辨别的。
显然,用于深度学习的神经网络真的很擅长提出图像特征的高级和低级表示。为了形象化,让我们看看 CNN 的图片。
Style and content representations taken at each layer of a Neural Network.
研究网络每一层的要素表示,您会发现每一层都会逐渐产生一个抽象概念,即样式(顶部一行图像)和语义内容(底部一行图像)。
在卷积神经网络(CNN)中,每一层都用来进一步抽象我们提供给它的图像的像素表示。最初,我们将向 CNN 提供一幅图像。但是 CNN 并不像我们人类那样将这张图像视为图像,而是将图像视为值的矩阵(更准确地说是张量)。
A matrix representation of the image of the letter a. Pixel values closer to 1 correspond to colors closer to black white values closer to 0 correspond to a lighter hue. Image taken from http://pippin.gimp.org/image_processing/chap_dir.html
在每一层,将内核应用于图像的一个小块,在整个图像上移动,最终生成图像的中间矢量表示。虽然这些生成的向量可能实际上没有任何意义,但它们允许我们捕捉图像的特征。你可以看看这篇文章,深入了解 CNN 是如何做到这一点的。
自然,这些图像绝不是风格和内容的确切定义,但这是神经网络如何感知它的。
定义内容和风格
现在我们已经大致了解了图像的抽象特征在哪里(图层之间的矢量表示)。但是我们如何让他们离开 CNN 呢?
这就是我们前面提到的论文的关键贡献发挥作用的地方。
该论文的工作基于一个流行且强大的架构(当时),被称为 VGG ,它赢得了 2014 年 ImageNet 分类挑战赛。
Layers of the VGG19 neural network, taken from https://ethereon.github.io/netscope/#/gist/3785162f95cd2d5fee77
在这种架构中,特别是在“conv4_2”层的中间向量最好地代表了图像的语义内容。而 style 最好由以下层的特征组合来表示:“conv1_1”、“conv2_1”、“conv3_1”、“conv4_1”和“conv5_1”。你会问,我们是如何选择特定的层的?真的只是试错。
Image taken from A Neural Algorithm for Artistic Style. The image structures captured by the style representations increase in size and complexity when including style features from higher layers of the network. This can be explained by the increasing receptive field sizes and feature complexity along the network’s processing hierarchy.
这个想法是,你在神经网络中的位置越低(越接近对物体进行分类),特征向量就越能代表图像的语义内容。而网络中的高层能够更好地捕捉图像的风格。
秘密成分
既然我们已经弄清楚了样式和内容表示,我们需要一种方法来迭代地匹配随机生成的白噪声图像(我们的仿作)和表示。
A randomly generated white noise image, which we will iteratively match to the style and content representations.
为了可视化在层级的不同层编码的图像信息,我们在白噪声图像上执行梯度下降,以找到与原始图像的特征响应相匹配的另一个图像。
因此,我们定义了内容表示和我们的仿作之间的平方误差损失:
The content loss
其中向量 P 是原始图像,向量 x 是生成的图像,并且 P_l 和 F_l 在层 l 中它们各自的特征表示。
由此可以使用标准误差反向传播来计算生成图像的梯度 w.r.t.。因此,我们可以逐步更新随机白噪声图像,直到它在“conv4_2”层中给出与我们想要从中获取语义内容的图像相同的响应。
输入克矩阵
然而,风格并不直截了当。我们首先构建一个样式表示
,它计算不同滤波器响应之间的相关性。这是通过克矩阵完成的:
其中 G^l_ij 是层 l 中矢量化特征 i 和 j 的内积。关于为什么 Gram 矩阵可以捕捉样式信息的更多信息可以在本文中找到。
通过最小化来自原始图像的 Gram 矩阵和要生成的图像的 Gram 矩阵的条目之间的均方距离,我们可以使用来自白噪声图像的梯度下降来找到与原始图像的风格表示相匹配的另一个图像。
风格的损失定义为:
其中 E_l 为:
G 和 A 分别是 l 层中原始图像和生成图像的风格的 Gram 矩阵表示。然后,可以使用类似于上述内容表示的标准误差反向传播来计算梯度。
把它们放在一起
现在,我们已经拥有了生成图像所需的所有要素,给定了一个我们想要从中学习风格的图像和一个我们想要从中学习内容的图像。
为了生成将照片的内容与绘画风格混合的图像,我们共同最小化白噪声图像与网络的一层中的照片的内容表示和 CNN 的多层中的绘画风格表示的距离。
我们最小化以下损失函数:
其中α和β是用于在新图像的构建期间确定总体贡献的权重。
如果呢?
尽管这篇文章是在 2018 年写的,但所描述的技术并不新,而且已经存在多年了。
事实上,已经对现有模型进行了大量的改进和修改,极大地提升了它的许多方面的性能,例如提高风格转换的速度、减少损失以生成更好的图像、制作艺术(这是由论文的原作者制作的)等等。
最让我感兴趣的是,我曾经认为抽象和不可量化的东西——即图像的抽象风格和内容——现在可以用数学来表示。自然,这使人想知道同样的事情是否可以用于其他抽象的品质。不仅仅是图像,而是所有形式的媒介,无论是视频还是文本。
如果情感、动机和情节等抽象概念也可以量化,那会怎样?那我们该拿这些做什么?我们生活在令人兴奋的时代,我迫不及待地想看看更多的技术和人工智能能给我们的文化带来什么。
*觉得这个有用?请随意击碎拍手,并检查我的其他作品。*😄
李中清是 培育的人工智能研究员。AI 。他最近从莫纳什大学计算机科学专业毕业,撰写了关于人工智能和深度学习的文章。在 Twitter 上关注他@jamsawamsa。
向杰弗里·辛顿学习反向传播
Image from this website
机器学习掌握的所有路径都要经过反向传播。
自从开始我的机器学习之旅以来,我最近第一次发现自己被难住了。我一直在稳步完成吴恩达广受欢迎的 ML 课程。
线性回归。检查。逻辑回归。检查。梯度下降。检查检查检查。
然后我们到了神经网络和用来训练它们的算法:反向传播。尽管安德鲁尽了最大努力,我还是不明白这项技术是如何工作的。尽管 Andrew 向我们保证这没什么,你可以在没有更深入理解的情况下使用神经网络,而且他自己也这样做了很多年,但我决心更好地理解这个概念。
为了做到这一点,我求助于大师 Geoffrey Hinton 和他合著的 1986 年《自然》论文,其中首次提出了反向传播(几乎 15000 次引用!).我会鼓励每个人读报纸。它很短,只有 4 页,在详细研究之后,我对反向传播有了更好的理解,现在我将试着传授给大家。
本质上,反向传播只是链式法则的巧妙应用。
链式法则是任何本科课程中教授的导数的一个基本性质。它指出,如果你有 3 个函数 f 、 g 和 h ,其中 f 是 g 的函数,而 g 是 h 的函数,那么 f 关于 h 的导数等于 f 关于的导数的乘积
The chain rule
现在让我们用这个来弄清楚反向传播是如何工作的。
假设我们正在使用下面的简单神经网络。这个网络有三层:蓝色的输入层,绿色的隐藏层和红色的输出层。前一层中的每个单元都连接到下一层中的每个单元。每个连接都有一个权重,每当一个单元向另一个单元发送信息时,该信息就会乘以该连接的权重。一个单元从上一层获得与其连接的所有输入的总和,对该总和执行逻辑函数,并将该值向前传递。
A 3–4–3 Neural Network
让我们想象我们被给予了 m 个训练的例子:
i-th input output pair
其中 x 和 y 为三维向量, i 为第 I 个训练样本。对于输入 x ,我们将我们的神经网络的预测/输出称为 g ,它也是一个三维向量,每个向量元素对应一个输出单元。因此,对于每个培训示例,我们都有:
Vector form of training Input, output and neural network prediction
给定输入 x ,我们希望找到导致 g 等于(或至少非常相似)y 的权重值。
为此,我们将使用一个误差函数,我们定义为:
Total error for the Neural Network above
为了计算总误差,我们采用训练集中的每个案例 i ,并针对红色输出层中的每个单元,计算该单元的预测和真实输出之间的平方误差。如果我们对每种情况都这样做,我们将得到总误差。
由于网络预测的值 g 依赖于网络的权重,我们可以看到误差会随着权重的变化而变化,我们希望找到使 E 最小的权重。
我们可以使用梯度下降来实现。但是梯度下降要求我们找到 E 相对于每个权重的导数。这就是反向传播让我们实现的目标。
我们现在将考虑单一情况,而不是 3 个输出单元,假设我们有任意数量的输出单元 n 。对于这种情况,我们的错误现在变成了:
Error for a single case
为了方便起见,我们去掉了下标,因为它是常数。
我们现在可以问自己,当一个输出单位的预测变化时,这个误差是如何变化的。这是简单的导数:
The derivative of the total error with respect to each output unit (Note that we used the chain rule to the derivative of the squared term.)
有趣的是,似乎随着一个输出单位的预测值的变化,误差将以等于预测值和真实值之间的“误差”或差异的速率变化。酷!
现在,我们可能会问自己,当这些输出单元的总输入发生变化时,误差会如何变化。同样,这只是一个导数。让我们用 z 来表示一个输出单元的总输入。那么我们有兴趣发现:
Derivative of E with respect to the total input for output unit j
但是由于 g 是 z 的函数,通过应用链式法则,我们可以将其重写为:
Applying the chain rule!!
还记得我们说过,每个单元在传递输入之前,都将逻辑函数应用于它的输入。这意味着 g 是一个逻辑函数, z 是它的输入,所以我们可以写:
Logistic function and it’s derivative
所以现在我们可以说:
The derivative of the total error with respect to the total input to an output unit
我们已经计算出,当一个输出单元的总输入发生变化时,误差是如何变化的。太神奇了!
我们现在可以找到误差对某些重量的导数。这就是我们梯度下降所需要的。
让我们将绿色单元的预测/输出称为g’,并且我们将绿色层中的单元 k 和红色/输出层中的单元 j 之间的连接的权重称为:
Weight of connection between unit k in green and unit j in red layers
考虑下面黄色输出单元的总输入 z 。我们可以通过计算每个绿色单元的输出,乘以连接绿色单元和黄色单元的红色箭头的重量,然后将它们相加来计算输入。
The red arrows represent the connections that are added up to get the total input of the yellow unit
而不是 4,如果我们有任意数量的绿色单元 n (这个 n 与我们之前定义的不同)我们就可以说:
Total input to output j
因此,我们似乎可以将 z 写成连接到它的重量的函数和连接到它的单元的输出的函数。
链式法则的时间到了。
我们自问,当连接到输出单元的砝码发生变化时,误差会如何变化。这可以写成:
The derivative of the total error with respect to weights connecting to an output unit
我们刚刚算出了误差相对于连接到输出层的权重的导数。这正是我们需要让梯度下降工作。
但是我们还没完。我们仍然需要计算出误差相对于连接第一层和第二层的权重的导数。
谢天谢地,链式法则也还没有完成。
我们计算当第 k 个绿色单元的输出变化时,误差如何变化:
The derivative of error with respect to the output of unit k in the green layer
由于有来自单位 k 的 j 重量,我们认为:
The derivative of total error with respect to the output of unit k in the green layer
如果你仔细想想,我们又回到了起点。我们得到了误差对某个单位输出的导数。我们现在可以完全忽略红色输出层,将绿色层视为网络的最后一层,并重复上述所有步骤来计算 E 相对于传入权重的导数。
Image from this website
你会注意到我们计算的一阶导数等于预测值和真实输出值之间的“误差”。同样,最后一个导数也有这个误差项,乘以一些其他的项。该算法被称为反向传播,因为这种误差的一种形式从最后一层反向传播到第一层,并用于计算 E 相对于网络中每个权重的导数。
一旦计算出这些导数,我们就可以在梯度下降中使用它们来最小化 E 并训练我们的神经网络。
希望这篇文章能让你更好地理解反向传播是如何工作的。如果你有任何问题,请告诉我,我会尽力回答你。谢谢!