人工智能的训练、验证和测试阶段——以一种你永远不会忘记的方式解释
跟着憨豆先生参加他的第一次微积分考试…
像往常一样,我们最喜欢的笨手笨脚的人忘了买指定的教科书,但是,幸运的是,他从黑板上草草写下了一个方程(现在是 90 年代),现在他有一个晚上的时间来学习足够通过你的考试。顺便说一句,在这个故事中,你是他的微积分教授——我很欣赏你的耐心。
如果你在机器学习 (ML)和人工智能的背景下听说过验证,但你不太确定所有这些大惊小怪都是关于什么的——验证只是有史以来最重要的 应用人工智能 概念之一,没什么大不了的——那么这就是你一直在等待的类比。想象一下憨豆先生即将参加他的第一次微积分考试…
如果你更喜欢视频版本,我来告诉你这个例子。
ML/AI 中的训练和调整阶段
憨豆先生找到了他藏起来的那个方程式,并开始为明天的考试做准备。他没有其他例子(数据点)或资源来帮助他,他也懒得写下任何明确的规则来解释微积分是如何工作的,所以他所能做的就是在他的方程中寻找模式:
除非另有说明,否则学生可以假定为 x ∈ R x \in \mathbb{R} x∈R美元。
就像一个 AI 算法一样,他的目标是找到一个数据模式,他可以把它变成一个配方(模型),成功地把他从“=”左边的输入带到右边的输出。这正是在一个应用人工智能项目的训练和调整步骤中所发生的事情(在我的分步指南中的步骤 6-7)。**
培训就是根据可用示例中的模式制作食谱。
所以让我们再仔细看看那个例子…我们能看到什么模式吗?
找到了。憨豆先生找到了一个!抓住那个 8,把它翻过来,就有答案了。
这个配方当然适用于这个例子。非常合身。恭喜你!憨豆先生该不该直接冲考?根据我评分的一些本科考试,答案并不明显。但是我敢打赌,你——我亲爱的开明的读者——比那更好。如果你是憨豆先生,你会先做些别的事情,对吗?
我几乎能听到你从这里一直在喊。“试试另一个例子!!!"没错。你和憨豆先生的不同之处在于,你们都会尝试另一个……但是你足够聪明,可以在考试前做这件事。换句话说,是在一个有挽回余地的低风险环境中,而不是在一个没有倒退的高风险环境中。
ML/AI 中的验证阶段
机器学习中的验证( step 8 )正是如此。验证为学生创造了一个安全、低风险的机会,让他们在没有明确研究过的例子上尝试他们的食谱。这就是他们获得是否准备好真正考试的信号的方式。
验证给了你的项目一些挽回的空间。
(憨豆先生很幸运有你这样的教授。谢谢你给他发来额外的例子,让他在舒适安全的宿舍里尝试!)
所以,憨豆先生准备尝试一个新的。我们来验证一下!
憨豆先生应用他的模型…
他把那个 5 翻过来,嘣!答案已经送达。
然后他会根据你提供的正确答案检查你的表现,然后…
可悲的是,数学不是这样工作的。
迭代时间!
现在怎么办?
憨豆先生看到了他的模型表现有多差,他有一个选择:要么从头开始,要么就去考试。
如果他选择重新开始,他可能会回到培训(步骤 6 )并在他已经拥有的数据中搜索新的模式(那个微不足道的数据点),或者他可能会更进一步,并考虑调整他正在处理的数据(如果是我,我会求你提供一堆额外的例子,这将意味着回到我的指南中的步骤 2)。
学生(和 ML/AI 工程师)被允许在寻找模式和验证他们的新配方之间来回跳跃。(但是不要熬夜学习!)
最后,在凌晨 3 点,憨豆先生找到了一种模式,它可以正确地工作在您给他的所有验证示例上。
研究这些微积分对憨豆先生的外貌造成了损害。
您在凌晨 3:08 收到以下电子邮件:
“亲爱的教授,
我发现了一个对你给我的那些验证例子 100%有效的配方。我可能是你教过的最好的微积分学生。不如我们节省时间,我跳过你的考试?我睡懒觉的时候你可以给我 A+。
真诚,
m . r . Bean
你说呢,教授?
ML/AI 中的测试阶段
你简直是在咆哮,“当然不是!!!!"
为什么不呢?通过回答这个问题,你就已经回答了为什么每个自尊的应用人工智能项目都需要验证和测试。
每一个自尊的应用 ML/AI 项目都需要验证和测试。
憨豆先生可能在欺骗自己,以为自己理解了那些方程式,而实际上他并不理解。据你所知,他可能已经拼凑出一个疯狂的食谱,过度拟合,呃,扭曲自己,以适应那些他整晚都在盯着的具体例子。也许他已经记住了所有的答案,这就是他的秘诀——凭记忆查找答案。不能保证他理解的任何东西足以处理新问题。
去参加考试,憨豆先生!
所以我们不要相信他的话。ML/AI 的一个关键原则是我们从不相信…我们测试。
ML/AI 布丁的证明总是在吃的过程中。
如果憨豆先生试图通过告诉我他的配方或“理解”来说服我放过他——嗯,这很可爱,但就像所有经验丰富的应用人工智能专家一样,如果他没有向我展示他在从未见过的新例子上的表现,我不会信任他。(虽然理解他的食谱可以帮助我帮助他改进,所以可解释性在训练阶段非常方便。)
ML/AI 的一个关键原则是我们从不相信…我们测试。
当然,憨豆先生尽了最大努力,他的最终食谱反映了他最好的理解。这并不意味着他应该及格。不幸的是,有时一个学生(或系统)的最好成绩还不够好。也许他的例子(数据)不够丰富,也许他没有足够的数据点来揭示有用的模式(抱歉,仅仅一个例子永远不足以学习如何解决极限微积分问题),或者也许他的算法——呃,大脑——找不到获胜的秘诀。憨豆先生运气不好——当任务具有挑战性时,不是每个学生或人工智能系统都会成功。这就是生活。
那么,《憨豆先生的最好》够好吗?你应该给他一张通行证,让他继续下一步(在那里你生产他在野外的数学)还是应该委婉地建议他转专业?
测试阶段可以让你发现你的学生对任务的掌握程度。
让我们通过邀请他参加一个高风险的考试来找出答案,在这个考试中,他将在他可能记不住的新例子上尝试他的食谱…如果他这次失败了,游戏就结束了。祝你好运,憨豆先生!
本文是第 1 部分,共 2 部分。故事在这里继续。
感谢阅读!人工智能课程怎么样?
如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:
在这里欣赏整个课程播放列表:bit.ly/machinefriend
喜欢作者?与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格取得联系。
轨迹预测的发展(1/2)
詹姆斯·科尔曼在 Unsplash 上拍摄的照片
无人驾驶汽车
无人驾驶汽车依赖于此
“如果你认识到自动驾驶汽车将防止车祸,人工智能将负责减少世界上主要的死亡原因之一。”—马克·扎克伯格
每当我们想到人工智能世界,汽车行业立即浮现在我们脑海中。自动驾驶汽车是一个迷人的未来,似乎不是一个遥远的现实。我们只是坐在车里看电影,然后它带你去目的地。
但是汽车能完全自动驾驶,注意一个环境中的所有语境,有那么容易吗?在过去的几年中,已经发表了许多论文来预测汽车和行人未来可能的轨迹,这些轨迹是社会可以接受的。
问题:自动驾驶汽车最大的挑战之一会是什么?
回答:了解行人行为及其未来轨迹。
人类运动可以被描述为多模态,即人类有可能在任何给定的时刻向多个方向移动。而这种行为是自动驾驶汽车最大的挑战之一。因为他们必须穿越一个以人类为中心的世界。
在第一部分,我将简要讨论三篇论文,它们的主要目的是预测行人未来可能的轨迹。
社会 GAN
这是最初的论文之一,其中指出使用 GAN 来预测人类可能的轨迹。
这篇论文试图通过预测人类在社会上看似合理的未来轨迹来解决这个问题,这将有助于无人驾驶汽车做出正确的决定。
目标:
该文件旨在解决两大挑战:
- 在场景中的所有人之间建立一个计算高效的交互模型。
- 去学习和创造多种被社会接受的轨迹。
方法
图一。 截图来自社会甘研究论文
提出了一种基于 GAN 的编解码网络,该网络由每个人的 LSTM 网络和模拟他们之间交互的池模块组成。
整个模型(如图图 1 所示。 ) 可以用 3 种不同的成分来表示:
发电机
发生器由编码器和解码器组成。对于每个人,编码器将输入作为 X_i。它嵌入每个人的位置,并在时间 t. 将其作为固定长度向量提供给 LSTM 单元
LSTM 权重在场景中的所有人之间共享,这将有助于池模块开发人与人之间的交互。
与之前的工作不同,本文使用了以下两种方法:
a)对于反向传播期间的简单训练过程,代替预测二元高斯分布,解码器直接产生人的位置的(x,y)坐标。
b)不是直接提供社会背景作为编码器的输入,而是提供一次作为解码器的输入。这导致速度增加到的 16x 倍。
鉴别器
鉴别器由一个编码器组成,每个人都有 LSTM 层。这个鉴别器的想法是区分真实轨迹和虚假轨迹。
理想情况下,如果这些轨迹不被社会接受或不可行,它应该将其归类为**、**。
汇集模块
图 2。 截图来自社会甘论文
这种方法的基本思想如图 2 所示。该方法计算人 1(用红色表示)和所有其他人(用蓝色和绿色表示)的相对位置。然后,它与隐藏状态连接,并通过 MLP(多层感知)独立处理。
最终,每个元素被顺序汇集以计算一个人的 1 汇集向量 P1。
这种方法减少了考虑特定网格内人员的限制(图 2 中的 S-Pool 网格)。)
损耗
本文使用了 3 种不同的损失:
- 对抗性损失:这种损失是典型的 GAN 损失,有助于区分真假轨迹。
- L2 损失:该损失获取预测轨迹和真实轨迹之间的距离,并测量生成的样本与真实样本之间的距离。
- 品种损失:这种损失有助于产生多种不同的轨迹,即多模态轨迹。这个想法很简单,对于每一个输入,从 N(0,1)中随机抽样*‘z’*来预测 N 个不同的可能结果。最终,选择具有最小 L2 值的最佳轨迹。
苏菲:细心的甘
这篇论文扩展了社会 GAN 的工作,并试图在物理和社会信息的帮助下预测智能体的未来路径。
虽然目的仍然与社交 GAN 相同,但本文也借助每帧图像添加了风景信息。
网络学习两种类型的注意力成分:
- 身体注意力:这种注意力成分有助于注意和处理周围的局部和全局空间信息。正如的论文 : “例如,当到达一条弯曲的路径时,我们更关注曲线而不是环境中的其他约束”
- 社会关注:在这个组件中,想法是给予周围环境中其他代理的运动和决策更多的关注。例如:“在走廊里行走时,我们更关注前面的人,而不是后面的人”
方法
图 3。 截图来自苏菲研究论文。
本文提出的方法分为 3 个模块(如图图 3 所示。)。
特征提取模块
该模块以两种不同的形式从输入中提取特征,第一作为每一帧的图像,第二作为每一帧的每个代理在某一时刻的状态’ t '。
为了从图像中提取视觉特征,他们使用了 VGGnet-19 作为 CNN 网络。该网络的权重由 ImageNet 初始化。为了从所有代理的过去轨迹中提取特征,他们使用与社交 GAN 类似的方法,并使用 LSTM 作为编码器。
为了理解每个代理之间的交互并捕捉每个代理轨迹对另一个代理的影响,在社交 GAN 中使用了池模块。本文提到了这种方法的两个局限性:
- 最大值函数可能会丢弃输入的重要特征,因为它们可能会失去唯一性
- 最大值运算后,所有轨迹将被连接在一起,这可能会导致具有相同的连接要素制图表达。
由于这些限制,它们定义了一个排序结构。在此,他们用排序作为排列不变函数而不是 max (用于社交 GAN)。他们通过计算目标代理和其他代理之间的欧几里德距离来对代理进行排序。
注意模块
在身体或社会注意力的帮助下,本模块有助于突出下一模块输入的重要信息。
这个想法是,随着人类更多地关注环境中的某些障碍或物体,如即将到来的转弯或走向它们的人,需要学习类似的注意力。
如前所述,这个网络倾向于学习两种不同的注意力。
在物理注意中,来自 GAN 模块的 LSTM 的隐藏状态和来自视觉环境的学习特征被提供作为输入。这有助于了解更多的物理约束,如路径是直的还是弯曲的,当前的移动方向和位置是什么,等等。
在社会关注中,从特征模块学习到的 LSTM 特征连同来自 GAN 模块的 LSTM 的隐藏状态被提供作为输入。这有助于关注所有对预测未来轨迹很重要的因素。
GAN 模块
该模块利用突出显示的输入特征,为每个智能体生成一条符合所有社会和物理规范的现实未来路径。
这个 GAN 模块主要受社交 GAN 的启发,几乎没有进一步的变化。
生成器的输入是从注意力模块中选择的特征以及从多元正态分布中采样的白噪声*‘z’*。
损耗
这种方法使用了 2 个损耗,也类似于社会 GAN。
- 对抗性损失:这种损失有助于学习区分生成样本和真实样本。
- L2 损耗:该损耗类似于社会 GAN 中使用的“品种损耗”。
社交方式
在这篇论文中,他们还试图预测行人的轨迹及其相互作用。然而,他们也致力于解决所有先前方法中的一个问题:模式崩溃。
模式崩溃 是多模态的反义词。在这种情况下,发生器试图产生相似的样本或相同的样本集,从而在输出中产生相似的模式。
为了解决模式崩溃问题,本文使用 info-GAN 代替 L2 损耗或品种损耗。
方法
图 4。 截图来自社会方式研究论文
该方法包括 3 个不同的部分:
发电机
发生器由一个编码器-解码器网络组成。在这里,每个代理的过去轨迹被输入到各自的 LSTM-E(编码器)中,该编码器对代理的历史进行编码。每个 LSTM-E 的输出被输入到注意力集中和解码器中。
对于解码,来自 LSTM E 的未来轨迹、隐藏状态、噪声向量“z”、潜在代码“c”和来自注意力集中的重要交互代理特征被馈送到解码器。
潜在代码“c”有助于最大化所产生的输出的分布和“c”之间的交互信息的下限。
注意力集中
这篇论文使用了与《苏菲:一个细心的甘》相似的方法。
然而,除了代理之间的欧几里德距离(在 Sohpie 中使用)之外,还使用了另外两个特征:
- 方位角 : “代理 1 的速度向量与连接代理 1 和代理 2 的向量之间的角度。”
- 最近接近的距离 : “如果两个智能体都保持当前速度,则两个智能体在未来可能达到的最小距离。”
注意力权重由隐藏状态和上述三个特征之间的标量积和 softmax 运算获得,而不是排序。
鉴别器
鉴别器由基于 LSTM 的多层密集编码器组成。从发生器为每种制剂生成的轨迹和地面实况轨迹被输入鉴别器。
作为输出,提供了生成的轨迹是真实的概率。
损耗
此过程中使用了两种损耗:
- 对抗性损失:这是正常的 GAN 损失,有助于区分真假样品。
- 信息损失:这种损失的基本思想是最大化互信息。这是通过最小化显著变量‘c’的负损失可能性实现的。
结果
这三篇论文都试图从以前的方法中学习,并获得了一些新的见解。
有两个指标用于评估该应用程序:
- 【ADE】:生成轨迹与地面真实轨迹在所有预测时间步上的平均 L2 距离。
- 【最终位移误差(FDE) :这是在最终预测时间步长上,生成轨迹与地面真实轨迹之间的最小距离。
有 5 个数据集,用作该应用的基准测试。所有这些方法都在所有这些数据集上进行了测试,它们提供了有价值的可比结果。
图 5。 社交方式结果截图
从图 5。可以说,所有三种方法在一些数据集上都显示出有希望的结果。然而,我认为随着超参数调整和小的调整,结果也可能会混乱。我相信这三种方法都有一定的优势,可以用于这方面的进一步研究。
结论
对场景中的人体运动预测以及人与人的交互进行建模的问题对于自动驾驶汽车来说是具有挑战性的,但也是至关重要的。如果不对这种行为进行建模,无人驾驶汽车就不可能完全投入使用。
如何对人与人的交互进行建模是上述方法之间的主要区别。从理论和建议的方法来看,我认为关注两个智能体之间的距离和方位角是前进的最关键的方法之一。
但这完全是我的观点。可以有多种方式来实现和增强这一点。我们也将在第 2 部分中看到这一点。
以自动驾驶汽车为重点,我将在第 2 部分继续介绍更多方法,重点是汽车的轨迹预测。
我很乐意就这份文件和这一领域进行任何进一步的讨论。你可以在这里发表评论,或者通过我的 LinkedIn 个人资料联系我。
参考
- 亚历山大·阿拉希,克拉塔斯·戈埃尔,社交 LSTM:拥挤空间中的人类轨迹预测,CVPR 2016
- 阿格里姆·古普塔,贾斯廷·约翰逊,李菲菲,社会 GAN:具有生成性对抗网络的社会可接受轨迹,CVPR 2018
- Amir Sadeghian,Vineet Kosaraju,Ali Sadeghian, Sophie:一个专注的 GAN,用于预测符合社会和物理约束的路径,CVPR 2018
- 卡伦·西蒙扬,安德鲁·齐泽曼,用于大规模图像识别的极深度卷积网络,ICLR,2015
- 社会方式:用 GANs 学习行人轨迹的多模态分布,CVPR 2019
转录器
基于变压器的手写识别体系结构
[ 图像来源
数字化手写文档以改善存储、访问、搜索和分析是一个迫在眉睫的挑战。在深度学习革命之前,不存在以可扩展的方式实现这一目标的明确路径。
随着计算机视觉和语言处理的进步,可靠的自动化手写识别指日可待。为此,我努力设计了一个实用的应用程序,平衡了准确性、可推广性和推理速度。这篇文章是对这一尝试的回顾,也是对设计选择和训练程序的解释。在这里查看一个演示。
阅读手写文本是非常困难的。风格(如草书与印刷体和印刷体)、大小、间距、修饰和易读性之间存在极大的差异。拼写错误、划掉和遗漏也很常见。
解决这个问题的许多方法将任务分成 3 个独立的部分:分割、特征提取和分类。【1,13,2】
- 检测和分割图像中的文本区域。
- 从每个文本片段中提取高维特征。
- 从给定的词汇(例如单词或字符)中对这些特征进行分类。
鉴于手写的异质性,自动文本检测和分割可能容易出错,并且通常需要定制的预处理。另一种方法是将文本识别框架为序列对序列问题。二维图像输入和一维文本输出。NLP 中表现最好的序列转导模型包括一个编码器-解码器结构,通常包括一个注意机制。其中最具革命性的可能是转换器架构*【3】*,它的独特之处在于它仅依赖于对输入和输出的编码表示,而不借助任何形式的递归或卷积。在这里可以找到对 Transformer 架构的一个很好的解释:带注释的 Transformer 。
这并不是第一次尝试在端到端的方法中用注意力取代分段。*【5】*然而,我没有用注意力模块来增加传统的递归层,而是利用 Transformer 架构来超越以前的最先进的结果。亲自试用转录器应用!
架构
Transformer 架构基于多头关注(“比例点积”)层,之后是按位置完全连接的网络。点积或乘法注意力比加法注意力更快(计算效率更高),尽管在更大的维度上表现较差。缩放有助于调整乘法的收缩梯度。根据这篇论文,“多头注意力允许模型在不同位置共同关注来自不同表征子空间的信息。”编码器和解码器都利用自我关注层。解码器还包括编码器输出上的源注意力层。
图像适配器
机器翻译(Transformer 就是为机器翻译而设计的)和手写识别之间的显著区别在于,图像在输入到 Transformer 之前必须被处理成一个连续的表示。
为此,预训练的卷积神经网络(ResNet18)在最终的最大池层之前被截断,以输出空间特征图。沿空间维度对要素地图进行展平、归一化和转置,以创建粗略的一维表示序列。(注 : ResNet18、ResNet34 和 Xception 模型经过测试,精度没有显著差异。我选择了 3 个中最小的一个。)
编码器
Transformer 编码器中的自我关注有助于从图像适配器输出的扁平化空间表示中解析语义(和顺序)关系。
解码器
变压器解码器是自动回归的,这意味着先前生成的令牌通过自我关注被考虑在内。为了加速训练,“之前生成的令牌”被实际的目标序列嘲讽(ala 老师强制)。然后,解码器可以使用更高效的批量矩阵乘法来一次生成所有输出令牌,而不是进行推理所需的耗时的顺序解码。在注意力计算中使用字节掩码,以防止解码器在正在生成的内容之前关注位置。
序列的二维空间编码
因为转换器不使用任何卷积或递归,所以模型不包含内置的空间或顺序感知。需要位置编码。在原始论文中,使用了固定的正弦编码。作者推测,这将有助于模型学习相对位置,并推断出比训练中看到的更长的序列长度。
CNN 具有隐含的位置感知,因此输入图像不需要额外的编码。然而,目标序列确实需要位置编码。有趣的是,当包含换行符(’ \n ')时,多行段落有一个潜在的第二维度。利用这个换行符,我使用了一个已学习的二维空间编码方案,该方案基于离最近的换行符的距离(大约宽度)和自序列开始以来的换行符的数量(大约高度)。
(*注:语言模型也作为模型的补充进行了实验。一个预先训练的 AWD-LSTM【6】语言模型产生了准确性的提高,但推理成本太大。双向转换编码器【7】语言模型是另一个潜在的选择。BERT 结合了令牌两端的上下文,并且是非自回归的,这意味着推理时间的增加是最小的。由于 GPU 的限制,我用一个经过提炼的版本【8】*进行了实验,但是即使在预训练之后也没有看到准确性的提高。)
数据
这项任务的大部分训练都是基于流行的 IAM 手写数据库。该数据集由 657 位不同作者的 1539 页扫描手写文本组成。IAM 是建立在 20 世纪 70 年代编纂的英国英语文本语料库(Lancaster-Oslo/Bergen)的基础上的。这些文本的内容跨越包括新闻报道,科学写作,散文和流行的传说体裁。
理解深度学习算法性能受训练数据质量的约束;这个数据集对于现代的美式英语应用程序来说并不理想。然而,作为最大的公开可用的带注释的手写图像汇编,它是该领域中最受研究应用欢迎的数据集,并提供了与以前的体系结构的良好基线比较。
除了 1,539 页的文本之外,数据集还被进一步分割成约 13k 行和约 115k 孤立词。这些额外的分段对于扩展训练数据集至关重要。
在扩展之前,需要对数据进行彻底检查,并手动纠正注释/分段错误。删除了 15 页的测试集,即大约 1%的总数据。小测试规模并不理想,但数据集的整体规模较小,这是必要的。
单词组合
通过组合从分词列表中随机选择的图像来创建第二数据集。从一个单词到 4 行,每行 4 个单词,以随机配置创建了 50k 个新图像。
行连接
另一个数据集是通过连接随机选择的长度为 3 至 13 行的行图像(按高度归一化)而创建的。两万张这样的图片被制作出来。
合成字体
另一个策略是使用谷歌字体来创建类似手写文字的图像。来自维基百科、IMDB 电影评论和开源书籍的文本使用 95 种不同大小的手写字体进行渲染,以创建大约 129k 不同长度的图像。背景噪声、模糊、随机填充/裁剪、像素倾斜等。以反映原始数据集以及一般手写文本的有机不规则性。
下载的手写样本
为了提高算法在 IAM 数据集之外的泛化性能,另一个数据集由 161 幅人工注释的图像构成,这些图像可在互联网上公开获得。这些图像用 11 种不同的图像调制组合进行处理,产生包含 1771 个图像的最终数据集。
标记化
对于涉及文本序列的任务,标记化至关重要。字符标记化有很多好处。字符大小是相对标准的。词汇表很小,几乎没有超出词汇表的标记。然而,字符推断是缓慢的(1000+字符的自回归顺序解码需要时间……),并且如上所述,手写的异质性意味着字符经常重叠、难以辨认,甚至被省略。
单词标记化是直观的,因为在人类阅读中,单词似乎优先于字符*。推理时间比用字符快得多。但是,为了限制词汇表外的标记,词汇表的大小必须非常大。
一个固定长度的子词分词器 SentencePiece 被用作折衷。使用一个习得的、单字母语言模型编码*【9】,*,我创建了一个 10k 子词单位的词汇表。重要的是,添加了额外的特殊标记来表示空格、换行符、字母和单词的大写指示符以及标点符号。(注意 : 10k、30k 和 50k 词汇表都经过了测试,10k 是性能最好的,同时保持模型占用空间较小。赢赢!)
子词标记化提供了良好的推理速度、适中的词汇量和很少的词汇外标记。然而,文本图像显然不能划分成子词单元。我发现同时使用字符和子词标记进行训练有助于编码更健壮的特征表示,并提高两者的准确性。
通过一封特别的信向你传达一个爱好者的信息。TL;dr:而单词被识别为组块;人类阅读(尤其是在困难的条件下)包括单词和字符两个过程。]
培训
数据集被组合成 3 个连续的训练组。该模型首先在单词组合数据集(256 像素的 50k 图像)上进行训练,以比较建筑设计决策,测试超参数设置,并为后面的组进行预训练。然后,该模型在合成字体生成的数据(512 像素的 129k 图像)上进行训练,以读取长的、格式良好的文本序列。最终的手写数据集(25k 图像,512 像素)由主要的 IAM 页面图像、连接的行和下载的文本图像组成,用于微调手写文本的模型。
数据增强包括:旋转、扭曲、对比度和亮度的随机变化。
转录参数
- 模型尺寸:512
- 激活函数:GELU [10]
- 层数:4
- 注意头:8
- 辍学率:0.1
- AdamW 优化器[11]:(修复自适应梯度算法的权重衰减)
- 最大学习率:1e–3(根据“1 周期”政策[12]而变化)
- 贝塔系数:(0.95,0.99),ε:1e–8
- 动量:(0.95,0.85)
- 重量衰减:1e–2
- 标签平滑度:0.1
结果
以下与以前发表的架构的比较在科学上并不严谨,因为这个项目的目的是构建一个实用的工具,而不是发表一篇学术论文。因此,训练/测试分割并不是所有架构的标准,其他架构的结果基于预先分段的行级数据集,而 Transcribr 架构是针对整页数据集进行测量的。然而,使用公布的结果作为一个松散的比较,Transcribr 架构证明是非常有竞争力的。
来自 IAM 手写测试数据集的 4 个图像的 Transcribr(单词块令牌)结果
局限性
在这个项目中,我受到了一些限制。主要障碍是直接影响计算资源的预算。培训是在 NVIDIA P6000 (24GB 内存,3840 个 CUDA 内核)上进行的。对转录应用的推断是在一个只有 1GB RAM 的不起眼的 1CPU 上进行的,并且以可怜的大约 3 令牌/秒的速率转录图像:(
(*注:*在一台 2015 款 MacBook Pro 上用 4 核 16GB RAM 进行本地测试推理,产生了约 30 令牌/秒的良好转录率。)
数据的数量和质量是另一个因素。合成数据和增强在实现这些结果方面至关重要,但仍然是多样化、高质量笔迹样本的拙劣替代品。当在包含不同的数据集外图像的测试集上测试时,Transcribr 的性能相当差。
4 个非数据集测试图像的转录结果
架构选择优先考虑推理时间,而不是其他因素,包括准确性。为此,该模型尽可能保持精简,以大约 50.8 米的参数进行称重。使用贪婪解码代替更精确但昂贵的波束搜索。transcript br 应用使用不太准确但更快的单词片段标记化方案。
结论
我没有达到最初的目标,即构建一个有用的(快速、通用且准确的)工具。Transcribr 既不快速也不通用:/可以使用量化和提取等技术来进一步减小模型大小,将 python 模型转换为 C++将加快推理速度。从更多种类的来源和上下文中收集更多的手写图像将提高概括的准确性。
然而,从学术数据集的狭窄范围来看,我的结果令人鼓舞。我使用的一些新技术包括:
- 使用转换器架构进行手写识别
- 基于换行符的 2d 学习位置编码(’ \n ')
- 句子片段标记化,而不是单词或字符
- 同时训练令牌和字符解码器
- 使用手写字体生成合成数据
这些技术使 Transcribr 的表现超过了之前发表的结果,并表明即使是财力不足的独立研究人员也可以帮助推动深度学习的边界(研究,如果不是实际的话)。
更新(2020 年 3 月)
量化和 TorchScript 序列化将模型大小从 203MB 减少到 21MB,并将转录速率提高到更合理的约 15 令牌/秒。生产应用程序现在大约需要 10 秒来处理测试集中的图像,而不是以前可怕的 1 分钟。巨大的进步!
链接
-
[1] J. Chung & T. Delteil,“一种计算高效的全页脱机手写文本识别流水线方法”,2019 年
-
[2] C. Wigington 等人,“开始、跟随、阅读:端到端全页手写识别”,2018
-
[3] A. Vaswani 等人,“注意力是你所需要的全部,2017
-
[4] T. Bluche,“用于端到端手写段落识别的联合行分割与转录”,2016
-
[5] T. Bluche 等人,“扫描、出席和阅读:带 MDLSTM 注意力的端到端手写段落识别”,2016
-
[6] S. Merity 等人,“正则化和优化 LSTM 语言模型”,2017
-
[7] J. Devlin 等人,“ BERT:用于语言理解的深度双向变换器的预训练”,2019
-
[8] V. Sanh 等人,“蒸馏伯特,伯特的蒸馏版本:更小、更快、更便宜、更轻”,2020 年
-
[9] T. Kudo,“子词正则化:用多个候选子词改进神经网络翻译模型”,2018
-
[10] D. Hendrycks & K. Gimpel,“高斯误差线性单位(GELUS) ”,2018 年
-
[11] I. Loshchilov & F. Hutter,“解耦权重衰减正则化”,2019 年
-
[12] L. Smith,“神经网络超参数的规范方法:第 1 部分—学习速率、批量大小、动量和权重衰减”,2018 年
-
迪库松夫人
将艺术图像风格转换为任何内容图像
深度学习概念和实用程序,以开始神经风格转移!
你有过被过去的革命画家所画的愿望吗?你有没有被画家遗留在这个世界上的错综复杂和精美的艺术逗乐过?让我们深入探讨如何使用机器学习来使用这些无可挑剔的风格重现我们的内容。到本文结束时,您将熟悉各种机器学习概念和实用程序,它们将使您能够将任何样式转换为自己选择的任何内容图像。
卷积神经网络
卷积神经网络也称为 CNN,是神经类型转移所需的基本构建模块。这些类型的深度网络用于检测图像中的图案/笔画,并且通常用于图像分类和生成。下面是一个基本 CNN 的结构。
CNN 意味着在一个层中的部分激活上进行卷积,由许多层上的过滤器(内核)大小定义,直到我们最终到达我们的输出层,它根据遇到的输入对我们的图像进行分类。你可以从这里熟悉 CNN 的各种细微差别。
我们的主要想法是使用 CNN 中间层提供的不同激活,使用我们的内容和风格图像作为输入。我们将定义两个独立的 损失函数 ,内容损失和样式损失,这将跟踪我们生成的图像与我们的内容图像和样式图像有多“远”。我们的神经网络的目标将是最小化这些损失函数,从而在内容和风格方面改进我们生成的图像。
迁移学习
我们将使用预先训练的模型,而不是从头开始训练 CNN 模型,这将节省我们所有的训练和计算。我们将使用一个 VGG-19 模型,这是一个 19 层的 CNN。使用预训练模型背后的主要思想是,使用在不同任务上训练的模型,并将该学习应用于新任务。您可以使用任何带有预训练权重的 VGG19 开源实现。预训练的重量可以从这里获得。这个是我用的实现,礼遇 deeplearning.ai 。
神经网络模型需要大量的计算和时间,因此,GPU 肯定会派上用场。一定要试试 Google Colab ,它可以让你在一次会话中免费访问 GPU 超过 12 个小时。
神经类型转移
必需的包
我们将使用 TensorFlow 作为我们的深度学习框架。还导入了其他几个库来启用一些实用程序,如保存、导入、调整大小和显示图像。
注意:nst_utils 不是一个库,而是我们目录中的 python 文件的名称,在这个目录中,我们实现并存储了预先训练好的 VGG-19 模型。
内容图像和样式图像
我们将使用巴黎的卢浮宫博物馆作为我们的内容图像,砂岩作为我们的风格图像。
卢浮宫博物馆
砂岩
标准化数据
我们需要根据我们的 VGG-19 模型所需的输入来预处理我们的图像。该图像由 3 个 RGB 通道上 0–255 范围内的像素值组成。图像也有不同的分辨率,取决于它包含的像素数量。
注意:输入图像中的通道对于彩色图像(即 RGB 图像)保持为 3,而对于灰度图像保持为 1。根据分辨率,改变的是宽度和高度,即像素数。
假设我们的图像有一个维度(W,H,C),其中 W 和 H 分别是宽度和高度,C 是通道的数量。需要输入到我们的 VGG-19 模型中的输入张量的维数必须是(m,300,400,3)并且是归一化的。变量 m,也称为批处理大小 i 是一个超参数,它通过使用 np.expand_dims()实用程序扩展我们的图像维度来处理单个图像训练示例,从而被设置为 1。我们的函数返回预处理过的图像。
内容成本
生成的图像应该在内容上与我们的内容图像相似。为此,我们从一个层中选择激活来表示我们的内容图像的激活。CNN 中的早期层检测图像的边缘和纹理等低级特征,而较深的层检测对象等高级特征。我们选择了最后一层的激活来代表我们内容的激活。这又是一个超参数,可以随意调整结果。
在为我们挑选了合适的中间层之后,我们将内容图像设置为我们的 VGG-19 模型的输入,并通过正向传播获得 Ac 作为激活。我们对生成的图像重复相同的过程,并获取 Ag 作为激活。注意为了清楚起见,Ac 和 Ag 是多维向量。内容成本 J 定义为:
风格成本
直观上,图像的风格可以被视为不同通道的纹理和像素相对于彼此如何变化。风格可以通过 Gram 矩阵定量定义。在这种情况下,Gram 矩阵捕获矩阵与其转置矩阵的内积。风格也可以被识别为激活之间的相关性,在单个层中的不同通道之间的相关性。为了计算不同通道之间的相关性,我们得到维数为(nw,nh,nc)的 3-D 激活输出,并将其展平到(nh*nw,nc)的维数。
计算这种扁平化版本的 gram 矩阵实质上将为我们提供一个通道(对应于一行)中不同激活之间的相关性,以及不同通道(对应于一列)。
在计算了 gram 矩阵之后,为了定义样式成本,我们选择了一个样式层,即 VGG-19 中的一个层,我们的样式成本将基于该层的激活。假设‘G’被定义为单个样式层的激活的 gram 矩阵,上标‘S’和‘G’分别表示作为样式图像和生成图像的输入图像。那么单层的样式成本被定义为:
最小化上述成本将最终帮助我们在生成的图像中重新定义风格。
该成本函数仅针对单层激活。如果我们从不同的层次*【合并】*风格成本,我们会得到更好的结果。为了计算样式成本,迭代多个样式层(一个超参数)。每一层都提供了反映单个层对样式贡献多少的权重。
在此功能中,可以调整可变的“权重”,以将权重分配给各个风格层。我们已经在所有的样式层之间保持了相等的权重。所有的权重总和为 1。
显示图像
为了显示并最好保存我们的输出图像,我们需要撤销我们对原始图像所做的所有更改。这包括反规格化和剪裁输出的像素,使我们的像素严格保持在 0–255 的范围内。
构建我们的模型
我们已经评估了构建模型所需的所有预处理和成本函数。为了构建我们的模型,我们首先用 Tensorflow 创建一个交互会话。交互式会话使自己成为默认会话,使我们在运行 run() 或 eval() 函数时不能显式调用我们的会话。
我们在目录中指定路径,从中可以获取我们的内容和样式图像。
我们将我们的网络最终将最小化的最终成本定义为内容成本和风格成本的加权和。
总成本
权重和𝛽也是超参数,可以对其进行调整,以便为我们生成的图像中的内容和风格分配优先级。它们定义了内容和风格之间的相对权重。
现在让我们将所有的函数编译成最终的模型!
我们分别从内容和样式路径加载内容和样式图像,并定义成本函数所需的内容和样式层(超参数)。
在我们的模型中,我们已经初始化了生成的图像(变量“input_image”)作为我们的内容图像。这允许我们的模型更快地收敛,因为我们生成的图像将更快地匹配并最终收敛到我们的内容图像。
注意:不要简单的赋值 input_image = content_image。这将通过引用分配输入图像,这将在输入图像发生变化时改变内容图像。
在加载我们预先训练的 VGG-19 模型之后,我们通过将内容图像和风格图像分别作为输入通过我们的 CNN 网络来计算内容成本和风格成本。
在定义了总成本之后,使用带有参数和𝛽的内容成本和样式成本,我们将这个总成本传递给我们的优化器来最小化。优化器是一种梯度下降算法,帮助我们的模型更新权重,以便我们的成本函数收敛到最小值。
在我们的训练程序中,我们使用 Adam 作为优化算法。
我们以 2 的学习率运行我们的模型 2000 次迭代(时期)。在 Google Colab 提供的 GPU 上运行这个模型大约需要 5-10 分钟。
使用我们的模型返回的像素,并将这些像素传递给我们的实用程序“display_image ”,就可以输出我们生成的图像,如图所示。
生成的图像
我们可以清楚地分辨出砂岩风格的卢浮宫博物馆的轮廓,正如我们所希望的那样!继续用你自己的克洛德·莫内风格的照片来试试这个模型吧!
瞧啊。由此,我们成功地实现了神经风格的转移。剩下你要做的就是找到新的内容和风格,让你的内在艺术家展现出来!
你可以试着看看不同学习率和不同时期生成的图像的变化。为内容和风格成本指定不同的层次也是一个值得探索的有趣方面。尝试分析其他预训练模型,如 Inception 或 ResNet ,看看它们是否能提供任何进一步的改进。您可以跟随这个库来获得我们模型的编译版本,并分析对应于每次迭代的损失。
您可以关注更多资源:
万事如意!
图像分类中的迁移学习:我们真正需要多少训练数据?
训练数据集的大小如何影响通过迁移学习训练的分类器的性能的实验评估。
弗兰基·查马基在 Unsplash 上拍摄的照片
术语迁移学习指的是通过在某个(通常是大的)可用数据集上训练的神经网络获得的知识的杠杆作用,用于解决很少训练样本可用的新任务,将现有知识与从特定于任务的数据集的少数样本中学习的新知识相结合。因此,迁移学习通常与数据扩充等其他技术一起使用,以解决缺乏训练数据的问题。
但是,在实践中,迁移学习到底能有多大帮助,我们到底需要多少训练实例才能使它有效呢?
在这个故事中,我试图通过应用两种迁移学习技术(例如,特征提取和微调)来解决图像分类任务来回答这些问题,改变模型训练的示例数量,以便了解数据的缺乏如何影响所采用方法的有效性。
实验案例研究
为实验迁移学习选择的任务包括将花的图像分为 102 个不同的类别。这项任务的选择主要是由于 flowers 数据集的容易获得性,以及问题的领域,该问题足够通用,适合于有效地应用迁移学习,其中神经网络在众所周知的 ImageNet 数据集上进行了预训练。
所采用的数据集是由 M. Nilsback 和 A. Zisserman [3]创建的 102 类别花数据集,它是属于 102 个不同类别的 8189 个标记花图像的集合。对于每个类,有 40 到 258 个实例,并且所有数据集图像都有显著的比例、姿态和光照变化。102 个类别的详细列表以及相应的实例数量可在这里找到。
图 1 :从 102 类别数据集中提取的图像示例。
为了创建不同大小的训练数据集并评估它们如何影响训练网络的性能,将原始的花卉图像集分成训练集、验证集和测试集几次,每次采用不同的分割百分比。具体来说,使用下表中所示的百分比创建了三个不同的训练集(从现在起将被称为大型、中型和小型训练集)。
表 1 :用于执行实验的数据集的实例数量和分割百分比(参考完整的未分割 flowers 数据集)。
所有的分割都采用分层采样来执行,以避免引入采样偏差,并以这种方式确保所有获得的训练、验证和测试子集都代表整个初始图像集。
采用的策略
上述图像分类任务是通过采用两种流行的技术来解决的,这两种技术在利用预训练的 CNN 应用迁移学习时通常使用,即特征提取和微调。
特征抽出
特征提取基本上包括获取以前训练的网络的卷积基,通过它运行目标数据,并在输出的基础上训练新的分类器,如下图所示。
图 2 :应用于卷积神经网络的特征提取:在保持相同卷积基的同时交换分类器。“冻结”表示体重在训练期间不会更新。
堆叠在卷积基础之上的分类器可以是全连接层的堆叠,或者仅仅是单个全局池层,两者之后都是具有 softmax 激活功能的密集层。关于应该采用哪种分类器没有特定的规则,但是,如 Lin 等人所描述的。al [2],仅使用单个全局池层通常导致较少的过拟合,因为在该层中没有要优化的参数。
因此,由于实验中使用的训练集相对较小,所选择的分类器仅包括单个全局平均汇集层,其输出被直接馈送到 softmax 激活层,该层输出 102 种花类别中每一个的概率。
在训练过程中,只有顶级分类器的权重被更新,而卷积基的权重被“冻结”,从而保持不变。
以这种方式,浅层分类器从先前由源模型为其领域学习的现成表示中学习如何将花图像分类到可能的 102 个类别中。如果源域和目标域是相似的,那么这些表示可能对分类器有用,并且一旦被训练,转移的知识因此可以带来其性能的改进。
微调
微调可以被视为比特征提取更进一步的步骤,特征提取包括选择性地重新训练先前用于提取特征的卷积基的一些顶层。通过这种方式,由最后一层学习的源模型的更抽象的表示被稍微调整,以使它们与目标问题更相关。
这可以通过解冻卷积基的一些顶层,保持所有其他层冻结,并用先前用于特征提取的相同分类器联合训练卷积基来实现,如下图所示。
图 3 :特征提取对比微调。
需要指出的是,根据 F. Chollet 的说法,预训练卷积基的顶层只有在它上面的分类器已经被预先训练过的情况下才能被微调。原因是如果分类器还没有被训练,那么它的权重将被随机初始化。结果,在训练期间通过网络传播的误差信号将会太大,并且未冻结的权重将会被更新,从而破坏了先前由卷积基学习的抽象表示。
出于类似的原因,也建议采用比用于特征提取的学习速率更低的学习速率来执行微调。
此外,值得一提的是,只有顶层未被冻结的原因是,较低层指的是一般的与问题无关的特征,而顶层指的是与问题相关的特征,这些特征更多地与网络最初被训练的特定领域相关联。因此,由第一层学习的特征通常适合于处理大量的域,而由顶层学习的特征需要针对每个特定的域进行调整。
实验
所有实验都是在 Google 联合实验室云平台上使用 Keras 和 Tensorflow 2.0 作为后端开发和执行的。
ResNet50-v2 是被选择用于执行实验的源网络,并且 ImageNet 数据集是它已经被预训练的源域。该网络的选择完全是任意的,因为为了实验的目的,也可以使用在大型数据集上预先训练的任何其他最先进的网络(我只是决定在由 Keras 应用模块提供的几个预先训练的模型中选择一个基于 ResNet 的网络)。
对于在本文开头定义的每个生成的训练数据集(例如,大、中、小数据集),已经执行了以下实验:
- 特征抽出
- 微调
- 数据增强的特征提取
- 通过数据扩充进行微调
在每个实验中,加载的图像总是被调整到 224×224 像素。这是应用于数据集图像的唯一预处理操作。
数据扩充
数据扩充是一种技术,它由“组成,通过生成每个训练实例的许多真实变量来人工增加训练数据集的大小【1】。在所进行的实验中,这是通过四个简单的图像处理操作来实现的:
- 随机裁剪,最小裁剪尺寸等于原始图像尺寸的 90%
- 垂直和水平轴上的随机镜像
- 随机亮度调节,最大亮度增量为 0.2
由于 Tensorflow 被用作后端,所以上面定义的所有操作都是使用框架提供的 tf.image 模块实现的,该模块很容易与用于构建向开发的模型提供数据的输入管道的 tf.data API 集成。
特征抽出
有了 Keras,这个堆叠在预训练 ResNet 上的分类器可以很容易地实现如下:
conv_base = tf.keras.applications.ResNet50V2(
include_top=False,
weights='imagenet',
input_shape=(IMAGES_SIZE, IMAGES_SIZE, 3),
pooling='avg'
)model = Sequential()
model.add(conv_base)
model.add(Dense(len(np.unique(labels)), activation='softmax'))
请注意,通过将参数“None”和“avg”分别传递给“include_top”和“pooling parameters”,ResNet50V2 类已经构建了一个网络,用全局平均池层替换了最后一个 softmax 层。
为了执行特征提取,有必要冻结预训练卷积基的权重,使得它们在整个模型的训练期间不会更新。这可以通过简单地将卷积基的每一层的属性“可训练的”设置为假来实现:
for layer in conv_base.layers:
layer.trainable = False
因此,创建的综合模型如下:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
resnet50v2 (Model) (None, 2048) 23564800
_________________________________________________________________
dense (Dense) (None, 102) 208998
=================================================================
Total params: 23,773,798
Trainable params: 208,998
Non-trainable params: 23,564,800
正如所料,该模型只有 208,998 个可训练权重,对应于最后一个 softmax 激活的全连接层的权重。
在所有执行的实验中,上面定义的模型仅被训练 30 个时期,使用批量大小 16、具有学习速率 1e-4 的 Adam 优化器、稀疏分类交叉熵损失和具有耐心 10 的早期停止回调(监控确认损失度量)。
微调
微调可以通过解冻之前用于特征提取的模型的最后几层,然后以较低的学习速率重新训练来实现。
应该解冻多少层的选择取决于
源和目标域和任务的相似程度。在这种情况下,由于 flower 域与 ImageNet 域没有太大的不同,因此只解冻卷积基础的最后两层是合理的,在 ResNet50-v2 的情况下,这两层是“conv5_block3”和“post”层。这可以通过以下方式完成:
for layer in conv_base.layers:
if layer.name.startswith('conv5_block3'):
layer.trainable = True
if layer.name.startswith('post'):
layer.trainable = True
编译完成后,将要微调的整体模型如下所示:
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
resnet50v2 (Model) (None, 2048) 23564800
_________________________________________________________________
dense_2 (Dense) (None, 102) 208998
=================================================================
Total params: 23,773,798
Trainable params: 4,677,734
Non-trainable params: 19,096,064
可以注意到,由于 ResNet50 的架构,即使仅解冻卷积基础的最后两层,可训练参数的数量也是显著的。这是将解冻层限制为最后两层的另一个原因,因为解冻更多的层会导致更多的可训练权重,这可能会导致过度拟合,因为模型微调的数据集大小有限。
在所有执行的实验中,使用先前用于特征提取的相同配置来训练模型,唯一的不同是,这次模型以 1e-5 的学习率(比用于特征提取的学习率低 10 倍)被训练更多的时期。
评估指标
选择用于评估被训练的分类器的性能的度量是 F1 分数,其对应于精确度和召回率的调和平均值,并且代表了比较两个不同分类器的性能的简单且有效的方式。
由于我们正在处理多类分类问题,所有类的平均精度和召回率用于计算 F1 分数。这些平均值可以用两种不同的方法计算:通过微平均或宏平均。
对于微平均,考虑所有类别的真阳性(TP)、假阳性(FP)和假阴性(FN)来计算平均精度和召回值。取而代之的是宏平均,首先评估每个类的精度和召回率,然后通过对不同类获得的结果进行平均来计算各自的平均值。
下图通过显示计算平均精度所需的相应方程,阐明了微观平均和宏观平均之间的区别(平均召回率可以类似地计算)。
图 4 :分别通过微观和宏观平均计算平均精度的方程。
尽管微观平均的计算成本更高,但它强调了模型在通用性较低的类别上表现良好的能力(例如,较少的示例),而宏观平均则缓解了这一问题[4]。由于这些原因,由于 102 种花的类别具有不同的普遍性,下一节中给出的所有 F1 分数都是使用微平均计算的,因此,如果一个模型对于具有较少实例的类别表现不佳,这将在其最终的平均 F1 分数中得到强调。
结果
下图总结了在不使用任何数据扩充策略的情况下,应用“简单”特征提取和微调时,从三个不同数据集获得的结果。
图 5 :在没有采用数据扩充的情况下,应用特征提取和微调在三个训练数据集上获得的 F1 分数(微平均)。
正如所料,对于所有数据集,通过微调 ResNet 获得的结果优于仅通过特征提取获得的结果。事实上,通过微调预训练的卷积基,其与域特定特征相关联的最后层的权重被轻推,使得先前由网络在 ImageNet 域上学习的表示适应于新的 flowers 域,因此它们变得更有代表性和更有效,从而导致更高的 F1 分数。
最重要的结果是,仅通过在仅由大约 800 个示例组成的最小数据集上微调 ResNet50-v2,仍有可能达到 0.79 的微观平均 F1 分数。
这一结果转化为这样一个事实,即通过迁移学习,开发的模型能够学习如何将花卉图像分类(具有相当的准确性)到 102 个可能的类别中,平均而言,每个类别仅看到 8 个图像。
下图显示了进行与之前相同的实验所获得的 F1 分数,但是这一次使用了之前描述的数据扩充技术。
图 6 :应用特征提取和微调,采用数据扩充,在三个数据集上获得的 F1 分数(微观平均)。
从图 4 和图 5 所示的两个图表中,可以注意到,当执行特征提取时,数据扩充不会带来任何好处,因为所有数据集的 F1 分数保持不变。然而,这可能与在所有三个数据集上用特征提取训练的分类器仅被训练了 30 个时期的事实有关。
表 1 :用于执行实验的数据集的实例数量和分割百分比(参考完整的未分割 flowers 数据集)。
回想大、中、小集合的大小,为了方便起见,在上面的表格中再次示出,与在其他集合上获得的 F1 分数相比,在小训练集合上获得的 F1 分数更有意义,因为与小训练集合相关联的测试集合分别是与中、大集合相关联的测试集合的两倍和十倍。这是假设仅用 800 幅图像(大约)训练的分类器实际上表现良好的另一个原因。
下图中的图表显示了训练数据的缺乏对通过迁移学习训练的分类器的影响程度。
图 7 :与大型数据集相比,中型和小型数据集的 F1 分数和训练数据集大小减少的百分比。
图表的蓝色条表示在中型和小型数据集上训练的分类器相对于在大型数据集上训练的分类器的 F1 分数减少百分比(仅考虑每个数据集获得的最佳 F1 分数),而橙色条对应于中型和小型数据集相对于大型数据集的训练集大小减少百分比。
该图表强调了通过迁移学习训练的分类器如何在缺乏训练数据的情况下特别稳健:
将训练分类器的数据集的大小减少 50%会导致 F1 分数仅下降 2%,而将数据集的大小减少 87%会导致 F1 分数仅下降 14%。
最后,有趣的是,数据扩充基本上不会为大中型数据集带来任何性能改善,但它允许我们在小型数据集上达到略高的 F1 分数,但只有在进行微调时。下图中的图表更清楚地表明了这一点,该图显示了在对三个不同的训练集执行微调时,数据扩充带来的 F1 改进百分比。说到底,数据增广带来的 F1 最大提升只有 2.53%。
图 8 :与仅仅“普通”微调相比,使用数据增强的微调的 F1 改进百分比。
结论
再次提出故事开始时提出的问题,即为了使迁移学习有效,到底需要多少训练实例,根据实验结果,可能的答案是,在这个具体的案例研究中,每班十个实例就足够了。事实上,即使采用只有大约 800 个训练样本的小数据集,仍然有可能在可能的 102 个类别上训练出具有显著准确性的分类器,其性能与在较大数据集上训练的分类器的性能相差不远。
因此,这些结果证实了在数据非常少的情况下迁移学习的有效性,显示了使用迁移学习方法训练的分类器的性能如何仅受到训练数据集大小的轻微影响。由于这个原因,数据扩充不会严重影响通过特征提取或微调训练的分类器的性能,即使它仍然带来轻微的改进,因此可以被认为是值得使用的,特别是考虑到它实现起来相对快速和简单。
然而,有必要指出的是,用于执行实验的数据集在通过迁移学习训练的分类器的优异性能中起着决定性的作用,因为所选数据集的 flower 域与 ImageNet 数据集的域没有太大的不同,在 ImageNet 数据集上卷积基已经被预先训练(即使 102 个类别 Flower 数据集的少数类别被包括在 ImageNet 类别集中)。如果使用属于完全不同于 ImageNet 域的特定域的数据集(例如,由标记的 X 射线照片的集合组成的数据集)进行实验,那么由预训练网络学习的表示可能对在目标数据集上训练的分类器没有用,从而导致更差的性能,而不管训练集的大小。
总之,假设目标域类似于所采用的卷积基已被预训练的域,特征提取和微调允许即使在极其有限的数据集上也能实现高性能,当很少训练数据可用时,以这种方式使迁移学习优于从头训练。
参考
[1] Aurlien Gron,用 Scikit-Learn 和 Ten-
sorFlow 进行机器学习:构建智能系统的概念、工具和技术第 1 期。 (2017)、奥莱利传媒
、、、水城颜【2】、网络中的网络(2013)
【3】玛利亚-艾琳娜·尼尔斯巴克和安德鲁·齐塞曼、 (2008)、印度计算机视觉、图形和图像处理会议
、【4】法布里吉奥·塞巴斯蒂安尼、(2008)
资源
Log-Mel 谱图图像分类中的 VGG-16 迁移学习
作为我之前帖子的后续,我将对 RAVDESS 音频数据集应用迁移学习,希望提高模型的准确性。回顾一下,迁移学习是一种深度学习方法,其中已经在一项任务上训练的模型被用作训练类似任务的模型的起点。在 DJ Sarkar 的这篇文章中,他用例子为理解迁移学习提供了很好的指导。
我们将首先尝试使用 VGG-16 预训练模型作为我们数据集的特征提取器,在这里我们冻结预训练模型的卷积块并修改密集层。然后,我们将尝试图像增强的预训练模型。
所以让我们开始吧!
数据准备
在导入必要的库之后,我们必须导入带有标签的训练和测试图像。
# For training set onlyimport globangry = glob.glob('/content/drive/My_Drive/train_logmel/angry/*.*')
calm = glob.glob('/content/drive/My_Drive/train_logmel/calm/*.*')
disgust = glob.glob('/content/drive/My_Drive/train_logmel/disgust/*.*')
fearful = glob.glob('/content/drive/My_Drive/train_logmel/fearful/*.*')
happy = glob.glob('/content/drive/My_Drive/train_logmel/happy/*.*')
neutral = glob.glob('/content/drive/My_Drive/train_logmel/neutral/*.*')
sad = glob.glob('/content/drive/My_Drive/train_logmel/sad/*.*')
surprised = glob.glob('/content/drive/My_Drive/train_logmel/surprised/*.*')data = []
labels = []for i in angry:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Angry')
for i in calm:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Calm')
for i in disgust:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Disgust')
for i in fearful:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Fearful')
for i in happy:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Happy')
for i in neutral:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Neutral')
for i in sad:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Sad')
for i in surprised:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Surprised')train_data = np.array(data)
train_labels = np.array(labels)
让我们来看看训练集中的图像示例:
作者图片
数据预处理
- 标准化数据
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
2.一键编码目标类
lb = LabelEncoder()
y_train = np_utils.to_categorical(lb.fit_transform(y_train))
y_test = np_utils.to_categorical(lb.fit_transform(y_test))
VGG-16 模型
首先,我们导入 VGG16 并设置必要的参数:
from keras.applications import VGG16vgg_model = VGG16(weights='imagenet',include_top=False, input_shape=(224, 224, 3))
权重= ‘imagenet’ :使用预训练的权重,而不是从头开始训练模型
include_top=False : 我们希望加载没有分类器层的模型,并添加我们自己的模型
input_shape=(224,224,3) :指定数据集中图像的首选形状
接下来,我们冻结卷积模块:
for layer in vgg_model.layers:
layer.trainable = False# Make sure you have frozen the correct layers
for i, layer in enumerate(vgg_model.layers):
print(i, layer.name, layer.trainable)
然后创建我们的密集层:
x = vgg_model.output
x = Flatten()(x) # Flatten dimensions to for use in FC layers
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x) # Dropout layer to reduce overfitting
x = Dense(256, activation='relu')(x)
x = Dense(8, activation='softmax')(x) # Softmax for multiclass
transfer_model = Model(inputs=vgg_model.input, outputs=x)
最后,我们编译并拟合模型:
learning_rate= 5e-5
transfer_model.compile(loss="categorical_crossentropy", optimizer=optimizers.Adam(lr=learning_rate), metrics=["accuracy"])history = transfer_model.fit(X_train, y_train, batch_size = 1, epochs=50, validation_data=(X_test,y_test))
经过 50 个时代,我们达到了 69%的准确率。
作者图片
这比以前的模型表现稍好,但必须有一种方法来提高模型的准确性!让我们尝试使用 VGG-16 作为图像增强的特征提取器。在处理小数据集时,图像增强是添加更多训练数据的好方法。
图像增强
使用 ImageDataGenerator,我们可以增强图像:
train_datagen = ImageDataGenerator(rescale=1./255, zoom_range=0.3, rotation_range=40, width_shift_range=0.3, height_shift_range=0.3, shear_range=0.3, horizontal_flip=True, fill_mode="nearest")train_generator = train_datagen.flow(train_data, train_lb, batch_size=1)val_datagen = ImageDataGenerator(rescale=1./255)val_generator = val_datagen.flow(test_data,val_lb,batch_size=1)
接下来,我们将构建我们的深度学习模型,编译该模型,然后拟合它:
x = vgg_model.output
x = Flatten()(x) # Flatten dimensions to for use in FC layers
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x) # Dropout layer to reduce overfitting
x = Dense(256, activation='relu')(x)
x = Dense(8, activation='softmax')(x) # Softmax for multiclass
transfer_model = Model(inputs=vgg_model.input, outputs=x)learning_rate= 5e-5
transfer_model.compile(loss="sparse_categorical_crossentropy", optimizer=keras.optimizers.Adam(lr=learning_rate), metrics=["accuracy"])history = transfer_model.fit_generator(train_generator, validation_data=val_generator, epochs=100, shuffle=True, verbose=1)
经过 100 个时期后,我们获得了 78%的准确率
作者图片
如您所见,这并不比我们之前的模型表现得更好,当前的模型过度拟合了训练数据。
我将研究其他预训练模型(如 Inception_V3 和 Resnet50 ),并探索微调,而不是使用预训练模型作为特征提取器。感谢您的阅读!😃
水果分类的迁移学习
迁移学习的简要概述,以及如何利用预训练模型通过 InceptionV3 进行深度学习的示例。
来自 Freerange 的 Jack Moreh 的“一个假想的人工智能大脑的图像”
或者你们当中不知道什么是深度学习的人,它是机器学习中的一种类型,属于人工智能的范畴。这种类型的技术不是我,机器人人工智能,我们作为一个物种,离真正开发这样的东西还很远。然而,当我们谈论像人类一样行动的可编程计算机时,深度学习和许多其他类型的机器学习方法非常接近。
“神经网络图”
深度学习
简而言之,深度学习是一种机器学习方法,它超越了可能只需要一两层学习的更小的学习方法,这可能被称为浅层学习。但是,深度学习是一层层的学习。代表这些层的模型被称为神经网络,其名称源于对神经生物学的研究,但不要混淆,因为它实际上不是一个模拟大脑功能的网络。神经网络有许多不同的形式,但我们将只关注我们的迁移学习中的一种,即卷积神经网络。
“卷积过程”
那么什么是卷积神经网络呢?
卷积神经网络只是训练网络(模型)以给出精确分类的另一种方法。在所有其他神经网络中,卷积神经网络在计算机视觉学习方面表现出色。CNN 或 covnets 的惊人之处在于,它们从图像中学习的模式是平移不变的,这意味着如果它们在图像的角落中找到一个模式,它们就会在任何不同的图像角落中识别出相同的模式,而常规网络必须一次又一次地重新学习它。Covnet 还能够学习模式的空间层次,这意味着 cov net 的每一层都将学习不同的东西。第一层可以学习小模式,下一层可以学习作为第一层特征的较大模式。这些特征是如何通过卷积函数获得的。
“卷积运动的 gif 图”,Narges Khatami,来自维基共享资源
卷积
卷积函数用于获得卷积层的特征图(特征矩阵)。在基线上,covnets 已经配置了由一个内核组成的权重。核用于从输入图像(输入层)中获得独特的特征,就像它可以用于收集输入图像的锐度、边缘或收集关于如何检测边缘的信息一样。这个函数可以表示为 nn,这是一个包含许多唯一值的矩阵。内核在输入图像的顶部进行*(滑动和相乘),假设输入图像是(10,10),内核是(3,3)。第一张幻灯片(stride)将在输入图像的左上角乘以 9 个像素,以在称为特征图的新矩阵的左上角产生单个像素的输出。
更新:第二次乘法应该是 4 * 2 = 8,用 8 代替 1 的乘积,得到所有乘积的和。
由 Krut Patel 从走向数据科学2019;詹姆斯·纳尔逊注释,2020 年。
当内核像这样滑过输入图像时,这种乘法继续进行。
由 Krut Patel 从走向数据科学2019;詹姆斯·纳尔逊注释,2020 年。
由 Krut Patel 从走向数据科学2019;詹姆斯·纳尔逊注释,2020 年。
这个连续的过程不会停止,直到整个特征矩阵已经被这些卷积值填充,并且一旦特征矩阵完成,它就被堆叠在卷积层内。如果网络被设计成这样,则另一个内核将产生具有相同输入图像的另一个特征矩阵,以将下一个特征矩阵存储在相同的卷积层中。
“制作特征矩阵时会发生什么”
简而言之,卷积神经网络的训练是关于定位每个核的所有正确值,以便当输入图像通过各层时,它将激活最后一个输出层上的不同神经元,以预测和准确分类图像。
什么是迁移学习,它有什么帮助?
迁移学习让每个人的生活变得更轻松、更美好。尽管从头开始创建卷积神经网络很有趣,但它们可能有点贵并且耗费大量计算能力。因此,为了减少网络所需的功率量,我们使用转移学习,这是已经在另一个图像上经过训练的预训练权重,以便提高我们网络的性能。使用预训练模型是一个最佳选择,因为它们已经在数以百万计的其他图像上进行配置和训练,这些图像由数千个类别组成,每次持续多天,以提供我们所需的高性能预训练权重,以便轻松训练我们自己的网络(Aditya Ananthram,2018)。
实际应用
现在,为了展示迁移学习能力的实际应用,我将介绍所使用的数据、选择的预训练模型、模型架构,然后是代码。
“意大利的一个农贸市场替身”,作者是散养农场的梅里泽
数据的描述
数据集包含 81,104 张不同水果和蔬菜的图像,由每张水果和蔬菜图像的 120 个唯一分类组成。图像的总数被分成训练和测试数据集。训练数据集包含 60,486 幅图像,测试数据集是 20,618 幅图像。
所有图像的大小都是 100x100 像素,是用罗技 C920 相机收集的,该相机用于拍摄水果/蔬菜(Mihai Oltean,2019)。所有的水果和蔬菜都被种植在一个装有低速马达的轴内,在那里它们被记录下来,每一个持续时间为 20 秒。水果和蔬菜测试图像是用 5X 智能手机拍摄的。
模型
“一个‘图腾’旋转的图像。你还在做梦吗?”照片由现代灵感的灰烬在 Unsplash 上拍摄
选择的迁移学习模型叫做 InceptionV3 。该模型是一个卷积神经网络,在架构上设计为 48 层深度,对 299×299 的图像形状进行训练。最初的 Inception 架构网络被称为“GoogLeNet”,这是一个 27 层深度卷积神经网络,早在 2014 年就已建立(Shaikh,2018)。该模型的名称来自克里斯托弗·诺兰导演的电影“盗梦空间”,基于深入梦境的概念“梦中的梦”,转化为卷积神经网络中的卷积神经网络。
GoogLeNet 设计背后的想法是消除在处理更深层次的神经网络时经常发现的过度拟合问题。当数据集太小并且正在大型神经网络中进行训练时,通常会发生过度拟合,过度拟合带来的问题是对模型的验证准确性(测试准确性)的错误陈述。测试准确性是衡量训练好的网络准确预测它没有看到的图像的精确度。设计一个巨大的网络来产生这种准确性的解决方案是创建一个稀疏连接的神经网络来代替完全连接的神经网络(Shaikh,2018),这就是为什么 GoogLeNet 模型在 2014 年以 80%以上的预测准确性赢得了 ImageNet 视觉识别挑战。
模型架构
InceptionV3 模型连接到底部的两个完全连接的层,但在此连接之前,其维度从 3D 减少到 1D,具有全球平均池 2D 。汇集还将为每个特征矩阵输出一个响应。在汇集之后,架构的下一层是具有 512 个单元(神经元)的第一密集隐藏层,其将连接到具有 10 个神经元的最终输出层,以匹配水果和蔬菜类的数量。这就是 InceptionV3 架构的样子。
英特尔的 Milton-Barker Adam 的“预训练 InceptionV3 模型的架构设计”
这是连接到架构的底层全连接层的样子。
“附加到完全连接的层的 InceptionV3 模型的底层架构布局”
还值得一提的是,微调这些预先训练的模型以及与它们相关联的权重。我可以从模型中选择我想要使用的权重,它可以是上半部分、下半部分、中间部分,或者我可以冻结所有权重。这样做意味着我冻结的预训练模型的任何部分都不会是可以为我正在制作的模型更新的可训练权重。我还可以选择模型被训练的图像的权重,但是在这个例子中,通过反复试验,我选择不冻结权重。我对 InceptionV3 的实现将使用在 ImageNet 上预先训练的权重。“ImageNet 是一个根据 WordNet 层次结构(目前只有名词)组织的图像数据库,其中层次结构的每个节点都由成百上千的图像描述。目前,我们平均每个节点有超过 500 张图像。”(《影像网》,2017 年)
最后是代码
现在,您已经对数据集的外观和模型架构有了一个概念,是时候执行了。
准备数据和训练网络
加载库。
先做最重要的事情;我们必须加载必要的库。加载库时,确保导入所有必需的模块,以便我们可以准备数据和训练模型。
*# read in libraries*
import tensorflow as tf
from tensorflow.keras import backend, models, layers, optimizers
import numpy as np
from tensorflow.keras.layers import GlobalAveragePooling2D
from tensorflow.keras.callbacks import EarlyStopping
from sklearn.model_selection import train_test_split
from tensorflow.keras.utils import plot_model
from IPython.display import display
from PIL import Image
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import os, shutil
from tensorflow.keras.models import Model
np.random.seed(42)
加载数据并准备好。
接下来,为了准备数据,我们需要用 ImageDataGenerator 设置一个 train_datagen 和 test_datagen。然后,使用这些生成器调整训练数据和测试数据的图像大小,以匹配预训练模型的像素图像输入。以确保神经网络不会学习不相关的模式,并反过来提高整体性能。
*# Specify the base directory where images are located.*
base_dir = '/kaggle/input/fruits/fruits-360/' *# Specify the traning, validation, and test dirrectories.*
train_dir = os.path.join(base_dir, 'Training')
test_dir = os.path.join(base_dir, 'Test')*# Normalize the pixels in the train data images, resize and augment the data.*
train_datagen = ImageDataGenerator(
rescale=1./255,*# The image augmentaion function in Keras*
shear_range=0.2,
zoom_range=0.2, *# Zoom in on image by 20%*
horizontal_flip=True) *# Flip image horizontally* *# Normalize the test data imagees, resize them but don't augment them*
test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(299, 299),
batch_size=16,
class_mode='categorical')test_generator = test_datagen.flow_from_directory(
test_dir,
target_size=(299, 299),
batch_size=16,
class_mode='categorical')
准备 InceptionV3 模型
既然已经准备好了图像,现在是时候导入并设置迁移学习的预训练 InceptionV3 模型了。
*# Load InceptionV3 library*
from tensorflow.keras.applications.inception_v3 import InceptionV3*# Always clear the backend before training a model*
backend.clear_session()*# InceptionV3 model and use the weights from imagenet*
conv_base = InceptionV3(weights = 'imagenet', *#Useing the inception_v3 CNN that was trained on ImageNet data.*
include_top = False)
创建一个功能 API 模型。
现在,让我们将预训练的 InceptionV3 模型权重与密集层(完全连接的层)相结合,并减少两者之间的模型维度。
*# Connect the InceptionV3 output to the fully connected layers*
InceptionV3_model = conv_base.output
pool = GlobalAveragePooling2D()(InceptionV3_model)
dense_1 = layers.Dense(512, activation = 'relu')(pool)
output = layers.Dense(120, activation = 'softmax')(dense_1)
显示功能 API 模型。
为了了解模型架构,我们可以将功能 API 模型作为一个整体来显示,以直观地看到网络的深度。
*# Create an example of the Archictecture to plot on a graph*
model_example = models.Model(inputs=conv_base.input, outputs=output)
*# plot graph*
plot_model(model_example)
(模型太大,无法在介质上显示,请点击此链接查看)
定义模型并编译它。
为了让我们训练模型,我们需要定义函数式 API 模型,并使用类别交叉熵作为损失函数以及具有学习速率和动量参数的随机梯度下降来编译该模型。
*# Define/Create the model for training*
model_InceptionV3 = models.Model(inputs=conv_base.input, outputs=output)*# Compile the model with categorical crossentropy for the loss function and SGD for the optimizer with the learning*
*# rate at 1e-4 and momentum at 0.9*
model_InceptionV3.compile(loss='categorical_crossentropy',
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy'])
查看 GPU 要使用的设备列表。
现在我建议使用 GPU 来训练这个模型,因为 InceptionV3 模型有超过 2100 万个参数,在 CPU 上训练可能需要几天才能完成。如果你有一个 GPU,你可以使用你自己的,但我使用了 Kaggle 的 GPU 提供给他们的笔记本电脑,这花了我大约 20-25 分钟来完成培训。找到可用的 GPU 设备,以便加快训练过程。
*# Import from tensorflow the module to read the GPU device and then print*from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())
训练模型。
在找到要使用的 GPU 后,我们将把它合并到我们的代码中,最终使用 train_generator 为训练数据训练模型,并将 validation_data 参数设置为 test_generator。
*# Execute the model with fit_generator within the while loop utilizing the discovered GPU*
import tensorflow as tf
with tf.device("/device:GPU:0"):
history = model_InceptionV3.fit_generator(
train_generator,
epochs=5,
validation_data=test_generator,
verbose = 1,
callbacks=[EarlyStopping(monitor='val_accuracy', patience = 5, restore_best_weights = True)])
99%的验证准确性和 0.0187 的损失是非常好的。
显示模型的测试精度和测试损失值
现在,让我们通过绘制各个时期的训练精度/验证精度和训练损失/验证损失来看看我们的模型是什么样子,然后打印最终的测试精度和测试损失。
*# Create a dictionary of the model history*
import matplotlib.pyplot as plt
history_dict = history.history
loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
acc_values = history_dict['accuracy']
val_acc_values = history_dict['val_accuracy']
epochs = range(1, len(history_dict['accuracy']) + 1)*# Plot the training/validation loss*
plt.plot(epochs, loss_values, 'bo', label = 'Training loss')
plt.plot(epochs, val_loss_values, 'b', label = 'Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()*# Plot the training/validation accuracy*
plt.plot(epochs, acc_values, 'bo', label = 'Training accuracy')
plt.plot(epochs, val_acc_values, 'b', label = 'Validation accuracy')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show() *# Evaluate the test accuracy and test loss of the model*
test_loss, test_acc = model_InceptionV3.evaluate_generator(test_generator)print('Model testing accuracy/testing loss:', test_acc, " ", test_loss)
结果分析
对 120 类果蔬图像的准确预测结果表明,测试准确率为 99%,损失值为 1.8%。损失价值是衡量我们的产出与我们的期望之间的距离。训练分 5 个阶段进行,每个阶段大约需要 20-25 分钟,在 ka ggle GPU 的帮助下可以达到这一精度,从而加快了整个过程。训练数据是一个 3781 步的过程(迭代),每 16 个样本取一批数据,向前和向后传播给我们一遍。一遍等于一次迭代。
结论
总之,迁移学习是训练数据集识别和分类图像的一种非常有效的方法。它允许快速设置,而无需从头开始详细设计卷积神经网络架构,并且它利用预训练模型的先前训练来提供高精度。你可以去我项目的 Kaggle 笔记本了解更多信息。
参考
阿迪蒂亚·阿南瑟拉姆。(2018 年 10 月 17 日)。Keras 中使用迁移学习的初学者深度学习。2020 年 4 月 24 日检索,来自 Medium 网站:https://towards data science . com/keras-transfer-learning-for-初学者-6c9b8b7143e
f .谢赫(2018 . 10 . 18)。从头开始理解 Inception 网络(带 Python 代码)。2020 年 5 月 7 日检索,来自 Analytics Vidhya 网站:https://www . Analytics vid hya . com/blog/2018/10/understanding-inception-network-from-scratch/
f . chollet(2018)。用 Python 进行深度学习。庇护岛(纽约,州联合):曼宁,警察。
文件:Valid-padding-convolution.gif —维基共享。(2018 年 7 月 6 日)。2020 年 5 月 6 日检索,来自 wikimedia.org 网站:https://commons . wikimedia . org/wiki/File:Valid-padding-convolution . gif
获取蓝色背景的人脑智能概念的免费库存照片在线|下载最新免费图片和免费插图。(2020).2020 年 5 月 9 日检索,来自 freerangestock.com 网站:https://freerangestock . com/photos/65677/concept-of-intelligence-with-human-brain-on-blue-background . html
在线获取意大利水果和蔬菜供应商的免费图片|下载最新免费图片和免费插图。(2020).检索于 2020 年 5 月 9 日,来自 freerangestock.com 网站:https://freerangestock . com/photos/37652/fruit-and-vegetables-vendor-Italy . html
https://github.com/syt123450,的 syt123450。(2020).图层 GlobalPooling2d。2020 年 5 月 6 日检索,来自 Tensorspace.org 网站:【https://tensorspace.org/html/docs/layerGlobalPooling2d.html
ImageNet。(2017).2020 年 5 月 7 日检索,来自 Image-net.org 网站:【http://www.image-net.org/
克鲁特·帕特尔。(2019 年 9 月 8 日)。卷积神经网络-数据科学初学者指南。2020 年 4 月 24 日检索,来自 Medium 网站:https://towards data science . com/convolution-neural-networks-a-beginners-guide-implementing-a-m NIST-hand-written-digit-8aa 60330d 022
米哈伊·奥尔特安。(2020).水果 360。检索于 2020 年 5 月 6 日,来自 Kaggle.com 网站:https://www.kaggle.com/moltean/fruits
米尔顿-巴克,A. (2019 年 2 月 17 日)。用于急性髓细胞/淋巴细胞白血病分类的 Inception V3 深度卷积架构。2020 年 5 月 6 日检索,来自 intel.com 网站:https://software . Intel . com/en-us/articles/inception-v3-deep-convolutional-architecture-for-classification-acute-myeloid-lymphoblastic
普拉哈尔·甘尼什。(2019 年 10 月 18 日)。卷积核的类型:简化—面向数据科学。2020 年 4 月 24 日检索,来自 Medium 网站:https://towardsdatascience . com/types-of-convolution-kernels-simplified-f 040 CB 307 c 37
Unsplash。(2020).现代灵感的灰烬。检索于 2020 年 5 月 9 日,来自 Unsplash.com 网站:https://unsplash.com/@modernafflatusphotography
可视面包屑。(2016).2020 年 4 月 27 日检索,来自 mathworks.com 网站:https://www . mathworks . com/help/deep learning/ref/inceptionv3 . html
维基百科贡献者。(2020 年 5 月 2 日)。我,机器人(电影)。2020 年 5 月 5 日检索,来自维基百科网站:https://en . Wikipedia . org/wiki/I,Robot(film)
用 PyTorch 迁移学习
以及为什么您不应该从头开始编写 CNN 架构
如今,训练深度学习模型,尤其是与图像识别相关的模型,是一项非常简单的任务。有很多原因可以解释为什么你不应该过分强调架构,主要是有人已经为你做了这一步。至于其他的,你得继续读下去。
由 Unsplash 上的 drmakete 实验室拍摄的照片
源代码: Colab 笔记本
如今,作为一名工程师,你唯一应该关注的是数据准备——在深度学习的世界中,这个术语概括了数据收集、加载、规范化和扩充的过程。
今天的议程很简单——解释什么是迁移学习以及如何使用它,然后是模型培训的实际示例,包括有无预培训架构。
听起来很简单,所以让我们直入主题吧!
数据集下载和基本准备
先说进口。这里我们有常见的嫌疑人,如 Numpy 、 Pandas 和 Matplotlib ,但也有我们最喜欢的深度学习库——py torch——以及它所提供的一切。
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetimeimport torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision.utils import make_grid
from torchvision import models, transforms, datasets
我们正在用 Colab 或者更准确地说是 Colab Pro 编写这段代码,所以我们将利用 GPU 的能力进行训练。如果您不知道 Colab 是什么,或者想知道升级到 Pro 版本是否值得,请随时查看这些文章:
[## Google Colab:它与支持 GPU 的笔记本电脑相比如何?
Colab 简介、运行时、性能比较…以及疑难解答
towardsdatascience.com](/google-colab-how-does-it-compare-to-a-gpu-enabled-laptop-851c1e0a2ca9) [## Colab Pro:物有所值吗?
10 美元有多大作用?我们做了测试。你做阅读。
towardsdatascience.com](/colab-pro-is-it-worth-the-money-32a1744f42a8)
因为我们是在 GPU 上训练,而这可能不适合你,所以我们需要一个健壮的方法来处理这个问题。这里有一个标准方法:
device = torch.device(‘cuda:0’ if torch.cuda.is_available() else ‘cpu’)
device**>>> device(type=’cuda’, index=0)**
如果你正在进行 cpu 方面的培训,它应该会显示类似于 type=‘cpu’ 的内容,但是因为 Colab 是免费的,所以没有必要这样做。
现在进入数据集。为此,我们将使用狗或猫数据集。它有大量不同大小的图像,我们将在后面处理。现在我们需要下载并解压它。方法如下:
%mkdir data
%cd /content/data/
!wget [http://files.fast.ai/data/dogscats.zip](http://files.fast.ai/data/dogscats.zip)!unzip dogscats.zip
大约一分钟后,根据你的网速,数据集就可以使用了。现在,我们可以将它声明为一个数据目录——这不是必需的,但会节省我们一点时间。
DIR_DATA = ‘/content/data/dogscats/’
数据准备
第一部分的第一部分现在完成了。接下来,我们必须对训练和验证子集应用一些转换,然后用 DataLoaders 加载转换后的数据。以下是我们应用的转换:
- 随机旋转
- 随机水平翻转
- 调整到 224x224 大小—预训练体系结构需要
- 转换为张量
- 正常化
代码如下:
train_transforms = transforms.Compose([
transforms.RandomRotation(10),
transforms.RandomHorizontalFlip(p=0.5),
transforms.Resize(224),
transforms.CenterCrop((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])valid_transforms = transforms.Compose([
transforms.Resize(224),
transforms.CenterCrop((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
现在我们用数据加载器加载数据。这一步也很简单,您可能很熟悉:
train_data = datasets.ImageFolder(os.path.join(DIR_DATA, ‘train’), transform=train_transforms)
valid_data = datasets.ImageFolder(os.path.join(DIR_DATA, ‘valid’), transform=valid_transforms)torch.manual_seed(42)
train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
valid_loader = DataLoader(valid_data, batch_size=64, shuffle=False)class_names = train_data.classes
class_names**>>> ['cats', 'dogs']**
如果我们现在对单个批次进行逆归一化并将其可视化,我们会得到这样的结果:
快速看一下上面的图像,就可以看出我们的转换工作符合预期。
数据准备部分现在已经完成,在下一部分,我们将声明一个定制的 CNN 架构,训练它,并评估性能。
定制建筑 CNN
对于这一部分,我们想做一些非常简单的事情——3 个卷积层,每个卷积层后面是 max-pooling 和 ReLU,然后是一个完全连接的层和一个输出层。
这是这个架构的代码:
class CustomCNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1)
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1)
self.conv3 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1)
self.fc1 = nn.Linear(in_features=26*26*64, out_features=128)
self.out = nn.Linear(in_features=128, out_features=2) def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = F.relu(self.conv3(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = x.view(-1, 26*26*64)
x = F.relu(self.fc1(x))
x = F.dropout(x, p=0.2)
x = self.out(x)
return F.log_softmax(x, dim=1)torch.manual_seed(42)
model = CustomCNN()
model.to(device)
在这里,我们可以定义一个优化器和标准,并准备好进行培训:
custom_criterion = nn.CrossEntropyLoss()
custom_optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
因为你可以访问源代码、和 train_model 函数太长了,我们决定不把它放在这里。因此,如果您继续学习,请参考源代码。我们将为 10 个时期训练模型:
custom_model_trained = train_model(
train_loader=train_loader,
test_loader=valid_loader,
model=model,
criterion=custom_criterion,
optimizer=custom_optimizer,
epochs=10
)
一段时间后,获得的结果如下:
无论如何,结果并不可怕,但我们如何才能做得更好?转学为救。
迁移学习法
你可以很容易地在网上找到正式的定义。对我们来说,迁移学习意味着下载一个预制的架构,它是在 1M 以上的图像上训练的,并调整输出层,以便它可以根据你的需要分类许多类别。
因为我们这里只有猫和狗,所以我们需要将这个数字修改为 2。
现在,我们将下载预训练版本的 ResNet101 架构,并使其参数不可训练——因为网络已经训练好了:
pretrained_model = models.resnet101(pretrained=True)for param in pretrained_model.parameters():
param.requires_grad = False
太好了!让我们看看输出图层是什么样子的:
pretrained_model.fc**>>> Linear(in_features=2048, out_features=1000, bias=True)**
所以这个架构默认有 1000 个可能的类,但是我们只需要两个——一个用于猫,一个用于狗。以下是调整方法:
pretrained_model.fc = nn.Sequential(
nn.Linear(2048, 1000),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(1000, 2),
nn.LogSoftmax(dim=1)
)pretrained_model.to(device)
这就是我们要做的。
我们仍然需要定义一个优化器和一个标准,但是你知道怎么做:
pretrained_criterion = nn.CrossEntropyLoss()
pretrained_optimizer = torch.optim.Adam(pretrained_model.fc.parameters(), lr=0.001)
训练过程与定制架构相同,但是我们不需要这么多的纪元,因为我们已经知道权重和偏差的正确值。
pretrained_model_trained = train_model(
train_loader=train_loader,
test_loader=valid_loader,
model=pretrained_model,
criterion=pretrained_criterion,
optimizer=pretrained_optimizer,
epochs=1
)
一段时间后,获得的结果如下:
多神奇啊?不仅准确度提高了,而且我们还通过不训练太多纪元节省了大量时间。
现在你知道迁移学习能做什么,以及如何和为什么使用它。让我们在下一部分总结一下。
结论
这就是 PyTorch 最简单的迁移学习指南。当然,如果网络更深,定制模型的结果可能会更好,但这不是重点。重点是,没有必要强调多少层是足够的,以及最佳超参数值是多少。至少在大多数情况下是这样。
请务必尝试不同的架构,并随时在下面的评论部分告诉我们结果。
感谢阅读。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
使用 ggplot2 转换数据并创建美丽的可视化效果
创建可视化的目的是探索数据,发现隐藏的趋势和交流趋势。为了创造任何观想,我们需要一个我们希望探索的问题,我们需要可以帮助我们回答这个问题的数据。我们要探讨的问题是“新冠肺炎事件如何改变了人们的流动模式?”我们将使用的数据比较了因新冠肺炎而在不同地方发生的基线移动趋势的变化,这些数据由 Google 在这里提供。
遵循本教程将帮助您理解如何转换 R 中的数据并绘制堆积条形图。以下是最终结果:
新冠肺炎事件导致欧洲国家人口流动模式的变化
感兴趣的列的描述:
国家代码— “country_region_code”
国家名称— “country_region”
零售/娱乐空间变更—" Retail _ and _ Recreation _ percent _ avg "
杂货店/药房空间变更—“杂货店 _ and _ 药房 _percent_avg”
停车位变更— “parks_percent_avg”
公交站空间变更—" Transit _ stations _ percent _ avg "
工作场所空间变更— "workplaces_percent_avg "
如果您仅对 ggplot2 定制感兴趣,请跳至步骤 3。
我们的数据探索之旅开始了:
步骤 0:导入库
# Importing librarieslibrary("dplyr")
library("magrittr")
library("plotly")
library("ggplot2")
library("tidyr")
library("cowplot")
第一步:读取数据
# Reading downloaded csv
data <- read.csv(file = 'Global_Mobility_Report.csv')# Print column names in dataframe
colnames(data)
数据中的列
步骤 2:转换数据
我们将为欧洲国家创建可视化,因此我们将不得不过滤掉其他国家。
# Required country codes
country_region_code <- c("GB","SK","SI","SE","RO","PT","PL","NO","NL","MT","LV","LU","LT","IT","IE","HU","HR","GR","FR","FI","ES","EE","DK","DE","CZ","BG","BE","AT")# Creating a subset using required country codes
subset = data[data$country_region_code %in% country_region_code ,]# Check countries in subset
unique(subset$country_region)
将包含在可视化中的国家
在我们的数据中,我们列出了每天的移动趋势变化,但我们希望绘制整个期间的变化,因此我们必须汇总数据。为此,我们将使用country_region_code
进行分组,并为每个移动类别计算mean
。
# Aggregating data to get average percent change
aggdata <- subset %>%group_by(country_region_code, country_region) %>%
summarize(retail_and_recreation_percent_avg = mean(retail_and_recreation_percent_change_from_baseline, na.rm = TRUE),
grocery_and_pharmacy_percent_avg = mean(grocery_and_pharmacy_percent_change_from_baseline, na.rm = TRUE),
parks_percent_avg = mean(parks_percent_change_from_baseline, na.rm = TRUE),
transit_stations_percent_avg = mean(transit_stations_percent_change_from_baseline, na.rm = TRUE),
workplaces_percent_avg = mean(workplaces_percent_change_from_baseline, na.rm = TRUE),
residential_percent_avg = mean(residential_percent_change_from_baseline, na.rm = TRUE))colnames(aggdata)
聚合数据中的列
我们将添加另一列overall_mob_percent
,它将显示流动性百分比的总体变化,以便我们可以将受流动性变化影响最大的国家的数据排序为最小。
# Adding additional average change column
aggdata <- transform(aggdata, overall_mob_percent=(retail_and_recreation_percent_avg+grocery_and_pharmacy_percent_avg+ parks_percent_avg+transit_stations_percent_avg+ workplaces_percent_avg+residential_percent_avg))colnames(aggdata)
新列`总体百分比’
目前,我们的数据以宽格式存储,其中每一类流动性变化都有单独的列。
列形式的类别
在使用gather
绘图之前,我们必须将数据转换成长格式。
# Gathering data
aggdata_tsfm <- gather(aggdata, key = "parameter", value = "percent", -country_region_code, -country_region, -overall_mob_percent)colnames(aggdata_tsfm)
列“参数”有类别,“百分比”有相关值
订购数据。
# Sort based on overall_mob_percent
aggdata_tsfm <- aggdata_tsfm[order(aggdata_tsfm$overall_mob_percent),]
将country_region
转换为因子,以便在我们的绘图中保留顺序。
# Converting to factor for preserving sequence in our visualisation
aggdata_tsfm$country_region <- factor(aggdata_tsfm$country_region,
levels = unique(aggdata_tsfm$country_region))
第三步:创建堆积条形图
这里,aggdata_tsfm
是我们的数据框架,x
轴是国家,y
轴是移动值的百分比变化,我们将fill
用不同的地点类别堆积条形图。我们已经将position
设置为stack
来创建一个堆叠条形图。您可以将position
设置为dodge
来创建并排条形图。为了给我们的条形块一个黑色轮廓,我们将color
设置为黑色。
# Creating the plot
mainplot <- ggplot(aggdata_tsfm, aes(width = 0.9, fill=parameter, y=percent, x=country_region)) +
geom_bar(position="stack", stat="identity", color="black")
添加水平线以区分-ve、+ve y 轴,因为我们的数据在 y 轴上有正值也有负值。
# Adding line to differentiate -ve and +ve y axis
mainplot <- mainplot + geom_hline(yintercept=0, color = “white”, size=2)
添加 y 刻度,因为默认情况下刻度数非常少。我们的刻度将从-250 增加到 100,增加 50。
# Adding y ticks
mainplot <- mainplot + scale_y_continuous(breaks = round(seq(-250, max(100), by = 50),1))
我们将定制我们的地块的图例,以改变颜色,标签和顺序。
您可以使用 ColorBrewer 为定性和定量数据提供漂亮的调色板,并为色盲人士进行了优化,而不必纠结于使用哪种调色板。
一旦我们选择了颜色,我们就可以通过设置values
参数来使用它们。要更改图例中的标签名称,我们可以设置labels
。为了改变标签的顺序,我们使用breaks
来指定所需的顺序。
# Changes to legend
mainplot <- mainplot + scale_fill_manual(name="Mobility categories",values = c("retail_and_recreation_percent_avg" = "#8dd3c7",
"grocery_and_pharmacy_percent_avg" = "#ffffb3",
"parks_percent_avg" = "#bebada",
"transit_stations_percent_avg" = "#fb8072",
"workplaces_percent_avg" = "#80b1d3",
"residential_percent_avg" = "#fdb462"),
labels=c("retail_and_recreation_percent_avg" = "Retail/Recreation",
"grocery_and_pharmacy_percent_avg" = "Grocery/Pharmacy",
"parks_percent_avg" = "Parks",
"transit_stations_percent_avg" = "Transit stations",
"workplaces_percent_avg" = "Workplaces",
"residential_percent_avg" = "Residential"),
breaks=c("residential_percent_avg", "workplaces_percent_avg",
"transit_stations_percent_avg",
"retail_and_recreation_percent_avg", "parks_percent_avg",
"grocery_and_pharmacy_percent_avg"))
改变我们的标题。
# plot title
mainplot <- mainplot + ggtitle(“Changes in movement of people due to COVID-19”)
设置我们的情节的副标题和标题。
# plot subtitle, caption
mainplot <- mainplot + ggtitle(“Changes in movement of people due to COVID-19”)
设置 x 和 y 标签。
# Setting x and y labels
mainplot <- mainplot + xlab(“Countries”) + ylab(“Percent change in mobility rate across different activities”)
由于 x 轴有国家名称,我们将旋转文本以避免文本重叠。
# x axis markings rotated
mainplot <- mainplot + theme(axis.text.x = element_text(angle = 90))
更改文本大小以提高可读性。
# change text size
mainplot <- mainplot + theme(text = element_text(size=20))
现在我们可以画出我们开始时展示的图表。
# Plotting
mainplot
看着可视化,更容易从数据中得到推论,比如人们在住宅区的活动增加了。负流动性最小的地方是杂货店和药店,这表明这些地方的客流量仍在增加,但不像过去那样了。
如果您希望在一个网格中绘制多个图表,您可以使用cowplot
的plot_grid
轻松完成。图resplo
、parplot
、recplot
、groplot
、traplot
和worplot
是使用与上述步骤 3 中演示的相同方法创建的每个类别的不同图,我们将它们绘制在网格中。
# Plotting a grid of plots
plot_grid(resplot, parplot, recplot, groplot, traplot, worplot,
ncol = 2, nrow = 3)
新冠肺炎事件导致欧洲国家人口流动模式的变化
我们已经看到了使用 ggplot2 创建强大的可视化是多么容易,以及如此多的定制你的情节的方法。我们几乎没有探索 ggplot2,它提供了如此多的东西。我强烈推荐探索 ggplot2 提供的其他图表和功能。
感谢阅读。
使用 Nyströ方法将数据转换到高维核空间
Nyströ方法将作为一种使核空间显式地可用于解决非线性问题的方法来介绍。
约书亚·索蒂诺在 Unsplash 上拍摄的照片
介绍
在机器学习领域,有许多算法,监督的(SVM,岭回归)和非监督的(k-means,PCA),它们可以被内核化以将问题映射到更高维的内核空间。这有助于解决更复杂的问题,但也带来了对核矩阵的依赖性,这种依赖性与给定样本的数量成平方关系。对于具有大量样本的问题,这可能会非常耗费成本和内存,因此很难应用。
本文将介绍 Nyströ方法,这是一种通过计算低秩核近似来有效存储和计算核矩阵的方法。此外,本文将说明 Nyströ方法可用于将任何样本从特征空间映射到近似的核空间。将给出一个示例来演示内核映射的潜在应用和由此产生的优势。
本文的目的是展示使用 Nyströ方法的优势和机会,无论是作为后续分类的预处理步骤,还是作为提高基于内核的算法性能的一个选项。
内核基础知识的简短回顾
核函数使得能够在高维核空间中操作,而不需要显式地将特征空间𝑋X 映射到核空间φφ。这是一个优点,因为对于一些映射函数(𝜙(𝑥)ϕ(x ),内核空间中的最终维数可能变得无限大,因此,映射过程非常耗费成本,甚至根本不可能计算。
基于核的算法利用了某些函数可以在另一个空间中被表示为内积的事实,而不是使用函数𝜙(𝑥对样本进行显式映射。
或者反过来:对于某些函数𝜙(𝑥),有函数𝜅(𝑥,𝑦),这样𝜅(𝑥,𝑦)= 。因此,使用核给仅使用给定特征空间的内积/距离操作的算法带来了优势。
常见的内核函数𝜅(𝑥,𝑦有:
- 径向基函数核
- 多项式核
- Sigmoid 内核
将核应用于给定的特征空间𝑋意味着必须计算包含𝜅(𝑥,𝑦结果的𝑛×𝑛核矩阵𝐾,使得
很明显,随着𝑛n.数量的增加,𝐾K 的大小呈二次方增加。因此,对于较大的数据集,计算可能会占用大量内存和时间。
Nyströ方法简介
假设𝜅(𝑥,𝑦)是导致核空间的函数,其中核维数𝑑大于样本数𝑛n,那么𝐾的最大秩必定是𝑚𝑖𝑛(𝑛,𝑑)=𝑛.所以,对于φ中的𝑛独立样本,k 是满秩的。现在让我们考虑,并非所有的𝑑d 维度都是必需的,使用具有𝑘
使用 nyströ方法计算低秩核矩阵𝐾̃can:从 x 中随机抽取𝑘样本。设 XS 是 x 的子集,仅包含这些样本,因此
设(𝑘×𝑘)矩阵𝑊是在 k 中转置的 c 和 c 的重叠,则近似核可以表示为
这个公式的推导和更详细的描述可以在[1][2]中看到。
因此,可以使用(𝑛×𝑘)和(𝑘×𝑘)矩阵来保存整个近似核。因为𝑘通常选择得比𝑛小得多,这在内存效率方面带来了巨大的优势。此外,核函数只需应用𝑛×𝑘次数,这可以大大减少计算时间。
定义参数 k
选择𝑘的值总是在近似质量和计算时间之间进行权衡。在内核近似的整个领域中有许多最近的研究,以及关于如何最佳地估计𝑘或者如何最有效地采样𝑘值来计算 c 的一些想法[3][4][5]。这些方法要么试图保证特定的运行时间与最坏情况下的近似质量保证相结合,要么保证近似内核的某些统计性能。
在这里讨论这些方法超出了范围。考虑这一主题的论文,可以在参考文献中找到。
一般来说,应该选择𝑘,这样𝑑≤𝑘
将样本映射到近似核空间
出于某些原因,比如数据的可视化或者不能直接使用内核的模型的应用,显式地访问φ可能是有用的。为了这个目的,我们可以修改 nyströ公式,以具有从核矩阵内积到显式近似核空间φ̃.的有效映射过程
可以看出,仅使用一次矩阵乘法就可以实现到低秩核空间的映射。𝑊的必要分解可以使用 Cholesky 分解、奇异值分解或特征分解来完成。
关于这一点,通过在内存中保存𝑋𝑆,任何任意采样𝑥都可以映射到内核空间:
- 计算𝑐,使𝑐[i]包含𝜅(𝑥,𝑋𝑆[i 的结果]))
- 计算𝜙̃ (𝑥)=𝑐𝑊^(−1/2)
实验——解决核空间中的非线性问题
以下示例演示了如何使用 Nyströ方法将非线性问题映射到近似核空间。因此,RGB 内核将被近似,并且所获得的内核部分 c 和𝑊将被用于将给定的特征空间变换到内核空间。
首先,让我们创建一个非线性问题:为了可视化的目的,数据具有包括 75 个样本的二维特征空间。在给定的空间中,有两类以不可线性分离的方式排列。在下面的图中,这两个类以不同的颜色显示。
然后,我们来定义核函数,也就是 RBF 核。这个内核有一个参数是γ,通常设置为 1/(2𝜎,𝜎是𝑋.的方差
现在,nyströ映射应用于𝑋和𝑘=100.结果是φ̃与𝑘维度。
出于可视化的目的,我们使用 PCA 来提取 3 个最相关的特征(具有最高方差的维度)。得到的 3 个维度在散点图中可视化。
散点图显示,来自第三维(PCA)的附加信息是决定性的,使得给定的问题可以线性分离。
为了证实这个视觉结果,我们将对这个问题应用线性回归,首先在特征空间,其次在核空间。此外,我们将比较两个模型的准确度训练分数(定义为(𝑇𝑃+𝑇𝑁)/(𝑇𝑃+𝑇𝑁+𝐹𝑃+𝐹𝑁))。因为问题是非线性的,而回归只能解决线性问题,所以在特征空间中应用的模型肯定是不合适的,从而导致较低的准确度分数。这意味着,如果第二个模型实现了非常高的训练精度,则问题必须在核空间中是线性可解的。
特征空间中的线性回归模型仅达到约 10%的训练精度。不出所料,这个问题是由于它的非线性太复杂,无法通过简单的线性回归来解决,而且该模型非常不适合。相比之下,在更高维度空间中应用的相同模型导致 100%的准确度,这意味着每个单个样本都可以被模型正确识别。
摘要
Nyströ方法可用于以非常有效的方式计算和保存核矩阵。此外,由于 Nyströ方法带来的高效映射过程,它使所有分类器都可以使用内核的全部功能。因此,如果与基于 Nyströ的特征映射相结合,线性回归等简单易懂的分类器可用于解决复杂的非线性问题。
参考
[1]https://en . Wikipedia . org/wiki/Low-rank _ matrix _ approximations
[2] Williams,C.K.I .和 Seeger,m .使用 Nystroem 方法加速内核机器。神经信息处理系统进展,2001
[3]亚历克斯·吉滕斯和迈克尔·马奥尼。重新审视 Nyströ方法以改进大规模机器学习。《第 30 届机器学习国际会议论文集》,第 567–575 页,2013 年。
[4]弗朗西斯·巴赫。低秩核矩阵近似的精确分析。《第 26 届学习理论会议论文集》,第 185-209 页,2013 年。
[5]艾哈迈德·阿拉维,迈克尔·马奥尼。具有统计保证的快速随机化核方法。arXiv:1411.0306,2015。
将 Jupyter 笔记本转变为电子书
将你的 Jupyter 笔记本转换成 PDF、EPUB 和 AWZ3 格式的精美电子书的一些技巧。不要像我一样花几个小时研究!
Kourosh Qaffari 在 Unsplash 上拍摄的照片
一个月前,我决定开始学习,这是一门专注于数据科学实用方面的在线课程。在 Jupyter 笔记本上写一本电子书似乎是一个不错的选择,因为它提供了文本、可视化和代码的完美结合。虽然写一本电子书本身已经是一个挑战,但我在将笔记本转换成电子书格式时遇到了很多问题。这些技巧对技术作家和希望在电子阅读器上查看笔记本的数据科学家很有用。遵循这些建议,这样你就不会像我一样花几个小时去研究了!
编译脚本将 Jupyter Notebook 转换成 PDF、EPUB 和 AWZ3 格式的精美电子书。编译…
github.com](https://github.com/romanorac/jupyter-notebook-to-ebook)
这里有几个你可能会感兴趣的链接:
- [Labeling and Data Engineering for Conversational AI and Analytics](https://www.humanfirst.ai/)- [Data Science for Business Leaders](https://imp.i115008.net/c/2402645/880006/11298) [Course]- [Intro to Machine Learning with PyTorch](https://imp.i115008.net/c/2402645/788201/11298) [Course]- [Become a Growth Product Manager](https://imp.i115008.net/c/2402645/803127/11298) [Course]- [Deep Learning (Adaptive Computation and ML series)](https://amzn.to/3ncTG7D) [Ebook]- [Free skill tests for Data Scientists & Machine Learning Engineers](https://aigents.co/skills)
上面的一些链接是附属链接,如果你通过它们进行购买,我会赚取佣金。请记住,我链接课程是因为它们的质量,而不是因为我从你的购买中获得的佣金。
1.创建 Github 存储库
我建议你从一个专门的 Github 库开始,它将帮助你跟踪你的电子书的版本。Github 免费提供私有库,所以没有理由不使用它。JupyterLab 还有一个 Git 扩展,可以帮助您跟踪版本之间的变化:
JupyterLab 刚刚成为一个成熟的 IDE,具有代码辅助、调试和 Git 等功能——欢迎来到未来……
towardsdatascience.com](/3-must-have-jupyterlab-2-0-extensions-41024fe455cc)
请确保将以下内容添加到。gitignore,这样您就不会意外地将不必要的文件推到您的 Git 存储库中:
*.csv
.DS_Store
.ipynb_checkpoints/
*.mov
*.pdf
*.html
*.azw3
*.epub
cpdf
2.在自己的笔记本中组织每一章
按章节组织笔记本中的内容,便于写作时专注于某个主题。它减少了干扰,也简化了对版本控制差异的审查。
以这种方式组织电子书还有另一个好处,这一点在最后变得很明显。电子书中的每一章都应该在新的一页开始,而不是在前一页的中间。
第二章从这一页的中间开始。
我花了几个小时寻找一种解决方案,在将 Jupyter 笔记本导出到 HTML 或 PDF 时,在章节之间添加分页符,但运气不好。
然后我想到了一个主意!将 Jupyter 笔记本中的每一章转换成 PDF,然后将它们合并成最终的 PDF。
3.不要用 Jupyter 的导出笔记本当 PDF
JupyterLab 有一个简洁的“将笔记本导出为 PDF”功能,起初看起来像是节省时间。JupyterLab 的导出功能不考虑 CSS,所以熊猫数据帧在 PDF 中是普通格式。
有很多列的 Dataframe 在 JupyterLab 看起来很棒,但是当我们把它导出到 PDF 时会发生什么?
当我们将笔记本导出为 PDF 时,一个包含许多列的数据框架会被分解到多个页面上。
我试图说服自己“出口”看起来没有那么糟糕,但我仍然不满意。在花了一些时间进行研究后,我尝试将笔记本导出为 HTML 格式,在 Chrome 浏览器中打开,并使用 Chrome 的保存为 PDF 功能。
Jupyter 笔记本导出为 HTML 格式,用 Chrome 浏览器保存为 PDF 格式。
啊哈!数据帧格式现在看起来更好了。
4.不要手动保存到 PDF
拥有多个章节并将每个章节导出为 HTML 并保存为 PDF 是一个繁琐的过程。我们能自动化它吗?
Chrome 有一个无头模式,所以我们可以从命令行保存 HTML 到 PDF。这在理论上应该行得通,但对我来说行不通。
chrome --headless --print-to-pdf=Chapter1.pdf --print-to-pdf-no-header Chapter1.html
尽管使用了-print-to-PDF-no-header 标志,但使用 Chrome 导出的 PDF 仍填充了页眉和页脚。
经过研究,我发现了 wkhtmltopdf 工具,它使用 Qt WebKit 渲染引擎将 HTML 渲染为 pdf 和各种图像格式。在 macOS 上,您可以使用 Brew 安装它。该工具使我们能够自定义 CSS 和边距。
Chrome 浏览器生成了一个不错的 PDF,所以我从 Chrome 中提取 CSS 格式并保存到一个 custom.css 文件中,这是 wkhtmltopdf 工具默认采用的:
div#notebook {
font-size: 18px;
line-height: 26px;
}img {
max-width: 100% !important;
page-break-inside: avoid;
}tr, img {
page-break-inside: avoid;
}*, *:before, *:after {
background: transparent !important;
box-shadow: none !important;
text-shadow: none !important;
}p, h2, h3 {
orphans: 3;
widows: 3;
page-break-inside: avoid;
}*, *:before, *:after {
page-break-inside: avoid;
background: transparent !important;
box-shadow: none !important;
text-shadow: none !important;
}*, *:before, *:after {
page-break-inside: avoid;
background: transparent !important;
box-shadow: none !important;
text-shadow: none !important;
}
获取 HTML 并输出 pdf 的 wkhtmltopdf 命令:
wkhtmltopdf — enable-internal-links -L 10mm -R 9.5mm -T 10mm -B 9.5mm Chapter1.html Chapter1.pdf
5.合并 pdf
我使用 cpdf 工具将 pdf 合并成最终的 PDF。我下载了 cpdf 工具,放在 Jupyter 笔记本的文件夹里。要合并 pdf,请使用以下命令:
./cpdf Chapter1.pdf Chapter2.pdf -o Ebook.pdf
最终合并的 PDF。
6.将 Jupyter 笔记本转换为 EPUB 格式
我们把每一章都放在一个单独的笔记本里。让我们用 nbmerge 工具合并笔记本。可以用 pip 安装:pip install nbmerge。
nbmerge Chapter1.ipynb Chapter2.ipynb > Ebook.ipynb
JupyterLab 的“导出到 HTML”命令也可以导出 CSS,这对于 PDF 来说很好,但是对于电子书来说就有问题了,因为它太复杂了。Jupyter 带有 nbconvert 工具,可以导出不同的格式。要将笔记本导出为不带 CSS 的 HTML 格式:
jupyter nbconvert --to html Ebook.ipynb --template=basic
我们需要安装 Calibre ,将 HTML 转换成 EPUB。如果你是一个狂热的电子书读者,我敢肯定你以前见过 Calibre。Calibre 是一个用于电子书管理的跨平台开源套件。
运行以下命令将 HTML 转换为 EPUB 和 AWZ3(命令在 macOS 上有效):
/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.epub
/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.azw3
在苹果 iBooks 上阅读 EPUB 电子书。
7.让我们把所有的东西放在一起
作为软件开发人员,我们将事情自动化。我编写了一些可以很好地打包到 bash 脚本中的命令。因此,每当我在 Jupyter 笔记本中进行更改时,我都可以运行编译脚本来创建电子书的新版本:
#!/bin/bashnbmerge Chapter1.ipynb Chapter2.ipynb > Ebook.ipynbjupyter nbconvert --to html Ebook.ipynb --template=basic/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.epub/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.azw3jupyter nbconvert --to html Chapter1.ipynbjupyter nbconvert --to html Chapter2.ipynbwkhtmltopdf --enable-internal-links -L 10mm -R 9.5mm -T 10mm -B 9.5mm Chapter1.html Chapter1.pdfwkhtmltopdf --enable-internal-links -L 10mm -R 9.5mm -T 10mm -B 9.5mm Chapter2.html Chapter2.pdf./cpdf Chapter1.pdf Chapter2.pdf -o Ebook.pdf
我还用上面提到的命令创建了一个 Git 存储库:
编译脚本将 Jupyter Notebook 转换成 PDF、EPUB 和 AWZ3 格式的精美电子书。编译…
github.com](https://github.com/romanorac/jupyter-notebook-to-ebook)
在你走之前
在 Twitter 上关注我,在那里我定期发布关于数据科学和机器学习的。
用熊猫改变现实
使用转置、融合、合并等功能重塑数据。
尼克·伍德在 Unsplash 上的照片
熊猫让 python 变得简单
纯 python 是一种非常清晰的语言。但是熊猫真的让它变笨了。简化 python 脚本语言使得更复杂的编程变得更加容易。DataFrame 操作使数学、科学、探索、艺术和魔法变得简单而直观。图书馆是研究者最好的朋友。
熊猫让我们可以更专注于研究,而不是编程。我们发现熊猫易学、易用、易维护。最重要的是,它提高了我们的生产力。”(* Roni Israelov,博士 ,投资组合经理, AQR 资本管理)*
改变现实
数据科学项目的第一步是将数据集加载到 pandas 数据框架中,并使用 检查前五行。(head())法,翻着白眼,不可置信地呻吟,并对数据集的作者缺乏远见感到绝望。他们不知道您需要列级聚合吗?在 Excel 中,这将是一场复制粘贴的噩梦。熊猫给你一条走出迷雾的路,非常聪明,可读性强。
照片由 Ibrahim Rifath 在 Unsplash 上拍摄
本文的目标
在本文中,我将演示几个 pandas DataFrame 操作,用于转换数据和构建可重现的函数,您可以使用这些操作来自动化 ETL-Transform 的“T ”,并节省您的脑力和耐心来关注真正重要的问题。指导我的数据集设计的许多原则来自于 R tidyverse。
1.下载数据
我们将使用世界银行的数据,该数据给出了自 1960 年以来每个国家的年度 GDP。乍一看,很可怕。专栏的年份?
2.想象你的目标。
现在我可以看到数据是如何排列的,我需要设想转换的最终目标,并考虑完成转换所需的步骤。
这是我希望的最终结果,一个显示年份、GDP 四分位数和平均 GDP 百分比变化的 3 列数据框架。这将用于创建一个线图,显示每个四分位数每年的增长百分比。这有助于可视化全球经济趋势,并衡量某些事件和趋势对全球 GDP 不同四分位数的影响。(滚动到底部查看最终图表)
3.移项
我们需要得到每个国家 GDP 的百分比变化,这样我们就可以按四分位数计算总平均值。现在,数据框架指数是按国家的,所以我们需要将数据框架转换成按年份的指数,以国家为列。以年份为索引,以国家为列,这将非常适合于计算每一列(即各个国家)各行的百分比变化。
4.百分比变化
熊猫有一个很优秀的 DataFrame 操作叫做 DataFrame.pct_change()。 此操作向下计算列,并用该列与其上一行之间的百分比变化替换该值。
5.转置回原始形状
使用转换后的 GDP 百分比变化数据框架,将行和列转置回原始形状。
6.熔化数据帧
别忘了我们还有原始的 GDP 数据框架。计算每个国家的四分位数需要原始数据框架和未转换的国内生产总值分数。我们现在必须做的是融合国内生产总值和国内生产总值百分比变化数据框架,以便在下一步将其合并。
国内生产总值数据框架
国内生产总值百分比差异数据框架
7.合并数据帧
在我们迄今所做的一切之后,与熊猫融合是如此容易,这是一种解脱。我们可以简单地对原始 GDP 数据执行 DataFrame.merge(),并告诉它将百分比差异数据作为参数进行合并。
日常管理—删除、重命名、转换为数字、删除 NAs
您会注意到 _x 和 _y 后缀,Pandas 很好地分配了它们,这样我们就不会被合并后的数据集弄糊涂了。现在我们必须删除不必要的列,重命名这些列,并将 year 转换为 numeric(它被错误地作为对象读入)。
8.计算每年 GDP 的四分位数
我们需要的是每个国家的 GDP 指标,以了解它在年度 GDP 分布中的位置。四分位数是根据数据在后 25%(按计数,而不是值)、26–50%、51–75%和 76–100%中的位置对数据进行分组的极好方法。我们将使用这个优秀的 StackOverflow 答案,结合 groupby()、transform()和 lambda(),为该年计算的 GDP 四分位数创建一个新列。
9.平均 GDP 百分比变化——按四分位数
现在我们有了创建最终数据帧的所有部分。我们将再次使用**data frame . group by()**按年份和四分位数分组,并将平均值计算集中在 GDP_pct_change 上。
你现在可以呼吸了。我们已经走了这么远!
*当我们第一次用 检查时,数据集几乎无法辨认。*头()。我们现在可以看看过去 60 年的全球 GDP 变化,并检查不同的 GDP 四分位数对全球趋势的反应。
让我们用一个简单的 Seaborn line 情节来享受我们的劳动成果吧。
谢谢你陪我坚持下来!数据清理和转换是分析过程中至关重要的一部分,我们刚刚经历了一次令人费解的旅程,数据采用了许多不同的形状和维度。
用蜂鸟把你的 ML 模型变成 Pytorch
机器学习
利用张量加速您的机器学习模型
在过去的几年里,深度学习的能力已经大大增加了。随着这一点,许多服务于你的神经网络的标准已经找到了通往大众的道路,如 ONNX 和 TVM 。
这种流行导致了对通过利用张量计算来优化深度学习管道、训练、推理和部署的关注。
相比之下,传统的机器学习模型,如随机森林,通常在推理任务上是基于 CPU 的,并且可以受益于基于 GPU 的硬件加速器。
用蜂鸟把你训练过的机器学习模型转换成 Pytorch
现在,如果我们可以在传统的随机森林中使用神经网络的许多优势,会怎么样呢?更好的是,如果我们能够转换随机森林并且利用 GPU 加速推理会怎么样?
这就是微软的蜂鸟的用武之地!它将你的机器学习模型转换为张量计算,这样它就可以使用 GPU 加速来加速推理。
在本文中,我不仅将描述如何使用这个包,还将描述相应论文和路线图中的基础理论。
注:蜂鸟只是用来加快时间做预测的,不是用来加快训练的!
1.理论
在深入研究这个包之前,重要的是要理解为什么这种转换是可取的,以及为什么可以这样做。
为什么要把你的模型转换成张量?
在某种程度上,张量是一个 N 维数据数组(见下图)。在 TensorFlow 的上下文中,它可以被视为一个矩阵的一般化,允许您拥有多维数组。您可以对它们执行优化的数学运算,而无需知道每个维度在语义上代表什么。例如,如何使用矩阵乘法来同时操作几个向量。
张量的简化。注意,在数学、物理和计算机科学中,张量的应用和定义可能不同!
将你的模型转换成张量有几个原因:
- 张量作为深度学习的中坚力量,在让深度学习走向大众的背景下,得到了广泛的研究。张量运算需要显著优化,以便深度学习在更大规模上可用。这极大地加快了推断的速度,从而降低了进行新预测的成本。
- 与使用传统的机器学习模型相比,它允许一个更加统一的标准。通过使用张量,我们可以将传统模型转换为 ONNX,并在所有人工智能解决方案中使用相同的标准。
- 神经网络框架中的任何优化都可能导致传统机器学习模型的优化。
转换算法模型
向张量的转换不是一项简单的任务,因为有两个模型分支:代数(例如,线性模型)和算法模型(例如,决策树)。这增加了将模型映射到张量的复杂性。
在这里,算法模型的映射尤其困难。张量计算已知用于执行大量或对称运算。这对于算法模型来说是很难做到的,因为它们本质上是不对称的。
让我们以下面的决策树为例:
要映射到张量的示例决策树。此处检索到。
决策树分为三个部分:
- 输入特征向量
- 四个决策节点(橙色)
- 五个叶节点(蓝色)
产生的神经网络的第一层是连接到四个决策节点(橙色)的输入特征向量层。在这里,所有条件一起评估。接下来,通过使用矩阵乘法来一起评估所有叶节点。
作为神经网络,生成的决策树如下所示:
作为神经网络的决策树。此处检索到。
所产生的神经网络引入了冗余度因为所有条件都被评估。然而,通常只评估一条路径。这种冗余部分地被神经网络向量化计算的能力所抵消。
注:模型转化为张量的策略还有很多,在他们的论文里有描述,这里这里和这里。
加速
根据他们的论文,与传统模型相比,GPU 加速的使用大大提高了推理速度。
比较使用 Sklearn 的相同模型与使用 Hummingbird 的神经网络的推理结果。此处检索到。
上述结果清楚地表明,将您的森林转换为神经网络可能是值得的。
我想亲眼看看在启用了 GPU 的 Google 联合实验室服务器上的结果会是什么样子。因此,我在谷歌联合实验室上做了一个快速的,但绝不是科学的实验,结果如下:
在启用 GPU 的情况下,在 Google Colab 上运行二元分类模型 100 次的结果。数据集是随机生成的,包含 100000 个数据点。看来用蜂鸟推断肯定比基础款快。实验的代码可以在这里找到。
我们可以清楚地看到,在使用单一数据集时,对新数据的推断速度大大加快。
2.使用
蜂鸟是
幸运的是,使用这个包非常简单。很明显,作者已经花费了大量的时间来确保这个包可以直观地在许多模型上使用。
现在,我们从通过 pip 安装包开始:
pip install hummingbird-ml
如果您还想安装 LightGBM 和 XGboost 依赖项:
pip install hummingbird-ml[extra]
然后,我们简单地开始创建我们的 Sklearn 模型,并在数据上对其进行训练:
这样做之后,我们实际上要做的唯一一件事就是导入蜂鸟并使用convert
函数:
得到的model
只是一个torch.nn.Module
,然后可以像平常使用 Pytorch 一样使用。
正是转换到 Pytorch 的便利首先吸引了我对这个包的注意。只需几行代码,您就已经改造了您的模型!
3.路标
目前,以下型号已实施:
- 大多数 Scikit-learn 模型(例如,决策树、回归和 SVC)
- LightGBM (分类器和回归器)
- XGboost (分类器和回归器)
- ONNX。ML (TreeEnsembleClassifier 和 TreeEnsembleRegressor)
虽然一些模型仍然不见踪影,但是他们的路线图表明他们正在前进:
- 特征选择器(例如,变量阈值)
- 矩阵分解(例如 PCA)
- 特征预处理(例如,MinMaxScaler、OneHotEncoder 等。)
你可以在这里找到蜂鸟的完整路线图。
感谢您的阅读!
如果你像我一样,对人工智能、数据科学或心理学充满热情,请随时在 LinkedIn 上添加我,或者在 Twitter 上关注我。
点击下面的一个帖子,了解更多关于其他有趣的软件包的信息:
使用稳定基线和 Gym 训练 SOTA RL 算法
towardsdatascience.com](/reinforcement-learning-in-a-few-lines-of-code-d6c8af1e0fd2) [## 借助 Streamlit 快速构建和部署应用
将您的 Streamlit 应用程序部署到 Heroku,展示您的数据解决方案
towardsdatascience.com](/quickly-build-and-deploy-an-application-with-streamlit-988ca08c7e83) [## 打开黑盒:如何利用可解释的机器学习
使用 PDP、LIME 和 SHAP 制定可解释的决策,为利益相关方创造价值
towardsdatascience.com](/opening-black-boxes-how-to-leverage-explainable-machine-learning-dd4ab439998e)