PaperSpace 博客中文翻译(三)
原文:PaperSpace Blog
协议:CC BY-NC-SA 4.0
用数据和人工智能对抗新冠肺炎:活跃研究小组和数据集综述
原文:https://blog.paperspace.com/fighting-covid-19-using-artificial-intelligence-and-data/
经济已经停止了;人被隔离;工作停滞不前;政府担心这会变成公共健康危机。致命的冠状病毒(新型冠状病毒,或“冠状病毒疾病 2019”的新冠肺炎)正在快速传播。现在是 2020 年,但即使有最先进的技术,我们也无法阻止它。到目前为止,这里有超过 100 万的病例和 60,000 例死亡病例。正如人们所说,预防胜于治疗:是时候呆在家里照顾自己了。在这篇文章中,我们将看到人工智能如何帮助人们跟踪、控制和找到治疗新冠肺炎的方法。
在过去的十年里,我们在计算机科学和人工智能领域看到了大量的研究和积极的成果。数学和算法已经存在很长时间了;这种爆炸的真正原因是数据的可用性、更高的计算能力、开源工具和框架。从制造业和能源到医疗保健和教育,人工智能已经彻底改变了所有这些行业。
让我们从数据科学的角度出发,了解问题到底是什么。
问题——新冠肺炎!
2019 年 12 月底,中国卫生部门报告了武汉市大量急性呼吸综合征病例。2020 年 2 月 11 日,世界卫生组织(世卫组织)将这种病毒命名为新冠肺炎病毒。这种病毒通过感染者咳嗽、打喷嚏或说话时产生的呼吸道飞沫在密切接触的人之间传播。这些飞沫可以被吸入,落在嘴、眼睛或鼻子里,甚至通过触摸自己的脸被带到这些地方。通常报告的症状包括发烧、干咳和疲劳。在轻微的情况下,人们可能只是流鼻涕或喉咙痛。与此同时,世界各地的计算机科学家和机器学习研究人员一直在热情合作,广泛开展工作,以找到解决冠状病毒相关问题的方法,要么通过汇编数据集,要么通过构建算法从中学习。
目前还没有针对这种病毒的疗法或疫苗,但我们可以通过呆在家里、经常洗手/消毒以及避免去公共场所来阻止它的传播。如果你感到不适或生病,确保你自我隔离。
在下一部分,我们将看到不同的研究小组如何使用人工智能来解决这个问题。
基于人工智能的系统检测新冠肺炎
1.DAMO 研究院(阿里巴巴集团)在 CT 扫描中发现冠状病毒病例
2 月初,阿里巴巴研究院(DAMO 研究院)提出了一个基于人工智能的解决方案,可以在 20 秒内以 96%的准确率检测到新冠肺炎。该网络是一个深度计算机视觉模型,它将患者的 CT 扫描作为输入,并输出他们是否显示冠状病毒的迹象。该模型通过 5000 多个训练样本进行了微调,并部署在中国超过 26 家医院。到目前为止,它已经帮助诊断了超过 30,000 个病例[3]。
AI-Based Software developed by DAMO Academy [3]
同一个小组还开发了一个基于预训练模型的 NLP 解决方案,用于分析新冠肺炎的医疗报告以寻找治疗方法。他们的模型在 2020 年 3 月 3 日荣登 GLUE 基准排名榜首,这是一个被视为 NLP 相关任务最重要的基准测试的行业表格。它目前正被中国不同城市的 CDC 用于病历文本分析和流行病学调查[4]。
2.肺部感染量化
为了减少 CT 扫描的分析时间,研究人员建立了一个使用深度学习的系统来量化新冠肺炎引起的肺部感染[5]。核心思想是开发一个基于深度学习的模型,用于自动分割和量化受影响的区域,以及胸部 CT 扫描的整个肺部。
作者开发了一个名为 VB-NET 的网络,它是对 V-Net [6]的改进。他们声称,由于其特殊的瓶颈结构,他们提出的模型比原始的 V-NET 更快。下面是该建筑的图像。
VB-Net Architecture [5]
如图所示,瓶颈架构是一个堆叠的三层结构。它使用(1 x 1 x 1 ) 、(3 x 3 x 3)和(1 x 1 x 1)卷积核从图像中提取特征。为了训练这个网络,作者使用了一种叫做人在回路策略的特殊策略。在这种策略中,为了节省爆发期间放射科医生的时间,训练数据被分成几个批次:
- 第一批包含由放射科医师手动绘制轮廓的 CT 数据。
- 接下来,分割网络对该批次进行训练,并且创建初始模型。
- 然后,将该初始模型应用于下一批中的分割感染区域,并且放射科医师手动校正由分割网络提供的分割结果。
- 这些结果作为新的训练数据,增加了训练数据集以及网络的准确性。
该网络对 249 名新冠肺炎患者进行了训练,并使用 300 名新新冠肺炎患者进行了验证。为了评估结果,VB-Net 使用了骰子相似系数 (DSC)和皮尔逊相关系数。这些指标在自动分割和手动分割之间产生了 91.6%±10.0%。
Results from VB-NET. In the last column the CT Images have the overlaid Segmentation, and a 3D surface rendering of segmented infections
Results from VB-NET. The whisker plots of infected areas in bronchopulmonary segments
3.用于大规模筛查的异常呼吸模式分类
根据临床研究,已经观察到患有新冠肺炎的人具有不同的呼吸模式。注意到这一点,华东师范大学的研究人员与其他研究机构合作开发了一种基于深度学习的算法,可以根据呼吸特征帮助诊断、预后和筛查受感染的患者。
具有双向和注意力机制的 GRU 神经网络被用于使用深度相机对六种临床上显著的呼吸模式进行分类。这六种呼吸模式分别是:正常呼吸、呼吸急促、呼吸缓慢、Biots、Cheyne-Stokes 和中枢性呼吸暂停。
Real-Time Respiratory Patterns Classification for COVID-19 [7]
这项研究包括四个核心步骤:
- 开发用于生成模拟数据的呼吸模拟模型。
- 使用深度相机获取真实世界的数据。
- 建立并验证双 GRU 模型。
- 进行对比实验。
在第一步中,作者使用数学正弦波近似受试者的呼吸周期。由于呼吸是一个吸气和呼气的循环过程,它的图形形式通过波的上升和下降来反映。
在第二步中,为了记录受试者呼吸时的深度图像,Kinect v2 深度相机用于捕捉真实数据。选择了三个感兴趣的区域,即胸部、腹部和肩部,每次捕捉一分钟的特定呼吸模式。下图显示了如何通过相机捕捉数据。
Actual Measured Central-Apnea Waveforms [7]
对于步骤 3 中的呼吸模式分类任务,作者使用了 BI-AT-gru。BI-AT-GRU 网络基本上是对 GRU 的改进,增加了双向和注意力机制。GRU 网络是 LSTM 的简化变体。为了让你有个概念,下面是网络设置的图像。
最后,结果表明,所提出的模型能够以 94.5%、94.4%、95.1%和 94.8%的准确度、精确度、召回率和 F1 分数对所提到的六种呼吸模式进行分类。
4.卷积神经网络用于新冠肺炎和肺炎筛查
卷积神经网络是识别不同图像中模式的简单技巧。为了加快中国的筛查过程,几个研究小组合作开发了一个基于 CNN 的深度学习模型,以通过 CT 扫描识别早期新冠肺炎。为了进行这项研究,总共收集了 618 次肺部 CT 扫描。
下图解释了从输入到输出的管道。
为了确定 CT 扫描中感染的位置,作者研究了新冠肺炎的几个特征,包括:
- 毛玻璃外观
- 显著的周边分布
- 不止一个独立的感染病灶
基于不同感染的外观和产生的结构,图像分类模型应该能够区分疾病。
网络结构由两个 3d CNN 分类模型组成。第一种是用于特征提取的经典 ResNet-18 网络。接下来,使用一些池操作来降低数据的维度,以防止过度拟合并提高泛化能力。最后,三个完全连接的层输出最终的分类结果,以及置信度得分。训练是在与 NVIDIA GeForce GTX 1080 Ti 连接的英特尔 i7-8700k CPU 上进行的。用一个共同的交叉熵损失函数对网络进行 1000 次迭代训练。下面是 CNN 架构的图像。
Network structure of the location-attention oriented model
该模型能够以 86.7 %的准确率识别早期阶段的新冠肺炎[8]。
5.使用深度学习进行 CT 图像分析的新冠肺炎识别和患者监测
这项研究主要由位于波士顿的 RADLogics 公司与世界各地的许多其他研究小组合作进行。主要目的是建立一种基于人工智能的自动化 CT 图像分析工具,可以在冠状病毒阳性患者的检测中实现高精度,并在整个治疗过程中对他们进行监控。这项研究通过对由感染引起的体积和直径(来自 ct 扫描)方面的较小混浊进行定量测量,为患者提供了及时的分析。他们还提出了一种“电晕评分”来连续测量疾病随时间的进展。
以下是他们检测病毒的步骤:
- 首先,使用在 6,150 幅图像上训练的 U-Net 架构分割 CT 扫描的肺部区域。
- 接下来,使用在 ImageNet 数据集上预先训练的 Resnet-50-2D 卷积网络,从分割的肺图像中检测新冠肺炎相关异常。
- 为了将一个病例标记为阳性,首先,计算阳性检测的切片在肺的总切片中的比率(“阳性比率”)。接下来,如果正比率超过预定义的阈值,则做出肯定的情况判定。
- 在该病例被检测为阳性后,作者建议对结节状和局灶性弥漫性不透明进行 3D 分析(以下以绿色和红色显示)。
- 最后,作者使用了一个异常定位步骤,其中产生网络激活图。Grad-CAM 技术用于创建更合适的可视化效果。
Monitoring COVID-19 in a patient over time
在中国对照和感染患者的数据集上,训练的网络的结果是 0.996 AUC (95%CI: 0.989-1.00)。他们报道了两个可能的工作点:98.2%的敏感性和 92.2%的特异性(高敏感性点),或 96.4%的敏感性和 98%的特异性(高特异性点)。可以在这里找到原论文。
6.新冠肺炎药物筛选
人工智能不仅可用于诊断冠状病毒,还可用于筛选药物。中国的几个研究小组合作研究深度学习如何比传统方法更快地找到药物。
在研究了可从 GISAID 数据库获得的 RNA 序列后,作者得出结论,新冠肺炎与 SARS-CoV-1 高度同源。他们通过将 RNA 序列翻译成蛋白质序列,然后使用同源建模(比较蛋白质并构建它们的原子分辨率)建立 3D 蛋白质模型,找到了这种同源性。使用 DFCNN(深度完全卷积神经网络)来识别和排列蛋白质-配体相互作用,以快速进行虚拟筛选,因为不需要对接或分子动力学模拟。为了澄清,分子动态模拟是指用于分析原子和分子的物理运动的计算机模拟方法;对接是指有意移除零件以进行分析。利用这些技术,,作者能够通过对四个化合物数据库进行药物筛选来确定治疗新冠肺炎的潜在药物。
提议的 DFCNN 架构在来自 PDBbind 数据库的蛋白质-配体数据集上进行训练。该模型能够预测三种得分高于 0.997 的潜在药物,以及三种得分高于 0.99 的药物。
下面是整体工作流程的截图。
The workflow of virtual screening of chemical compounds for COVID-19 proposed by Haiping Zhang and team.
7.用α折叠进行蛋白质结构的计算预测
DeepMind 是全球顶尖的研究公司之一。2019 年 4 月 2 日,他们向《自然》杂志提交了他们关于“利用深度学习的潜力改进蛋白质结构预测”的研究。2019 年 12 月被接受并出版。2020 年 1 月 15 日,他们在一篇名为 AlphaFold:使用人工智能进行科学发现的博客文章中回顾了这项研究,该研究目前正在帮助识别与新冠肺炎相关的蛋白质结构。
这项研究主要解决蛋白质折叠的问题。你可以把蛋白质想象成复杂的大分子。它们的三维结构随着它们执行不同的操作而改变。作者给了我们一个例子,说明为什么识别蛋白质的结构是至关重要的。下面是文章中的几行文字:
我们的免疫系统所利用的抗体蛋白呈“Y 形”,并形成独特的钩子。通过锁定病毒和细菌,这些抗体蛋白可以检测并标记致病微生物,以将其清除。【11】
这解释了为什么蛋白质折叠对我们至关重要。但是我们怎么做呢?深度学习在哪里起作用?我们来分析一下。这种蛋白质是通常在 DNA 中编码的氨基酸的组合。但是 DNA 不包含关于蛋白质结构的信息。使用传统技术,在达到真正的三维结构之前,需要花费很长时间来计算一个典型蛋白质的所有可能构型。
那么我们如何从复杂的蛋白质序列中解读三维结构呢?五十年前,这些是通过使用显微镜和 X 射线人工确定的,这涉及到大量的反复试验。但是有了像 AlphaFold 这样的深度学习和研究,我们现在可以更容易地识别蛋白质的三维结构。
Alphafold 的工作分为两个阶段。在第一阶段,使用标准生物学技术,通过重复替换现有蛋白质片段来产生新的蛋白质片段。这些结构在生成式对抗性神经网络(GAN)的帮助下不断得到改进。GAN 输出的蛋白质结构具有两种特性:
- 氨基酸对之间的距离
- 连接这些氨基酸的化学键之间的角度
在第二阶段,通过梯度下降算法改进距离和角度,直到它们达到最佳分数。下面是 AlphaFold 整个工作流程的图片。
**
Working of AlphaFold [12]**
同一个团队使用 AlphaFold 系统预测了 3D 新冠肺炎蛋白质结构。用来理解新冠肺炎的先决条件数据是从一个开放存取的数据库中收集的。然而,作者引用说,这并没有得到实验验证。但它有助于研究病毒是如何发挥作用的。下面是预测的 3D 渲染的 SARS-COV-2 膜蛋白的图像:
**
Rendering of COVID-19 Protein Membrane [12]**
8.重度新冠肺炎患者的危险度预测
在这项研究中,作者提出了基于三个指标的预后预测模型,这些模型将预测死亡风险和识别危重病例的临床途径。作者使用了 2779 份电子病历,包括他们在 2020 年 1 月 10 日至 2 月 18 日在中国武汉同济医院的医疗状况。
**
Image explaining how the patients are enrolled and classified [10]**
构建这个机器学习模型包括以下三个步骤:
- 数据预处理:作者从他们最近的可用数据中导入了所有的临床测量数据作为特征,并添加了两个新标签:生存和死亡。这些患者的临床测量包括输入数据中超过 35 个特征,包括性别、武汉居住地、家族聚集性、发热、咳嗽、疲劳、胸闷、淋巴细胞等。如果有任何不完整的临床测量,那么这些值被设置为-1。
- 数据分割:根据传统的 ML 指南,数据被分成 70%用于训练,30%用于测试。
- 训练:在这一步中,作者选择多树 XGBoost 算法来使用输入数据预测患者的严重程度。树的深度设置为 4,学习率为 0.2。正则化参数 subsample 和 colsample_bytree 的值分别被设置为 1、0.9 和 0.9。这是为了减少过度拟合。
**
XGBoost Machine Learning Algorithm for Severity Detection of COVID-19**
使用该管道,该模型能够实现 90%以上的准确性,从而实现早期检测、早期干预,并降低受新冠肺炎影响的高危患者的死亡率。
新冠肺炎数据集
在前面的章节中,我们已经看到了使用机器学习和人工智能对抗新冠肺炎的不同方法和技术。这些都有一个共同点:数据!数据越多,算法越来越好。为了帮助理解新冠肺炎,几家公司和开源组织开发了不同的数据集。下面是一些个人和团队如何利用现有数据应对新冠肺炎病毒传播及其后果的案例。
- 监测医务人员防护设备的分发
- 寻找新冠肺炎的平均潜伏期和恢复期
- 跨大洲的实时可视化和监控
- 新冠肺炎症状的快速诊断
- 为前线医护人员提供人工智能援助
- 医院病床管理
- x 光或计算机断层扫描的诊断
- 使用集群网络的患者跟踪
- 疫苗或解毒剂蛋白质预测
- 连续患者监护
以下是一些被广泛使用的数据集的链接:
- 新冠肺炎开放研究数据集挑战赛(CORD-19 ): CORD-19 是艾伦人工智能研究所与几家公司和组织合作开发的数据集。它由超过 45,000 篇学术文章组成,其中 33,000 篇为全文,内容涉及新冠肺炎、新型冠状病毒和相关冠状病毒。
- 新冠肺炎韩国数据集:这是大韩民国的一个开源数据集,用于跟踪 COVID 阳性患者的旅行史。使用这个数据集,开发了一个基于 ML 和 web 的平台来可视化患者路线。
- 【2019 年新型冠状病毒数据集:该数据集包含来自不同地区的病例数、死亡数和恢复数的每日信息,包括时间戳。
- COVID19 ChextXRay 数据集:该数据包含新冠肺炎病例的胸部 x 光片。感谢约瑟夫·保罗·寇恩让这个数据集在 Github 上公开。利用这一点,人们可以尝试建立一个使用 X 射线检测新冠肺炎的神经网络分类器(然而,请注意,数据对于创建有效的模型来说是非常有限的)。然而,人们不应该在没有临床研究的情况下宣称模型的诊断性能。
摘要
人工智能已经彻底改变了许多行业,现在正被用来彻底改变对抗新型冠状病毒(新冠肺炎)的战斗。虽然人工智能社区正在紧张地工作,以提供有助于遏制病毒后果的应用程序,但人工智能系统仍处于初级阶段,这些措施的结果还需要时间才能看到。这个悲惨的故事还远没有结束。然而,社区已经取得了很大的进步,下一个重大突破也不远了。
参考
- 世界卫生组织
- https://www.newscientist.com/term/covid-19/-新冠肺炎
- DAMO 学院的人工智能系统如何检测冠状病毒病例
- 用技术对抗冠状病毒:阿里巴巴 NLP 研究的又一突破
- 利用深度学习对 CT 图像中新冠肺炎肺部感染进行量化
- V-Net:用于体积医学图像分割的全卷积神经网络
- 异常呼吸模式分类器可能有助于以准确和不引人注目的方式对新冠肺炎病毒感染者进行大规模筛查
- 深度学习系统筛查冠状病毒疾病 2019 肺炎
- 基于深度学习的新型冠状病毒 2019-NovCov 药物筛选
- 使用三种临床特征预测严重新冠肺炎感染患者的危急程度:基于机器学习的武汉临床数据预后模型
- AlphaFold:利用人工智能进行科学发现
- 与新冠肺炎相关的蛋白质结构的计算预测
- 计算机科学家正在构建解决新冠肺炎问题的算法
卷积神经网络中的滤波器
原文:https://blog.paperspace.com/filters-in-convolutional-neural-networks/
Photo by DHANYA A V / Unsplash
在多层感知器(MLP)中,可学习参数是映射到特征向量的网络权重。然而,在卷积神经网络的上下文中,可学习的参数被称为滤波器,滤波器是通常大小为正方形的二维矩阵/阵列。
在我的上一篇文章中,我们通过一个叫做卷积的过程,对这些滤镜如何用于图像发展了一点我们的直觉。在本文中,我们将探讨当图像通过卷积神经网络(CNN)的各层时,这些滤波器实际上会对图像做什么。
神经网络和特征提取
神经网络的基本能力是它们从数据中提取特征的能力,以便然后使用它们来实现某个目标,无论是分类、回归等。在 MLPs 中,这个过程很容易概念化,数据点通常是特定数据实例的属性,被映射到训练的权重,以便以某种形式将它们组合或转换成本质特征。另一方面,当涉及到细胞神经网络时,特征提取并不明确,因为它们不处理属性向量,而是处理属性(像素)的二维矩阵的图像。
此外,说到图像,什么能代表一个特征呢?例如,当谈到房屋的表格数据集时,包含卧室数量或客厅大小等属性的列被称为特定房屋实例的特征。那么,一张尺寸为(640,480)像素的猫的增强清晰度(480p)图像呢?该图像有 640 列和 480 行,总共 307,200 个属性(像素),在这种情况下什么代表特征?
边缘图像
构成图像的许多细节实际上包含在图像的边缘或轮廓中。这也是我们在漫画速写中很容易区分物体的原因之一。事实上,有大量研究表明,边缘感知是人类大脑在处理来自眼睛的视觉线索时首先利用的技术之一(威利安·麦克尔哈加,2018 )。边缘感知不仅仅局限于人类的视觉,一些研究认为这也是为什么鸟类如此擅长以如此高的速度在飞行途中躲避障碍,以及以极高的精确度降落在如此远的小目标上的原因之一(帕萨·巴加瓦图拉等人,2009 )。
The only information we have are edges and we all know what this is.
CNN 和人类视觉
有很多关于神经网络如何模仿人脑的讨论。一种对此给予一定信任的情况是,正如人脑通过感知边缘开始处理来自眼睛的视觉线索一样,卷积神经网络也开始通过检测边缘从图像中提取特征,事实上可以说边缘代表图像特征。它用于这个目的的工具是它的可学习参数,它的过滤器。
这正是卷积神经网络中滤波器的具体用途,它们有助于从图像中提取特征。虽然 CNN 的前几层由边缘检测过滤器(低级特征提取)组成,但更深的层通常学会聚焦于图像中的特定形状和对象。为了这篇文章的目的,我将集中在边缘检测的前几层,因为这是一个非常有趣的过程,过滤器很容易理解。
过滤掉边缘
卷积神经网络的酷之处在于,它们可以根据特定数据集中像素的概率分布和网络的特定目标来学习定制的边缘检测过滤器。尽管如此,仍有一些经典的手动制定的边缘检测滤波器可用于开发边缘检测在计算机视觉环境中看起来像什么的直觉。它们是 Prewitt、Sobel、Laplacian、Robinson 罗盘和克里施罗盘滤波器。
为了真正检查这些过滤器做什么,让我们做一些繁重的工作,使用下面给出的手动编写的卷积函数将它们应用于图像。
import numpy as np
import torch
import torch.nn.functional as F
import cv2
from tqdm import tqdm
import matplotlib.pyplot as plt
def convolve(image_filepath, filter, title=''):
"""
This function performs convolution and
returns both the original and convolved
images.
"""
# reading image in grayscale format
image = cv2.imread(image_filepath, cv2.IMREAD_GRAYSCALE)
# defining filter size
filter_size = filter.shape[0]
# creating an array to store convolutions (x-m+1, y-n+1)
convolved = np.zeros(((image.shape[0] - filter_size) + 1,
(image.shape[1] - filter_size) + 1))
# performing convolution
for i in tqdm(range(image.shape[0])):
for j in range(image.shape[1]):
try:
convolved[i,j] = (image[i:(i+filter_size),
j:(j+filter_size)] * filter).sum()
except Exception:
pass
# converting to tensor
convolved = torch.tensor(convolved)
# applying relu activation
convolved = F.relu(convolved)
# producing plots
figure, axes = plt.subplots(1,2, dpi=120)
plt.suptitle(title)
axes[0].imshow(image, cmap='gray')
axes[0].axis('off')
axes[0].set_title('original')
axes[1].imshow(convolved, cmap='gray')
axes[1].axis('off')
axes[1].set_title('convolved')
pass
Convolution function.
该函数复制了卷积过程,并增加了一个 ReLU 激活步骤,这在典型的 convnet 中是意料之中的。利用这个功能,我们将使用上面列出的过滤器来检测下图中的边缘。
Specimen
您可以通过点击下面的链接在渐变笔记本中免费运行此代码!
Prewitt 过滤器
The vertical and horizontal Prewitt filters.
Prewitt 算子由两个滤波器组成,有助于检测垂直和水平边缘。水平(x 方向)滤镜有助于检测图像中垂直穿过水平轴的边缘,垂直(y 方向)滤镜则相反。
# utilizing the horizontal filter
convolve('image.jpg', horizontal)
# utilizing the vertical filter
convolve('image.jpg', vertical)
索贝尔过滤器
The vertical and horizontal Sobel filters
就像 Prewitt 算子一样,Sobel 算子也由垂直和水平边缘检测滤波器组成。检测到的边缘非常类似于使用 Prewitt 滤波器获得的结果,但是具有更高的边缘像素强度的区别。换句话说,与 Prewitt 滤波器相比,使用 Sobel 滤波器检测的边缘更清晰。
# utilizing the horizontal filter
convolve('image.jpg', horizontal)
# utilizing the vertical filter
convolve('image.jpg', vertical)
拉普拉斯滤波器
The Laplacian filter
与 Prewitt 和 Sobel 滤波器不同,拉普拉斯滤波器是检测不同方向的边缘的单个滤波器。从数学的角度来看,它计算像素值的二阶导数,而 Prewitt 和 Sobel 滤波器计算一阶导数。
# utilizing the filter
convolve('image.jpg', filter)
罗宾逊指南针面具
All 8 Robinson Compass masks
Robinson 罗盘蒙版是边缘检测过滤器,由 8 个不同的过滤器组成,分别代表 8 个地理罗盘方向,如上图所示。这些过滤器有助于检测罗盘方向上的边缘。为简洁起见,仅使用两个过滤器进行说明。
# utilizing the north_west filter
convolve('image.jpg', north_west)
# utilizing the north_east filter
convolve('image.jpg', north_east)
克里施指南针面具
All 8 Krisch Compass masks
与 Robinson 罗盘遮罩类似,克里施罗盘遮罩也由 8 个过滤器组成,帮助检测地理罗盘方向上的边缘。下面使用了两个过滤器。
# utilizing the south_west filter
convolve('image.jpg', south_west)
# utilizing the south_east filter
convolve('image.jpg', south_east)
过滤器符号
上面有一个非常重要的陈述,你很可能忽略了,
水平(x 方向)滤镜有助于检测图像中垂直穿过水平轴的边缘,垂直(y 方向)滤镜则相反。
这种说法可能看起来有点混乱,但我将在这一部分进一步分解它。考虑下面的图像,右边的数字是人眼看到的,而左边的数字是计算机感知的。如图所示,白线在黑色“画布”上勾勒出一条清晰的垂直边缘,对于人眼而言,这是显而易见的,因为该线与其周围环境之间的对比度(在同一叶片中,计算机需要能够感知像素级对比度的变化,这也是边缘检测所必需的)。
然而,为了实际碰到这个边缘,需要从左向右(水平地)移动手指,反之亦然。这同样适用于边缘检测过滤器,要检测垂直边缘,您需要利用水平过滤器。
What you see VS What a computer 'sees'
让我们尝试使用水平和垂直 Prewitt 滤波器来检测图像中的边缘,边缘检测过程背后的数学原理如下图所示。卷积过程背后的数学很容易理解;
- 将过滤器放在左上角。
- 执行逐元素乘法。
- 计算累计和。
- 将获得的和作为空数组中的相应像素返回。
- 将过滤器向右移动一个像素,并重复步骤 1 - 4,同时继续向右填充空数组中的第一行。
- 当过滤器超出界限时停止。
- 将过滤器向下移动一个像素到第二行。
- 重复步骤 1 - 6,填充空数组中的第二行。
- 对所有行执行相同的操作,直到过滤器超出垂直轴的界限(dim 1)。
激活是使用 ReLU 函数完成的,该函数简单地将任何负像素转换为 0。在卷积和激活之后,水平滤波器突出显示垂直边缘,而垂直滤波器返回涂黑的图像(全零像素),这意味着它没有检测到边缘。
Edge detection math
检测到的边缘如下图所示。按照相同的逻辑,如果线是水平的,表示水平边缘,则垂直过滤器将突出显示水平边缘,而水平过滤器返回涂黑的图像。
Edge detected by the horizontal filter
使用卷积函数
对于那些想在不同的图像上使用上述卷积函数,或者想测试不同的滤波器来进行边缘检测或其他图像处理任务的人来说,这一节是如何做的快速指南。
该函数采用 3 个参数,即“图像文件路径”、“过滤器”和“标题”。
'图像文件路径'
这是指所需映像在本地驱动器或云上的位置。如果您的图像在当前工作目录中,您需要做的就是输入图像名称及其文件扩展名。如果没有,您将需要提供一个绝对路径,格式为“C:/Users/Username/Downloads/image _ name . jpg”(因为我们使用的是 Python,所以这里是正斜杠)。
过滤器
事实上,这是您希望在卷积过程中使用的过滤器。使用 NumPy 很容易制作过滤器,如下所示。接下来您需要做的就是在函数中提供过滤器对象。
# creating a Prewitt horizontal filter
prewitt_x = np.array(([-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]))
# creating a laplacian filter
laplacian = np.array(([-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]))
标题
这将是使用该函数时提供的图像可视化的标题。默认情况下,它是一个空字符串,但是您可以根据需要随意提供一个合适的标题。
结束语
卷积神经网络的特殊之处在于它们能够从图像像素等数据的二维表示中提取特征。使用包含在称为过滤器的神经网络中的工具将特征提取为边缘。
在本文中,我们使用一些预定义的过滤器,从计算机视觉的角度研究了边缘检测/特征提取是什么样子的。然而,值得注意的是,CNN 将不会使用这些预定义的滤波器来进行边缘检测,而是学习最佳滤波器来检测边缘并提取感兴趣的数据集中的特征。
用 Keras 微调浅层网络进行有效的图像分类
原文:https://blog.paperspace.com/fine-tuning-shallow-networks-keras/
具有广泛深度架构的神经网络通常包含数百万个参数,这使得训练它们在计算上既昂贵又耗时。在本教程中,我们将使用 DenseNet 实现最先进的图像分类性能,最初只有一个隐藏层。在这项研究中,我们系统地调整了隐藏层中神经元的数量,并在基准图像分类数据集上训练了我们的模型。这项研究表明,建立更深层次的神经网络并不总是必要的;相反,更重要的是关注每层神经元的正确数量。
您可以跟随代码并从 ML Showcase 中免费运行它。
介绍
2012 年,Krizhevsky 等人推出了用于图像分类的 Alex net $[ 1]$,它拥有 66 万个神经元,6100 万个参数和 6 亿个连接。作者花了六天时间在两个 Nvidia Geforce GTX 580 GPU 上并行训练他们的网络超过 90 个时代。后来,在 2014 年,西蒙扬等人推出了 VGG-16。它总共包含 138M 个参数。从那时起,设计越来越复杂的神经网络结构,结合大量的参数,已经成为一种趋势。
动机
创建更深层次的神经网络需要更复杂的硬件,如高 GPU 内存,这可能很昂贵。训练一个网络几天或几周也不总是可行的。在这项研究中,我们调整了神经元的数量、不同的激活功能、辍学率,并且只有一层,试图获得 AlexNet 级别的准确性。我们在肖等人介绍的时尚基准数据集上进行了实验。
目标
本实验的主要目标是在不影响性能的情况下,用尽可能少的参数确定最佳神经网络架构。此外,这项工作还揭示了具有最佳神经元数量的隐藏层的能力。此外,我们尽力保持我们的方法尽可能简单,没有花哨的阶段,如图像放大,图像放大,或其他。标准 AlexNet 需要 256×256 RGB 图像,但我们应用了 28×28 灰度图像,并比较了性能,以便在低质量数据集上对浅层网络稳定性有一个适当的了解。这项研究对于提高低内存资源的性能尤为重要,因为即使是 256×256 灰度图像数据集也需要大量内存。
资料组
时尚-MNIST 数据集包含 60,000 个训练和 10,000 个测试 28×28 像素灰度图像,跨越 10 个类别\([3]\)。据马等报道,AlexNet 在时尚数据集上的准确率为 86.43% \([4]\)。
关于 AlexNet 输入的说明(从到此处):
AlexNet 的输入是大小为 256×256 的 RGB 图像。这意味着训练集中的所有图像和所有测试图像的大小都需要为 256×256。
如果输入图像不是 256×256,则需要将其转换为 256×256,然后再用于训练网络。为此,将较小的尺寸调整为 256,然后裁剪生成的图像以获得 256×256 的图像。
...
如果输入图像是灰度图像,则通过复制单通道以获得 3 通道 RGB 图像,将其转换为 RGB 图像。
我们将在本文中为每一步提供相应的代码片段。我们将 Keras 用于实现目的。注意你可以在 Gradient 上免费运行代码。
导入数据集
import keras
(x_train, y_train), (x_test, y_test) = keras.datasets.fashion_mnist.load_data()
print('x_train.shape =',x_train.shape)
print('y_train.shape =',y_train.shape)
print('x_test.shape =',x_test.shape)
print('y_test.shape =',y_test.shape)
预期产出:
x_train.shape = (60000, 28, 28)
y_train.shape = (60000,)
x_test.shape = (10000, 28, 28)
y_test.shape = (10000,)
获取数据集中的类的数量
import numpy as np
num_classes = np.unique(y_test).shape[0]
print('num_classes =', num_classes)
预期产出:
num_classes = 10
数据准备
归一化数据集中$[ 0,1]$范围内的每个图像矩阵中的值:
x_train_norm = x_train/255
x_test_norm = x_test/255
密集网络期望输入形状为(batch_size,width,height,channel)。因此,扩展了训练和测试数据集的维度:
final_train_imageset = np.expand_dims(x_train_norm, axis = 3)
final_test_imageset = np.expand_dims(x_test_norm, axis = 3)
y_train2 = np.expand_dims(y_train, axis = 1)
y_test2 = np.expand_dims(y_test, axis = 1)
print('final_train_imageset.shape =', final_train_imageset.shape)
print('final_test_imageset.shape =', final_test_imageset.shape)
print('y_train2.shape =', y_train2.shape)
print('y_test2.shape =', y_test2.shape)
预期产出:
final_train_imageset.shape = (60000, 28, 28, 1)
final_test_imageset.shape = (10000, 28, 28, 1)
y_train2.shape = (60000, 1)
y_test2.shape = (10000, 1)
为了使用分类交叉熵作为损失函数,我们必须对标签应用一键编码。
final_train_label = keras.utils.to_categorical(y_train2, num_classes)
final_test_label = keras.utils.to_categorical(y_test2, num_classes)
print('final_train_label.shape =',final_train_label.shape)
print('final_test_label.shape =',final_test_label.shape)
预期产出:
final_train_label.shape = (60000, 10)
final_test_label.shape = (10000, 10)
通用 DenseNet 结构
我们首先只用一个隐藏层进行了彻底的实验。在本文的后面,我们将向两个性能最佳的单层架构添加另一个隐藏层。
单层 DenseNet
Fig 1. A generic Dense network with single hidden layer.
在图 1 中,我们已经可视化了带有一个隐藏层的通用 DenseNet 结构。它包含三层:输入层、隐藏层和输出层。在输入层,28×28 像素的图像被提供给网络。输出层具有对应于十个类别的十个神经元。我们根据图像中的总像素数来调整隐藏层中神经元的数量。一幅图像有 28×28 (784)个像素。我们开始用相当于总像素数 1%的神经元数量( n )来训练我们的模型,也就是只有 7 个神经元(784×0.01)。然后,我们逐渐增加了 78 (10%)、392 (50%)和 784 (100%)个神经元。隐藏层的输出在输出层之前被展平。
设置必要的参数和变量:
percentile = 1.0 # 1.0, 0.5, 0.1, 0.01
NUM_NEURONS = (x_train.shape[1]*x_train.shape[2])*percentile
NUM_LAYERS = 1
BATCH_SIZE = 128
NUM_EPOCHS = 3000
LEARNING_RATE = 0.0001
EPSILON = 1e-4
DROPOUT = 0.5 # 0.5, 0.8
LOSS = 'categorical_crossentropy'
DENSE_ACTIVATION_FUNCTION = 'default' # default, relu, LeakyReLU, PReLU, ELU
FINAL_ACTIVATION_FUNCTION = 'softmax'
early_stop_after_epochs = 50 # stop after 50 consecutive epochs with no improvement
validation_split = 0.1
checkpointer_name = "weights.Dense.Fashion.nLayers"+str(NUM_LAYERS)+".nNeurons"+str(NUM_NEURONS)+".act."+DENSE_ACTIVATION_FUNCTION+".p"+str(percentile)+".dropout"+str(DROPOUT)+".batch"+str(BATCH_SIZE)+".hdf5"
总的来说,我们对隐藏层使用了 50%的落差( d )。然而,在两种情况下,我们应用了 80%的压降来减少过度拟合。每个隐藏单元在没有任何激活功能的情况下进行测试,并且有一个 ReLU 激活。在最后一层,我们应用 softmax 激活作为分类器。此外,在所有情况下,我们用零初始化偏差,并使用glorot_uniform
作为内核初始化器。它有助于网络更快地收敛到最优解。所有的任务都是用 Keras 函数 API 实现的。
选择激活功能列表:
def dense_activation():
if DENSE_ACTIVATION_FUNCTION == 'relu':
keras.layers.ReLU(max_value=None, negative_slope=0, threshold=0)
elif DENSE_ACTIVATION_FUNCTION == 'LeakyReLU':
keras.layers.LeakyReLU(alpha=0.3)
elif DENSE_ACTIVATION_FUNCTION == 'PReLU':
keras.layers.PReLU(tf.initializers.constant(0.3)) # "zeros"
elif DENSE_ACTIVATION_FUNCTION == 'ELU':
keras.layers.ELU(alpha=1.0)
elif DENSE_ACTIVATION_FUNCTION == 'default':
return None
对于隐层中相应的 7、78、392 和 784 个神经元,可训练参数的总数约为 54K、611K、3M 和 6.1M。没有不可训练的参数。我们抽取 10%的训练数据进行验证,并继续训练每个模型,直到有 50 个连续的时期验证损失没有改善。
型号代码:
from keras.layers import *
from keras.models import * #Model, load_model
input_shape = final_train_imageset.shape[1:]
# Input tensor shape
inputs = Input(input_shape)
x = inputs
for _ in range(NUM_LAYERS):
x = Dense(NUM_NEURONS, activation=dense_activation())(x)
x = Dropout(DROPOUT)(x)
x = Flatten()(x)
outputs = Dense(num_classes, activation=FINAL_ACTIVATION_FUNCTION)(x)
model = Model(inputs=inputs, outputs=outputs)
model.summary()
结构应该如下所示:
Model: "functional_9"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_6 (InputLayer) [(None, 28, 28, 1)] 0
_________________________________________________________________
dense_8 (Dense) (None, 28, 28, 784) 1568
_________________________________________________________________
dropout_4 (Dropout) (None, 28, 28, 784) 0
_________________________________________________________________
flatten_4 (Flatten) (None, 614656) 0
_________________________________________________________________
dense_9 (Dense) (None, 10) 6146570
=================================================================
Total params: 6,148,138
Trainable params: 6,148,138
Non-trainable params: 0
_________________________________________________________________
定义一些广泛使用的优化器:
optimizer_1 = keras.optimizers.RMSprop(lr = LEARNING_RATE, epsilon=EPSILON)
optimizer_2 = keras.optimizers.Adam(lr = LEARNING_RATE, epsilon=EPSILON, beta_1=0.9, beta_2=0.999, amsgrad=False)
optimizer_3 = keras.optimizers.SGD(lr = LEARNING_RATE, momentum=0.85)
让我们选择 Adam optimizer 并编译模型:
model.compile(
optimizer=optimizer_2, # 'Adam'
loss=LOSS,
metrics=['accuracy', 'Precision', 'Recall']
)
设置模型检查指针:
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
checkpointer_best = ModelCheckpoint(filepath = checkpointer_name,
monitor='val_loss',
save_weights_only=False,
mode='auto',
verbose = 2,
save_best_only = True
)
early_stopping = EarlyStopping(monitor='val_loss', patience=early_stop_after_epochs)
训练模型:
list_callbacks = [checkpointer_best, early_stopping]
history = model.fit(final_train_imageset, final_train_label,
shuffle=True,
batch_size = BATCH_SIZE,
epochs = NUM_EPOCHS,
validation_split = validation_split,
callbacks=list_callbacks
)
单层 DenseNet 的性能评价
现在,加载训练好的模型,并使用测试集进行评估:
model_loaded = load_model(checkpointer_name)
result = model_loaded.evaluate(final_test_imageset, final_test_label)
我们在表 1(下面)中总结了我们的实验结果。
Table 1. Performance evaluation for the applied single-layer models in comparison to AlexNet (accuracy 86.43%).
由于单层模型中最高的测试准确度(86.20%)和最低的测试损失(0.39)是用 392 个神经元(具有 ReLU 激活)实现的,因此我们用 392 个神经元训练模型,其中 80%的神经元在激活和未激活的情况下都丢失。需要注意的一点是,就测试准确性而言,第二好的模型也是 392 个神经元;但是,没有激活(84.69%)。
最后,对于丢失率为 80%的 392 个隐藏神经元,在没有激活的 377 个时期内,相应的测试准确度、测试丢失率、精确度和召回率分别为 86.36%、0.38%、88.77%和 84.55%。然而,在 ReLU 激活的情况下,仅 68 个时期的相应结果分别为 84.45%、0.44%、87.55%和 81.52%。
总的来说,由 392 个隐藏单元组成的模型实现了最佳性能,其中 80%的丢失没有任何激活功能。性能( 86.36% 测试准确率)与 AlexNet 的准确率水平(86.43%)相差无几。
让我们绘制学习曲线:
import matplotlib.pyplot as plt
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title(title)
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train_loss','val_loss'], loc = 'best')
Fig 2. Training loss vs. validation loss for the single-layer model with 392 neurons and 80% dropout.
此外,在培训时可视化培训和验证准确性趋势:
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title(title)
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train_accuracy','val_accuracy'], loc = 'best')
Fig 3. Training accuracy vs. validation accuracy for the single-layer model with 392 neurons with 80% dropout.
多层 DenseNet
现在,我们已经用单个隐藏层对网络中的神经元数量进行了调整,让我们添加另一个隐藏层。这里,我们研究了两个不同的双隐藏层网络。第一个包含 78 个神经元,第二个在每个隐藏层包含 392 个神经元。
多层 DenseNet 的性能评估
对于第一种架构,测试损失、测试准确度、精确度和召回率分别为 0.3691、、86.71% 、89%和 84.72%。图 4 显示了该网络的训练与验证曲线。
Fig 4. Training loss versus validation loss for the model with 2 layers (78 neurons and 50% dropout in each layer).
相反,第二种网络架构的性能分别为 0.3721、、86.66% 、88.87%和 84.91%。图 5 展示了学习曲线(验证损失)与训练损失曲线的对比。
Fig 5. Training loss versus validation loss for model with 2 layers (392 neurons and 50% dropout in each layer).
请注意,虽然单个隐藏层中的几个实现几乎达到了 AlexNet 级别的精度,但是两个具有双隐藏层的实现都克服了 AlexNet 的性能。此外,添加更多的神经元并不总能得到更好的结果,并且会使神经网络更容易受到过拟合的影响。
对未来贡献的建议
在这项工作中,我们体验了浅层网络中隐藏神经元学习数据的强大能力。我们将单隐层和双隐层模型与更深的 AlexNet 架构进行了比较。尽管如此,这些结果应该用其他基准数据集进一步深入研究。此外,我们可以检查这些行为是否适用于低得多的维度的图像。我们应该另外构建一个相似类型的浅层卷积模型来观察效果。考虑具有正确配置的最佳数量的神经元,我们希望这种类型的浅层模型将在很大程度上消除我们对重量级模型的需要,从而降低昂贵的硬件要求和时间复杂性。
参考
- Krizhevsky,a .,Sutskever,I .,& Hinton,G. E. (2012 年)。基于深度卷积神经网络的图像网分类。神经信息处理系统进展(第 1097-1105 页)。
- Simonyan 和 a . zisser man(2014 年)。用于大规模图像识别的非常深的卷积网络。arXiv 预印本 arXiv:1409.1556。
- 肖,h .,拉苏尔,k .,,沃尔格拉夫,R. (2017)。Fashion-mnist:一种用于机器学习算法基准测试的新型图像数据集。arXiv 预印本 arXiv:1708.07747。
- 马,李,夏,杨,张(2020)。自主深度学习:用于图像分类的遗传 DCNN 设计器。神经计算,379,152-161。
利用深度学习进行股票价格预测(下)
原文:https://blog.paperspace.com/forecasting-stock-price-prediction-using-deep-learning/
在关于使用深度学习进行股票价格预测的系列的第一部分中,我们涵盖了使用神经网络进行股票市场分析所需的所有基本概念。在第二篇文章中,我们将使用深度学习模型执行股票市场价格预测的实际实现。获得的最终结果可能有助于用户确定他们的投资结果从长远来看是否值得。
在深入探讨这个主题之前,让我们先来看看本文的目录。这个列表将帮助我们理解这篇文章的现实期望,你可以随意跳到你认为最有必要的部分。
目录
- 介绍
- 准备数据
- 数据可视化
- 数据预处理和进一步可视化
- 分析数据
1。第一组数据元素
2。第二组数据元素
- 构建深度学习模型
1。堆叠式 LSTM 模型
2。模式摘要和情节
3。编译和拟合模型
4。分析结果
- 做预测
- 结论
介绍
本文将研究神经网络在时间序列预测中的应用,以实现股票市场价格的预测。您可以找到本教程的完整代码,并在 ML Showcase 的免费 GPU 上运行它。虽然您可以自己直接运行整个笔记本,但建议您理解和研究每个方面,并单独考虑代码块,以便更好地理解这些概念。这个项目的方法可以扩展到其他类似的应用,如加密货币价格预测。
预测模型的堆叠 LSTM 模型的整个架构将使用 TensorFlow 深度学习框架来构建。除了 TensorFlow 库,我们还将利用其他库,如 pandas、matplotlib、numpy 和 scikit-learn,在整个项目过程中执行各种操作。这些操作包括加载和查看数据集,可视化我们的数据,将其转换为适合堆叠 LSTMs 计算的形状,以及开始项目前的最后准备工作。
本文的主要目的是通过股票市场价格预测的例子,提供深度学习在时间序列预测中的应用这一主题的教育观点。这个例子只是你可以用神经网络构建的许多用例选项中的一个(在这个例子中是堆叠的 LSTMs)。需要注意的是,在现实生活中实现这些想法之前,请确保您研究了众多可用的工具,并确定哪些想法最适合您的应用程序。现在让我们从头开始对我们的项目进行一个完整的分解。
准备数据
使用 LSTMs 的深度学习来完成这个股票价格预测项目的第一步是收集数据。我们将考虑一个来自 Kaggle 的随机数据集,它由苹果的历史股票数据组成。我们将使用 Panda 的库读取 CSV 文件,然后查看数据的前五个元素。
# Importing the Pandas Library for loading our dataset
# Grab The Data Here - https://www.kaggle.com/tarunpaparaju/apple-aapl-historical-stock-data
import pandas as pd
df = pd.read_csv('HistoricalQuotes.csv')
df.head()
Image by Author
尽管股票市场预测模型需要考虑许多参数,但我们将只参考收盘/最后一列,因为它给我们提供了更多的平均方法。出于训练和验证的目的,只考虑其中一列也更容易。敏锐地观察数据集是解决大多数数据科学问题的必要步骤。查看数据集后,我们可以很容易地推断出日期是按降序排列的。我们需要纠正这一点。
data = df.reset_index()[' Close/Last'] # Make Sure You add a space
data.head()
结果
0 $273.36
1 $273.52
2 $292.65
3 $288.08
4 $298.18
Name: Close/Last, dtype: object
然后,我们需要按照升序排列数据集。虽然您可以从 panda 的模块中使用 reindex 函数,但我更喜欢构建一个新的列表并反转该列表,最后创建新的数据框来处理以下任务。
选项 1
df3 = data.reindex(index=data.index[::-1])
df3.head()
结果
2517 $29.8557
2516 $29.8357
2515 $29.9043
2514 $30.1014
2513 $31.2786
Name: Close/Last, dtype: object
选项 2
df2 = []
for i in data:
i = i[2:]
i = float(i)
df2.append(i)
df2.reverse()
df1 = pd.DataFrame(df2)[0]
df1.head()
结果
0 29.8557
1 29.8357
2 29.9043
3 30.1014
4 31.2786
Name: 0, dtype: float64
可视化您的数据
对于任何机器学习和深度学习问题来说,最关键的步骤之一就是你的数据的可视化。对数据进行可视化和预处理后,您可以对正在处理的模型类型以及解决任务所需的必要步骤和措施有一个简要的了解。Python 编程语言中最好的可视化库之一是 Matplotlib 库。它将允许您相应地可视化数据集。让我们用数据和它们各自的索引来绘制模型。数据由股票在各自区间的价值组成。
# Using the Matplotlib Library for visualizing our time-series data
import matplotlib.pyplot as plt
plt.title("Data Plot")
plt.xlabel("Index")
plt.ylabel("Data")
plt.plot(df1)
Image By Author
从我们的可视化,我们可以注意到,每天的美元汇率的绘图通常似乎有一个增加的趋势。
数据预处理和进一步可视化
我们的下一步将是准备我们的数据集。我们将导入 numpy 和 scikit-learn 库,以数组的形式可视化我们的数据,以便更好地建模。由于数据有点大,值也很高,为了更好地计算,最好将它们设置在 0 和 1 之间。我们将使用 scikit-learn 库中的 Min Max Scaler 函数来执行特定的操作。
import numpy as np
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0,1))
df1 = scaler.fit_transform(np.array(df1).reshape(-1,1))
df1
结果
array([[6.72575693e-05],
[0.00000000e+00],
[2.30693463e-04],
...,
[8.83812549e-01],
[8.19480684e-01],
[8.18942624e-01]])
从同一个 sklearn 模块中,我们可以导入函数来将我们的数据分成训练和测试数据集。这里要考虑的基本步骤是确保 shuffle 被设置为 False,因为我们不希望我们的数据被随机分布。因此,我们将给出一个小的测试规模,并将其余部分分配给训练数据。一旦我们完成了所需的数据分割,让我们继续可视化我们已经创建的训练和测试数据集。
from sklearn.model_selection import train_test_split
X_train, X_test = train_test_split(df1, test_size=0.20, shuffle=False)
训练数据可视化
plt.title("Data Plot")
plt.xlabel("Index")
plt.ylabel("Data")
plt.plot(X_train)
Image By Author
X_train[:5]
结果
array([[6.72575693e-05],
[0.00000000e+00],
[2.30693463e-04],
[8.93516807e-04],
[4.85229733e-03]])
测试数据可视化
plt.title("Data Plot")
plt.xlabel("Index")
plt.ylabel("Data")
plt.plot(X_test)
Image By Author
X_test[:5]
结果
array([[0.49866208],
[0.4881699 ],
[0.49223898],
[0.49429034],
[0.49378591]])
现在,我们已经对我们的训练和测试数据有了一个简单的了解,下一个重要的步骤是考虑这些元素,并对它们中的每一个进行预测。这个过程就是我们将如何创建我们的最终训练和测试数据以及它们各自的最终训练和测试预测。这些预测或结果将按照我们为数据集考虑的时间步长来考虑。我将假设一个大约 100 个数据元素的时间步长和每个数据集的一个特定预测。这种假设是深度学习模型的时间序列分析的组成部分。
从 0 到 99 的元素(基本上是前 100 个元素)将构成第一组训练或测试数据集的元素。第 100 个元素将构成第一个预测。第一个结果将分别存储在结果(Y)训练或测试列表中。从 1 到 100 的下一个元素将构成训练或测试数据集中的下一个(或第二个)元素集,而下一个预测或结果将由数据集的第 101 个元素组成。通过使用这种方法,我们可以建立我们的数据集。我们的深度学习模型架构将最适合解决这类数据集和股票价格预测问题。
X_train_data = []
Y_train_data = []
X_test_data = []
Y_test_data = []
train_len = len(X_train)
test_len = len(X_test)
# Create the training dataset
for i in range(train_len-101):
a = X_train[i:(i+100), 0]
X_train_data.append(a)
Y_train_data.append(X_train[i + 100, 0])
# Create the test dataset
for j in range(test_len-101):
b = X_test[j:(j+100), 0]
X_test_data.append(a)
Y_test_data.append(X_test[j + 100, 0])
X_train_data = np.array(X_train_data)
Y_train_data = np.array(Y_train_data)
X_test_data = np.array(X_test_data)
Y_test_data = np.array(Y_test_data)
分析数据
现在,我们已经对前面的解释和代码块所要达到的目的有了一个简单的了解,让我们通过实际分析和查看前两组训练数据以及它们各自存储在输出数据集中的输出,来进一步深入了解这个主题。
第一组训练数据和输出
X_train_data[0]
结果
array([0\. , 0.00023069, 0.00089352, 0.0048523 , 0.00491451,
0.00680747, 0.00768182, 0.00799894, 0.00852725, 0.00720127,
0.00749451, 0.00733578, 0.00759035, 0.00643756, 0.00763844,
0.00937268, 0.00985794, 0.00855146, 0.01059307, 0.01130902,
0.0129686 , 0.01256271, 0.0130288 , 0.01423944, 0.01474387,
0.01525301, 0.01494093, 0.0158247 , 0.01606481, 0.01613206,
0.01769849, 0.01925013, 0.01851971, 0.01836132, 0.01716985,
0.02419826, 0.0276812 , 0.02977593, 0.02913699, 0.02555317,
0.02534164, 0.02872369, 0.02509683, 0.02762369, 0.02393899,
0.02264428, 0.01796752, 0.012976 , 0.02168586, 0.0229012 ,
0.02557704, 0.0237853 , 0.02160414, 0.02179616, 0.02090264,
0.01897134, 0.01388869, 0.01607927, 0.01821234, 0.01747251,
0.01693882, 0.02137815, 0.02307405, 0.02497173, 0.02647056,
0.02607206, 0.02263453, 0.02022065, 0.01944719, 0.01650198,
0.02001383, 0.02145516, 0.02182508, 0.02442425, 0.02805616,
0.03027566, 0.03133429, 0.02945882, 0.03122668, 0.02984319,
0.02889688, 0.02779184, 0.02856059, 0.02273306, 0.02050381,
0.0190386 , 0.01829877, 0.0191109 , 0.02393159, 0.0236555 ,
0.02439062, 0.02326876, 0.0206326 , 0.02107886, 0.02046547,
0.01972093, 0.01764536, 0.02067699, 0.02180591, 0.0241041 ])
Y_train_data[0]
结果
0.024104103955989345
第二组训练数据和输出
X_train_data[1]
结果
array([0\. , 0.00023069, 0.00089352, 0.0048523 , 0.00491451,
0.00680747, 0.00768182, 0.00799894, 0.00852725, 0.00720127,
0.00749451, 0.00733578, 0.00759035, 0.00643756, 0.00763844,
0.00937268, 0.00985794, 0.00855146, 0.01059307, 0.01130902,
0.0129686 , 0.01256271, 0.0130288 , 0.01423944, 0.01474387,
0.01525301, 0.01494093, 0.0158247 , 0.01606481, 0.01613206,
0.01769849, 0.01925013, 0.01851971, 0.01836132, 0.01716985,
0.02419826, 0.0276812 , 0.02977593, 0.02913699, 0.02555317,
0.02534164, 0.02872369, 0.02509683, 0.02762369, 0.02393899,
0.02264428, 0.01796752, 0.012976 , 0.02168586, 0.0229012 ,
0.02557704, 0.0237853 , 0.02160414, 0.02179616, 0.02090264,
0.01897134, 0.01388869, 0.01607927, 0.01821234, 0.01747251,
0.01693882, 0.02137815, 0.02307405, 0.02497173, 0.02647056,
0.02607206, 0.02263453, 0.02022065, 0.01944719, 0.01650198,
0.02001383, 0.02145516, 0.02182508, 0.02442425, 0.02805616,
0.03027566, 0.03133429, 0.02945882, 0.03122668, 0.02984319,
0.02889688, 0.02779184, 0.02856059, 0.02273306, 0.02050381,
0.0190386 , 0.01829877, 0.0191109 , 0.02393159, 0.0236555 ,
0.02439062, 0.02326876, 0.0206326 , 0.02107886, 0.02046547,
0.01972093, 0.01764536, 0.02067699, 0.02180591, 0.0241041 ])
Y_train_data[1]
结果
0.024544304746736592
作为我们分析和预处理的最后一步,让我们打印训练和测试数据集的形状以及它们各自的输出。训练和测试数据集的形状必须在其每个集合中包含 100 个元素,而它们各自的结果必须具有与训练或测试数据集中存在的集合数量相等的形状。使用这些图形和形状,我们终于可以进入预处理的最后一步。
### Printing the training and testing shapes
print("Training size of data = ", X_train_data.shape)
print("Training size of labels = ", Y_train_data.shape)
print("Training size of data = ", X_test_data.shape)
print("Training size of labels = ", Y_test_data.shape)
结果
Training size of data = (1913, 100)
Training size of labels = (1913,)
Training size of data = (403, 100)
Training size of labels = (403,)
现在,我们对测试和训练数据集产生的形状以及它们各自的输出有了一个简单的概念,最后的关键步骤是将数据集的这些现有形状转换成适合长短期记忆(LSTM)模型的形式。由于我们当前的结构只是一个二维空间,我们需要把它转换成一个三维空间,使它适合于执行 LSTM 运算和计算。
### Converting the training and testing data shapes into a 3-dimensional space to make it suitable for LSTMs
X_train_data = X_train_data.reshape(1913, 100, 1)
X_test_data = X_test_data.reshape(403, 100, 1)
print(X_train_data.shape)
print(X_test_data.shape)
结果
(1913, 100, 1)
(403, 100, 1)
构建深度学习模型
我们将使用堆叠的 LSTM 层,并构建我们的深度学习神经网络来构建一个架构来完成这项任务。首先,我们将导入执行这个计算的所有基本库。我们将使用简单的顺序架构。我强烈建议查看我之前的两篇关于 TensorFlow 和 Keras 介绍的文章,以了解关于这些特定主题的更详细的方法。
The Absolute Guide to TensorFlow | Paperspace BlogTensorFlow is one of the most popular deep learning libraries of the modern era.Developed by Google and released in 2015, TensorFlow is considered to be one ofthe best platforms for researching, developing, and deploying machine learningmodels. The core structure of TensorFlow is developed with …Paperspace BlogBharath KThe Absolute Guide to KerasIn this article, we will dive deeper into the world of TensorFlow and Keras. Ifyou haven’t already, I would highly recommend first checking out my previousarticle, The Absolute Guide to TensorFlow [/absolute-guide-to-tensorflow]. Itwill help you to better understand the various concepts that we w…Paperspace BlogBharath K
然而,为了对这些主题进行简短的总结,序列模型是构建深度学习模型和构建神经网络以解决各种复杂任务的最简单的架构之一。顺序模型适用于简单的层堆叠,其中每层都有一个输入张量和一个输出张量。
在处理股票市场价格预测问题的计算时,我们只需要两层:密集层和 LSTM 层。密集层基本上就像神经网络中完全连接的层。它们既可以用作具有特定数量节点的隐藏层,也可以用作具有特定数量节点的输出层。这里,我们的密集层将只有一个输出节点,因为我们只需要一个特定参数集或数据集的输出或预测。我们之前已经更详细地讨论了 LSTM 层的主题。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import LSTM
# Build The Architecture
model=Sequential()
model.add(LSTM(100,return_sequences=True,input_shape=(100,1)))
model.add(LSTM(100,return_sequences=True))
model.add(LSTM(100))
model.add(Dense(1))
随着这一步的完成,我们已经完成了我们的堆栈 LSTM 架构的构建,我们可以利用它来进行股票市场预测。然而,在该模型能够被部署并用于进行实际预测和找出结果之前,我们还剩下一些步骤。让我们看看模型概要和模型情节。
model.summary()
结果
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 100, 100) 40800
_________________________________________________________________
lstm_1 (LSTM) (None, 100, 100) 80400
_________________________________________________________________
lstm_2 (LSTM) (None, 100) 80400
_________________________________________________________________
dense (Dense) (None, 1) 101
=================================================================
Total params: 201,701
Trainable params: 201,701
Non-trainable params: 0
_________________________________________________________________
模型图
# Plot the Model
from tensorflow import keras
from keras.utils.vis_utils import plot_model
keras.utils.plot_model(model, to_file='model.png', show_layer_names=True)
Image By Author
在我们进入编译和训练过程之前,我将实现一个回调函数,它对于保存我们的检查点以获得处理的验证损失的最佳值非常有用。保存最佳模型的原因是为了以后可以使用它们来加载这些模型,并选择使用保存的模型进行进一步的预测。当您只是根据需要加载模型时,您不必重复整个训练和装配过程。在部署阶段,保存的模型可用于进一步预测。
我们将在模型中使用的第二个也是最后一个回调是 tensorboard 回调。我们将主要使用 Keras 模块的 tensorboard 回调来可视化训练损失和验证损失的图形。我们将使用一个随机日志目录来存储培训和验证数据的文件夹。最后,我们将通过 Tensorboard 回调函数来利用这个目录。一旦我们的培训完成,我们可以利用日志目录中保存的信息来可视化我们的培训和验证损失。
你也可以使用其他回调,正如我在之前的 Keras 文章中提到的。然而,我认为没有必要使用任何其他 Keras 或自定义回调来执行这个特定的任务。如果您想更详细地探索其他回调,您可以随意尝试。
# Initializing the callbacks
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.callbacks import TensorBoard
checkpoint = ModelCheckpoint("checkpoint1.h5", monitor='val_loss', verbose=1,
save_best_only=True, mode='auto')
logdir='logs1'
tensorboard_Visualization = TensorBoard(log_dir=logdir)
在初始化我们的回调之后,我们可以继续编译我们使用顺序建模架构完成构建的堆栈 LSTM 模型。我们将用于模型编译的损失函数是均方误差。均方误差函数计算标注和预测之间误差的均方值。该方程的公式解释如下:
\(loss = square(y_true - y_pred)\)
正如我在之前的 Keras 文章中所讨论的,默认情况下,用于执行模型编译的最佳优化器之一是 Adam 优化器。Adam 优化是一种随机梯度下降方法,它基于一阶和二阶矩的自适应估计。您也可以随意尝试其他优化器,如 RMS Prop,看看您是否能够在训练过程中获得更好的结果。
# Model Compilation
model.compile(loss='mean_squared_error', optimizer='adam')
构建和编译模型后的最后一步是训练模型并确保拟合过程完美进行。在训练阶段,我们将使用模型的拟合函数,然后相应地训练 X_train(训练数据)和 X_test(验证数据)。确保您相应地给出了等效的参数。拟合函数中的前两个属性应该分别具有训练数据和它们的预测。对于接下来的参数,我们将确保计算由测试数据及其预测组成的验证数据,分别用括号括起来。
我将运行该模型 20 个纪元。如果您想获得稍微好一点的结果,可以选择运行它超过指定的时期。对于拟合过程,我将使用 32 的批量大小,但是如果您正在增加历元的数量,您也可以随意以增加批量大小的形式进行编辑,如 64、128 等。当 verbose 参数设置为 1 时,将允许您查看每个步骤中发生的训练过程。根据您的方便,您也可以选择将其设置为 0 或 2。最后,我们将实现回调函数,它由我们之前定义的回调函数组成。既然我们已经为 fit 函数定义了所有必需的参数,我们可以运行代码块并观察训练过程。
# Training The Model
model.fit(X_train_data,
Y_train_data,
validation_data=(X_test_data, Y_test_data),
epochs=20,
batch_size=32,
verbose=1,
callbacks=[checkpoint, tensorboard_Visualization])
结果
Epoch 19/20
1824/1913 [===========================>..] - ETA: 0s - loss: 1.0524e-04
Epoch 00019: val_loss improved from 0.04187 to 0.03692, saving model to checkpoint1.h5
1913/1913 [==============================] - 1s 508us/sample - loss: 1.0573e-04 - val_loss: 0.0369
Epoch 20/20
1824/1913 [===========================>..] - ETA: 0s - loss: 1.3413e-04
Epoch 00020: val_loss did not improve from 0.03692
1913/1913 [==============================] - 1s 496us/sample - loss: 1.3472e-04 - val_loss: 0.0399
张量板分析
在执行拟合代码块之后,我们将有一个标记为“checkpoint1.h5”的保存的检查点,使用它我们可以直接加载我们的模型的最佳权重,并开始用它直接进行预测。没有必要再次重新训练整个模型。
我们现在将再次参考工作环境,注意我们可以找到一个新创建的名为 logs1 的目录。logs1 目录包含培训和验证事件,我们将利用这些事件来理解和分析培训和验证组件。生成的图表将为我们提供一个简短的描述,并让我们了解随着时代的增加,损失和验证损失的表现。要访问这些图形,请在各自的工作区中打开命令提示符,并在激活各自的虚拟环境后键入以下命令- tensorboard - logdir= "。/logs1 "。
在执行以下内容之后,您应该能够从您的 web 浏览器访问您的本地主机,这应该会将您引导到您可以查看的张量板图形。现在让我们继续分析训练和验证损失图。
from IPython.display import Image
pil_img = Image(filename='image1.png')
display(pil_img)
Image By Author
由于张量板图在第一幅图像中似乎没有给出精确的所需值,所以让我们放大一点,放大一点,以便更仔细地分析这些值。
pil_img = Image(filename='image2.png')
display(pil_img)
Image By Author
从第二幅图像中,可以清楚地看到,训练损失和验证损失都随着时期的增加而减少。因此,我们可以确定模型的拟合过程工作得相当好,因为模型随着时代数量的增加而不断改进。
预测和评估
在接下来的几个代码块中,我们将使用模型中的预测函数对定型和测试数据分别进行预测。需要注意的是,我们之前已经将训练和测试数据的值范围从 0 转换为 1,以便更快、更有效地进行计算。因此,现在应该使用 scaler 函数的逆变换属性来返回所需的值。通过使用这种程序方法,我们将获得我们需要达到的结果,因为获得的值被按比例缩小到它们的原始数字。
我们还将分析训练和测试数据的均方误差(MSE)参数。执行这项任务的方法相当简单。我们将需要两个额外的库,即 math 和 sklearn.metrics。从 metrics 模块中,我们将导入均方误差函数,该函数将帮助我们有效地计算训练值和测试值与其各自预测值之间的均方误差。一旦我们计算、计算并获得了所需的 MSE 值,我们就可以确定这些 MSE 值的根并不太差,并且能够产生有效的结果。它们与所建立的模型相兼容,并可用于做出适当的预测。
train_predict = model.predict(X_train_data)
test_predict = model.predict(X_test_data)
# Transform back to original form
train_predict = scaler.inverse_transform(train_predict)
test_predict = scaler.inverse_transform(test_predict)
# Calculate RMSE performance metrics for train and test data
import math
from sklearn.metrics import mean_squared_error
print("Train MSE = ", math.sqrt(mean_squared_error(Y_train_data, train_predict)))
print("Test MSE = ", math.sqrt(mean_squared_error(Y_test_data, test_predict)))
最后,我们将评估我们的模型并分析众多参数。这里考虑从到的小代码引用。我建议查看以下网站,以获得更多关于这个主题的信息。关于如何建立预测系统来评估和测试你的模型的更多细节,你可以查看这个链接。下面的图形结构是一个例子,是我们在尝试进行相应预测时将获得的图形类型的简要表示。您可以根据提供的参考资料自行尝试这些方法。我不会在本文中涉及它们,因为我的目的只是提供一个关于这些概念的教育视角和观点。
Image By Author
由此,我们在 LSTMs 和深度学习的帮助下,成功构建了我们的股市预测模型。
结论
我们已经完成了堆叠 LSTM 深度学习模型的构建,该模型可用于股票市场价格预测、加密货币挖掘和趋势以及日常生活的许多其他方面。你甚至可以建立类似的模型来理解每天的天气模式等等。利用深度学习进行时间序列预测的领域非常难以探索。它为我们提供了各种各样的机会,让我们致力于许多不同类型的独特项目,并构建深度学习模型。这些建立的深度学习模型将为我们思考许多时间序列问题的工作创造一种惊人的方法。
上一篇文章讨论了与时间序列预测相关的大多数常见主题。请阅读本系列的第 1 部分,以便更直观地理解大多数概念。第一部分从概念上概述了时间序列预测的一些基本主题所需的知识,并包括对第二部分内容的一些理论解释。在本文提供的 Jupyter 笔记本的帮助下,您可以随意探索和尝试各种集成来提高模型的性能并创建更多的创新项目。
感谢大家阅读这个由两部分组成的系列。在以后的文章中,我们将涉及更多关于 GANs、预测、NLP 模型等主题。在那之前,享受编码并坚持练习!
利用深度学习进行股票价格预测(上)
原文:https://blog.paperspace.com/forecasting-stock-prices-using-deep-learning/
在本文关于使用深度学习进行股价预测的第一部分中,我们将掌握理解使用机器学习和深度学习模型进行预测和时间序列分析的基本方面所需的大多数主题。时间序列分析(或预测)正在成为现代机器学习算法中最受欢迎的用例之一。为了理解时间序列分析中的不同类型的模式,并确定现实的未来预测,我们的机器学习或深度学习模型需要进行适当的训练。
在本文中,我们的主要目标仍然是理解解决时间序列分析和预测相关问题的所有基本工具和核心概念。在本系列的第二部分也是最后一部分,我们将介绍一个完整而直观的例子,说明如何从头开始构建自己的股票市场价格预测模型(即堆叠 LSTM 模型)。现在,让我们看一下目录,以设定您对第 1 部分的期望。
目录
- 介绍
- 为什么我们需要时间序列分析?
- 时间序列分析的基本要素
1。趋势
2。季节性
3。不规则性
4。循环的
- 了解平稳和非平稳序列
- 详细了解 LSTMs
- 何时不使用时间序列分析
- 结论
介绍
世界上发生的大多数自然事件都涉及到一定量的时间序列预测。时间序列预测的主要对象是一个变量,即 时间 。时间序列数据分析对于提取有意义的统计数据和其他重要特征是有用的。
时间序列分析包括以固定的时间间隔对一组观察值或数据点进行连续绘图。通过研究以前的结果及其随时间的进展,我们可以在这些研究观察的帮助下做出未来的预测。
解决时间序列分析相关问题的方法有其独特之处。大多数机器学习问题利用具有某些要预测的结果的数据集,例如类别标签。这种说法意味着大多数机器学习任务通常利用自变量和因变量来计算特定问题。
这个过程包括机器学习算法分析数据集(比如\(X\))并使用预测(\(Y\))来形成评估和解决问题陈述的方法。大多数受监督的机器学习算法都以这种方式执行。但是,时间序列分析是独特的,因为它只有一个变量: 时间 。我们将在本文的下一部分更深入地探讨如何用深度学习解决股票市场价格预测任务。目前,我们的主要目标是理解完成这项任务所需的术语和重要概念。让我们开始吧!
为什么我们需要时间序列分析?
Image By Author
时间间隔内数据点的有序序列构成了时间序列分析的基本要素。时间序列分析在现实世界中有着广泛的应用。因此,时间序列的研究对于理解,或者至少对于获得关于你在不久的将来可以预期什么的基本知识是有意义的。除了我们将在这些文章的下一部分中构建的股票市场价格预测模型之外,时间序列分析还应用于经济预测、销售预测、预算分析、过程和质量控制、天气模式检测、库存和效用研究、人口普查分析等许多领域。
理解数据元素以前的行为模式至关重要。考虑一个商业或经济预测的例子。当你可以从以前的模式中提取有用的信息时,你就可以相应地规划未来。通常情况下,借助于时间序列分析和预测做出的预测会产生良好的结果。这些将帮助用户计划和评估当前的成就。
时间序列分析的基本组成部分
时间序列分析就是收集以前的数据来研究各种趋势的模式。通过对这些时间序列预测模式进行详细分析,我们可以在我们的建设性深度学习模型的帮助下确定未来的结果。虽然有其他方法来确定未来趋势的现实结果,但深度学习模型是一种出色的方法,可以获得每个概念的一些最佳预测。
现在让我们关注时间序列预测的组成部分,即趋势、季节性、周期性和不规则性。时间序列分析的这四个组成部分指的是它们的图形表示中的变化类型。让我们分别来看一下这些概念,并借助一些现实的例子来获得更多的直觉。
趋势
Image Source
时间序列预测中的趋势被定义为在一段较长的时间内数据的持续增加或减少。在不同的时间点,数据点和元素可能会有轻微的波动,但总体变化和变化方向在较长的时间内保持不变。当一个趋势从长时间的持续上涨到长时间的持续下跌,这通常被称为“改变方向”趋势。
有几个术语用来定义我们在时间序列预测中处理的趋势类型。在很长一段时间内轻微或适度上升的趋势可以称为上升趋势,而在很长一段时间内轻微或适度下降的趋势称为下降趋势。如果趋势遵循逐渐增加或逐渐减少的一致模式,并且在图形的整体模式中没有太大影响,则该趋势可以被称为水平或静止趋势。上图所示的图形表示在很长一段时间内有一个向下的轨迹。因此,此图显示了下降趋势的代表,也称为下降趋势。
让我们考虑一个例子来更好地理解趋势的概念。像亚马逊、苹果、微软、特斯拉和类似的科技巨头这样的成功公司有一个合理的股价上升曲线。我们已经知道,我们可以将股票价格的上升确定为上升趋势。虽然成功的公司有上升趋势,但一些在股市表现不佳的公司的股价有下降趋势。那些在固定时间间隔内盈利和亏损都相当可观的中性利润率的公司被定义为水平或稳定趋势。
季节性
Image Source
受季节性频率变化影响的模式称为季节性模式。时间序列分析的这一组成部分可以根据时间戳的不同而不同,如季度、月或半年。然而,需要注意的重要一点是,这些波动通常发生在一年之内。每当频率是固定的和已知的,并且也是及时发生的,通常在一年内,时间序列分析的这个组成部分被称为季节性。
考虑一个现实的例子,想想某些季节性水果的销售。像西瓜这样的水果在夏季销售会增加,而在冬季,西瓜的销售会逐渐减少。同样,像苹果这样的季节性水果在冬季比其他季节有更高的销量。冰淇淋和嫩椰子是在夏季销售额增加而在其他季节销售额下降的食品的其他例子。除了食品之外,像 4 月这样的时期或月份可能会有较高的投资份额,并经历股票的下跌,直到 6 个月后的 10 月份,在 10 月份出现峰值上涨。这种模式也可以看作是一种季节性。
周期性
Image Source
当一个模式表现出混合频率的上升和下降,并且图形表示具有在一段时间内随机出现的波峰和波谷时,它被称为循环分量。这些事件通常持续至少一年。难以预测的某些公司的股票价格通常具有循环模式,即在某个时期股价上涨,而在其他时期利润较低。对于我们的深度学习模型来说,循环趋势是最难预测的。上面的图表显示了二十年间房屋销售的周期性行为。
周期性行为模式的一个很好的现实例子是当一个人决定投资他们自己的初创企业。在初创企业的建立和发展过程中,每个企业都会经历一个循环阶段。这些周期性波动经常发生在商业周期中。通常,创业阶段会包括投资阶段,这会对我们的价格产生轻微的负面影响。接下来的阶段可能包括你的营销和盈利阶段,在这个阶段你开始从你成功的创业中赚取利润。在这里,你会感受到图形曲线的增加。然而,你最终也会经历一个贬值阶段。这些将显示较少的利润,直到你不断改进和投资。这个过程再次开始循环阶段,持续几年的时间。冲洗并重复。
不规则
Image Source
不规则性是一个几乎不可能用深度学习模型进行准确预测的组件。不规则性或随机变化(顾名思义)涉及一种异常或非典型的模式,在这种模式下,很难推断出数据元素相对于时间的出现次数。上面是谷歌股票价格以不规则和随机模式快速变化的图形表示;这很难读。尽管存在信息和数据模式,但这种表示的建模过程将很难破解。该模型的主要目标是根据以前的结果预测未来的可能性。因此,不规则模式的模型稍微难以构建。
为了给不规则模式提供一个现实的例子,让我们来分析一下世界的现状,许多商业和其他行业都受到了大规模的影响。全球疫情是任何人都无法预测的不规则活动的一个很好的例子。这些由于自然灾害或现象而发生的干扰将影响交易价格、股票价格图表、公司和企业。你构建的模型不可能发现这些情景悲剧的发生。因此,不规则模式是时间序列预测的一个有趣的组成部分来分析和研究。
了解平稳和非平稳序列
Image Source
本文中我们将遇到的时间序列分析的另一个重要方面是平稳性的概念。当我们有一定数量的数据点或元素,并且所有这些数据点的均值和方差随时间保持不变时,那么我们称之为平稳序列。但是,如果这些元素随时间变化,我们称之为非平稳序列。大多数预测任务,包括股票市场价格或加密货币图表,都是非平稳的。不幸的是,由非平稳模式获得的结果是无效的。因此,它们需要被转换成固定模式。
下一篇文章中股票市场价格预测的整个数据准备过程将通过代码片段进行解释。在这里,我们将讨论其他几个重要的话题。将非平稳序列转化为平稳序列的几种方法有差分和变换。差分包括从高到低的顺序减去两个连续的数据点,而变换包括变换或发散序列。通常,对数转换用于此过程。有关此主题的更多信息,请参考上面图像来源中提供的链接。
为了测试 Python 中的平稳性,使用的两种主要方法包括滚动统计和ADCF 测试。滚动统计更多的是一种可视化技术,包括绘制移动平均值或移动方差,以检查它是否随时间变化。扩展的迪基-富勒检验(ADCF) 用于给出各种有助于识别平稳性的值。这些测试结果包括一些统计数据和临界值。它们用于验证平稳性。
现在让我们开始详细理解用于时间序列分析的 LSTMs 的概念。
详细了解 LSTMs
Image from Wiki
递归神经网络,也称为 RNNs,是一类允许以前的输出用作输入,同时具有隐藏状态的神经网络。关于 RNNs 的更深入的细节将在以后的文章中讨论。这里的主要焦点和目标仍然是研究和获得对 LSTM 建筑更好的直觉。由于梯度爆炸和消失的可能性,rnn 在传输长期数据元素方面存在问题。这些问题的解决方案是由(【LSTM】)模型提供的,这也是一种递归神经网络(RNN)架构,用于解决许多复杂的深度学习问题。LSTMs 对于我们使用深度学习进行股票价格预测的任务特别有用。**
具有有效的存储单元机制的 LSTM 体系结构对于解决复杂问题和以更高的准确度做出全面有效的预测是非常有用的。LSTMs 学习什么时候忘记,什么时候记住提供给他们的信息。LSTM 结构的基本解剖可以分为三个主要步骤。第一阶段是包含要重新收集的基本和初始数据的单元状态。第二阶段是隐藏状态,主要由三个门组成:遗忘门、输入门和输出门。我们将很快讨论这些门。最后,我们有一个循环阶段,在每个时间步结束时重新连接数据元素进行计算。
一旦所有的状态都被更新,一个时间步就完成了。让我们来探讨一下门的概念,并解读它们各自的用途。
- ****遗忘门:lstm 中最重要的门之一,利用 sigmoid 函数来选择保留或丢弃哪些值。接近 1 的值通常被保留,而接近 0 的值被忽略。
- ****输入门:输入门用于决定向 LSTM 单元添加什么。它利用了 sigmoid 和 tanh 函数。双曲正切函数将值压缩在-1 和 1 之间,以防止任一频谱上的值变得过大。
- ****输出门:输出门决定下一个隐藏状态的组件。它也是整个 LSTM 结构的整体汇编。
下面是一个堆栈 LSTM 结构的简单代码表示,将在本系列的下一部分中用于计算目的。代码块是在顺序模型架构的帮助下,用 TensorFlow 深度学习框架编写的。
**`model.add(LSTM(100,return_sequences=True,input_shape=(100,1)))
model.add(LSTM(100,return_sequences=True))
model.add(LSTM(100))`**
LSTMs 的数学计算可以有多种解释。下图是 LSTMs 数学计算的两种方法。有时偏置函数会被忽略,如第二幅图所示。然而,我们不会涵盖第一张图片的复杂细节。您可以从 image source 中提供链接查看更多关于该表示的信息。考虑到第一幅 LSTM 图像的数学方程对于计算来说相当精确。
**
Image Source** **
Image Source**
其中:
$ x(t)\(:LSTM 单元的输入向量
\) f(t)\(忘记门的激活向量
\) I(t)\(输入/更新门的激活向量
\) o(t)\(输出门的激活向量
\) h(t)\(隐藏状态向量,也称为 lstm 单元的输出向量
\)c̃(t)\(:单元输入激活向量
\) c(t)\(单元状态向量
\) w \(,\)U
关于长期短期记忆的进一步信息和探索(【LSTM】**),我强烈推荐阅读几篇广泛涉及这一概念的文章。我的两个主要建议是查看维基百科的官方文档,其中包含了学习 LSTMs 的大部分理论信息。为了更直观地理解,您应该查看的第二个链接是这里的。现在让我们来了解一些情况,在这些情况下,使用时间序列分析可能是不必要的,甚至是有害的。**
何时不使用时间序列分析
从本文的前几节可以明显看出,时间序列预测是当今世界的宝贵财富。时间序列分析的应用非常广泛,在我们的日常生活中有各种各样的用例。尽管有这些事实,但在某些情况下,如果不利用预测,你会过得更好。下面是两个这样的场景。
这些值在分析模式中是恒定的
**
Image from Wiki**
当这些值不变时,应用时间序列预测的全部意义就变得无用了。无论外部变量如何变化,你总是有一个固定不变的值。因此,这种情况不需要时间序列分析的参与。上面的图形表示是一个常数图的例子,其中无论其他约束是什么,值都保持固定在 4。考虑一个现实生活中的例子,你可以假设任何特定巧克力的成本在很长一段时间内保持不变。
分析模式中可用的值是函数的形式
**
Image from Wiki**
类似于前面讨论的点,当有一个固定函数具有每个期望时间点的所需值时,那么整个时间序列预测点就无效了。当时间序列分析以函数的形式出现时,这种模式的数学计算可以用计算器来完成。考虑一些这样的例子,上图显示了正弦和余弦波的表示。任何形式为\(Y(x) = X\)的函数计算起来都很简单,我们不需要深度学习就能做到。
结论
在本文中,我们已经涵盖了处理时间序列预测相关问题的大部分基本要求。涵盖的最重要的主题是长短期记忆(LSTM)。LSTM 将用于我们在下一个教程中构建的股票价格预测项目。
在我们的下一篇文章中,我们将致力于使用深度学习进行股票市场价格预测的项目,即 stacked LSTMs。我们将准备数据集,可视化数据点,并构建我们的模型结构。请随意查看我以前的几篇文章,并从下面提供的链接中直观地了解深度学习框架,如 TensorFlow 和 Keras。我们将需要这些库来完成我们的项目。
The Absolute Guide to TensorFlow | Paperspace BlogTensorFlow is one of the most popular deep learning libraries of the modern era.Developed by Google and released in 2015, TensorFlow is considered to be one ofthe best platforms for researching, developing, and deploying machine learningmodels. The core structure of TensorFlow is developed with …Paperspace BlogBharath K****The Absolute Guide to Keras | Paperspace BlogIn this article, we will dive deeper into the world of TensorFlow and Keras. Ifyou haven’t already, I would highly recommend first checking out my previousarticle, The Absolute Guide to TensorFlow [/absolute-guide-to-tensorflow]. Itwill help you to better understand the various concepts that we w…Paperspace BlogBharath K
强烈建议您查看这些库的指南,因为它们将在我们的文章系列的第二部分中被广泛使用。下一部分将包含完整的演练。在此之前,加强你的基础知识,为下一篇文章做好准备!
在免费云 gpus 上训练 ML 模型
原文:https://blog.paperspace.com/free-cloud-gpu/
当我们在 2014 年创建 Paperspace 时,我们的使命是让云 GPU 资源对每个人来说都更容易访问,更便宜。自成立以来,我们一直在提供各种各样的低成本 GPU 实例,价格往往是其他云提供商的零头。
今天,我们很高兴地宣布,我们已经将这一使命向前推进了一步。
介绍我们新的自由渐变 GPU 计划
我们想向您介绍我们在云中运行支持 GPU 的 Jupyter 笔记本电脑的新方法——完全免费!
为什么要在免费的 GPU 上运行 jupiter 笔记本
在免费的专用云 GPU 上运行:如果您没有经验,从任何主要提供商那里设置和运行云 GPU 都可能是一个复杂的过程。即使您是,不仅设置一个实例是不必要的时间浪费,而且可用的机器通常非常昂贵。
有了 Gradient 笔记本,您不仅不必担心设置和维护自己的实例,现在还可以在免费的专用云 GPU 实例上运行您的笔记本。
开始使用您的第一台免费 GPU 笔记本电脑
推出你的第一个渐变公共笔记本很简单:首先, 注册一个免费的渐变订阅! 注意:自由层中的所有笔记本默认设置为公共。
接下来,选择 M400 云 GPU 实例(运行在 NVIDIA Quadro M4000 上)或我们的高性能 C2 云 CPU 实例(英特尔至强 E5-2630 v3)。
点击“创建笔记本”,就可以了!
您可以在一个免费的专用 GPU/CPU 实例上一次运行一个渐变公共笔记本,最多 6 个小时。不要担心,您的笔记本将保持完整的版本,并且您可以重新启动您的实例来运行另外 6 个小时,次数不限。
要运行多个公共笔记本实例并访问私有笔记本,只需升级到我们的按秒付费云实例之一。
注册使用免费的渐变 GPU 计划
我们期待着帮助您与世界分享您的 ML 和深度学习模型。注册提前访问,让我们知道你的想法!
论文综述:视觉识别的漏斗激活(ECCV 2020)
原文:https://blog.paperspace.com/funnel-activation/
近年来,计算机视觉中基于组件的研究已经显著加速,特别是随着对抗性攻击和注意机制的出现。在最近一段时间内广泛复兴的主要组件之一是非线性变换层,称为激活函数。虽然激活函数在当前研究领域的大格局中可能被认为有些微不足道,但它们对深度学习所有领域中现代神经网络的训练动力学的贡献是巨大的,不可否认的。
在本帖中,我们将对最近在 2020 年(ECCV)第 16 届欧洲计算机视觉大会上公布的一种激活函数形式进行深入分析。标题为“用于视觉识别的漏斗激活”的论文介绍了被称为 FReLU 的校正非线性家族中的新激活。
本文分为四个部分。首先,我们将简要回顾激活函数,特别是校正的非线性函数族。接下来,我们将深入到小说 FReLU,以及论文背后的直觉和理论。此外,我们将讨论论文中提出的结果,并通过分析该想法的潜在缺点以及需要改进或进一步研究的领域来得出结论。
您可以在 ML Showcase 上免费运行本文的相关代码。
目录:
- 激活功能
- ReLU 家庭
- 非线性的最新进展
- 漏斗激活
- 密码
- 结果
- 缺点
- 参考
激活功能
激活函数是神经网络训练动力学中的一个关键因素。它们用于在网络中引入非线性的目的。这一切都始于感知器中一个简单的阶跃函数,阶跃函数的输出是二进制的(1 或 0)。这给出了一个概念,即由之前的神经元传播的信息是否足够重要,足以激发/激活该神经元(想象它类似于开关)。然而,这造成了巨大的信息损失,并很快被一种更平滑的替代方法所取代,这种方法有助于梯度反向传播:sigmoid 函数。
General representation of a Perceptron
后来,在 2010 年发表论文“整流线性单元改进受限玻尔兹曼机器”之后,整流线性单元(ReLU)的使用变得更加标准化。十年过去了,已经出现了无数新形式的激活函数,然而,由于它们在不同深度学习任务中的通用性及其计算效率,ReLU 仍然占据着至高无上的地位。
在理解激活函数的重要性方面的最新进展导致了对更平滑的变体的探索,这些变体提高了深度神经网络的泛化能力和鲁棒性。一些最突出的例子包括:
- Swish ( 搜索激活功能
- GELU ( 高斯误差线性单位
- Mish ( Mish:自正则化的非单调激活函数
ReLU 家庭
如前一节所述,ReLU 一直是大多数深度学习或机器学习实践者和研究人员长期以来忠实的非线性。然而,ReLU 有一些明显的缺点,这些缺点已由不同形式的 ReLU 解决,如泄漏 ReLU 和 PReLU。我们将在 ReLU 系列中包括这些激活函数,因为它们都具有分段线性函数的形式,与标准 ReLU 相比,函数的负半部分明显不同。
The ReLU family
首先,让我们了解一下 ReLU 的缺点。ReLU 是一个形式为\(y(x) = \max(0,x)\)的简单分段线性函数。因此,它将输入信号的所有负分量阈值设置为零。这导致潜在有用信息的大量丢失。虽然使用 ReLU 还有其他缺点,但我们将只使用 thresholding 属性来建立我们对 FReLU 的讨论,因为它已经成为其他形式的 ReLU 的基本动机。
为了解决这个问题,Leaky ReLU 作为一个潜在的解决方案被引入。Leaky ReLU 通过将输入信号的负分量调制一个小的预先确定的标量值,表示为\(\alpha\)(通常为 0.01),引入了一个非零的负半函数。因此,该函数采用\(\max(\alpha x,x)\)的形式。虽然这被证明是成功的,但使用预定义值来调节函数负半部分的斜率背后的直觉让位于一种更基本正确、可学习的泄漏 ReLU 形式,称为参数 ReLU (PReLU)。
PReLU 和 Leaky ReLU 的函数形式是相同的,但是,唯一的警告是\(\alpha\)现在是一个可学习的参数,而不是预定义的。因此,这有助于网络学习函数负半部分的稳定且良好的斜率,并消除硬编码的差异。这将构成理解 FReLU 动机的基础。
在这篇评论文章的范围内,我们不会涉及 ReLU 家族中的其他激活函数,如 RReLU、Smooth ReLU、eLU 等。
非线性的最新进展
激活函数的作用最近也已经扩展到包括神经网络的对抗鲁棒性,更平滑的激活函数提供了比配备有例如 vanilla ReLU 的相同网络更好的鲁棒性。最近一篇名为平稳对抗训练的论文对此进行了探讨。
平滑函数主要是从逐点标量变换的角度进行研究的,其中最成功和最流行的选项是将现有函数组合成一个复合结构,以形成更平滑的替代函数,如 mish($ f(x)= x\tanh(\log(1+e^{x}))$)和 Swish ( \(f(x)=xsigmoid(\beta x)\))。
然而,也有其他方面的探索。让我们使用本文使用的确切术语对这些类型做一个简短的案例研究。
上下文条件激活
顾名思义,上下文条件激活是那些受上下文制约的非线性转换,因此是多对一的函数。这种类型的激活函数的一个最突出的例子是 Maxout 函数。定义为\(max(\omega_{1}^{t}x+b_{1},\omega_{2}^{t}x+b _ { 2 })\),它基本上使层成为双/多分支结构,并选择两者的最大子节点,不像其他点态标量激活函数直接应用于权重和输入的点积,表示为:\(\psi(x)=f(\omega^{T}x + b)\)。因此,最大输出网络将 ReLU 和泄漏 ReLU/ PReLU 结构合并到同一框架中。
选通
这些激活主要是由使用门控卷积网络的语言建模引入的,主要是复合函数或乘积函数,其中一个子函数由输入的另一个函数变换或原始输入本身门控。
如图所示,在基于 RNN 的结构中,卷积后的激活函数有两组输入,表示为 a 和 b。这里选择的激活是 sigmoid,表示为\(\sigma(x) = \frac{1}{1+e^{-x}}\).因此,门控激活的形式为$ H _ { 0 } = A \ bigo times \ sigma(B)$ 1。因此,这里的激活\(\sigma(B)\)由 a 门控。门控的思想后来通过用于强化学习中神经网络函数近似的 Sigmoid 加权线性单元扩展到视觉,其中作者引入了 Sigmoid 加权线性单元(路斯),它可以近似为\(f(x)=xsigmoid(x)\)。这里唯一的变化是,不是不同的输入用于选通,而是传递给非线性函数的相同输入用作选通。这种形式的门控被恰当地命名为“自门控”。这后来通过搜索激活函数得到了验证,作者使用 NAS 方法找到了定义为\(f(x) = xsigmoid(\beta x)\)的 Swish。这基本上以\(\beta\)的形式引入了一个简单的可训练参数,以在训练过程中增加更多的灵活性和适应性。
自门控的思想可以在与跳跃连接的作用相关的非常抽象的层面上,其中非调制输入被用作卷积层块的输出的受控更新。跳跃连接形成残差网络的基础,并且已经明显证明跳跃连接使得网络的优化轮廓更加平滑,因此更容易优化,并且导致更好的泛化和改进的鲁棒性。
还有更多最近提出的采用自门控的平滑激活函数的例子,如:
- GELU ( 高斯误差线性单位)😒 f(x)= x \ frac { 1 } { 2 }[1+ERF(x/\ sqrt { 2 })]$
- Mish ( Mish:一个自正则化的非单调激活函数)😒 f(x)= x \ tanh(soft plus(x))$
继续上下文条件激活函数,这些在基于 RNN 的结构中更相关,因为序列长度与图像域中的输入空间相比要小得多,在图像域中, B × C × H × W 维的 4-D 张量可以具有大量的输入数据点。这就是为什么大多数上下文相关的激活函数只受通道相关的。然而,作者认为空间条件作用与通道适应性条件作用同等重要。为了满足不断增长的计算需求,FReLU 在默认情况下集成了 3×3 窗口大小的深度可分离卷积核(DWConv ),这在参数和触发器方面非常便宜。
空间依赖性建模
尽管空间自适应激活函数在早期没有被详细研究过,但是引入空间自适应建模的目标并不是新的,也不是从激活函数中派生出来的。空间依赖性建模的目标更多地是在注意力机制的背景下讨论的,其中捕获长距离依赖性是至关重要的。例如,全球背景网络(GCNet)(参见 GCNet:非局域网络满足挤压激励网络和超越),一种非局域网络(NLNet)(参见非局域神经网络)使用一种通道注意力的变体来捕捉长期依赖性。
此外,为了捕捉非线性空间相关性,在不同的范围,不同大小的卷积核被用于聚集特征表示。但是,这样做效率不高,因为它增加了更多的参数和操作。相比之下,FReLU 通过引入一种廉价的模块来解决这些问题,该模块允许在捕捉空间依赖性的同时计算激活,并且不引入任何附加模块。
随后,FReLU 能够通过使用简单有效的 DWConv 使用自适应感受野来对单个像素应用非线性变换,而不是使用复杂的卷积算子来根据输入引入可变感受野,这已知可以提高深度卷积神经网络(CNN)的性能。
由于具备所有这些特性,FReLU 能够在图像识别和迁移学习的不同领域中始终比其他激活函数表现得更好,如下面的图表所示:
漏斗激活
如前所述,ReLU 是一个分段线性函数。但是,它可以解释为应用于输入的每个点(像素)的条件逐点标量操作。该条件是一个最大值函数,两个操作数是问题中的像素和零。因此,每个负值点都变为零。
为了解决这个问题,并使泄漏 ReLU 更强大,引入了参数 ReLU (PReLU)。PReLU 旨在保持相同的条件运算,除了比较值是参数缩放输入的值,其中标量参数是在训练期间学习的。如上所述,本质上,在计算给定像素 x 的激活时,使用最大运算将其与由可学习标量值\(\alpha\)缩放的相同像素进行比较。因此,这有助于保留负面信息和减少信息损失,并通过神经网络的深度改善信号传播。
这样做的缺点是可学习的标量\(\alpha\)与输入的上下文表示脱节。这就是 FReLU,或漏斗激活,提供了一个简单的修复方法。它不是学习单个固定标量值,而是学习目标像素周围局部邻域的聚合表示,并将其用作 max 运算中的比较值。本地上下文的这种学习到的聚集表示被表示为被定义为\(\mathbb{T}(x)\)的函数。因此,FReLU 的等式变成\(f(x) = \max(x,\mathbb{T}(x))\)了。空间条件依赖函数\(\mathbb{T}(x)\)被定义为参数池窗口,大小为\(k *×* k\),并且总是以目标像素为中心。
函数形式的参数池窗口在数学上可以定义为:
\(\mathbb{t}(x_{c,i,j}^{\omega})= x_{c,i,j}^{\omega} \ x_{c}^{\omega}\)
其中 c 表示该像素的通道索引; i,j 表示该通道中该像素的空间坐标;并且 p 是每个通道的那个窗口的系数 c 。因此,参数池窗口函数本质上是输入与系数的逐通道点积。
作者使用高斯初始化来初始化系数,以具有初始条件来保持值接近于零。这近似于 ReLU,因为$ \ mathbb { T }(x)\ approximate 0 \(近似于\)\max(x,0)$这是 ReLU 的定义。作者在他们的论文中报告说,他们研究了非参数的聚集方法,如平均池和最大池。他们观察到性能比参数形式相对更差,因此通过涉及聚合的参数形式来证明模型权重的增加是合理的。
因此,简而言之,参数池窗口本质上是由大小 k × k 定义的深度卷积核,其中 k 默认设置为 3。随后是批处理规范化层。因此,对于维数为 C × H × W 的输入张量,FReLU 引入的额外参数的量等于 C × k × k ,并且由于 k < < < C,引入的额外复杂度的顺序等于输入张量中的通道数。
密码
作者提供了基于 MegEngine 框架的 FReLU 的官方实现。
您还可以从 ML Showcase 中免费运行与本文相关的 Jupyter 笔记本。
原始实现
"""
To install megengine, use the following command in your terminal:
pip3 install megengine -f https://megengine.org.cn/whl/mge.html
Or if on Google Colab then:
!pip3 install megengine -f https://megengine.org.cn/whl/mge.html
"""
import megengine.functional as F
import megengine.module as M
class FReLU(M.Module):
r""" FReLU formulation. The funnel condition has a window size of kxk. (k=3 by default)
"""
def __init__(self, in_channels):
super().__init__()
self.conv_frelu = M.Conv2d(in_channels, in_channels, 3, 1, 1, groups=in_channels)
self.bn_frelu = M.BatchNorm2d(in_channels)
def forward(self, x):
x1 = self.conv_frelu(x)
x1 = self.bn_frelu(x1)
x = F.maximum(x, x1)
return x
PyTorch 实现
import torch
import torch.nn as nn
class FReLU(nn.Module):
r""" FReLU formulation. The funnel condition has a window size of kxk. (k=3 by default)
"""
def __init__(self, in_channels):
super().__init__()
self.conv_frelu = nn.Conv2d(in_channels, in_channels, 3, 1, 1, groups=in_channels)
self.bn_frelu = nn.BatchNorm2d(in_channels)
def forward(self, x):
x1 = self.conv_frelu(x)
x1 = self.bn_frelu(x1)
x = torch.max(x, x1)
return x
注意:您可以将内核大小增加到大于 3,但是,您必须相应地增加填充以保持卷积的空间和深度保持性。
结果
在看 FReLU 的对比结果之前,我们先来看看作者进行的消融研究。作者们在探索寻找 FReLU 的最佳形式时不遗余力。我们将对条件类型、标准模型和层中 FReLU 的兼容性、使用的标准化类型等进行实验。
模型 |
激活 |
头号错误 |
A |
Max(x,ParamPool(x)) |
22.4 |
B |
Max(x,MaxPool(x)) |
Twenty-four point four |
C |
Max(x, AvgPool(x)) |
Twenty-four point five |
|
|
|
A |
Max(x,ParamPool(x)) |
22.4 |
D |
Sum(x,ParamPool(x)) |
Twenty-three point six |
E |
最大(DW(x),0) |
Twenty-three point seven |
在这里,作者验证了参数池窗口\(\mathbb{T}(x)\)的重要性,他们将它用作条件。在模型 B 和 C 中,它们分别用 MaxPool 和 Average Pool 替换了参数池窗口。此外,在模型 D 中,他们用求和运算代替最大运算,以模拟类似于跳过连接的形式。最后,在模型 E 中,他们使用由表示为 DW 的深度方向可分离卷积核计算的上下文条件作为 ReLU 的操作数。他们使用 ImageNet-1k 数据集上的 ResNet-50 主干来验证这些设置,并确认参数池窗口在图像分类任务中的错误率方面优于其他变体。
正常化 |
头号错误 |
没有正常化 |
Thirty-seven point six |
批量标准化 |
Thirty-seven point one |
图层规范化 |
36.5 |
实例规范化 |
Thirty-eight |
群体规范化 |
36.5 |
作者在计算 FReLU 中的空间条件时试验了不同的标准化层。注意:这不包括没有 FReLU 的模型中已经存在的默认批处理规范化层。在这个实验中,他们使用 ShuffleNet v2 网络来完成 ImageNet-1k 分类任务。尽管作者发现层标准化和组标准化实现了最低的误差,但是他们使用批量标准化,因为它对推断速度没有任何影响。这个问题是在他们的官方知识库的这一期中提出的,在那里作者指出这篇文章进一步阐明了规范化层对推理速度的影响。
模型 |
窗口大小 |
头号错误 |
A |
1×1 |
Twenty-three point seven |
B |
3×3 |
22.4 |
C |
5×5 |
Twenty-two point nine |
D |
7×7 |
Twenty-three |
E |
总和(1×3,3×1) |
Twenty-two point six |
F |
最大值(1×3,3×1) |
22.4 |
作者进一步对不同的核大小进行了实验,从大小为 1 × 1 的逐点卷积开始,到使用 sum 和 max 函数聚集水平和垂直滑动窗口。他们发现,对于 ImageNet-1k 分类任务上的 ResNet-50,3 × 3 的内核大小表现最佳。
第二阶段 |
第 3 阶段 |
第四阶段 |
头号错误 |
-好的 |
|
|
Twenty-three point one |
|
-好的 |
|
Twenty-three |
|
|
-好的 |
Twenty-three point three |
-好的 |
-好的 |
|
Twenty-two point eight |
|
-好的 |
-好的 |
Twenty-three |
-好的 |
-好的 |
-好的 |
22.4 |
ResNet 被定义为总共 4 个阶段的模型。每一级包含预定义数量的包含卷积层的瓶颈/基本块。作者验证了在每个阶段添加 FReLU 和替换现有 ReLU 层的重要性。如上表所示,在使用 ResNet-50 网络的 ImageNet-1k 分类任务中,FReLU 在所有阶段都表现最佳。
|
1×1 conv。 |
3×3 conv。 |
头号错误 |
ResNet-50 |
-好的 |
|
Twenty-two point nine |
ResNet-50 |
|
-好的 |
Twenty-three |
ResNet-50 |
-好的 |
-好的 |
22.4 |
|
|
|
|
MobileNet |
-好的 |
|
Twenty-nine point two |
MobileNet |
|
-好的 |
Twenty-nine |
MobileNet |
-好的 |
-好的 |
28.5 |
为了了解层兼容性,作者观察了在 MobileNet 和 ResNet-50 的瓶颈结构中用 1×1 卷积层、3×3 卷积层以及 1×1 和 3×3 卷积层替换 ReLU 的性能。根据上表,用 FReLU 替换所有 ReLU 的性能优于其他配置。注意:通常 1×1 卷积层被称为通道卷积,因为它们负责改变输入张量的通道维度,而 3×3 卷积层被称为空间卷积层,因为它们通常不改变输入的通道维度。相反,它们负责改变输入的空间维度。
模型 |
参数数量(百万) |
拖鞋 |
头号错误 |
热卢 |
25.5 米 |
3.9 克 |
Twenty-four |
弗雷卢 |
25.5 米 |
3.9 克 |
Twenty-two point four |
拒绝 |
26.7 米 |
3.9 克 |
Twenty-two point eight |
FReLU+SE |
26.7 米 |
3.9 克 |
22.1 |
此外,作者验证了 FReLU 与其他即插即用准确性提高方法的兼容性,例如,注意机制。这里选择的模块是挤压和激发(SE)模块,这是一种通道注意方法。作者验证了使用 SE 和 FReLU 提供了最佳的比较性能。
注意:我们已经在计算机视觉系列的注意力机制中讨论了挤压和激励网络。你可以在这里阅读。
现在来看看 ImageNet 分类和语义分割的比较基准。
resnets 上的 imagenes 基准测试
模型 |
激活 |
参数数量(百万) |
拖鞋 |
头号错误 |
ResNet-50 |
热卢 |
25.5 米 |
3.86 克 |
Twenty-four |
ResNet-50 |
普雷卢 |
25.5 米 |
3.86 克 |
Twenty-three point seven |
ResNet-50 |
嗖嗖 |
25.5 米 |
3.86 克 |
Twenty-three point five |
ResNet-50 |
弗雷卢 |
25.5 米 |
3.87 克 |
22.4 |
|
|
|
|
|
ResNet-101 |
热卢 |
44.4 米 |
7.6G |
Twenty-two point eight |
ResNet-101 |
普雷卢 |
44.4 米 |
7.6 克 |
Twenty-two point seven |
ResNet-101 |
嗖嗖 |
44.4 米 |
7.6 克 |
Twenty-two point seven |
ResNet-101 |
弗雷卢 |
44.5 米 |
7.6 克 |
22.1 |
轻量级移动架构的 imagenes 基准测试
模型 |
激活 |
参数数量(百万) |
拖鞋 |
头号错误 |
MobileNet 0.75 |
热卢 |
2.5 米 |
325 米 |
Twenty-nine point eight |
MobileNet 0.75 |
普雷卢 |
250 万 |
325 米 |
Twenty-nine point six |
MobileNet 0.75 |
嗖嗖 |
250 万 |
325 米 |
Twenty-eight point nine |
MobileNet 0.75 |
弗雷卢 |
250 万 |
328 米 |
28.5 |
|
|
|
|
|
ShuffleNetV2 |
热卢 |
1.4 米 |
41M |
Thirty-nine point six |
ShuffleNetV2 |
普雷卢 |
1.4 米 |
41 米 |
Thirty-nine point one |
ShuffleNetV2 |
嗖嗖 |
1.4 米 |
41 米 |
Thirty-eight point seven |
ShuffleNetV2 |
弗雷卢 |
1.4 米 |
45 米 |
37.1 |
MS-COCO 上使用 RetinaNet 检测器的对象检测基准
模型 |
激活 |
参数数量(百万) |
拖鞋 |
地图 |
AP[50] |
美联社[75] |
美联社[s] |
米 |
AP[l] |
ResNet-50 |
热卢 |
25.5 米 |
3.86 克 |
Thirty-five point two |
Fifty-three point seven |
Thirty-seven point five |
Eighteen point eight |
Thirty-nine point seven |
Forty-eight point eight |
ResNet-50 |
嗖嗖 |
25.5 米 |
3.86 克 |
Thirty-five point eight |
Fifty-four point one |
Thirty-eight point seven |
Eighteen point six |
Forty |
Forty-nine point four |
ResNet-50 |
弗雷卢 |
25.5 米 |
3.87 克 |
36.6 |
55.2 |
39.0 |
19.2 |
40.8 |
51.9 |
|
|
|
|
|
|
|
|
|
|
ShuffleNetV2 |
热卢 |
3.5 米 |
299 米 |
Thirty-one point seven |
Forty-nine point four |
Thirty-three point seven |
Fifteen point three |
Thirty-five point one |
Forty-five point two |
ShuffleNetV2 |
嗖嗖 |
3.5 米 |
299 米 |
Thirty-two |
Forty-nine point nine |
Thirty-four |
Sixteen point two |
Thirty-five point two |
Forty-five point two |
ShuffleNetV2 |
弗雷卢 |
3.7 米 |
318 米 |
32.8 |
50.9 |
34.8 |
17.0 |
36.2 |
46.8 |
使用具有预训练 ResNet-50 架构的 PSP-Net 的城市景观语义分割基准
|
热卢 |
嗖嗖 |
弗雷卢 |
平均 IU |
Seventy-seven point two |
Seventy-seven point five |
78.9 |
路 |
Ninety-eight |
98.1 |
98.1 |
人行道 |
Eighty-four point two |
85.0 |
Eighty-four point seven |
建筑物 |
Ninety-two point three |
Ninety-two point five |
92.7 |
墙壁 |
Fifty-five |
Fifty-six point three |
59.5 |
栅栏 |
Fifty-nine |
Fifty-nine point six |
60.9 |
杆 |
Sixty-three point three |
Sixty-three point six |
64.3 |
红绿灯 |
Seventy-one point four |
Seventy-two point one |
72.2 |
交通号志 |
Seventy-nine |
80.0 |
Seventy-nine point nine |
植物 |
Ninety-two point four |
Ninety-two point seven |
92.8 |
地带 |
Sixty-five |
Sixty-four |
64.5 |
天空 |
Ninety-four point seven |
94.9 |
Ninety-four point eight |
人 |
Eighty-two point one |
Eighty-three point one |
83.2 |
扶手 |
Sixty-two point three |
65.5 |
Sixty-four point seven |
汽车 |
Ninety-five point one |
Ninety-four point eight |
95.3 |
卡车 |
Seventy-seven point seven |
Seventy point one |
79.8 |
公共汽车 |
Eighty-four point nine |
Eighty-four |
87.8 |
火车 |
Sixty-three point three |
Sixty-eight point eight |
74.6 |
摩托车 |
Sixty-eight point three |
Sixty-nine point four |
69.8 |
自行车 |
Seventy-eight point two |
Seventy-eight point four |
78.7 |
缺点
- 尽管结果相对好得多,但这并不能掩盖 DWConv 没有针对硬件进行优化并且效率低下的事实。参数和触发器没有反映这一点,但是延迟和吞吐量将明显比普通模型差。
- FReLU 最终是一种不平滑的 ReLU 形式,正如前面几节所示,与非平滑激活相比,平滑激活往往具有更高程度的对抗性鲁棒性。
唉,这取决于用户/读者/从业者/研究人员在他们自己的模型上尝试 FReLU 并评估其效率。
参考
- 视觉识别的漏斗激活,ECCV 2020。
- 【FReLU 的原始知识库。
- 搜索激活功能
- 米什:一个自正则化的非单调激活函数,BMVC 2020。
- 高斯误差线性单位
- 用门控卷积网络进行语言建模
- 激活功能的维基百科页面
理解 GauGAN 第 3 部分:模型评估技术
原文:https://blog.paperspace.com/gaugan-evaluation-techniques/
大家好,欢迎来到高根系列的第三部分。在前两部分中,我们已经介绍了高根的组件和损耗,以及如何用它来设置定制训练。在这一部分,我们将讨论:
- 评估 GauGAN 的结果
- 与对手相比,高根的表现如何
我们将以如何调试培训的讨论来结束这个系列,并决定 GauGAN 是否适合你。
本教程中的代码可以在 ML Showcase 上获得,你可以在一个免费的 GPU 上运行它。
所以,让我们开始吧!
对 GauGAN 的评价:为什么这么难?
与甘一起工作最困难的事情之一是他们的评估。像对象检测和图像分类这样的任务通常预测有限的标签集,像对象类或边界框坐标,这使得定义理想的预测变得容易。例如,对象检测任务的理想预测将是当所有预测的边界框与预定义的类别标签完全对齐时。这反过来使我们很容易设计一个损失函数,惩罚偏离理想行为。
现在考虑为 GauGAN 做同样的事情。GauGAN 的输入是潜在向量和语义分割图。如何定义随机抽样的潜在向量的期望值?对于语义分割图,我们可以说 GauGAN 的理想行为是完全重建地面真实图像。然而,这种方法有几个问题。
首先,让我们假设我们实际上使用一些图像距离度量,如 GauGAN 产生的图像和真实图像之间的平方距离。考虑一个场景,其中 GauGAN 随机改变图像中出现的汽车的颜色。由于汽车的像素不同,两幅图像之间的平方距离将非零。
类似地,考虑下面的图像。
Day and Night Renditions of the Same Image
These 两个图像是白天和晚上同一场景的真实再现(请原谅超级月亮)。这两者将具有相同的原始语义分割图。假设白天的图像是我们的地面真实,而夜晚的图像是我们的干产生的。由于后者的颜色更深,像素不同,图像也会被解释为不同。每像素平方距离度量将严厉惩罚暗图像,而如果 GAN 产生另一个白天图像,则给出零误差。虽然这两个图像是同样有利的,我们看到一个平方距离完全没有认识到这一点。
样本的多样性
从上面继续,我们可以说,我们的平方距离度量正在惩罚我们图像生成中的多样性,即,如果我们产生真实但不同外观的样本。事实上,图像多样性是数据生成的最基本要求之一。如果我们只是在生产我们已经拥有的数据,这就违背了数据生成的初衷。
那么解决办法是什么呢?好吧,最好的解决办法就是人的层面的评价。虽然看起来很耗时,但这是目前评估 GANs 的最佳方法,几乎所有关于图像生成的最新论文都报道了这一指标。
人类水平的研究
GauGAN 的论文使用人类水平的评估来声称其优于同时代的人,如 Pix2PixHD、CRN 和西姆斯。
这项测试被称为“用户偏好研究”。一组用户看到两幅图像,一幅由 GauGAN 生成,另一幅由与之比较的算法生成。然后询问用户哪个图像更真实。用户偏好 GauGAN 图像的百分比称为偏好百分比。
GauGAN,主要是因为 SPADE,比它的前身 Pix2PixHD 好得多。
Performance of GauGAN vs its adversaries
我们可以自己看一些图像。
One 肯定能观察到 Pix2PixHD 输出的模式平均问题。我们注意到在 Pix2PixHD 的输出中看到的平滑/平均效果似乎模糊了许多对象的低级特征。
例如,查看第一行图像中的建筑物,或者第二行图像中的含水量。当有大面积的单个对象时,Pix2PixHD 会明显挣扎,并且可以观察到导致细节丢失的平滑效果。这可能是因为我们之前讨论的关于 GAN 努力从输入值的统一拭子中创建不同信息的问题。
高根人在这里的情况要好得多,也许是因为他们使用了铁锹。
除此之外,作者还尝试了几种方法来定量测量 GauGAN 的性能。我们现在来回顾一下。
米欧试验
mIoU 测试的第一步是获取生成的图像,并对其运行最先进的语义分割算法。结果将是每个生成的图像的语义分割图。然后,我们计算原始语义分割图和从生成的图像得到的语义分割图之间的 mIoU。
为了检查 SPADE 的重要性,作者将 Pix2PixHD 的结果与 GauGAN bar SPADE 生成器的所有增强进行了比较。他们称之为 Pix2PixHD++。他们还在这一系列实验中纳入了其他架构,包括层数较少的紧凑架构和 Pix2PixHD 风格的编码器-解码器架构,这与 GauGAN 的纯解码器架构相反。
弗雷歇初始距离
最近,Frechet 初始距离(FID)已成为研究 GANs 的研究人员最喜欢的评估指标。再说一次,它并不完美,但肯定是我们最好的之一。那么,FID 是如何工作的呢?
我们首先获取真实图像\(R\)和虚假图像\(F\)并将它们放入 Inception v3 网络。然后,我们对两幅图像取网络的 2048 维池层,并计算两个提取特征之间的距离。我们对数据集中的所有语义地图图像对都这样做。然后,使用所有这些向量,我们使用以下公式计算 FID。
其中\(\mu_r\)和\(\mu_g\)分别是真实图像和生成图像的特征的平均值。\(\sum_r\)和\(\sum_g\)是方差矩阵。\(T_r\)表示跟踪运算符。
这里的想法是捕捉图像的统计(Inception v3 被作为统计计算函数),而不是两幅图像的像素。因为 Inception v3 是颜色不变的(也就是说,它会将一辆车分类为一辆车,不管那辆车是什么颜色),所以它是一个比平方距离更好的选择。此外,它还确保了像一天中的时间、颜色和方向这样的因素不会真正影响到多样化图像的分数,因为《盗梦空间》被训练成对这样的差异不变。
这也有助于确保约束不会迫使网络过度适应训练数据。
FID 分数的限制
然而,由于我们使用 Inception v3 网络作为图像的评判标准,理论上结果会受到 Inception v3 网络的限制。假设它在包含野生动物的图像上表现不太好。如果你试图生成斑马的图像,那么分数可能有点不可预测,因为算法可能对不同的斑马图像敏感,并且可能为两幅这样的图像产生随机不同的特征向量。野生动物只是网络的背景,我们还没有专门训练它来识别两个不同的斑马图像属于“斑马”类别。
现在考虑来自城市景观数据集的以下图像。
深度学习要发挥作用,最基本的要求之一是确保评估算法的数据与训练算法的数据相同或相似。例如,考虑在 ImageNet 数据集上训练的 Inception v3 模型。
如果你看一下来自 ImageNet 的数据,你会发现这些图像与 Cityscapes 数据集有很大的不同。首先,图像一般不是很详细;它们通常只包含一个对象,几乎占据了整个图像。举个例子,看看这些汽车的图片。
另一方面,城市景观中的图像通常非常详细,包含许多不同的对象。
当然,Inception v3 的层可以捕获这些对象,但是所有的对象不会同样难以合成。像天空和道路这样的区域,只构成渲染纹理,比汽车更容易渲染,汽车需要算法来创建结构细节(如保险杠,背光和窗户)。
现在,想象一个场景,其中汽车被渲染得很差,而天空和道路被渲染得很好。此外,与天空和道路相比,汽车占据了图像中很小的区域。渲染不良的汽车应该保持较高的 FID,但是它们对特征向量的贡献太小,因为它们在图像中占据相对较小的空间。另一方面,渲染良好的天空/道路几乎不增加 FID,并且在特征向量中占主导地位。这可能导致图像具有好的背景和坏的前景。这是一个在 Cityscapes 数据集上使用官方实现生成的数据的例子。
An example of an image with decent road and trees but poor car rendition.
计算 FID 分数
为了计算您的预测的 FID 分数,我们将使用 FID 的官方实现。请注意,我没有使用官方的 FID 分数回购协议,这是您在报告结果时应该使用的东西。我不使用它的原因是因为它是为 Tensorflow 编写的,而 TensorFlow 中最近的更新——尤其是 2.0 版本——已经破解了大量代码。所以,最好用更稳定的。出于好奇,官方回购可以在这里找到。
我们首先从克隆回购开始。
git clone https://github.com/mseitzer/pytorch-fid
cd pytorch-fid
现在,将使用 FID 来比较我们的原始数据和生成的数据。在我们这样做之前,请确保您正在比较相同大小的图像。例如,训练数据的大小可能是 2048 x 512,而 GauGAN 配置为生成 1024 x 512 的图像。这种大小上的差异可能导致比实际更大的 FID。
一旦完成,工作就很容易了。您可以使用以下代码计算 FID:
python fid_score.py /path/to/images /path/to/other_images --batch-size b
选择批量大小,使测试图像的总数是批量大小b
的倍数。如果这不可能,确保剩余部分最小化。例如,对于 71 个测试图像,您可以选择批量大小为 7(因为 71 是一个质数)。
人们也可以考虑将 GANs 生成的图像调整到原始图像的大小。然而,对于 GAN 来说,这不是一个公平的度量,因为它(在其训练期间)仅使用较低维度的图像进行监督。
总结高根的表现
这里有一张来自最初的高根论文的表格,总结了高根在对抗对手时的表现。
That 说,你可能会遇到再现性问题。无法重现结果是深度学习社区的一个大问题,英伟达的最新产品也不能幸免。
结论
我们系列的第 3 部分到此结束。在第四部分,也是最后一部分,我们来看看常见的故障排除技巧,从业务角度来看 GauGAN 的成本,以及为什么您可能希望跳过 GauGAN 并等待技术成熟。在那之前,这里有一些链接,你可以点击阅读更多关于文章中提到的主题。
了解 GauGAN 系列
进一步阅读
- 通过双时标更新规则训练的 GANs 收敛到局部纳什均衡(介绍 FID 的论文)
- 对有条件的 GANS 的评价
理解 GauGAN 第 2 部分:定制数据集培训
原文:https://blog.paperspace.com/gaugan-training-on-custom-datasets/
在第一部分中,我们介绍了 GauGAN 的基本组件以及它所使用的损失函数。在这一部分中,我们将讨论培训细节,并了解如何在您自己的自定义数据集上设置培训。
第一部分对于理解训练过程如何工作是必要的,但是如果您对训练细节不感兴趣,而只想在您自己的数据上实现 GauGAN,可以直接跳到标题为定制训练的部分。在第 3 部分中,我们将讨论模型评估技术,我们将通过查看如何调试培训并决定 GauGAN 是否适合您来结束这个系列。
您还可以在 ML Showcase 上跟随本教程中的代码,并在免费的 GPU 上运行模型。
在我们开始之前,另一件需要注意的重要事情是,Nvidia 的开源实现在许多方面与官方文件中报道的不同。这是可以理解的,因为这本书出版已经快一年了。只要有可能,我会尽力指出这些不同之处。
培训详情
预处理步骤
GauGAN 仅使用一种大小和纵横比的图像进行训练。这意味着,一旦经过训练,GauGAN 只能保证在与训练时相同大小的图像上工作得最好。如果推断过程中使用的图像太小或太大,预计输出会大大降低。
有多种方法可用于调整图像大小或将其裁剪为 GauGAN 要求的大小。
重量初始化
作者报告了使用 Glorot 初始化,而该实现使用卷积层的默认 PyTorch 初始化,即明凯统一初始化。
学习率政策
当然,被训练的时期的数量将取决于所使用的数据集和被训练的数据类型的复杂性。默认学习率设置为 0.0002。
该实现还利用了 两种时标更新规则学习策略 。在这里,我们为生成器和鉴别器选择不同的学习速率。通常,发生器的学习速率保持低于鉴别器,以便允许发生器通过缓慢学习来更好地执行,从而导致更稳定的学习。
对于总周期数的前半部分,训练学习率选项lr
保持为初始值不变。鉴别器和发生器都用这个学习率来训练。
在后半部分,学习率选项lr
线性衰减到零,在每个时期被更新。对于每次更新,鉴频器学习率设置为lr * 2
,而发电机学习率设置为lr / 2
。
批量
批量大小通常取决于您想要合成的图像有多大。GauGAN 可能需要大量的 GPU 资源才能很好地工作。在批量大小为 1 的大小为 768 x 576 的图像上训练实现中提供的默认 GauGAN 需要大约 12 GB 的 GPU 内存。Nvidia 开源实施的自述文件中写道:
要重现论文中报告的结果,你需要一台配有 8 个 V100 GPUs 的 NVIDIA DGX1 机器
这相当于 128 GB 的内存。呀!
【计算机】优化程序
Adam 优化器用于生成器和鉴别器。\(\beta_1\)和\(\beta_2\)分别设置为 0.5 和 0.999。
定制培训
试运转
在这一部分中,我们将了解如何使用自定义数据集来设置培训。我们首先从 GitHub 获取 GauGAN 并安装所有必要的依赖项。
git clone https://github.com/NVlabs/SPADE.git
cd SPADE/
pip install -r requirements.txt
如果你没有 pip,你可以手动在文件requirements.txt
中查找软件包,并通过 conda 安装。
然后,你必须安装同步批量定额。
cd models/networks/
git clone https://github.com/vacancy/Synchronized-BatchNorm-PyTorch
cp -rf Synchronized-BatchNorm-PyTorch/sync_batchnorm .
cd ../../
一旦完成,让我们试一试。从抓取预先训练好的模型开始。目前,Nvidia 为 COCO-stuff、Cityscapes 和 ADE20K 数据集提供预训练模型。
首先,从这个 google drive 链接下载模型。创建一个名为checkpoints
的文件夹,并将下载的文件放入其中。然后拉开拉链。
cd checkpoints
tar xvf checkpoints.tar.gz
cd ../
现在,您可以为这三个数据集运行模型,但让我们为 COCO-stuff 运行模型。因为我们刚刚克隆的 github repo 已经有了一些来自 COCO stuff 数据集的样本文件,这将节省我们一些时间。
确保你在SPADE
文件夹中并运行:
python test.py --name coco_pretrained --dataset_mode coco --dataroot datasets/coco_stuff
现在,如果您使用的是 PyTorch 版本> 1.1,代码可能会抛出以下错误并可能会停止。
Traceback (most recent call last):
File "test.py", line 36, in <module>
generated = model(data_i, mode='inference')
File "/home/ayoosh/miniconda3/lib/python3.7/site-packages/torch/nn/modules/module.py", line 541, in __call__
result = self.forward(*input, **kwargs)
File "/home/ayoosh/work/SPADE/models/pix2pix_model.py", line 43, in forward
input_semantics, real_image = self.preprocess_input(data)
File "/home/ayoosh/work/SPADE/models/pix2pix_model.py", line 130, in preprocess_input
instance_edge_map = self.get_edges(inst_map)
File "/home/ayoosh/work/SPADE/models/pix2pix_model.py", line 243, in get_edges
edge[:, :, :, 1:] = edge[:, :, :, 1:] | (t[:, :, :, 1:] != t[:, :, :, :-1])
RuntimeError: Expected object of scalar type Byte but got scalar type Bool for argument #2 'other' in call to _th_or
这是因为 PyTorch 1.1+中引入了bool
数据类型。简单的解决方案是转到文件models/pix2pix_model.py
中的函数get_edges
,做一个简单的修改。
在函数的第一行后添加行edge = edge.bool
。总而言之,你的函数get_edges
应该是这样的:
def get_edges(self, t):
edge = self.ByteTensor(t.size()).zero_()
edge = edge.bool()
edge[:, :, :, 1:] = edge[:, :, :, 1:] | (t[:, :, :, 1:] != t[:, :, :, :-1])
edge[:, :, :, :-1] = edge[:, :, :, :-1] | (t[:, :, :, 1:] != t[:, :, :, :-1])
edge[:, :, 1:, :] = edge[:, :, 1:, :] | (t[:, :, 1:, :] != t[:, :, :-1, :])
edge[:, :, :-1, :] = edge[:, :, :-1, :] | (t[:, :, 1:, :] != t[:, :, :-1, :])
return edge.float()
现在,您的代码应该运行了,您可以通过在浏览器中打开文件results/coco_pretrained/test_latest/index.html
来找到结果。
设置您的数据
您首先需要以 GauGAN 的实现可以读取的方式设置您的训练数据。
本质上,你将需要语义分割图,以及它们相应的真实图像来开始。或者,您也可以使用实例分段映射,这可能会提高您的性能。
出于演示目的,我们使用 CamVid 数据集。让我们从创建存储数据集的文件夹开始。
mkdir -p datasets/CamVid
cd datasets/CamVid
然后我们下载数据集。
wget -c http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/files/701_StillsRaw_full.zip
unzip 701_StillsRaw_full.zip
然后,获取注释。
wget -c http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/data/LabeledApproved_full.zip
mkdir LabeledApproved_full
cd LabeledApproved_full
unzip ../LabeledApproved_full.zip
cd ..
GauGAN 要求语义分割图是具有一个通道的图像,其中每个像素代表其所属语义类别的索引。如果你的语义分割图是 RGB 或者多边形格式,那么你需要把它们转换成上面提到的格式。您还需要确保类索引是从 0 到n-1
的连续整数,其中n
是类的数量。
CamVid 数据集具有 RGB 格式的语义分割图。让我们把它们转换成 GauGAN 要求的格式。
我们首先创建一个文件夹来存储我们的新注释。假设您在目录datasets/CamVid
中,运行
mkdir GauGAN_Annotations
然后,在CamVid
文件夹中创建一个名为convert_annotations.py
的 python 脚本。
touch convert_annotations.py
我们还需要 CamVid 数据集中每个标签对应的颜色列表。
wget http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/data/label_colors.txt
准备语义分割图
我们现在将使用代码填充脚本convert_annotations.py
来进行转换。首先进口必要的东西。
import numpy as np
import os
import cv2
from copy import deepcopy
from tqdm import tqdm
然后,我们编写一个函数,将文本文件中的标签提取到一个 numpy 数组中。
with open("label_colors.txt", "r") as file:
label_colors = file.read().split("\n")[:-1]
label_colors = [x.split("\t") for x in label_colors]
colors = [x[0] for x in label_colors]
colors = [x.split(" ") for x in colors]
for i,color in enumerate(colors):
colors[i] = [int(x) for x in color]
colors = np.array(colors)
获取要处理的注释文件的地址。
annotations_dir = "LabeledApproved_full"
annotations_files = os.listdir(annotations_dir)
annotations_files = [os.path.join(os.path.realpath("."), annotations_dir, x) for x in annotations_files]
现在,我们在一个循环中遍历这些文件。然后,我们在该循环中嵌套一个循环,该循环遍历每个标签类,检查哪些像素属于该标签类(通过检查 RGB 值),并为它们分配一个类索引。
for annotation in tqdm(annotations_files):
# for each file
img = cv2.imread(annotation)[:,:,::-1]
h,w, _ = img.shape
modified_annotation = np.zeros((h,w))
for i,color in enumerate(colors):
# for each class color, i is the index value
color = color.reshape(1,1,-1)
mask = (color == img)
r = mask[:,:,0]
g = mask[:,:,1]
b = mask[:,:,2]
mask = np.logical_and(r,g)
mask = np.logical_and(mask, b).astype(np.int64)
mask *= i
modified_annotation += mask
save_path = annotation.replace(annotations_dir, "GauGAN_Annotations")
cv2.imwrite(save_path, modified_annotation)
检查您的GauGAN_annotations
文件夹以查看转换后的 SegMaps。你可以在这里获得整个脚本
准备实例分段图
使用实例映射是可选的。然而,如果你使用它们,你需要用一个通道将它们转换成图像,其中属于每个实例的每个像素必须有不同的值。与语义分割图不同,实例索引不必是 0 到n-1
范围内的整数。每个实例有一个不同的任意整数就可以了。
这是因为负责创建边缘图的代码(参考上一篇文章阅读边缘图)只关注像素之间的差异。因此,如果您的三个实例编码为{0,1,2}或{34,45,50},这并不重要。您还可以省略背景对象的实例,并将值设置为背景索引,比如 0。
通常,任何实例分段算法(如 Mask RCNN)或注释工具都会给每个实例不同的 id,而不是颜色,这样您的工作会更容易。例如,Cityscapes 遵循的惯例是{class_index}000{instance_id}
。例如,一辆汽车的几个实例将被标记为120001
和120002
,其中 12 是汽车类别的索引,1 和 2 是汽车的实例。
当您有许多相同类的重叠对象时,实例 id 非常有用。如果没有实例,我尝试使用语义标签本身作为实例。它实际上不起作用。
对数据集进行分区
有两种方法可以对数据集进行分区。
- 首先对数据集进行分区,并在每个分区上分别运行预处理脚本。
- 做好预处理后再做分区。
在我们的例子中,我们执行步骤 2。为了简单起见,我只在训练集和测试集之间划分数据。您也可以创建一个验证集。
首先,让我们创建四个文件夹来存放我们的零件。确保你从运行这段代码
mkdir train_img train_label test_img test_label
注意,如果您也使用实例映射,那么您也需要创建train_inst
和test_inst
。
现在,创建一个包含分区代码的文件。
touch partition_data.py
现在让我们在这个文件中编写分区代码。首先,导入所需的库。
import os
from shutil import copy
import random
from tqdm import tqdm
Then,我们创建一个属于训练集和测试集的文件列表。
partition_percentage = 90
annotations_dir = 'GauGAN_Annotations'
annotations_files = os.listdir(annotations_dir)
annotations_files = [os.path.join(os.path.realpath("."), annotations_dir, x) for x in annotations_files]
train_labels = random.sample(annotations_files, int(partition_percentage / 100 * len(annotations_files)))
test_labels = [x for x in annotations_files if x not in train_labels]
train_images = [x.replace(annotations_dir, '701_StillsRaw_full').replace("_L", "") for x in train_labels]
test_images = [x.replace(annotations_dir, '701_StillsRaw_full').replace("_L", "") for x in test_labels]
这里,我们使用 80-20%的分割百分比进行训练测试分割。现在,我们使用 python 的shutil
包将文件复制到它们各自的文件夹中。
for file in tqdm(train_labels):
src = file
dst = file.replace(annotations_dir, 'train_label').replace("_L", "")
copy(src, dst)
for file in tqdm(test_labels):
src = file
dst = file.replace(annotations_dir, 'test_label').replace("_L", "")
copy(src, dst)
for file in tqdm(train_images):
src = file
dst = file.replace('701_StillsRaw_full', 'train_img')
copy(src, dst)
for file in tqdm(test_images):
src = file
dst = file.replace('701_StillsRaw_full', 'test_img')
copy(src, dst)
你可以在这里获得整个脚本
我们完了。现在开始训练模型吧。
训练模型
在我们开始培训之前,让我先回顾一下一些兼容性问题。TensorBoard 日志记录代码不适用于 TF 2.0,因为最新版本对 TensorBoard 和 Summary 语法进行了重大更改。您还需要确保您的 scipy 版本< = 1.2.0。如果您使用 PIL 7.0,也可能会遇到一些问题,因此使用 6.1.0 可能会更好。
pip install tensorflow==1.15.0
pip install scipy==1.2.0
pip install pillow==6.1.0
为了训练模型,我们转到主文件夹SPADE
并运行下面的命令。
python train.py --name CamVid --dataset_mode custom --no_instance --label_nc 32 --preprocess_mode scale_width --label_dir datasets/CamVid/train_label --image_dir datasets/CamVid/train_img/ --load_size 512 --aspect_ratio 1.3333 --crop_size 512 --ngf 48 --ndf 48 --batchSize 1 --niter 50 --niter_decay 50 --tf_log
这将开始训练。让我们了解每个参数的含义。
name
无论您在此处给出什么名称,都会在checkpoints
目录(checkpoints/name
)中创建一个同名文件夹。该文件夹将包含您保存的重量、TensorBoard 日志和中级训练结果。(可以通过checkpoints/name/web/index.html
看到)
- 这可以设置为 COCO-Stuff、Cityscapes、ADE20K 或 custom。在我们的例子中,它被设置为 custom。该模式允许我们使用
image_dir
和label_dir
标志定义用于训练的图像和标签目录。
- 您需要设置数据集中的类的数量,在 CamVid 中是 32。
- 这基本上定义了我们如何将我们的图像调整到网络可以接受的大小。我选择了
scale_width
,这意味着宽度缩放到由参数load_size
定义的值,同时保持纵横比不变(意味着高度变化)。您也可以选择简单的选项,如resize
(非等距地调整大小为(loadsize
、loadsize
)、crop
(从图像中裁剪一部分大小(crop_size
、crop_size
)和none
(将高度和宽度调整为最接近 32 的倍数)。这些也可以结合使用。例如scale_width_crop
,它会将图像的宽度缩放到load_size
,然后对crop_size
进行裁剪。管理该功能的代码可在data/base_dataset.py
的get_transform
功能中找到。
aspect_ratio
基本上就是图像的长宽比。GauGAN 的代码很奇怪,因为crop_size
总是被用来最终调整图像的大小,即使在preprocess
方法中没有使用crop
选项。因此,无论是否使用crop
,您都必须设置crop_size
。这可以在文件models/networks/generator.py
中的SPADEGenerator
类的compute_latent_vector_size
方法中找到。
def compute_latent_vector_size(self, opt):
if opt.num_upsampling_layers == 'normal':
num_up_layers = 5
elif opt.num_upsampling_layers == 'more':
num_up_layers = 6
elif opt.num_upsampling_layers == 'most':
num_up_layers = 7
else:
raise ValueError('opt.num_upsampling_layers [%s] not recognized' %
opt.num_upsampling_layers)
sw = opt.crop_size // (2**num_up_layers)
sh = round(sw / opt.aspect_ratio)
return sw, sh
正如您在第 16 行中看到的,输入的高度sh
是通过将宽度缩放到crop_size
并保持纵横比aspect_ratio
来计算的。所以,我用load_size
加载图像,用同样的值crop_size
进行上述操作。以上也意味着你的长宽比是固定的。您将不得不修改这段代码,使其以任意的纵横比运行。
ngf
和ndf
。基本上分别是发生器和鉴别器的第一层中的卷积滤波器。后续层中的过滤器数量是这些数量的函数。因此,这些数字是对网络容量的一种衡量。
batchSize
。嗯,名字很明显。不是吗?
niter
和niter_decay
。对于niter
时代,你的学习率将保持不变,而对于niter_decay
选项,你的学习率将线性衰减到零。所以从技术上来说,你是在为niter + niter_decay
时代训练。
tf_log
。使用这个代码可以在checkpoints/name/logs
中记录你的损失和中间结果
在多个 GPU 上训练您的模型
如果你有多个 GPU,你可以通过使用gpu_ids
标志来使用多个 GPU 训练。
python train.py --name CamVid --gpu_ids 0,1 --batch_size 2 --dataset_mode custom --no_instance --label_nc 32 --preprocess_mode scale_width --label_dir datasets/CamVid/train_label --image_dir datasets/CamVid/train_img/
你需要确保batch_size
是你将要使用的 GPU 数量的倍数,否则,代码会抛出错误。这是因为批处理需要跨 GPU 划分。
恢复训练
如果您的训练被中断,您可以使用--continue_training
标志恢复训练。在由标志--save_latest_freq
定义的每固定数量的循环迭代之后,模型检查点被更新。continue_train
将从最新的关卡恢复训练。
除了最新的检查点,学习到的重量每隔固定数量的时期存储一次,频率由--save_epoch_frequency
定义。默认情况下,这个数字是 10,如果您想从这些时期中的任何一个开始,而不是从最近的一个开始,您可以使用组合--continue_training --which_epoch 20
从比如说第 20 个时期开始恢复训练。
运行推理
训练完成后,您可以使用以下命令运行推理。
python test.py --name CamVid --dataset_mode custom --no_instance --label_nc 32 --preprocess_mode scale_width --label_dir datasets/CamVid/test_label --image_dir datasets/CamVid/test_img/ --load_size 512 --aspect_ratio 1.3333 --crop_size 512 --ngf 48
注意我们是如何将数据目录更改为test_img
和test_label
的。结果将保存在results/CamVid/test_latest
文件夹中。
CamVid 数据集严重缺乏多样性。首先,因为它太小了。其次,因为它是由视频流中的帧组成的,这意味着许多帧是相关的。对于车辆静止的帧来说尤其如此,这意味着背景也将是静止的。由于这些原因,即使是一个好的参数选择也会导致平庸的结果。
让我们看看一些结果,从像样的预测开始。
这里有一些不好的例子。
每当我们的车辆停在一个信号,我们往往会得到许多类似的帧,因为车辆一次又一次地看到相同的东西。这导致网络过度适应,尤其是当我们的数据集如此之小时。
我们看到过拟合太差,网络甚至已经记住了红绿灯的颜色;总是红灯。
结论
在第 3 部分和第 4 部分中,我们将更多地讨论在有效训练 GauGAN 时对数据的考虑,以及为什么 CamVid 是一个糟糕的选择。在下一篇文章中,我们将特别介绍 GauGAN 与其他类似算法相比表现如何,以及如何使用 FID 分数来评估其性能。
了解 GauGAN 系列
纸上空间的通才模型:图像、文本、音频和视频相结合
原文:https://blog.paperspace.com/generalist-agents-on-paperspace-images-text-audio-and-video-all-in-the-same-model/
如果我们只需要训练一个模型,而不是为许多不同的任务训练大量的模型,会怎么样?然后这个模型完成我们所有的任务。
虽然这听起来有点异想天开,但已经有一些例子可以证明,同一个模型可以执行许多不同的任务,包括图像、文本、音频、视频等等。
这样的通才型(又名。通才代理或多模态模型)正变得越来越能够承担大量不同的用例。这意味着,未来我们可能不需要管理许多模型,而是可以在少数甚至一个模型上运行端到端的人工智能。
以这种方式简化端到端人工智能将开放给更多人使用,因为剩余的一个或多个模型可能会被放在易于使用的无代码接口中,同时保留它们解决问题的全部能力。
因此,虽然我们还没有看到一个模型来统治他们,但探索他们目前的能力是值得的。
在这里,我们将回顾其中的 3 个:
具体来说,我们将展示如何在 Paperspace 上运行感知 IO。
感知者 IO 和加托来自 DeepMind,Data2vec 来自 Meta AI。
真正的人工通用智能(AGI)的日子还有一段路要走,但在未来,人们可能会记住这些朝着它迈出的一些步骤。
为什么是一般的?
这里介绍的 3 个模型在细节上各不相同,但是使它们通用的基本思想是,不管输入如何,传递给它们的数据都被转换成相同的形式。所以图像、文本、音频等。,都被转换成相同的基础形式,然后模型可以在此基础上进行训练。
然后,模型以这种形式输出对数据的预测,这些预测在用于输入的变换的反转中被转换回可操作的输出。
模型的可变组件成为处理数据的输入和输出,而模型本身保持相同的形式。
它们为什么重要?
这些模型的重要性不仅在于它们的潜在继任者为人工通用智能铺平了道路,还在于它们更为近期的效用:
- 需要管理的模型更少:用于许多任务的一个模型而不是数据流中串在一起的多个模型简化了管理,例如路由数据、版本控制、部署、检查责任、数据 I/O 与培训等等。
- 更简单的接口:拥有一个具有可交换输入和输出的通用模型意味着创建更通用的端到端接口和应用程序将变得更容易,向不太懂技术甚至没有代码的用户开放它们的使用。
- 他们可以胜过专家模型:除了更通用之外,在相同的任务中,这些通才模型有几个实例胜过以前的专家模型,因此过程不仅更简单,而且可以产生更好的结果。
在一个地方拥有多种能力的组合,并且能够同时处理多种类型的数据,这意味着它们也可以以新的方式组合在一起。例如,不仅要制作视频,还要为视频添加字幕和朗读单词,让所有观众都能更好地理解。随着能力的增加,可能性成倍增加。
三种模式
现在让我们看一下每个模型在做什么的概述。有关更深入的描述,请参见下面引用的原始博客和论文。
感知者 IO
感知者 IO ( 博客、 HF 库、论文、维基)基于一个变压器神经网络。最初用于自然语言处理(BERT 等。),然后将变形金刚应用于计算机视觉,例如,视觉变形金刚 ViT,现在作为通用模型。他们通过使用注意力来工作,这意味着在训练中发现更重要的数据部分会获得更大的权重,并在其上花费更多的计算时间。
输入数据,不考虑图像、文本、音频等来源。映射到一个更低维度的潜在空间。换句话说,网络本身不是特定于模式的,所有传入的数据都以相同的形式进行。潜在空间意味着原始数据中彼此相似的项目在新空间中靠得更近。该模型使用交叉注意力,即使用注意力将数据映射到不同的维度。以这种方式使用注意力来减少数据的大小也被称为注意力瓶颈。潜在数据的特定特征与它们的输入部分相关联,使通过网络的内容与原始数据保持顺序。
因为所有数据以相同的形式通过网络,这意味着所有输出也是相同的形式。因此,输出必须根据用户想要执行的任务进行解释,并适当地转换回来。这是通过用户传入一个查询来完成的,该查询与网络中的潜在数据相结合,以所需的形式产生输出。这种在开始时对数据进行编码,然后在结束时进行解码的方式类似于广泛使用的一类称为编码器-解码器神经网络的模型。
与变压器不同,在网络中使用比原始数据维数低的潜在数据意味着网络可以扩展以处理大量输入和输出,而不会变得太慢。
来自原始 DeepMind 博客的图表显示了感知者设置的有用概述:
Perceiver IO setup, showing its main components of input, output, its internal latent data, and the use of attention. From DeepMind's original blog entry.
Perceiver IO 的性能已被证明可与其他模型相媲美,如用于语言的 BERT 和用于图像的 ResNet50,以及用于光流的 AudioSet 音频+视频数据和 Sintel 视频的最新技术。进一步演示的应用包括分类三维点云和视频游戏。
Data2vec
2022 年 1 月由 Meta AI(又名。脸书)、Data2vec ( 博客、储存库、论文)是第一个自监督算法,适用于多种不同类型的数据。
自我监督算法很重要,因为它们能够在没有标签的数据上进行训练。没有标记的数据比有标记的数据多得多。自我监督模型通过直接观察它们的环境来学习。
但是,正如他们在博客中指出的,其他自我监督模型的学习方式在很大程度上取决于输入数据的类型。因此,能够在一个模型中从所有类型的数据中学习是向前迈出的一步,并开辟了自我监督学习的更广泛用途。
Data2vec 所处理的数据类型并不像 epider IO 那样普遍,但它们仍然包括图像、文本和语音。
与感知者 IO 的想法类似,Data2vec 通过将所有输入数据转换成特定的表示来实现其通用性,同样是最重要部分的潜在空间编码。然后,模型以自我监督的方式学习这种表示,而不是原始数据。
学习是通过教师网络和学生网络的结合来完成的。教师将输入数据转换成目标表示,然后学生预测隐藏部分数据的表示。然后重复这一过程,直到学生网络能够预测数据。目前,将数据转化为表示形式的确切方法取决于它是图像、文本还是语音。
来自原始 Data2vec 博客的动画给出了学习过程的示意图:
How Data2vec learns: a teacher plus student network are combined to learn on images, text, or speech data. From the original blog announcing Data2vec.
因为所有类型的输入数据都形成了特定的表示形式,所以模型的输出也是这种表示形式。然后必须将其转换回外部格式,以便直接查看模型的预测。
就像感知者 IO 一样,这里的通用模型不仅等同于以前专门模型的性能,而且在计算机视觉和语音任务方面优于它们。NLP 文本任务也做得很好。他们测试的数据包括用于视觉的 ImageNet、用于语音的 LibriSpeech 和用于文本的 GLUE。
该方法不限于这里研究的 3 种类型的数据。只要数据可以转换成模型的表示,就可以使用。
Data2vec 有一个 public GitHub repository ,可以在 Paperspace 上运行,方法是以通常的方式从渐变笔记本中指向它。
加托
像他们的感知者 IO 一样,DeepMind 的加托(博客、论文、维基百科)也是基于一个变压器神经网络。与感知者 IO 的不同之处在于,单个加托模型同时学习如何做许多不同的任务。
这些任务包括玩 Atari 视频游戏、为图像添加字幕、与用户进行文本聊天、用机器人手臂叠积木、在模拟的 3D 环境中导航、遵循指令等。
与感知者 IO 和 Data2vec 一样,不同的数据类型都被处理成一个更通用的类型,以便通过网络传递。这里,它是一个令牌序列:
- 文本被编码成子词
- 图像是按光栅顺序排列的非重叠面片序列,然后进行归一化
- 离散值是整数序列
- 连续值是浮点序列,经过标准化后入库
离散值和连续值的例子分别是玩游戏时的按钮按压和本体感受输入。加托总共接受了 604 项不同任务的训练。
标记化和排序的数据通过嵌入函数传递。该函数的作用取决于输入数据的类型,结果是输入到模型中的向量。这再次遵循了所有类型的输入都被转换成模型中的相同表示的思想。
来自原始博客条目的图表显示了事情如何经过的草图,以及可以执行的一些任务:
Sketch of how Gato takes in many data types all at once, and is therefore trained to perform multiple tasks all with the same model. From the original blog entry by DeepMind.
通过将对其输入执行的编码+排序的逆运算应用到模型的输出来部署模型,以给出要执行的下一个动作。它被发送一个对环境的初始观察,然后它行动,观察新的环境,再次行动,等等。
相对于更专业的模型,加托的性能取决于它正在执行的任务。因为结果是针对 604 个不同的任务呈现的,所以我们不试图在这里对它们进行量化,但是非常粗略地说,对于大多数任务,具有相同权重的相同训练模型最有可能获得专家性能。这与 epider IO 和 Data2vec 不同,有时这些模型的表现优于最先进的技术,但这个模型正在做最不同的事情。
因此,任何单个任务都有改进的空间,但这是一个通用方法的示例,其中所有 it 部分都将受益于未来不断增长的计算能力。作为其任务之一,所展示的模型大小被保持在可用于实时机器人的水平,因此以目前的标准来看,12 亿个参数相对较小。因此,事实上它在这个规模上可以做多少事情是惊人的,缩放分析表明,如果用更多的参数来训练,它还有进一步改进的空间。
在这里讨论的三个模型中,加托是最像 AGI 的,因为它是一个可以同时做很多事情的模型。事实上,这篇论文比其他两个模型的论文花了更多的文字来讨论它在人工智能的进化和安全方面的更广泛的背景。
目前,它更接近于现有的深度学习模型,而不是未来的超级智能。但这些模式只会随着时间的推移越来越好。
加托还没有公开版本,所以不能在 Paperspace 上运行。
图纸空间上的通用模型
现在让我们看看如何在 Paperspace 上运行通才模型。我们将把注意力集中在木卫一上。单击上面的链接或按照下面的说明在渐变笔记本中启动感知者 IO。
DeepMind 基于 JAX 的原创内容已经在 PyTorch 上的一个优秀的 GitHub 库和尼尔斯·罗格的博客拥抱脸中实现。虽然 JAX 可以在 Paperspace 上运行,但是我们使用 PyTorch 库作为运行感知者 IO 的路径。
要在 Paperspace 上运行它,以常规方式启动渐变笔记本。使用 PyTorch 运行时和所有默认设置的选项,除了:
Gradient Notebook creation to run Perceiver IO from Hugging Face's GitHub repository
这个工作区的 GitHub 存储库包含除了感知者之外的许多其他模型,所以要运行它,导航到Perceiver
目录,在那里我们会找到五个.ipynb
Jupyter 笔记本。
每一个都可以使用 Run All 打开并运行,或者通过遍历单元格来运行。
我们建议在运行后重新启动笔记本内核,以便 GPU 在后续运行时不会耗尽内存。我们还可以通过使用左侧 GUI 导航栏上的 metrics 选项卡或终端中的nvidia-smi
随时检查 GPU 内存使用情况。
💡Note: For Perceiver_for_Multimodal_Autoencoding.ipynb
, we will need to run pip install accelerate
before running the notebook. Either do so from the command line in the terminal, or add a cell to the notebook containing !pip install accelerate
. If we choose the cell method, restart the notebook kernel before proceeding to run the rest of the cells, so that accelerate
can be imported.
5 台笔记本电脑中显示的感知者 IO 功能如下:
Fine_tune_Perceiver_for_text_classification.ipynb
:显示文本分类,在互联网电影数据库(IMDB)数据集的子集上运行,执行电影评论好坏的二元分类。感知者 IO 不需要先对文本进行令牌化,所以直接提供字节作为输入。它经历了从微调训练到在测试集上显示推论,以及对评论好坏的预测。
Fine_tune_the_Perceiver_for_image_classification.ipynb
:这显示了使用 CIFAR-10 数据集的子样本将图像分类为 10 类。在 ImageNet 上训练的模型将其最终输出替换为在 CIFAR-10 上分类。该模式评估为 60 或 70%的准确率,但它可以在更多的数据上进行训练,比笔记本默认显示的更多。(如前所述,当经过充分训练的感知者 IO 可以与专门为图像设计的其他模型的性能相匹配时。)
Perceiver_for_Optical_Flow.ipynb
:这使用 Sintel 视频数据集的 2 帧,光流在像素级在它们之间进行插值。然后使用 Gradio 可视化生成的流。虽然感知者的使用简化了许多任务,但这一项比大多数专业设置更简单。
💡Note: If you want to use Gradio, make sure that the launcher has "Share" set to True by changing the line iface.launch(debug=True)
to iface.launch(debug=True, share=True)
. Otherwise, you will not be able to open the popup window, as Gradient doesn't currently support port forwarding.
Perceiver_for_Multimodal_Autoencoding.ipynb
:多模态自动编码是指模型同时处理多种类型的输入数据。在此笔记本中,使用 UCF101 数据集的视频、音频和类别标签数据加载该数据集。使用多模态预处理和后处理,并且结果输出示出了重构的视频、音频和视频正在显示的内容的预测类别标签。
Perceiver_for_masked_language_modeling_and_image_classification.ipynb
:在同一模型架构上显示屏蔽语言建模和图像分类,产生正确的文本和分类输出。
结论
我们已经介绍了通才模型感知者 IO、数据 2vec 和加托,并展示了感知者 IO 在 Paperspace 上的运行。它执行与图像、文本、音频和视频相关的任务。每一个都可以直接加载并运行,就像通常的 Paperspace 方式一样。
与大多数模型相比,这些模型在通用性方面向前迈进了一步。特别重要的是,它们不仅推广了早期的模型,而且在某些情况下还超越了它们。
虽然真正的人工智能在未来仍有一段路要走,但在短期内,这些模型可能会简化和拓宽人工智能和端到端数据科学的范围和能力。
感谢阅读!
后续步骤
我们可以采取的一些后续步骤是在 Paperspace 上运行这些模型,或者通过参考原始博客和论文来了解比这里给出的更详细的模型。请注意,许多更详细的论述将假设我们已经知道这里介绍的许多深度学习概念。
像他们的博客一样,感知者 IO 拥抱面部内容基于 DeepMind 的原始内容,但在 PyTorch + .ipynb
而不是 JAX + .py
中实现。
生成具有稳定扩散的图像
原文:https://blog.paperspace.com/generating-images-with-stable-diffusion/
用于图像合成的扩散模型的出现最近在互联网上掀起了风暴。这是有充分理由的。在今年早些时候发布了 CompVis 的“使用潜在扩散模型的高分辨率图像合成”之后,很明显,扩散模型不仅能够在给定的提示下生成高质量、精确的图像,而且这个过程的计算成本也远远低于许多竞争框架。
在这篇博文中,我们将在探索新的框架——稳定扩散——所带来的相应进步之前,考察扩散建模的基础。接下来,我们检查在当前状态下使用模型的优点和缺点。然后,我们进入一个简短的编码演示,展示我们如何在渐变笔记本上免费运行稳定的扩散。在这篇博文的结尾,读者将能够使用该模型生成他们自己的新颖艺术品/图像,同时建立对稳定扩散建模如何在幕后操作的理解。
扩散模型介绍
Source
在本质上,扩散模型是生成模型。特别是在计算机视觉任务中,他们首先通过向训练图像数据中连续添加高斯噪声来工作。一旦原始数据被完全去噪,该模型就学习如何完全逆转去噪过程,称为去噪。该去噪过程旨在迭代地重建原始图像的由粗糙到精细的特征。然后,一旦训练完成,我们可以使用扩散模型来生成新的图像数据,只需将随机采样的噪声通过学习的去噪过程。
A visual interpretation of the denoising process - Source
它通过使用固定的马尔可夫链映射图像的潜在空间来实现这一点。该链逐渐将噪声添加到数据中,以便使数据近似于后验函数,$ q(X1:T|X0) \(。它假设\)X0,...,XT\(是与\)X0$具有相同维数的图像潜变量。在序列结束时,图像数据被转换到类似于纯高斯噪声的程度。因此,扩散模型必须学会逆转这一过程,以获得所需的生成图像。因此,通过找到使训练数据的可能性最大化的反向马尔可夫转移来训练扩散模型。
稳定扩散
稳定扩散,后续的相同团队先前的工作对潜在扩散模型,大大改善了前人在图像质量和范围的能力。它通过更强大的训练数据集和对设计结构的有意义的改变实现了这一点。
该模型使用冻结剪辑 ViT-L/14 文本编码器来调节文本提示上的模型。用于训练的数据集是 laion2B-en ,它由英语中的 23.2 亿个图文对组成。经过训练后,凭借其 860M UNet 和 123M 文本编码器,该模型相对较轻,可以在至少 10GB VRAM 的 GPU 上运行。通过将数字格式的精度降低到半精度(FP16),它还可以进一步优化,以便在大约 8 GB VRAM 的 GPU 上运行。
在实践中,这些变化使得稳定扩散在许多计算机视觉任务中表现出色,包括:
- 语义合成-仅通过文本提示生成图像
- 修复——精确填充图像的缺失部分,使用深度学习来预测图像缺失部分的特征
- 超分辨率-一类增强(增加)成像系统分辨率的技术
稳定扩散有什么新特性?
更好的缩放
Source
稳定扩散与过去的扩散建模方法不同的一个关键方面是更容易扩展的能力。先前的相关工作,例如基于 GAN 的方法或纯变换器方法,需要在潜在空间中进行大量的空间下采样,以便降低数据的维数。因为扩散模型已经为空间数据提供了如此优秀的归纳偏差,所以对被移除的潜在空间进行下采样的效果对于我们的最终输出质量来说是无关紧要的。
在实践中,这允许模型功能的两个扩展:在压缩级别上工作,提供比以前的工作更忠实和详细的重建,以及有效地工作用于大图像的高分辨率合成。
运行训练和推理的成本更低
与其他 SOTA 竞争对手的框架相比,稳定扩散的运行计算成本相对较低。与其他方法相比,作者能够以低得多的运行成本展示修复、无条件图像合成和随机超分辨率生成任务的竞争性性能水平。与基于像素的扩散方法相比,他们还能够显著降低推理成本。
灵活性
Source
许多以前关于该模型框架的工作需要模型的重构和生成能力的精确加权,因为它们同时学习编码器/解码器架构和基于分数的先验。与以前的工作相比,稳定扩散不需要重建和生成能力的这种微妙的加权,这允许在潜在空间相对较小的正则化的情况下更忠实地重建图像。
用于密集条件任务的高分辨率图像生成
High resolution inpainting - Source
当使用该模型执行密集条件任务时,例如超分辨率、修补和语义合成,稳定扩散模型能够生成百万像素图像(大约 10242 像素大小)。当模型以卷积方式应用时,这种能力被启用。
稳定扩散:模型的应用&与竞争模型的比较
既然我们已经了解了扩散是如何工作的,为什么稳定扩散如此强大,那么了解什么时候应该使用这个模型,什么时候不应该使用这个模型是很重要的。具体来说,该模型是为一般研究目的而设计的。一些潜在的研究案例包括:
- 安全部署可能产生有害内容的模型
- 探索和理解生成模型的局限性和偏见
- 艺术品的生成以及在设计和其他艺术过程中的使用
- 教育或创意工具的应用
- 生成模型研究[ 来源
Source
当执行这些任务时,稳定扩散被认为是综合能力方面的 SOTA。从上表可以看出,当执行文本条件图像合成任务时,稳定扩散(潜在扩散)的前身能够获得与两个顶级图像合成模型框架 GLIDE 和 Make-A-Scene 相当的 FID 分数。此外,LDM-KL-8-G 能够获得所有测试模型中最高的初始得分(IS)。基于这些数据,我们可以推断,稳定扩散是一个非常强大的图像合成工具,具有精确的再现。当执行其他任务时,模型也反映了这些发现,表明模型的总体稳健性。
稳定扩散:模型的问题
虽然稳定扩散具有最先进的能力,但在某些情况下,它在某些任务上不如其他技术。像许多图像生成框架一样,稳定扩散内置了由许多因素造成的限制,包括训练期间图像数据集的自然限制、开发人员对这些图像引入的偏见以及内置于模型中以防止误用的阻止程序。让我们逐一分析一下。
训练集限制
用于图像生成框架的训练数据总是会对其能力范围产生重大影响。即使在处理海量数据时,如用于训练稳定扩散的里昂 2B(en)数据集,也有可能通过输入提示引用看不见的图像类型来混淆模型。原始训练步骤中未包含的特征将无法重新创建,因为模型不了解这些特征。
这一点最明显的例子是人的形象和面孔。该模型在很大程度上不是以在生成的输出中提炼这些特征为重点来训练的。如果这是模型的预期用途,请注意这些限制。
研究人员引入的偏见
稳定扩散背后的研究人员认识到这种任务中固有的社会偏见的影响。首先,在里昂-2B(en)数据集的子集上训练稳定扩散 v1。这些数据几乎全部是英语。他们认为,来自不说英语的文化和社区的文本和图像在很大程度上是无法解释的。这种聚焦于这些英语语言数据的选择使得英语语言提示和输出之间的连接更加稳固,但是同时通过迫使白人和西方文化影响成为输出图像中的主导特征来影响输出。类似地,使用非英语语言输入提示的能力被这种训练范例所抑制。
内置针对色情、暴力和恶意图像内容的拦截器
模型架构的作者已经设置并定义了一个使用稳定扩散的上下文列表,该列表将被描述为滥用。这包括但不限于:
- 对人们或他们的环境、文化、宗教等产生贬低、非人性化或有害的描述
- 故意推广或传播歧视性内容或有害的陈规定型观念
- 未经他人同意冒充他人
- 未经可能看到它的人的同意的性内容
- 错误信息和假信息
- 令人震惊的暴力和血腥的表现
- 违反使用条款共享受版权保护或许可的材料
- 共享违反使用条款的版权或许可材料变更内容- 【来源】
代码演示
现在,我们已经了解了扩散的基础知识、稳定扩散模型的功能以及使用该模型时必须遵守的限制,我们可以进入编码演示,使用稳定扩散来生成我们自己的新图像。点击下面的链接,打开一个有免费 GPU 的笔记本来测试这段代码。
建立
首先,我们将进行一些必要的安装。值得注意的是,我们将使用diffusers
来获取数据集和管道化我们的训练,并使用flax
来根据提示的准确性对我们的图像进行剪辑重新排序。
!pip install --upgrade diffusers transformers scipy ftfy
!pip install flax==0.5.0 --no-deps
!pip install ipywidgets msgpack rich
接下来,你需要获得稳定扩散的预训练模型。为此,您必须登录 Huggingface 并创建一个令牌。完成后,转到稳定扩散模型页面,确认并接受我们之前讨论的模型的使用条款。完成后,将你的令牌粘贴到笔记本上写有<your_huggingface_token>
的地方。然后,运行单元。这也将创建一个输出目录来保存我们稍后生成的样本。
!python login.py --token <your_huggingface_token>
!mkdir outputs
推理
现在我们已经完成了设置,我们可以开始生成我们的图像。在下面的示例中,我们展示了如何在 VRAM 小于 10 GB 的计算机上运行映像生成过程。
# Low cost image generation - FP16
import torch
from torch import autocast
from diffusers import StableDiffusionPipeline
model_id = "CompVis/stable-diffusion-v1-4"
device = "cuda"
pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16, revision="fp16", use_auth_token=True)
pipe = pipe.to(device)
sample_num = 10
lst = []
prompt = "Kermit the Frog on the Iron Throne"
for i in range(sample_num):
with autocast("cuda"):
lst.append(pipe(prompt, guidance_scale=7.5)["sample"][0])
for i in range(sample_num):
lst[i].save(f'outputs/gen-image-{i}.png')
让我们走一遍。首先,我们导入 or 包并定义model_id
和device
变量。这些将指导我们的StableDiffusionPipeline
从 HuggingFace Hub 下载适当的模型,设置模型处理半精度数据,并让它知道它必须使用我们的授权令牌来访问模型文件。然后,我们使用.to
方法来移动管道,使其在我们的 GPU 上运行。
接下来,我们定义sample_num
和lst
。前者代表我们希望生成的图像数量,后者将保存我们的图像数据以供以后排序。然后,可以将提示更改为用户认为合适的任何内容。最后,我们准备好通过循环sample_num
的范围来生成我们的图像,并在每一步从提示中生成一个新的图像来附加到lst
。这些文件最终被保存到outputs/
文件夹中,以备日后检查。下面是我们运行上述代码时的一个示例:
"Kermit the Frog sitting on the Iron Throne"
图像的剪辑排序
虽然对样本图像进行定性评估非常简单,但我们可以利用这里强大的 CLIP 模型,根据图像最终编码与原始输入提示编码的接近程度,对图像进行定量评估和排序。这段代码改编自 Boris Dayma 的 DALL-E Mini。
from transformers import CLIPProcessor, FlaxCLIPModel
import jax
import jax.numpy as jnp
from flax.jax_utils import replicate
from functools import partial
# CLIP model
CLIP_REPO = "openai/clip-vit-base-patch32"
CLIP_COMMIT_ID = None
# Load CLIP
clip, clip_params = FlaxCLIPModel.from_pretrained(
CLIP_REPO, revision=CLIP_COMMIT_ID, dtype=jnp.float16, _do_init=False
)
clip_processor = CLIPProcessor.from_pretrained(CLIP_REPO, revision=CLIP_COMMIT_ID)
clip_params = replicate(clip_params)
# score images
@partial(jax.pmap, axis_name="batch")
def p_clip(inputs, params):
logits = clip(params=params, **inputs).logits_per_image
return logits
首先,我们加载剪辑模型和处理器,复制参数,并定义我们的p_clip
评分函数。
from flax.training.common_utils import shard
import numpy as np
# get clip scores
clip_inputs = clip_processor(
text=prompt * jax.device_count(),
images=lst,
return_tensors="np",
padding="max_length",
max_length=77,
truncation=True,
).data
logits = p_clip(shard(clip_inputs), clip_params)
out = list(reversed(sorted(zip(logits[0], lst))))
for idx, v in enumerate(out):
display(v[1])
print(f"Score: {v[0][0]:.2f}\n")
然后我们用带有clip_processor
的剪辑处理器得到每幅图像的剪辑分数。然后,我们可以将这些图片与我们的原始图片列表进行压缩和排序,以创建一个按得分排序的图片合并列表。
The top scoring image for "Kermit the Frog sits on the Iron Throne"
通过剪辑排名,我们可以将我们自己的定性评估与图像生成的更客观的定量评分结合使用。CLIP 本身可以在给定提示的情况下找到最佳图像,但只有当它与人工代理结合时,我们才能找到生成的最佳图像。
结束语
总之,稳定扩散是一个强大且易于使用的图像合成框架。我们介绍了扩散是如何工作的,稳定的扩散模型有哪些新的和强大的功能,然后详细介绍了该框架的优点和缺点。之后,我们展示了如何在渐变笔记本上使用该技术执行文本条件图像合成。
现在就可以免费使用这些笔记本,看看你能创作出什么样的又酷又独特的作品!
用 Gradient 和 ml5.js 生成交互式 Pix2Pix 模型
原文:https://blog.paperspace.com/generating-interactive-pix2pix-models/
这篇文章将通过使用渐变训练一个生成式图像模型,然后将模型移植到 ml5.js 的过程,这样你就可以在浏览器中与它进行交互。为此,我们将使用 Pix2Pix 或图像到图像的翻译和条件对抗网络,并在成对的卫星图像和地图瓦片上训练它。这是一系列博客帖子的第三篇,这些帖子致力于在 Paperspace 中训练机器学习模型,然后在 ml5.js. 中使用它们
Pix2Pix 简介
Pix2Pix,即图像到图像的翻译,是一种训练机器学习模型的技术,以学习从输入到输出图像的相关图像对之间的映射。这意味着,模型将学习如何将一种类型或具有一组特征的图像转换成具有另一组特征的新图像。给定训练模型时使用的类似输入,您可以使用这种方法来合成新像素。为此,Pix2Pix 使用一种特殊的生成算法,称为条件对抗网络(cGAN) ,生成过程以输入图像为条件。原始论文和代码由 Phillip Isola 等人于 2016 年 11 月发表,从那时起,这项技术被许多人和研究人员广泛使用和探索。但是除了它有趣的技术新颖性之外,它的一些创造性成果也很吸引人。
Input, Output and target images using the CMP facades dataset. Image by Christopher Hesse
这篇文章将关注于运行和训练你自己的模型。如果你对 Pix2Pix 如何工作的更详细描述感兴趣,一个很好的资源是艺术家的机器学习(ml4a) Pix2Pix 帖子。在那里,你可以找到对模型如何学习归纳的深入解释,技术的技术细节和人们已经建立的创造性应用的种类。例如,您可以创建这样的实时交互式项目:
尝试对 @runwayml 和@ hello perspace中的角色进行图像到图像的翻译。我想我可以称之为“与 pic.twitter.com/sm8rAWdgUb 的另类晚间秀”
— Cris Valenzuela (@c_valenzuelab) August 6, 2018
用 PyTorch 上的 GPT-2 生成文本摘要
原文:https://blog.paperspace.com/generating-text-summaries-gpt-2/
在这篇文章中,我将描述一个抽象的文本摘要方法,在$[1]中首次提到,用来训练一个文本摘要器。然后,我们将看到如何在 CNN/Daily Mail 文本摘要数据集上微调预训练的基于 Transformer 解码器的语言模型(GPT、GPT-2 和现在的 GPT-3)。在不添加任何新参数的情况下,我们将在对来自训练数据集的 3000 个示例进行 5 个时期的训练后,获得一个非常强大的抽象文本摘要器。
介绍
当你希望机器学习传达文本的意思时,它可以做两件事之一:重新表述信息,或者只向你展示内容中最重要的部分。第一种方法被称为抽象概括,而第二种被称为提取概括。这两项任务都不容易,即使在当前的技术水平下,这两项任务也有其自身的局限性。摘要通常不能以自然的方式组织句子,因此所创建的摘要的可读性是不可接受的,并且很多时候甚至不能传达内容的要旨。同时,目前最先进的深度学习模型如 GPT-3 、 GPT-2 、伯特等。帮助我们在可读性方面生成类似人类的摘要,但是它们的正确性经常是有问题的。在这里,我们将重点关注用后一种方法实现可接受的结果。
带有 RNNs 或变形金刚的 Seq2Seq 架构在困难的自然语言处理任务中非常流行,比如机器翻译或文本摘要。Seq2Seq 架构上也做了很多改进,像注意(选择更相关的内容)、复制和覆盖机制(复制不太频繁的令牌,阻止重复)等。在这里,我们将使用标准语言模型目标,在 CNN/Daily Mail 数据集上微调预训练的 GPT/GPT-2 网络,以利用此类模型的强大文本生成能力。
数据集准备
我使用了 See 等人提供的非匿名的 CNN/Daily Mail 数据集\([2]\)用于将新闻文章总结为 2-3 句话。一个干净的和标记化的版本可以在这里 \([3]\)。为了将这些数据提供给 GPT/GPT-2 模型,我执行了一些专门针对 GPT 模型的预处理步骤。
由于 GPT 模型对上下文大小有限制(GPT 和 GPT-2 分别有 512 和 1024 个令牌),我只选择了那些在使用 GPT 令牌化器进行令牌化后最多有 512 和 1024 个令牌的文件。图 1 显示了 CNN 和 Daily Mail 数据集的文件大小分布(总字数)。为了进行训练,我只从 CNN 和 Daily Mail 的数据集中选择了 1500 个具有相关数量令牌的文件。
为了使这个实验在计算上更有效,我没有在完整的数据集上训练模型。为了加快数据加载过程,我将标记化的文章和摘要保存在。json 文件,带有用于训练的属性“id”、“article”和“abstract”。我的实验是在免费的渐变社区笔记本上完成的。可以找到要创建的脚本。json 文件和 NumPy 矩阵的数据分别是这里的和这里的。
Figure 1. Distribution of CNN/Daily Mail file sizes
这是我的Dataset
类,它从。json 文件:
class GPT21024Dataset(Dataset):
def __init__(self, root_dir, ids_file, mode='train',length=None):
self.root_dir = root_dir
self.tokenizer = add_special_tokens()
with open(ids_file,'r') as f:
if mode=='train':
self.idxs = json.load(f)['train_ids']
elif mode=='valid':
self.idxs = json.load(f)['valid_ids']
else:
self.idxs = json.load(f)['test_ids']
if len == None:
self.len = len(self.idxs)
else:
self.len = length
def __len__(self):
return self.len
def __getitem__(self,idx):
idx = self.idxs[idx]
file_name = os.path.join(self.root_dir,str(idx)+".json")
with open(file_name,'r') as f:
data = json.load(f)
text = self.tokenizer.encode(self.tokenizer.pad_token)*1024
content = data['article'] + self.tokenizer.encode(self.tokenizer.sep_token) + data['abstract']
text[:len(content)] = content
text = torch.tensor(text)
sample = {'article': text, 'sum_idx': len(data['article'])}
return sample
方法
在深入研究微调细节之前,让我们首先了解一般语言模型背后的基本思想,特别是 GPT 风格的语言模型。
语言模型是一种概率模型,它在给定序列中前面的标记的情况下预测该序列中的下一个标记。它根据在训练中看到的文本示例,学习一个句子或一系列标记出现的概率。它可以用下面的条件概率来表示:
Source
Source
GPT/GPT-2 是变压器模型的变体,它只有变压器网络的解码器部分。它使用多头掩蔽自我关注,这允许它在时间步长 t 只查看第一个 i 标记,并使它们能够像传统的单向语言模型一样工作。然而,这些模型不是像 rnn 那样顺序处理令牌,而是并行处理令牌,即通过一次预测所有时间步长的令牌。这种模型可以表示为:
Source
Source
Source
Source
微调细节
我使用拥抱面部变形库 \([4]\)来实现 GPT-2,因为它们超级简单的 API 可以帮助人们专注于模型训练的其他方面,如超参数优化等。事实证明,这在许多微调任务中更有价值。让我们首先加载所有的依赖项:
import argparse
from datetime import datetime
import json
import os
import pickle
import random
import sys
import time
import numpy as np
from pytorch_transformers import GPT2Tokenizer
import torch
from torch.nn import CrossEntropyLoss
import torch.nn.functional as F
from torch.utils.data import DataLoader, RandomSampler, SequentialSampler
from tqdm import tnrange, tqdm
from pytorch_transformers import ConstantLRSchedule, GPT2Config, GPT2LMHeadModel,AdamW, GPT2Tokenizer, WarmupLinearSchedule
from tensorboardX import SummaryWriter
from dataset import GPT21024Dataset
from utils import add_special_tokens, beam_search, generate_beam_sample, generate_sample, sample_seq, set_seed, top_k_top_p_filtering
在训练示例中,我用分隔符(<|sep|>
)、中间的分隔符(用填充符(<|pad|>
)和另一个分隔符连接源(摘要)和目标(文章),对于 GPT 和 GPT-2,上下文大小分别达到 512 和 1024。这种添加定界符的方法已经在 GPT 的论文中针对不同的 NLP 任务进行了探索,例如文本蕴涵等。可以使用 GPT 记号赋予器的add_special_tokens
方法向其添加新的分隔符或特殊记号:
def add_special_tokens():
""" Returns GPT2 tokenizer after adding separator and padding tokens """
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
special_tokens = {'pad_token':'<|pad|>','sep_token':'<|sep|>'}
num_add_toks = tokenizer.add_special_tokens(special_tokens)
return tokenizer
像 Seq2Seq 模型一样,我也考虑了目标(摘要)序列的交叉熵损失,因为考虑源(文章)和目标序列的交叉熵损失不会改变性能。我忽略了填充标记的损失,这提高了生成的摘要的质量。我尝试在每 15 步后逐层解冻,而不是一次微调所有权重。与完全微调相比,由于逐层解冻,训练和验证损失减少,但是生成的摘要的质量并没有最终更好,这可能是由于过度拟合。我还发现,如果仅在 3000 个例子(文章摘要对)上训练超过 5 个时期,GPT 和 GPT-2 都是过度拟合的。我注意到模型越大,生成的摘要质量越好。GPT-2 345M 生成了最好的摘要。您可以在下面找到一些示例生成的摘要。
我还试验了不同的超参数,如学习率、学习率调度器、优化器、时期数、gradient_accumulation_steps
、max_grad_norm
等。并且发现使用 5e-5 的学习速率,具有 200 个预热步骤的线性预热调度器,AdamW 优化器,总共 5 个时期(超过 5 个导致过拟合),gradient_accumulation_steps
为 32,max_grad_norm
为 1 似乎对于 GPT 和 GPT-2 模型都是最好的。但是,在我看来,仍然可以对超参数优化进行更彻底的分析,可以增加训练数据集的大小来改进模型。下面是我的train
函数,你可以在这里找到完整的训练脚本:
def train(args, model, tokenizer, train_dataset, valid_dataset, ignore_index):
""" Trains GPT2 model and logs necessary details.
Args:
args: dict that contains all the necessary information passed by user while training
model: finetuned gpt/gpt2 model
tokenizer: GPT/GPT2 tokenizer
train_dataset: GPT21024Dataset object for training data
ignore_index: token not considered in loss calculation
"""
writer = SummaryWriter('./logs')
train_sampler = RandomSampler(train_dataset)
train_dl = DataLoader(train_dataset,sampler=train_sampler,batch_size=args.batch_size,num_workers=args.num_workers)
loss_fct = CrossEntropyLoss(ignore_index=ignore_index) #ignores padding token for loss calculation
optimizer = AdamW(model.parameters(),lr=args.lr)
scheduler = WarmupLinearSchedule(optimizer,100,80000)
global_step = 0
tr_loss, logging_loss = 0.0, 0.0
model.zero_grad()
train_iterator = tnrange(int(args.num_train_epochs), desc="Epoch")
set_seed(args)
for _ in train_iterator:
epoch_iterator = tqdm(train_dl, desc="Training")
for step, batch in enumerate(epoch_iterator):
inputs, labels = torch.tensor(batch['article']), torch.tensor(batch['article'])
inputs = inputs.to(args.device)
labels = labels.to(args.device)
model.train()
logits = model(inputs)[0]
idx = batch['sum_idx'].item() # index of separator token
# only consider loss on reference summary just like seq2seq models
shift_logits = logits[..., idx:-1, :].contiguous()
shift_labels = labels[..., idx+1:].contiguous()
loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
loss = loss/args.gradient_accumulation_steps
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm)
tr_loss += loss.item()
if (step + 1) % args.gradient_accumulation_steps == 0:
optimizer.step()
scheduler.step() # Update learning rate schedule
model.zero_grad()
global_step += 1
writer.add_scalar('lr', scheduler.get_lr()[0], global_step)
writer.add_scalar('loss', (tr_loss - logging_loss)/args.gradient_accumulation_steps, global_step)
logging_loss = tr_loss
print("loss:", loss.item(), end='\n\n')
if (step + 1)/args.gradient_accumulation_steps == 1.0:
print('After 1st update: ', end='\n\n')
generate_sample(valid_dataset, tokenizer, num=2, eval_step=False)
if (step + 1) % (10*args.gradient_accumulation_steps) == 0:
results = evaluate(args, model, valid_dataset, ignore_index, global_step)
for key, value in results.items():
writer.add_scalar('eval_{}'.format(key), value, global_step)
print('After', global_step+1,'updates: ', end='\n\n')
generate_sample(valid_dataset, tokenizer, num=2, eval_step=True)
上述train
函数中的大部分代码都是不言自明的。我想指出的一点是,由于 GPT/GPT-2 非常大,我只能在 16GB 的 Nvidia V100 上容纳 1 或 2 的批量大小(取决于型号大小)。因此,为了增加批量大小,我使用了在更新权重之前为 n 步数累积梯度的想法,其中 n 将是我们的批量大小。
在生成摘要时,我分别用不同的top_k
、top_p
、温度和波束宽度值尝试了核采样和波束搜索,发现top_k
= 10、top_p
= 0.5 和温度= 0.8 生成了不错的核采样摘要,而波束宽度为 3 则很适合波束搜索。下面是使用核采样生成给定长度的样本摘要的代码,其中top_k_top_p_filtering
函数执行核过滤。
def top_k_top_p_filtering(logits, top_k=0, top_p=0.0, filter_value=-float('Inf')):
""" Filter a distribution of logits using top-k and/or nucleus (top-p) filtering
Args:
logits: logits distribution shape (vocabulary size)
top_k > 0: keep only top k tokens with highest probability (top-k filtering).
top_p > 0.0: keep the top tokens with cumulative probability >= top_p (nucleus filtering).
Nucleus filtering is described in Holtzman et al. (http://arxiv.org/abs/1904.09751)
From: https://gist.github.com/thomwolf/1a5a29f6962089e871b94cbd09daf317
"""
assert logits.dim() == 1 # batch size 1 for now - could be updated for more but the code would be less clear
top_k = min(top_k, logits.size(-1)) # Safety check
if top_k > 0:
# Remove all tokens with a probability less than the last token of the top-k
indices_to_remove = logits < torch.topk(logits, top_k)[0][..., -1, None]
logits[indices_to_remove] = filter_value
if top_p > 0.0:
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
# Remove tokens with cumulative probability above the threshold
sorted_indices_to_remove = cumulative_probs > top_p
# Shift the indices to the right to keep also the first token above the threshold
sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
sorted_indices_to_remove[..., 0] = 0
indices_to_remove = sorted_indices[sorted_indices_to_remove]
logits[indices_to_remove] = filter_value
return logits
def sample_seq(model, context, length, device, temperature=1, top_k=0, top_p=0.0):
""" Generates a sequence of tokens
Args:
model: gpt/gpt2 model
context: tokenized text using gpt/gpt2 tokenizer
length: length of generated sequence.
device: torch.device object.
temperature >0: used to control the randomness of predictions by scaling the logits before applying softmax.
top_k > 0: keep only top k tokens with highest probability (top-k filtering).
top_p > 0.0: keep the top tokens with cumulative probability >= top_p (nucleus filtering).
"""
context = torch.tensor(context, dtype=torch.long, device=device)
context = context.unsqueeze(0)
generated = context
with torch.no_grad():
for _ in tnrange(length):
inputs = {'input_ids': generated}
outputs = model(**inputs) # Note: we could also use 'past' with GPT-2/Transfo-XL/XLNet (cached hidden-states)
next_token_logits = outputs[0][0, -1, :] / temperature
filtered_logits = top_k_top_p_filtering(next_token_logits, top_k=top_k, top_p=top_p)
next_token = torch.multinomial(F.softmax(filtered_logits, dim=-1), num_samples=1)
generated = torch.cat((generated, next_token.unsqueeze(0)), dim=1)
return generated
Sample generated summary 1
Sample generated summary 2
Sample generated summary 3
限制
在将这种技术应用到现实世界的用例之前,我们必须意识到这种方法的局限性,以及一般的抽象概括模型。抽象摘要技术通常面临生成事实上不正确的摘要,或者语法上正确但没有任何意义的摘要的问题。例如:
伊朗在与美国达成核协议后赢得了世界杯。该协议有望结束伊朗在多年严重制裁下的国际孤立。
在最近由 OpenAI 和 Salesforce (独立)发表的研究中,他们发现在 CNN/Daily Mail 数据集上生成的摘要最多只有 70%的正确率,与使用的模型无关。然而,斯坦福大学和佛罗里达大学最近的一项工作提出了一种补救方法,即使用强化学习,对照参考摘要对生成的摘要进行事实检查。然而,这种方法仍然仅限于少数特定类型的数据集。
结果
经过 5 个时期 3000 个训练数据点的训练(在 Nvidia V100 上可以在 90 分钟内完成),这证明了使用 GPT-2 在小数据集上进行文本摘要的快速有效的方法。随着模型大小的增加,可以很容易地看到生成的摘要的质量的提高。此外,事实的不准确性和摘要的抽象性随着大型模型而减少,这可能是因为大型模型的记忆能力增加了。所生成的摘要表明,与其他文本摘要模型一样,微调后的模型正试图隐式地利用“倒金字塔结构。此外,我注意到在 5 个时期后,摘要的抽象性更差,对于 GPT-2 (345 米)这可能是由于过度拟合。在下面的图 2 中,我展示了不同 GPT 模型生成的摘要的实际准确性之间的比较。
Figure 2. Sample generated summaries from different models. Red color tokens indicate generated words or phrases which were factually incorrect. Green color words or phrases are appropriately copied replacements, and blue color tokens indicate unwanted generated tokens.
尾注
在这篇文章中,我们看到了基于 Transformer 解码器的语言模型,如 GPT/GPT-2,这些模型在大型数据集上进行了预训练,可以很容易地进行微调,以实现仅使用最少数据的抽象摘要的良好结果。这种方法利用了迁移学习的力量,这种力量在 Transformer 架构的许多其他自然语言处理任务中已经看到。由于这种方法需要最少的数据量,它可以应用于各种其他狭窄领域和低资源语言。所提出的方法产生的摘要与输入文档一致(在大多数情况下),并且具有高流畅性,正如基于 GPT 的模型所期望的那样(尽管一些产生的摘要的事实正确性存在问题)。最近由 OpenAI 和 Salesforce 所做的工作表明,这是一个独立于抽象概括模型的普遍问题。这个文本摘要项目的完整代码可以在这里找到。
参考
- 使用单个预先训练的变换器的高效文本摘要示例
- CNN-每日邮报数据集准备
- 符号化的 CNN-每日邮报数据集
- 抱脸变形金刚
5 使用 PyGAD 的遗传算法应用
原文:https://blog.paperspace.com/genetic-algorithm-applications-using-pygad/
本教程介绍 PyGAD,这是一个用于实现遗传算法和训练机器学习算法的开源 Python 库。PyGAD 支持为各种应用定制遗传算法的 19 个参数。
在本教程中,我们将讨论遗传算法的 5 种不同的应用,并使用 PyGAD 构建它们。
教程的大纲如下:
- PyGAD 装置
- PyGAD 入门
- 拟合线性模型
- 再现图像
- 8 皇后拼图
- 训练神经网络
- 训练卷积神经网络
您可以关注这些项目,并在 ML Showcase 上免费运行它们。让我们开始吧。
PyGAD 安装
PyGAD 可以通过 PyPI (Python 包索引)获得,因此可以简单地使用pip
进行安装。对于 Windows,只需使用以下命令:
pip install pygad
对于 Mac/Linux,在终端命令中使用pip3
而不是pip
:
pip3 install pygad
然后,确保通过从 Python shell 导入库来安装库:
python
import pygad
最新的 PyGAD 版本目前是 2.3.2,发布于 2020 年 6 月 1 日。使用__version__
特殊变量,可以返回当前版本。
import pygad
print(pygad.__version__)
现在已经安装了 PyGAD,让我们简单介绍一下 PyGAD。
【PyGAD 入门
PyGAD 的主要目标是提供遗传算法的简单实现。它提供了一系列的参数,允许用户为广泛的应用定制遗传算法。本教程将讨论五个这样的应用。
PyGAD 的完整文档可在阅读文档。在这里,我们将对这个库进行更容易理解的分解。
在 PyGAD 2.3.2 中有 5 个模块:
pygad
:主模块已经导入。
pygad.nn
:用于实现神经网络。
pygad.gann:
用于使用遗传算法训练神经网络。
pygad.cnn
:用于实现卷积神经网络。
pygad.gacnn
:用于使用遗传算法训练卷积神经网络。
每个模块在 GitHub 上都有自己的存储库,链接如下。
- pygad
- pygad . nn
- 皮加恩
- pygad.cnn
- pygad . gann
该库的主模块名为pygad
。这个模块有一个名为GA
的类。只需创建一个pygad.GA
类的实例来使用遗传算法。
使用pygad
模块的步骤如下:
- 创建健身功能。
- 为
pygad.GA
类准备必要的参数。
- 创建一个
pygad.GA
类的实例。
- 运行遗传算法。
在 PyGAD 2.3.2 中,pygad.GA
类的构造函数有 19 个参数,其中 16 个是可选的。三个必需的参数是:
num_generations
:世代数。
num_parents_mating
:选择作为父项的解决方案的数量。
fitness_func
:计算解的适应值的适应函数。
fitness_func
参数允许遗传算法针对不同的问题进行定制。此参数接受用户定义的函数,该函数计算单个解决方案的适合度值。这需要两个额外的参数:解和它在群体中的指数。
让我们看一个例子来让这一点更清楚。假设存在一个具有 3 个解的总体,如下所示。
[221, 342, 213]
[675, 32, 242]
[452, 23, -212]
分配给fitness_func
参数的函数必须返回一个代表每个解的适合度的数字。下面是一个返回解的总和的示例。
def fitness_function(solution, solution_idx):
return sum(solution)
3 种解决方案的适合值为:
- Seven hundred and seventy-six
- Nine hundred and forty-nine
- Two hundred and sixty-three
基于这样的适合度值来选择双亲。适应值越高,解决方案越好。
关于pygad.GA
类构造函数中参数的完整列表,请查看本页。
在创建了一个pygad.GA
类的实例之后,下一步是调用run()
方法,该方法会遍历进化解决方案的代。
import pygad
ga_instance = pygad.GA(...)
ga_instance.run()
这些是使用 PyGAD 的基本步骤。当然,还可以采取其他步骤,但这是最起码的要求。
接下来的部分将讨论在几种不同的用例中使用 PyGAD。
拟合线性模型
假设有一个具有 6 个输入、1 个输出和 6 个参数的方程,如下所示:
y = f(w1:w6) = w1x1 + w2x2 + w3x3 + w4x4 + w5x5 + 6wx6
假设输入为(4,-2,3.5,5,-11,-4.7)
,输出为44
。满足等式的 6 个参数的值是多少?遗传算法可以用来寻找答案。
首先要做的是准备如下给出的适应度函数。它计算每个输入与其相应参数之间的乘积之和。计算期望输出和乘积之和之间的绝对差值。因为适应度函数一定是最大化函数,所以返回的适应度等于1.0/difference
。具有最高适应值的解被选为父解。
function_inputs = [4,-2,3.5,5,-11,-4.7] # Function inputs.
desired_output = 44 # Function output.
def fitness_func(solution, solution_idx):
output = numpy.sum(solution*function_inputs)
fitness = 1.0 / numpy.abs(output - desired_output)
return fitness
既然我们已经准备好了适应度函数,下面是一个包含其他重要参数的列表。
sol_per_pop = 50
num_genes = len(function_inputs)
init_range_low = -2
init_range_high = 5
mutation_percent_genes = 1
您还应该指定您认为合适的所需强制参数。准备好必要的参数后,实例化pygad.GA
类。有关每个参数的信息,请参见本页。
ga_instance = pygad.GA(num_generations=num_generations,
num_parents_mating=num_parents_mating,
fitness_func=fitness_func,
sol_per_pop=sol_per_pop,
num_genes=num_genes,
init_range_low=init_range_low,
init_range_high=init_range_high,
mutation_percent_genes=mutation_percent_genes)
下一步是调用开始生成的run()
方法。
ga_instance.run()
在run()
方法完成后,plot_result()
方法可用于显示各代的适应值。
ga_instance.plot_result()
使用best_solution()
方法,我们还可以检索最佳解决方案是什么,它的适合度,以及它在群体中的索引。
solution, solution_fitness, solution_idx = ga_instance.best_solution()
print("Parameters of the best solution : {solution}".format(solution=solution))
print("Fitness value of the best solution = {solution_fitness}".format(solution_fitness=solution_fitness))
print("Index of the best solution : {solution_idx}".format(solution_idx=solution_idx))
这个项目的完整代码可以在 ML Showcase 上的拟合线性模型笔记本中找到。
再现图像
在这个应用程序中,我们将从随机图像(随机像素值)开始,然后使用遗传算法进化每个像素的值。
这个应用程序的棘手之处在于,图像是 2D 或 3D 的,而遗传算法期望解是 1D 向量。为了解决这个问题,我们将使用下面定义的img2chromosome()
函数将图像转换成 1D 矢量。
def img2chromosome(img_arr):
return numpy.reshape(a=img_arr, newshape=(functools.reduce(operator.mul, img_arr.shape)))
然后可以使用chromosome2img()
功能(如下)从矢量中恢复 2D 或 3D 图像。
def chromosome2img(vector, shape):
# Check if the vector can be reshaped according to the specified shape.
if len(vector) != functools.reduce(operator.mul, shape):
raise ValueError("A vector of length {vector_length} into an array of shape {shape}.".format(vector_length=len(vector), shape=shape))
return numpy.reshape(a=vector, newshape=shape)
除了使用 PyGAD 的常规步骤之外,我们还需要一个额外的步骤来读取图像。
import imageio
import numpy
target_im = imageio.imread('fruit.jpg')
target_im = numpy.asarray(target_im/255, dtype=numpy.float)
这个样本图片可以从这里下载。
接下来,准备健身功能。这将计算解决方案中的像素和目标图像之间的差异。为了使其成为最大化函数,从目标图像中所有像素的总和中减去差值。
target_chromosome = gari.img2chromosome(target_im)
def fitness_fun(solution, solution_idx):
fitness = numpy.sum(numpy.abs(target_chromosome-solution))
# Negating the fitness value to make it increasing rather than decreasing.
fitness = numpy.sum(target_chromosome) - fitness
return fitness
下一步是创建一个pygad.GA
类的实例,如下所示。使用适当的参数对应用程序的成功至关重要。如果目标图像中像素值的范围是 0 到 255,那么init_range_low
和init_range_high
必须分别设置为 0 和 255。原因是用与目标图像相同数据类型的图像初始化群体。如果图像像素值的范围从 0 到 1,那么这两个参数必须分别设置为 0 和 1。
import pygad
ga_instance = pygad.GA(num_generations=20000,
num_parents_mating=10,
fitness_func=fitness_fun,
sol_per_pop=20,
num_genes=target_im.size,
init_range_low=0.0,
init_range_high=1.0,
mutation_percent_genes=0.01,
mutation_type="random",
mutation_by_replacement=True,
random_mutation_min_val=0.0,
random_mutation_max_val=1.0)
当mutation_type
参数设置为random
时,默认行为是为每个选择突变的基因添加一个随机值。该随机值选自random_mutation_min_val
和random_mutation_max_val
参数指定的范围。
假设像素值的范围是 0 到 1。如果像素具有值0.9
并且生成了随机值0.3
,则新的像素值为1.2
。因为像素值必须在 0 到 1 的范围内,所以新像素值是无效的。要解决这个问题,将mutation_by_replacement
参数设置为True
非常重要。这会导致随机值替换当前像素,而不是添加到该像素。
参数准备好后,遗传算法就可以运行了。
ga_instance.run()
plot_result()
方法可以用来显示适应值如何逐代进化。
ga_instance.plot_result()
生成完成后,可以返回一些关于最佳解决方案的信息。
solution, solution_fitness, solution_idx = ga_instance.best_solution()
print("Fitness value of the best solution = {solution_fitness}".format(solution_fitness=solution_fitness))
print("Index of the best solution : {solution_idx}".format(solution_idx=solution_idx))
最佳解决方案可以转换成图像进行显示。
import matplotlib.pyplot
result = gari.chromosome2img(solution, target_im.shape)
matplotlib.pyplot.imshow(result)
matplotlib.pyplot.show()
这是结果。
你可以在 ML Showcase 上免费运行这个项目。
8 皇后拼图
8 皇后拼图包括分布在 8×8 矩阵中的 8 个国际象棋皇后,每行一个皇后。目标是放置这些皇后,使得没有皇后可以垂直、水平或对角地攻击另一个。遗传算法可用于找到满足这些条件的解决方案。
这个项目可以在 GitHub 上获得。它有一个使用 Kivy 构建的 GUI,显示一个 8×8 矩阵,如下图所示。
GUI 在屏幕底部有三个按钮。这些按钮的功能如下:
- 初始群体按钮创建遗传算法的初始群体。
- 显示最佳解决方案按钮显示 GA 停止的上一代的最佳解决方案。
- 启动遗传算法按钮启动遗传算法迭代/世代。
要使用该项目,首先按下初始填充按钮,然后按下开始 GA 按钮。下面是 Initial Population 按钮调用的方法,正如您可能已经猜到的那样,该方法生成初始填充。
def initialize_population(self, *args):
self.num_solutions = 10
self.reset_board_text()
self.population_1D_vector = numpy.zeros(shape=(self.num_solutions, 8))
for solution_idx in range(self.num_solutions):
initial_queens_y_indices = numpy.random.rand(8)*8
initial_queens_y_indices = initial_queens_y_indices.astype(numpy.uint8)
self.population_1D_vector[solution_idx, :] = initial_queens_y_indices
self.vector_to_matrix()
self.pop_created = 1
self.num_attacks_Label.text = "Initial Population Created."
群体中的每个解都是一个具有 8 个元素的向量,涉及 8 个皇后的列索引。为了在屏幕上显示皇后的位置,使用vector_to_matrix()
方法将 1D 向量转换成 2D 矩阵。下一张图显示了屏幕上的女王。
现在 GUI 已经构建好了,我们将使用 PyGAD 构建并运行遗传算法。
本项目中使用的适应度函数如下所示。它只是计算 8 个女王中的每一个可以进行的攻击次数,并将其作为适应值返回。
def fitness(solution_vector, solution_idx):
if solution_vector.ndim == 2:
solution = solution_vector
else:
solution = numpy.zeros(shape=(8, 8))
row_idx = 0
for col_idx in solution_vector:
solution[row_idx, int(col_idx)] = 1
row_idx = row_idx + 1
total_num_attacks_column = attacks_column(solution)
total_num_attacks_diagonal = attacks_diagonal(solution)
total_num_attacks = total_num_attacks_column + total_num_attacks_diagonal
if total_num_attacks == 0:
total_num_attacks = 1.1 # float("inf")
else:
total_num_attacks = 1.0/total_num_attacks
return total_num_attacks
通过按下 Start GA 按钮,创建了一个pygad.GA
类的实例,并调用了run()
方法。
ga_instance = pygad.GA(num_generations=500,
num_parents_mating=5,
fitness_func=fitness,
num_genes=8,
initial_population=self.population_1D_vector,
mutation_percent_genes=0.01,
mutation_type="random",
mutation_num_genes=3,
mutation_by_replacement=True,
random_mutation_min_val=0.0,
random_mutation_max_val=8.0,
callback_generation=callback)
ga_instance.run()
这里有一个可能的解决方案,其中 8 个皇后被放在棋盘上,没有皇后攻击另一个。
这个项目的完整代码可以在 GitHub 上找到。
训练神经网络
在其他类型的机器学习算法中,遗传算法可用于训练神经网络。PyGAD 通过使用pygad.gann.GANN
和pygad.gacnn.GACNN
模块支持训练神经网络,特别是卷积神经网络。本节讨论如何使用pygad.gann.GANN
模块为分类问题训练神经网络。
在构建遗传算法之前,需要准备训练数据。本例构建了一个模拟 XOR 逻辑门的网络。
# Preparing the NumPy array of the inputs.
data_inputs = numpy.array([[1, 1],
[1, 0],
[0, 1],
[0, 0]])
# Preparing the NumPy array of the outputs.
data_outputs = numpy.array([0,
1,
1,
0])
下一步是创建一个pygad.gann.GANN
类的实例。这个类构建了一个具有相同架构的神经网络群体。
num_inputs = data_inputs.shape[1]
num_classes = 2
num_solutions = 6
GANN_instance = pygad.gann.GANN(num_solutions=num_solutions,
num_neurons_input=num_inputs,
num_neurons_hidden_layers=[2],
num_neurons_output=num_classes,
hidden_activations=["relu"],
output_activation="softmax")
在创建了pygad.gann.GANN
类的实例之后,下一步是创建适应度函数。这将返回传递的解决方案的分类精度。
import pygad.nn
import pygad.gann
def fitness_func(solution, sol_idx):
global GANN_instance, data_inputs, data_outputs
predictions = pygad.nn.predict(last_layer=GANN_instance.population_networks[sol_idx],
data_inputs=data_inputs)
correct_predictions = numpy.where(predictions == data_outputs)[0].size
solution_fitness = (correct_predictions/data_outputs.size)*100
return solution_fitness
除了适应度函数之外,还准备了我们之前讨论过的其他必要参数。
population_vectors = pygad.gann.population_as_vectors(population_networks=GANN_instance.population_networks)
initial_population = population_vectors.copy()
num_parents_mating = 4
num_generations = 500
mutation_percent_genes = 5
parent_selection_type = "sss"
crossover_type = "single_point"
mutation_type = "random"
keep_parents = 1
init_range_low = -2
init_range_high = 5
准备好所有参数后,就创建了一个pygad.GA
类的实例。
ga_instance = pygad.GA(num_generations=num_generations,
num_parents_mating=num_parents_mating,
initial_population=initial_population,
fitness_func=fitness_func,
mutation_percent_genes=mutation_percent_genes,
init_range_low=init_range_low,
init_range_high=init_range_high,
parent_selection_type=parent_selection_type,
crossover_type=crossover_type,
mutation_type=mutation_type,
keep_parents=keep_parents,
callback_generation=callback_generation)
callback_generation
参数指的是每次生成后调用的函数。在该应用中,该函数用于在每次生成后更新所有神经网络的权重。
def callback_generation(ga_instance):
global GANN_instance
population_matrices = pygad.gann.population_as_matrices(population_networks=GANN_instance.population_networks, population_vectors=ga_instance.population)
GANN_instance.update_population_trained_weights(population_trained_weights=population_matrices)
下一步是调用run()
方法。
ga_instance.run()
在run()
方法完成之后,下图显示了适应值是如何演变的。该图显示达到了 100%的分类准确度。
构建和训练神经网络的完整代码可以在训练神经网络笔记本的 ML Showcase 上免费访问和运行。
训练卷积神经网络
类似于训练多层感知器,PyGAD 支持使用遗传算法训练卷积神经网络。
第一步是准备训练数据。数据可从以下链接下载:
- dataset_inputs.npy :数据输入。
- dataset_outputs.npy :类标签。
import numpy
train_inputs = numpy.load("dataset_inputs.npy")
train_outputs = numpy.load("dataset_outputs.npy")
下一步是使用pygad.cnn
模块构建 CNN 架构。
import pygad.cnn
input_layer = pygad.cnn.Input2D(input_shape=(80, 80, 3))
conv_layer = pygad.cnn.Conv2D(num_filters=2,
kernel_size=3,
previous_layer=input_layer,
activation_function="relu")
average_pooling_layer = pygad.cnn.AveragePooling2D(pool_size=5,
previous_layer=conv_layer,
stride=3)
flatten_layer = pygad.cnn.Flatten(previous_layer=average_pooling_layer)
dense_layer = pygad.cnn.Dense(num_neurons=4,
previous_layer=flatten_layer,
activation_function="softmax")
堆叠网络中的层后,就创建了一个模型。
model = pygad.cnn.Model(last_layer=dense_layer,
epochs=5,
learning_rate=0.01)
使用summary()
方法,返回模型架构的概要。
----------Network Architecture----------
<class 'cnn.Conv2D'>
<class 'cnn.AveragePooling2D'>
<class 'cnn.Flatten'>
<class 'cnn.Dense'>
----------------------------------------
准备好模型后,实例化pygad.gacnn.GACNN
类以创建初始群体。所有网络都有相同的架构。
import pygad.gacnn
GACNN_instance = pygad.gacnn.GACNN(model=model,
num_solutions=4)
接下来就是准备健身功能了。这将计算通过的解决方案的分类准确度。
def fitness_func(solution, sol_idx):
global GACNN_instance, data_inputs, data_outputs
predictions = GACNN_instance.population_networks[sol_idx].predict(data_inputs=data_inputs)
correct_predictions = numpy.where(predictions == data_outputs)[0].size
solution_fitness = (correct_predictions/data_outputs.size)*100
return solution_fitness
其他参数也准备好了。
population_vectors = pygad.gacnn.population_as_vectors(population_networks=GACNN_instance.population_networks)
initial_population = population_vectors.copy()
num_parents_mating = 2
num_generations = 10
mutation_percent_genes = 0.1
parent_selection_type = "sss"
crossover_type = "single_point"
mutation_type = "random"
keep_parents = -1
准备好所有参数后,就创建了一个pygad.GA
类的实例。
ga_instance = pygad.GA(num_generations=num_generations,
num_parents_mating=num_parents_mating,
initial_population=initial_population,
fitness_func=fitness_func,
mutation_percent_genes=mutation_percent_genes,
parent_selection_type=parent_selection_type,
crossover_type=crossover_type,
mutation_type=mutation_type,
keep_parents=keep_parents,
callback_generation=callback_generation)
callback_generation
参数用于在每次生成后更新网络权重。
def callback_generation(ga_instance):
global GACNN_instance, last_fitness
population_matrices = pygad.gacnn.population_as_matrices(population_networks=GACNN_instance.population_networks, population_vectors=ga_instance.population)
GACNN_instance.update_population_trained_weights(population_trained_weights=population_matrices)
最后一步是调用run()
方法。
ga_instance.run()
构建和训练卷积神经网络的完整代码可以在 ML Showcase 上找到,你也可以从你的免费 Gradient 帐户在免费 GPU 上运行它。
结论
本教程介绍了 PyGAD,这是一个用于实现遗传算法的开源 Python 库。该库支持许多参数来为许多应用定制遗传算法。
在本教程中,我们使用 PyGAD 构建了 5 个不同的应用程序,包括拟合线性模型、解决 8 皇后难题、再现图像和训练神经网络(传统的和卷积的)。我希望你觉得这个教程很有用,如果你有任何问题,请在评论中或者查看文档。
Cythonizing 遗传算法:快 18 倍
原文:https://blog.paperspace.com/genetic-algorithm-python-cython-speed-increase/
在前两个教程中,我们看到了对 Cython 的介绍,这是一种主要为 Python 中使用的变量定义静态数据类型的语言。这提高了 Python 脚本的性能,从而显著提高了速度。例如,当应用于 NumPy 数组时,Cython 完成 10 亿个数的求和比 Python 快 1250 倍。
本教程建立在我们之前讨论的基础上,以加速用 Python 实现遗传算法(GA)的项目的执行。基础项目可以在 GitHub 上获得。我们将检查代码,并按照前两个教程中讨论的说明进行尽可能多的更改以提高性能,并且与 Python 相比,运行各代所需的时间要少得多。
我们将从下载 GitHub 项目开始。然后,我们将着眼于使遗传算法的每一个部分都变得和谐;适应度函数、交配池、交叉和变异。我们还将看到如何用 C-speed 实现不同的 NumPy 函数,并以完整代码的最终实现和与 Python 的比较来结束这篇文章。
注意,你不需要知道遗传算法来完成本教程;我们将仔细检查它的每一部分,你所需要做的就是将 Python 代码具体化,不管它是遗传算法还是其他什么。如果你想知道更多关于遗传算法如何工作的细节,请看我在 LinkedIn 上的其他帖子(在 GitHub 上实现):
- 遗传算法优化简介
- 遗传算法在 Python 中的实现
让我们开始吧。
下载和使用 GitHub 项目
遗传算法的 Python 实现可在 GitHub 页面获得。该项目有两个文件。第一个是 ga.py 文件,它实现了遗传算法操作,包括:
- 使用
cal_pop_fitness()
功能计算适应度函数
- 使用
select_mating_pool()
功能的交配池
- 使用
crossover()
功能进行交叉(实现单点交叉)
- 使用
mutation()
函数进行突变(只有一个基因的值被更新)
第二个文件被命名为Example _ genetic algorithm . py .我们来看一个优化以下等式的基本示例,其中 x 是一个具有 6 个元素的随机输入向量:
y = w1*x1 + w2*x2 + w3*x3 + w4*x4 + w5*x5 + 6w*x6
Example _ genetic algorithm . py脚本准备初始群体并遍历各代。在每一代中,上面列出的 ga.py 中的函数都会被调用。
在本教程中,我们将检查 ga.py 和Example _ genetic algorithm . py脚本的实现,看看我们可以做些什么来减少计算时间。通过运行该项目并删除所有打印语句(这非常耗时),Python 代码需要大约 1.46 秒来完成 10,000 代(在 Core i7-6500U CPU @ 2.5 GHz 上运行,具有 16 GB DDR3 RAM)。
让我们从 ga.py 文件开始。
ga.py 内部的 Cythonizing 功能
在 ga.py 文件里面,第一个函数是cal_pop_fitness()
。这将计算群体中每个个体的适应值。这是遗传算法的第一步。
适应度函数
cal_pop_fitness()
函数接受两个参数:一个有 6 个值的向量(上式中的 x1 到 x6 ),以及将计算适合度值的人口。种群由个体组成,每个个体的长度为 6(因为有 6 个权重, w1 到 w6 ,用于 6 个输入 x1 到 x6 )。例如,如果有 8 个人,那么保存人口的数组的大小是 8 x 6。换句话说,它是一个 2D 阵列(或矩阵)。
该函数通过对每个人的 6 个权重中的每一个与 6 个方程输入之间的乘积求和来计算每个人的适合度值。然后,该函数将所有个体的适应值作为向量返回。
def cal_pop_fitness(equation_inputs, pop):
fitness = numpy.sum(pop*equation_inputs, axis=1)
return fitness
我们怎样才能使这一点变得清晰呢?根据上一篇教程中关于使用 Cython 和 NumPy 的四个技巧,第一步是在函数中处理 NumPy 数组——这已经是事实了。定义函数后,我们需要做的就是定义参数的数据类型、返回数据类型、函数中定义的局部变量的数据类型(可选地,我们也可以禁用不必要的功能,如边界检查)。以下是进行这些更改后的新函数:
import numpy
cimport numpy
import cython
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=1]
cal_pop_fitness(numpy.ndarray[numpy.double_t, ndim=1] equation_inputs, numpy.ndarray[numpy.double_t, ndim=2] pop):
cdef numpy.ndarray[numpy.double_t, ndim=1] fitness
fitness = numpy.sum(pop*equation_inputs, axis=1)
return fitness
在函数外部,Cython 用于调用几个 decoratorss,这些 decorator 禁用三个特性:回绕(因为我们不再使用负索引)、检查 None 值和边界检查。注意,我们只禁用了边界检查,因为我们确信没有索引会超出边界。
通常,我们可以用三种方式在 Cython 中定义函数:
def
:定义一个以 Python 速度工作的函数,因此有点慢。def
关键字可用于定义 Python 或 Cython 脚本中的函数。同样,使用def
定义的函数可以在 Cython/Python 脚本内部或外部调用。
cdef
:这只能在 Cython 脚本中定义,不能从 Python 脚本中调用。它比使用def
定义的函数运行得更快。
cpdef
:这给出了def
和cdef
的优点。该函数只能在 Cython 脚本中定义,但可以从 Cython 或 Python 脚本中调用。cpdef
和cdef
一样快。
因为我们可能会使用 Python 脚本中 Cython 脚本内部定义的所有函数,所以我们将使用cpdef
关键字来定义所有函数。
正好在 cpdef 之后,函数的返回数据类型被设置为numpy.ndarray[numpy.double_t, ndim=1]
。这意味着该函数将返回一个类型为numpy.ndarray
的变量。数组中元素的类型也使用numpy.double_t
设置为 double。最后,使用 ndim 参数将维数设置为 1,因为会返回一个 1D 数组(向量)。请注意,如果返回类型中定义的维数与实际返回的数据不匹配,将会引发异常。
接下来,定义两个参数的数据类型。它们都是numpy.ndarray
,元素类型是double
。第一个参数是一维的,而第二个参数是二维的。
现在函数头已经完全定义好了。在函数内部有一个局部变量,即适应度向量。它的定义与第一个函数自变量相同。最后,返回一维数组。
在这一点上,cal_pop_fitness()
被同步化;它不如 Python 可读,但现在速度更快了。
交配池
下一个函数select_mating_pool()
在 Python 中实现如下:
def select_mating_pool(pop, fitness, num_parents):
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = numpy.where(fitness == numpy.max(fitness))
max_fitness_idx = max_fitness_idx[0][0]
parents[parent_num, :] = pop[max_fitness_idx, :]
fitness[max_fitness_idx] = -99999999999
return parents
下面是 Cython 版本。您可以很容易理解 Cython 函数,因为它与 Python 版本没有太大区别。这个函数返回由多个个体组成的交配池。结果,返回的数组是 2D,因此 ndim 在返回数据类型中被设置为 2。函数中有 6 个局部变量,每个变量都是使用 cdef 关键字定义的。请注意,NumPy 数组的切片和索引与 Python 中的一样。遍历数组也使用索引,这是更快的方法。
import numpy
cimport numpy
import cython
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] select_mating_pool(numpy.ndarray[numpy.double_t, ndim=2] pop, numpy.ndarray[numpy.double_t, ndim=1] fitness, int num_parents):
cdef numpy.ndarray[numpy.double_t, ndim=2] parents
cdef int parent_num, max_fitness_idx, min_val, max_fitness
min_val = -999999
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = numpy.where(fitness == numpy.max(fitness))[0][0]
parents[parent_num, :] = pop[max_fitness_idx, :]
fitness[max_fitness_idx] = min_val
return parents
交叉
下一个函数是crossover()
,下面用 Python 定义。
def crossover(parents, offspring_size):
offspring = numpy.empty(offspring_size)
crossover_point = numpy.uint8(offspring_size[1]/2)
for k in range(offspring_size[0]):
parent1_idx = k%parents.shape[0]
parent2_idx = (k+1)%parents.shape[0]
offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]
offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]
return offspring
Cython 版本如下。注意,wraparound()
装饰器被设置为 True,因为这里需要负索引。另请注意,_ size 参数的类型是 tuple,因此您必须同样提供该参数。任何不匹配都会导致错误。
因为crossover_point
局部变量被定义为一个整数变量,所以我们使用numpy.uint8()
来加强这一点并防止任何错误。该函数的其余部分与 Python 中的完全相同。请注意,稍后仍有一些更改要做,我们将把一些耗时的操作替换为耗时较少的操作。
import numpy
cimport numpy
import cython
@cython.wraparound(True)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] crossover(numpy.ndarray[numpy.double_t, ndim=2] parents, tuple offspring_size):
cdef numpy.ndarray[numpy.double_t, ndim=2] offspring
offspring = numpy.empty(offspring_size)
cdef int k, parent1_idx, parent2_idx
cdef numpy.int_t crossover_point
crossover_point = numpy.uint8(offspring_size[1]/2)
for k in range(offspring_size[0]):
parent1_idx = k%parents.shape[0]
parent2_idx = (k+1)%parents.shape[0]
offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]
offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]
return offspring
变化
ga.py 文件中的最后一个函数是mutation()
,用 Python 显示如下:
def mutation(offspring_crossover, num_mutations=1):
mutations_counter = numpy.uint8(offspring_crossover.shape[1] / num_mutations)
for idx in range(offspring_crossover.shape[0]):
gene_idx = mutations_counter - 1
for mutation_num in range(num_mutations):
random_value = numpy.random.uniform(-1.0, 1.0, 1)
offspring_crossover[idx, gene_idx] = offspring_crossover[idx, gene_idx] + random_value
gene_idx = gene_idx + mutations_counter
return offspring_crossover
下面是 cythonized 版本。它遵循我们之前看到的步骤:禁用未使用的特性,使用cpdef
而不是def
,声明参数、返回值和局部变量的数据类型。因为不需要负索引,所以该功能禁用负索引。
import numpy
cimport numpy
import cython
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] mutation(numpy.ndarray[numpy.double_t, ndim=2] offspring_crossover, int num_mutations=1):
cdef int idx, mutation_num, gene_idx
cdef double random_value
cdef Py_ssize_t mutations_counter
mutations_counter = numpy.uint8(offspring_crossover.shape[1] / num_mutations)
for idx in range(offspring_crossover.shape[0]):
gene_idx = mutations_counter - 1
for mutation_num in range(num_mutations):
random_value = numpy.random.uniform(-1.0, 1.0, 1)
offspring_crossover[idx, gene_idx] = offspring_crossover[idx, gene_idx] + random_value
gene_idx = gene_idx + mutations_counter
return offspring_crossover
我们已经完成了 cytonizationga . py!下面列出了新的完整代码。只需将这段代码保存到一个名为ga . pyx的文件中,我们将在构建中构建它。使用 setup.py 文件的 pyx 文件部分。**
import numpy
cimport numpy
import time
import cython
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=1] cal_pop_fitness(numpy.ndarray[numpy.double_t, ndim=1] equation_inputs, numpy.ndarray[numpy.double_t, ndim=2] pop):
cdef numpy.ndarray[numpy.double_t, ndim=1] fitness
fitness = numpy.sum(pop*equation_inputs, axis=1)
return fitness
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] select_mating_pool(numpy.ndarray[numpy.double_t, ndim=2] pop, numpy.ndarray[numpy.double_t, ndim=1] fitness, int num_parents):
cdef numpy.ndarray[numpy.double_t, ndim=2] parents
cdef int parent_num, max_fitness_idx, min_val, max_fitness, a
min_val = -99999999999
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = numpy.where(fitness == numpy.max(fitness))[0][0]
parents[parent_num, :] = pop[max_fitness_idx, :]
fitness[max_fitness_idx] = min_val
return parents
@cython.wraparound(True)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] crossover(numpy.ndarray[numpy.double_t, ndim=2] parents, tuple offspring_size):
cdef numpy.ndarray[numpy.double_t, ndim=2] offspring
offspring = numpy.empty(offspring_size)
cdef int k, parent1_idx, parent2_idx
cdef numpy.int_t crossover_point
crossover_point = numpy.uint8(offspring_size[1]/2)
for k in range(offspring_size[0]):
parent1_idx = k%parents.shape[0]
parent2_idx = (k+1)%parents.shape[0]
offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]
offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]
return offspring
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] mutation(numpy.ndarray[numpy.double_t, ndim=2] offspring_crossover, int num_mutations=1):
cdef int idx, mutation_num, gene_idx
cdef double random_value
cdef Py_ssize_t mutations_counter
mutations_counter = numpy.uint8(offspring_crossover.shape[1] / num_mutations)
for idx in range(offspring_crossover.shape[0]):
gene_idx = mutations_counter - 1
for mutation_num in range(num_mutations):
random_value = numpy.random.uniform(-1.0, 1.0, 1)
offspring_crossover[idx, gene_idx] = offspring_crossover[idx, gene_idx] + random_value
gene_idx = gene_idx + mutations_counter
return offspring_crossover
第二个文件Example _ genetic algorithm . py,调用在 ga.py 文件中定义的函数。让我们在运行 GA 之前完成第二个文件。
Cythonizing 示例 _GeneticAlgorithm.py
Example _ genetic algorithm . py文件的 Python 实现如下。导入了时间模块,因此我们可以比较 Python 和 Cython 的性能。
`import numpy
import ga
import time
equation_inputs = [4,-2,3.5,5,-11,-4.7]
num_weights = len(equation_inputs)
sol_per_pop = 8
num_parents_mating = 4
pop_size = (sol_per_pop,num_weights)
new_population = numpy.random.uniform(low=-4.0, high=4.0, size=pop_size)
best_outputs = []
num_generations = 10000
t1 = time.time()
for generation in range(num_generations):
fitness = ga.cal_pop_fitness(equation_inputs, new_population)
best_outputs.append(numpy.max(numpy.sum(new_population*equation_inputs, axis=1)))
parents = ga.select_mating_pool(new_population, fitness,
num_parents_mating)
offspring_crossover = ga.crossover(parents,
offspring_size=(pop_size[0]-parents.shape[0], num_weights))
offspring_mutation = ga.mutation(offspring_crossover, num_mutations=2)
new_population[0:parents.shape[0], :] = parents
new_population[parents.shape[0]:, :] = offspring_mutation
t2 = time.time()
t = t2-t1
print("Total Time %.20f" % t)`
下面列出了 cythonized 代码。 ga 模块作为常规 Python 模块导入。你所要做的就是声明所有变量的数据类型。只需注意将传递的变量与之前编辑的函数所接受的类型相匹配。
`import ga
import numpy
cimport numpy
import time
cdef numpy.ndarray equation_inputs, parents, new_population, fitness, offspring_crossover, offspring_mutation
cdef int num_weights, sol_per_pop, num_parents_mating, num_generations
cdef tuple pop_size
cdef double t1, t2, t
equation_inputs = numpy.array([4,-2,3.5,5,-11,-4.7])
num_weights = equation_inputs.shape[0]
num_weights = equation_inputs.shape[0]
num_parents_mating = 4
sol_per_pop = 8
num_parents_mating = 4
pop_size = (sol_per_pop, num_weights)
new_population = numpy.random.uniform(low=-4.0, high=4.0, size=pop_size)
num_generations = 10000
t1 = time.time()
for generation in range(num_generations):
fitness = ga.cal_pop_fitness(equation_inputs, new_population)
parents = ga.select_mating_pool(new_population, fitness,
num_parents_mating)
offspring_crossover = ga.crossover(parents,
offspring_size=(pop_size[0]-parents.shape[0], num_weights))
offspring_mutation = ga.mutation(offspring_crossover, num_mutations=2)
new_population[0:parents.shape[0], :] = parents
new_population[parents.shape[0]:, :] = offspring_mutation
t2 = time.time()
t = t2-t1
print("Total Time %.20f" % t)`
我们只能将numpy.ndarray
数据类型赋给 NumPy 变量,仅此而已。我们无法指定维度的数量或元素的数据类型,因为 Cython 尚不支持这些功能。然而,如果代码被打包成一个函数,那么我们就可以定义一切并加快处理速度。我们将在以后做这方面的工作。
现在,只需将 Cython 代码保存到名为Example _ genetic algorithm . pyx的文件中,该文件将与 ga.pyx 文件一起构建。
构建。pyx 文件
下一步是建造。pyx 文件生成。pyd / 。所以 文件要导入到项目中。下面列出了用于此目的的 setup.py 文件。因为有两个。pyx 文件,函数cythonize()
没有给出明确的名称,而是要求用构建所有文件。pyx 扩展。
`import distutils.core
import Cython.Build
import numpy
distutils.core.setup(
ext_modules = Cython.Build.cythonize("*.pyx"),
include_dirs=[numpy.get_include()]
)`
为了构建文件,从命令行发出下面的命令。
`python setup.py build_ext --inplace`
命令成功完成后,我们可以使用下面的命令导入Example _ genetic algorithm . pyx文件。这将自动运行代码。
`import Example_GeneticAlgorithm`
Cython 代码完成需要 0.945 秒。与 Python 代码的 1.46 秒相比;Cython 比 T4 快 1.55 倍(注意,代码是在一台配备酷睿 i7-6500U CPU @ 2.5 GHz 和 16 GB DDR3 RAM 的机器上运行的)。
为了进一步减少时间,我们可以做一个简单的编辑:使用一个函数包装Example _ genetic algorithm . pyx文件的内容。****
在函数和脚本体中进化代
让我们在Example _ genetic algorithm . pyx中创建一个名为optimize()
的函数,并将该文件的内容放入我们的新函数中:**
**`import ga
import numpy
cimport numpy
import time
import cython
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef optimize():
cdef numpy.ndarray equation_inputs, parents, new_population, fitness, offspring_crossover, offspring_mutation
cdef int num_weights, sol_per_pop, num_parents_mating, num_generations
cdef list pop_size
cdef double t1, t2, t
equation_inputs = numpy.array([4,-2,3.5,5,-11,-4.7])
num_weights = equation_inputs.shape[0]
sol_per_pop = 8
num_weights = equation_inputs.shape[0]
num_parents_mating = 4
pop_size = [sol_per_pop,num_weights]
#Creating the initial population.
new_population = numpy.random.uniform(low=-4.0, high=4.0, size=pop_size)
num_generations = 1000000
t1 = time.time()
for generation in range(num_generations):
fitness = cal_pop_fitness(equation_inputs, new_population)
parents = select_mating_pool(new_population, fitness,
num_parents_mating)
offspring_crossover = crossover(parents,
offspring_size=(pop_size[0]-parents.shape[0], num_weights))
offspring_mutation = mutation(offspring_crossover, num_mutations=2)
new_population[0:parents.shape[0], :] = parents
new_population[parents.shape[0]:, :] = offspring_mutation
t2 = time.time()
t = t2-t1
print("Total Time %.20f" % t)
print(cal_pop_fitness(equation_inputs, new_population))`**
为了调用optimize()
函数,只需重新构建 Cython 即可。pyx 文件,并从命令行发出以下 Python 命令:
**`import Example_GeneticAlgorithm
Example_GeneticAlgorithm.optimize()`**
现在只需要 0.944 秒(T2)而不是 0.945 秒(T4);几乎没有任何变化。一个原因是由于调用外部模块 ga 用于每个所需的功能。相反,我们将通过在 ga.pyx 文件中复制并粘贴optimize()
函数来保存函数调用。因为这些函数是同一个文件的一部分,所以调用它们的开销较少。
因为optimize()
函数现在是 ga.pyx 文件的一部分,我们不再需要Example _ genetic algorithm . pyx文件。您可以编辑 setup.py 文件,指定只构建 ga.pyx 文件。**
以下命令用于调用优化()函数。时间现在是 0.9 秒,而不是 T2 的 0.944 秒,因此 Cython 代码现在比 Python 快了 T4 的 1.62 倍。
**`import ga
ga.optimize()`**
现在所有的代码都已经被同步化了,但是还可以做更多的工作来提高速度。让我们看看如何使用 C 函数,而不是 Python 函数——这将带来迄今为止最大的速度提升。
用 C 语言实现 Python 特性
Python 使得许多事情对程序员来说更容易,这是它的好处之一。但这在某些情况下会增加时间。在这一节中,我们将检查 Python 中可用但速度较慢的一些函数,并了解如何实现它们以 C 语言速度运行。
用 C 语言实现 NumPy sum()
在 cal_pop_fitness() 函数中,使用 numpy.sum() 函数计算每个个体与方程输入之间的乘积之和。我们可以根据下面的代码使用 2 for 循环手动实现这个函数。注意,循环以 C 速度运行。由于这个原因,变量适应度被声明为 numpy.ndarray 类型,并使用 numpy.zeros() 初始化为零数组。计算适应值的结果保存在该变量中。
**`@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef cal_pop_fitness(numpy.ndarray[numpy.double_t, ndim=1] equation_inputs, numpy.ndarray[numpy.double_t, ndim=2] pop):
cdef numpy.ndarray[numpy.double_t, ndim=1] fitness
fitness = numpy.zeros(pop.shape[0])
# fitness = numpy.sum(pop*equation_inputs, axis=1) # slower than looping.
for i in range(pop.shape[0]):
for j in range(pop.shape[1]):
fitness[i] += pop[i, j]*equation_inputs[j]
return fitness`**
编辑完成后,我们可以构建。pyx 文件,看看新代码有多快。使用上述功能后的新代码只需要 0.8 秒。因此,使用循环实现 numpy.sum() 函数节省了 0.1 秒( 100 毫秒)。让我们考虑一下其他需要优化的地方。
在select _ matting _ pool()函数中,健康数组中最大元素的索引是使用以下代码行返回的。
**`max_fitness_idx = numpy.where(fitness == numpy.max(fitness))[0][0]`**
我们可以使用下面的循环来编辑函数,以 C 速度实现这一行。通过这样做,执行时间现在是 0.44 秒,而不是 0.8 秒。与 Python 相比,Cython 现在快了 3.32 倍。
**`@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] select_mating_pool(numpy.ndarray[numpy.double_t, ndim=2] pop, numpy.ndarray[numpy.double_t, ndim=1] fitness, int num_parents):
cdef numpy.ndarray[numpy.double_t, ndim=2] parents
cdef int parent_num, max_fitness_idx, min_val, max_fitness, a
min_val = -99999999999
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = 0
# numpy.where(fitness == numpy.max(fitness))
for a in range(1, fitness.shape[0]):
if fitness[a] > fitness[max_fitness_idx]:
max_fitness_idx = a
parents[parent_num, :] = pop[max_fitness_idx, :]
fitness[max_fitness_idx] = min_val
return parents`**
C 速度下的 NumPy 数组切片
切片只是将数组的一部分返回到另一个数组中。我们可以在 Cython 中为下面列出的新函数中的parents
和pop
实现这一点。通过这样做,Cython 只需要 0.427 秒,而不是 0.44 秒。
**`@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] select_mating_pool(numpy.ndarray[numpy.double_t, ndim=2] pop, numpy.ndarray[numpy.double_t, ndim=1] fitness, int num_parents):
cdef numpy.ndarray[numpy.double_t, ndim=2] parents
cdef int parent_num, max_fitness_idx, min_val, max_fitness, a
min_val = -99999999999
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = 0
# numpy.where(fitness == numpy.max(fitness))
for a in range(1, fitness.shape[0]):
if fitness[a] > fitness[max_fitness_idx]:
max_fitness_idx = a
# parents[parent_num, :] = pop[max_fitness_idx, :] # slower han looping by 20 ms
for a in range(parents.shape[1]):
parents[parent_num, a] = pop[max_fitness_idx, a]
fitness[max_fitness_idx] = min_val
return parents`**
因为切片也在crossover()
函数中使用,所以我们可以编辑它,使用以 C 速度运行的循环来实现数组切片。新函数如下,耗时 0.344 秒而不是 0.427 秒。这些变化可能看起来很小,但是当您运行数百或数千行代码时,它们会产生巨大的影响。此时,这个函数的运行速度是 Python 的 4.24 倍。
分配给crossover_point
变量的值之前已使用numpy.uint8()
进行了转换。现在,它被转换成 C 风格使用(int)
。
**`@cython.wraparound(True)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] crossover(numpy.ndarray[numpy.double_t, ndim=2] parents, tuple offspring_size):
cdef numpy.ndarray[numpy.double_t, ndim=2] offspring
offspring = numpy.empty(offspring_size)
cdef int k, parent1_idx, parent2_idx
cdef numpy.int_t crossover_point
crossover_point = (int) (offspring_size[1]/2)
for k in range(offspring_size[0]):
parent1_idx = k%parents.shape[0]
parent2_idx = (k+1)%parents.shape[0]
for m in range(crossover_point):
offspring[k, m] = parents[parent1_idx, m]
for m in range(crossover_point-1, -1):
offspring[k, m] = parents[parent2_idx, m]
# The next 2 lines are slower than using the above loops because they run with C speed.
# offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]
# offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]
return offspring`**
在 C 中生成随机值
mutation()
函数使用numpy.random.uniform()
函数返回添加到基因中的随机双精度值:
**`random_value = numpy.random.uniform(-1.0, 1.0, 1)`**
我们可以避免使用这个函数,而是使用 c 语言的stdlib
库中的rand()
函数来生成随机数。mutation()
函数的实现就变成了:
**`from libc.stdlib cimport rand, RAND_MAX
cdef double DOUBLE_RAND_MAX = RAND_MAX # a double variable holding the maximum random integer in C
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] mutation(numpy.ndarray[numpy.double_t, ndim=2] offspring_crossover, int num_mutations=1):
cdef int idx, mutation_num, gene_idx
cdef double random_value
cdef Py_ssize_t mutations_counter
mutations_counter = (int) (offspring_crossover.shape[1] / num_mutations) # using numpy.uint8() is slower than using (int)
cdef double rand_num
for idx in range(offspring_crossover.shape[0]):
gene_idx = mutations_counter - 1
for mutation_num in range(num_mutations):
# random_value = numpy.random.uniform(-1.0, 1.0, 1)
rand_double = rand()/DOUBLE_RAND_MAX
random_value = rand_double * (1.0 - (-1.0)) + (-1.0)
offspring_crossover[idx, gene_idx] = offspring_crossover[idx, gene_idx] + random_value
gene_idx = gene_idx + mutations_counter
return offspring_crossover`**
首先,rand()
函数是从stdlib
导入的,这样我们就可以在 c 中访问这个函数,rand()
返回一个 0 到 RAND_MAX 范围内的整数值,它是一个常数(它的值至少是 32767)。因为我们希望随机数在 0 到 1 的范围内,所以我们需要将返回的随机值除以最大可能的随机整数。我们通过将 RAND_MAX 复制到一个名为 double_RAND_MAX 的 DOUBLE 变量中,并将随机数除以这个值来实现这一点。缩放后的随机值现在在rand_double
变量中可用。然后进行缩放,使其落在-1 到 1 的范围内,并保存在random_value
变量中。
通过使用 C rand()
函数生成随机值,Cython 现在只需要 0.08 秒(80 毫秒)就可以运行。与之前的 0.344 秒相比。这是迄今为止最大的不同。现在代码运行速度比 Python 快 18.25 倍。
现在我们已经完成了所有的编辑,完整的 ga.pyx 文件如下所示:
**`import numpy
cimport numpy
import time
import cython
from libc.stdlib cimport rand, RAND_MAX
cdef double DOUBLE_RAND_MAX = RAND_MAX # a double variable holding the maximum random integer in C
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef cal_pop_fitness(numpy.ndarray[numpy.double_t, ndim=1] equation_inputs, numpy.ndarray[numpy.double_t, ndim=2] pop):
cdef numpy.ndarray[numpy.double_t, ndim=1] fitness
fitness = numpy.zeros(pop.shape[0])
# fitness = numpy.sum(pop*equation_inputs, axis=1) # slower than looping.
for i in range(pop.shape[0]):
for j in range(pop.shape[1]):
fitness[i] += pop[i, j]*equation_inputs[j]
return fitness
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] select_mating_pool(numpy.ndarray[numpy.double_t, ndim=2] pop, numpy.ndarray[numpy.double_t, ndim=1] fitness, int num_parents):
cdef numpy.ndarray[numpy.double_t, ndim=2] parents
cdef int parent_num, max_fitness_idx, min_val, max_fitness, a
min_val = -99999999999
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = 0
# numpy.where(fitness == numpy.max(fitness)) # slower than looping by 250 ms.
for a in range(1, fitness.shape[0]):
if fitness[a] > fitness[max_fitness_idx]:
max_fitness_idx = a
# parents[parent_num, :] = pop[max_fitness_idx, :]
for a in range(parents.shape[1]):
parents[parent_num, a] = pop[max_fitness_idx, a]
fitness[max_fitness_idx] = min_val
return parents
@cython.wraparound(True)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] crossover(numpy.ndarray[numpy.double_t, ndim=2] parents, tuple offspring_size):
cdef numpy.ndarray[numpy.double_t, ndim=2] offspring
offspring = numpy.empty(offspring_size)
cdef int k, parent1_idx, parent2_idx
cdef numpy.int_t crossover_point
crossover_point = (int) (offspring_size[1]/2)
for k in range(offspring_size[0]):
parent1_idx = k%parents.shape[0]
parent2_idx = (k+1)%parents.shape[0]
for m in range(crossover_point):
offspring[k, m] = parents[parent1_idx, m]
for m in range(crossover_point-1, -1):
offspring[k, m] = parents[parent2_idx, m]
# The next 2 lines are slower than using the above loops because they run with C speed.
# offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]
# offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]
return offspring
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef numpy.ndarray[numpy.double_t, ndim=2] mutation(numpy.ndarray[numpy.double_t, ndim=2] offspring_crossover, int num_mutations=1):
cdef int idx, mutation_num, gene_idx
cdef double random_value
cdef Py_ssize_t mutations_counter
mutations_counter = (int) (offspring_crossover.shape[1] / num_mutations) # using numpy.uint8() is slower than using (int)
cdef double rand_num
for idx in range(offspring_crossover.shape[0]):
gene_idx = mutations_counter - 1
for mutation_num in range(num_mutations):
# random_value = numpy.random.uniform(-1.0, 1.0, 1)
rand_double = rand()/DOUBLE_RAND_MAX
random_value = rand_double * (1.0 - (-1.0)) + (-1.0)
offspring_crossover[idx, gene_idx] = offspring_crossover[idx, gene_idx] + random_value
gene_idx = gene_idx + mutations_counter
return offspring_crossover
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.boundscheck(False)
cpdef optimize():
cdef numpy.ndarray equation_inputs, parents, new_population, fitness, offspring_crossover, offspring_mutation
cdef int num_weights, sol_per_pop, num_parents_mating, num_generations
cdef list pop_size
cdef double t1, t2, t
equation_inputs = numpy.array([4,-2,3.5,5,-11,-4.7])
num_weights = equation_inputs.shape[0]
sol_per_pop = 8
num_weights = equation_inputs.shape[0]
num_parents_mating = 4
pop_size = [sol_per_pop,num_weights]
#Creating the initial population.
new_population = numpy.random.uniform(low=-4.0, high=4.0, size=pop_size)
num_generations = 10000
t1 = time.time()
for generation in range(num_generations):
fitness = cal_pop_fitness(equation_inputs, new_population)
parents = select_mating_pool(new_population, fitness,
num_parents_mating)
offspring_crossover = crossover(parents,
offspring_size=(pop_size[0]-parents.shape[0], num_weights))
offspring_mutation = mutation(offspring_crossover, num_mutations=2)
new_population[0:parents.shape[0], :] = parents
new_population[parents.shape[0]:, :] = offspring_mutation
t2 = time.time()
t = t2-t1
print("Total Time %.20f" % t)
print(cal_pop_fitness(equation_inputs, new_population))`**
结论
本教程使用 Cython 来减少使用 NumPy 的遗传算法 Python 实现的执行时间。我们将计算时间从 1.46 秒缩短到仅仅 0.08 秒,速度提高了 18 倍。因此,使用 Cython,我们可以在不到 10 秒的时间内完成 100 万次生成,而 Python 需要 180 秒。
同样的方法可以用于任何用 Python 编写的代码;一行一行地检查它,找出瓶颈,并通过实现我们在这里看到的技巧来减少计算时间。您不一定需要了解 C,但是了解 C 显然会帮助您实现更快的解决方法。即使没有对 C 语言的深刻理解,在运行长代码或计算量大的代码时,像定义变量类型这样的简单技巧也能产生很大的影响。
几何深度学习库比较
原文:https://blog.paperspace.com/geometric-deep-learning-framework-comparison/
在我们上一篇介绍几何深度学习的帖子中,我们将该主题置于当前深度学习淘金热的背景下。至关重要的是,我们概述了是什么使 GDL 在其潜力方面脱颖而出。对于这篇文章来说,知道 GDL 是在不规则的数据结构上执行的深度学习就足够了,例如图形,网格和点云。
我们还展示了相关的任务,在这些任务上,采用 GDL 方法可以实现最先进的性能。尽管在各种任务上表现良好,但 GDL 的不规则数据结构不容易适应现有的深度学习框架。这种不适合需要开发新的库来进行有效和高效的计算。我们将简要介绍 GDL 图书馆如何对这种不规则数据和计算建模的一些相关细节,重点关注这对最终用户的影响。
最后,正如文章标题所承诺的,我们将简要讨论和比较 GDL 目前的主要图书馆。基于示例性用例,通过形态分析来执行比较。我们提供了用于此分析的工具,您可以根据您的使用情形和需求轻松定制它们。最后,我们将讨论这两个库之间的一些关键差异,并给出三种典型用例使用哪个库的建议:科学研究、生产级开发和业余爱好。
这些部分如下:
- 实施细节
- PyTorch 几何
- 深度图形库
- 图形网
- 形态分析
- 荣誉奖
- 主要差异
- 我应该选择哪一个?
介绍
深度学习的进步引发了许多创新,其中之一是专门针对该领域的软件开发框架的创建。框架和库的列表是广泛的,它们的主要焦点各不相同,但是它们之间仍然有共同点。大多数都提供了一组基本的层和功能,支持 CPU 上的多线程和将并行计算卸载到 GPU,同时依靠自动微分来快速计算反向传播的梯度。
但是深度学习框架在多大程度上渗透了软件开发过程?为了回答这个问题,我们需要一个这些框架相关性的代理。对于软件开发人员来说,不管他们的背景如何,使用搜索引擎来帮助在项目前和项目期间收集信息是司空见惯的,我们将使用 Google Trends 作为相关性的代理。我们将搜索词与搜索主题进行了比较,因此请记住,在后者中隐藏了更多的术语(例如,对于 C 编程语言)。下图中的值是标准化的网络搜索,以便更好地比较不同的主题,并在全球范围内汇总。从图表中可以明显看出,人们对此很感兴趣,如果考虑到我们正在将框架与通用编程语言进行比较,这种兴趣就更大了。
Google Trends for "PyTorch" and "TensorFlow" search terms, and "C" search topic (programming language) / Google Trends Query
有趣的是,当接近前一个查询的特定国家时,这幅图发生了很大的变化。在中国,上面的框架甚至占了上风,如下图所示。
由于百度在中国拥有更大的市场份额,而且这些结果只涉及谷歌网络搜索,我们不得不对此数据持保留态度。此外,还有替代的、同样有价值的代理,例如来自 StackOverflow 或 GitHub 公共存储库数据的调查数据。网络搜索数据的优势在于,它非常接近人们对什么感兴趣的无偏见观点。一项调查有太多的偏差,在解释数据时必须加以考虑。关于 GitHub,仅仅依靠公开数据会扭曲我们的观点,因为这将只包含开源项目,而开源项目只占整个画面的一小部分。
GDL 图书馆在深度学习框架中表现如何?
Left: Google Trends for "pytorch geometric", "deep graph library", and "graph nets" search terms / Google Trends Query | Right: Google Trends as in Left graph with additional "tensorflow" search term / Google Trends Query
从上面的图表中,很明显,在深度学习社区中,GDL 图书馆仍然处于早期采用的微小利基中,正如几乎没有网络搜索的时期所证明的那样。在与 TensorFlow 的对比图中,他们的网络搜索几乎没有进展。关于单个 GDL 图书馆之间的兴趣,从 2019 年初开始,竞争和整体兴趣值明显上升。从 2019 年 5 月起,利率也将略微保持不变。这种步伐的改变并非巧合,因为 PyTorch Geometric 和 Deep Graph Library 都在当时的研讨会上正式推出,并附有研讨会论文出版物。
考虑到谷歌的趋势信息,为什么还要麻烦 GDL 呢?
好吧,你是想成为第 10 万个进行神经风格转换的人,还是走在深度学习发展的前沿?如果你对后者的回答是肯定的,请继续读下去。
GDL 图书馆
布朗斯坦等人在 2017 年的开创性工作中提到,GDL 的计算框架是 GDL 领域的未来需求之一。[3]
从下图中可以看出,这篇文章所围绕的库是在不久之后开发的。PyTorch 几何的命名也凸显了文章的影响。
这里有两件事值得注意:
- 新近性:这些库已经开发了两年多了;这对于一个同样年轻的领域来说,不算什么。
- 受欢迎程度:PyTorch Geometric 已经拥有其母框架 PyTorch 收集的 GitHub 星星的五分之一。这与我们的谷歌趋势结果相矛盾吗?一点也不,可能会有一个很大的早期用户群体。
在接下来的章节中,我们将简要介绍这些库的实现细节,总结每一个,执行一个典型的形态分析,并为您提供必要的工具来执行您的特定用例。
Development of GitHub stars / CodeTabs
实施细节
Or: How to efficiently exploit these data structures for deep learning?
当前的框架针对在欧几里德网格状数据上运行算法进行了优化,通过使用 GPU 或其他特定硬件来加速这些操作。现有的方法之一是尝试将类似图形的数据放入符合当前优化操作的数据结构中,如 Conv2D。例如,在“mesh CNN:a network with a edge”中,Hanocka 等人将网格和它们使用的特征转换为张量,以便能够使用 PyTorch 的正常操作。[7]
当然,这种方法会产生数据争论的开销,并且不能保证最佳性能。此外,如果试图遵循不是为这些数据结构开发的工具的要求,人们可能会错过创新的改进。巴塔格利亚等人引入的全局图属性,对于表示多体机械系统的图来说可能是引力场,就是一个很好的例子。
消息传递方案
在这个比较中提到的所有库的一个共同属性是,它们依赖于消息传递方案(如 Gilmer 等人所介绍的)来对图执行卷积和其他操作。[2]为了深入了解这到底意味着什么,巴塔格利亚等人和费等人提供了详细的解释,我建议你去看看。[1][4]此外,对于那些通过例子学得更好的人来说,这里有一个关于这个方案在实践中如何通过知识图工作的详细解释。
关于这些库的实现细节,在这种情况下还有一件更重要的事情需要注意。GPU 上的聚集分散操作用于加速遵循消息传递方案接口的方法。在技术实现上,它固有地存在一些缺点。通常,当使用随机过程执行实验时,最好为随机数生成器设置一个种子,以保证在开发过程中或测试他人执行的实验时结果的可再现性。其中,这确保了对于每个训练迭代,权重被初始化为相同的。更重要的是,这还意味着使用相同特定架构、超参数集和数据集的相同手动种子的不同训练会话将总是返回相同的结果。
Fey 等人在介绍他们的扩展库的研讨会论文中将此放在聚集分散操作的上下文中:
此外,应该注意,分散操作在 GPU 上本质上是不确定的。尽管我们没有观察到任何用于推断的偏差,但是在相同的手动种子中,训练结果会有所不同。
PyTorch 几何
PyTorch geometry是 py torch 的扩展库,可以对非欧几里德数据执行常规的深度学习任务。其名称中的“几何”是指由 Bronstein 等人[4][3]
创造的领域定义。为什么它是一个扩展库而不是框架?嗯,它完全符合 PyTorch API,只是扩展和改进了新用例的功能。
它源于多特蒙德大学的研究,主要由博士生 Matthias Fey 开发,除了开发这个库,他还开发了该领域的 SOTA 方法。[4][6]
这种与科学研究的接近性体现在大量最近研究的示例实现中,以及用于快速原型制作或结果复制的基准数据集中。此外,自 2019 年 4 月初以来,它已经成为官方 PyTorch 生态系统的一部分,并在主项目的登陆页面占据显著位置。这种变化可以在之前的 GitHub stars 历史图表中通过其斜率的显著增加看到。
图形网
Graph Nets 也是一个扩展库。作为 DeepMind 团队的内部项目,它与巴塔格利亚等人关于该主题的论文一起被开源。[1]
虽然它与 TensorFlow API 配合得很好,但它依赖于 DeepMind 的十四行诗,这使得它不太可能被纳入 TensorFlow 的生态系统(像 PyTorch Geometric 一样)。此外,尽管它是开源的,但它是内部开发的,很少对公共存储库进行更新。然而,有明显的迹象表明,通过 TensorFlow 2.0 的更新的例子,开发正在稳步地私下继续。
深度图形库
这个库没有扩展任何现有的深度学习框架。相反,它在其架构设计中紧密遵循了 API 和范式 NetworkX ,并提供了一个后端不可知的库。[5]
可以把它看作是图形深度学习的 Keras,其中有一组 API 函数可以使用不同的后端框架。它起源于美国和上海的 NYU 和 AWS 在 Yann LeCun 的指导下进行的研究,并在合作中不断发展。目前的活跃贡献者团队由大约 12 人组成,和 AWS 的项目负责人分别是王敏杰和(从官方 GitHub 知识库公开统计和 DGL 的关于页面检索的信息)。
在当前的稳定版本 v0.4.1 中,PyTorch 和 MxNet 都作为后端框架提供,而 TensorFlow 支持仍处于试验阶段。随着即将发布的 v0.5.0,TensorFlow 将被纳入稳定版本。
形态分析
在本节中,我们将非常依赖 Fil Salustri 的 DesignWIKI ,并参考相关文章以获得对过程和背景理论的更详细解释。此外,对于这一部分,我将从爱好开发者的角度进行分析。在这一部分的最后将会有更多的提示,告诉你如何调整分析以适应你的用例,以及工业或科学应用可能会改变什么。
我们想要的是一组规则,它们快速地和可靠地引导我们找到用例的最佳库或框架。如果您有一套要求来使整个过程更加正式,并且您的结果更具可重复性,那就更好了。
因为我们要比较的东西非常复杂,而且没有明显的方法可以用来相互比较,所以我们想把在它们之间进行选择的任务分解成小步骤。熟悉算法设计的人会看到对除法&征服范式的熟悉。幸运的是,我们可以通过使用一种叫做加权决策矩阵 (WDM)的简单方法来实现我们的目标。来自前面提到的维基条目的介绍:“一个加权决策矩阵是一个用来比较不同重要性水平的多个标准的备选方案的工具。”
太好了,这听起来正是我们要找的。第一步是定义比较备选方案的标准。
对于我的用例,我将标准设置如下:
- 性能:每个时间框架内我们可以进行多少次训练迭代?
- 生产差距:为训练有素的模型服务有多容易,是否有必要的基础设施?
- 示例数量:有多少个应用程序示例可以开始使用?
- 受欢迎程度:对更大的社区有显著的兴趣吗?
- 路线图:未来发展的计划是否清晰公开地传达了?
- 发展趋势:是否有持续发展,是否有积极的维护者?
- 框架开销:我要投入多少时间学习使用扩展库?
下一步是创建 WDM,并填写我们选择使用的标准的每个备选方案的分数。为什么我们不先计算一下每个标准的相对重要性呢?这样,我们可以保护自己免受不太重要的标准的影响。否则,我们会倾向于不去想太多,可能会得出错误的结论。对于每个标准的评分,李克特量表通常是推荐的选择。在我们的例子中,分数是相对的,可能的分数{1,2,3}可以被解释为具有一个特性的排名:如果差距足够大,第二名可能仍然没有被占据。
在对示例场景中的每个备选方案进行评级后,这是结果矩阵。
Preliminary Decision Matrix
在这一步之后,我们需要为每个标准设置相对重要性级别,在本例中,我们将通过执行成对比较来完成。和 WDM 一样,我们将把一个大而复杂的任务分解成一堆小而简单的任务。这部分也是你自己的经验和知识发挥作用的点。考虑到我们的要求,对于每个独特的标准组合,我们将决定哪一个对我们更重要,并记录这个决定。我们将通过使用电子表格应用程序来执行这一比较。下图是我们正在经历的例子的结果。
注意,每当两个字母在同一个单元格中,这意味着两个标准同等重要。此外,空单元格会导致遗漏不相关的信息。我们对标准本身的比较或者非唯一的比较不感兴趣。
Pairwise Comparison Table
现在我们简单地统计一下每个字符在表格中出现的次数。对于当前的例子,这导致了以下结果。
Weightings and counts sorted by criterion
为了计算我们标准的权重,我们将根据计数求解一个简单的线性方程,如下所示:
100% = 4x + x + 3x + 4x + 6x + 4x + 3x
从这个等式可以得出x = 4%
,我们可以将它乘以每个标准的计数来得到它的权重。
在谷歌电子表格中,一切都为你准备好了,你可以玩这些数字。公式也很容易根据您的需要进行调整。
现在我们可以将权重代入已经准备好的 WDM,看看它是否会以任何方式改变我们的结果。
Complete Weighted Decision Matrix
确实如此。有趣的是,包括我们的分数的加权增加了图形网和其他选择之间的差距。这种增长是 it 在本示例场景中不太重要的标准上表现出色的直接结果,例如生产差距。
另一方面,验证计算 WDM 和最小化偏差的步骤选择顺序,也是生产差距将我们的获胜者与亚军区分开来。如果我们在计算完权重后对备选方案进行评分,我们可能会得出不同的结论。
概括来说,为了打造 WDM,我们:
- 定义了一套相关且定义明确的标准
- 通过使用相对等级量表,根据这些标准对每个备选方案进行评分
- 通过使用成对比较计算每个标准的权重
- 计算每个备选方案的最终得分
此外,您选择的标准应该尽可能地相互独立。最重要的是,不要屈服于以不同的顺序完成程序,或者在计算出总分后改变权重。通过以正确的方式执行流程,我们可以最大限度地减少决策中的偏差,这一点至关重要。为了完成您的用例的分析,可以通过 Google Sheets 上下文菜单随意复制形态分析 Google 电子表格:文件- >制作副本
按照受影响标准的顺序,对用于分析的数据有一些说明。在 Fil Salustri 的设计网站上表演 WDM 的原始帖子依赖于一个真实的参考替代方案。在我们看来,这个参考是乌托邦式的,是一个完美选择的上限。使用基线替代方案也是可能的,并且非常适合这种方法。对于 GDL 来说,这可能代表了你的解决方案,试图将非欧几里德数据融入现有的数据结构,并使用没有任何扩展的常用深度学习框架。这样,分析将帮助您衡量额外的开销是否值得——可能是值得的。
我们自己没有执行任何基准测试,而是依赖于关于替代方案的公开文献。出于这个原因,我们假设在本分析中,业余爱好者的性能大致相同,因为据我所知,唯一可用的结果是 DGL 和 PyTorch Geometric 的比较。如果你有多余的时间,一定要执行一些你自己的基准。一个有用的资源是 PyTorch 几何文档中列出的外部资源列表。
PyTorch 的服务模型有其他选择,但与 Tensorflow 相比,这些选择会带来额外的成本,这就是图网的要点。大多数情况下,模型可以包含在代码中进行推理,而不需要任何进一步的特性。对于更复杂的服务场景,可以使用 ONNX 来转换 PyTorch 模型,以便与 Tensorflow 服务一起使用。最后, MLFlow 是一个后端不可知的替代方案,用于“...管理 ML 生命周期,包括实验、再现性和部署。”
细心的读者可能也注意到了分析中有一些细微的不一致。如果我们仔细观察我们进行的成对比较,我们做出的一些决定之间有一个矛盾的联系。根据我们的比较,这不可能是真的。换句话说,如果框架开销比生产差距更重要,而生产差距又比示例数量更重要,而示例数量本身又比框架开销更重要,那么我们就没有总顺序。Fil Salustri 在的原始帖子中也提到了这个错误,并将其称为箭头悖论。
最后,您可以包含一些额外的标准,或者与已经包含的标准进行交换。
- 数据集集成:为特定任务导入和使用基准数据集有多容易?
- (开发)失败的单点:如果首席开发人员突然离开,图书馆的开发会受到什么影响?
前者对于总体基准测试非常有用,尤其是在开发新方法并需要针对最先进的技术进行评估时,对于科学研究非常有用。PyTorch Geometric 非常适合这一点,因为它既提供了该领域论文中方法的各种实现,又具有迄今为止与标准数据集的最佳集成。对于需要可靠开源解决方案的工业应用来说,后者可能是成败的标准。如果项目背后的开发团队过于头重脚轻,一个关键人物的离开可能会导致开发中的重大缺口,在最糟糕的情况下,会导致开发完全停止。在持续开发和交付是必须的情况下,这种风险通常是不能冒的。
荣誉奖
主要面向中国读者:
Euler 是一个扩展框架,类似于 DGL,使用 TensorFlow 或者 X-DeepLearning(阿里)作为后端。根据文件,PyTorch 的支持已经在它的路线图上,有可能使它与 DGL 竞争,至少在中国市场上。尽管代码和文档都是英文的,但是关于问题和请求的交流都是中文的,这使得它对于不会说中文的人来说很难接受。
NeuGraph 是由北京大学和微软亚洲研究院的研究人员在中国开发的进一步框架。由于在公共渠道上没有可用的开源实现,在可预见的未来,开发似乎一直在内部进行。
主要差异
这三个被分析的框架之间存在一些关键的差异,在某些情况下,这可能会迫使做出特定的选择。首先,目前只有 Graph Nets 使用 TensorFlow 作为后端。如果您的解决方案严重依赖 TensorFlow 环境。使用 TensorFlow 作为后端的可能性将在 DGL 的 v0.5 中提供。尽管如此,Graph Nets 仍然是列出的备选方案中唯一有潜力成为 TensorFlow 生态系统的一部分的。
此外,如果您只想重现最近一篇论文中方法的结果,或者用您的数据进行测试,Pytorch Geometric 可能是正确的选择。它拥有来自该领域论文的惊人数量的已实现算法,加上与标准基准数据集的轻松集成,使其在快速开发和验证方面脱颖而出。
DGL 是唯一一个拥有清晰的公开路线图的公司,并且拥有众多积极参与项目的开发人员。
我应该选择哪一个?
首先,如前所述,确定哪一个库最适合您的最佳方法是使用我们提供的工具自己进行分析。
有了这个免责声明,下面是一些常见用例的建议:
- 科学研究:PyTorch Geometric 是由一名博士生开发的,他正在研究该领域的 SOTA 算法。这个图书馆可能是你最好的选择。从通用基准数据集的集成到其他论文的实现,如果您希望针对 SOTA 快速测试您的新发现,它允许无缝集成。
- 生产就绪的开发:在这里,它不是那么清晰。随着最新版本和对 TensorFlow 的全面支持,很难决定是使用 DGL 还是图网。随着 AWS 的深入参与,DGL 很可能很快就会为大规模应用程序提供高级支持。否则,如果你渴望能够将 Deepmind 的最新研究应用到你的应用程序中,那么 Graph Nets 将是唯一的选择。
- 业余爱好者:如果你对测试图形神经网络感兴趣,没有任何附加条件,最快的方法,那么 PyTorch Geometric 是最好的。您可以查看和调整的示例实现数量之多令人震惊。DGL 紧随其后,需要更多的时间投入。此外,它显示了公共路线图和支持的长期承诺,可能是您未来应用程序的宝贵工具。
参考文献:
1。彼得·w·巴塔格利亚等人,《关系归纳偏差、深度学习和图形网络》 arXiv 预印本 arXiv:1806.01261 (2018)。
2。量子化学的神经讯息传递。第 34 届
机器学习国际会议论文集——第 70 卷。JMLR。org,2017。
3。几何深度学习:超越欧几里得数据。 IEEE 信号处理杂志 34.4 (2017): 18-42。
4。菲,马提亚斯和简·埃里克·连森。"快速图形表示学习与 PyTorch 几何." arXiv 预印本 arXiv:1903.02428 (2019)。
5。王,闵杰,等,“深度图库:面向图的高效可扩展深度学习” arXiv 预印本 arXiv:1909.01315 (2019)。
6。《SplineCNN:用连续 B 样条核的快速几何深度学习》IEEE 计算机视觉和模式识别会议论文集。2018.
7。《网状 CNN:一个有边缘的网络》美国计算机学会图形汇刊(TOG) 38.4 (2019): 1-12。
与深度脸艺术家杰西·理查兹一起面对现实
原文:https://blog.paperspace.com/getting-real-with-deepfake-artist-jesse-richards/
抖音正在蓬勃发展。(你可能因为其他原因在新闻中看到过。)作为最热门和最新的社交媒体应用之一,它允许用户与关注者和陌生人分享短视频。抖音被比作Vine(2012 年被 Twitter 收购,2017 年关闭),但它已经比Vine 更受欢迎。据说抖音在短短四年内就获得了超过 10 亿的用户!
由于大多数抖音用户是 Z 世代和千禧一代,每个人都将抖音视为文化创造的重要论坛。无论是作为创作者还是观众,使用起来都非常有趣。每个三到六十秒的视频都是一个自成一体的世界,充满了荒谬、灵感、戏剧、喜剧以及介于两者之间的一切。
我们很幸运能够与抖音(和 Instagram)的新星杰西·理查兹(Jesse Richards)交谈。杰西(又名 @deepfaker 和 @iamjesserichards )正在制作一些我们见过的最有趣、质量最高的深度赝品——结果在抖音上拥有了一大批追随者。一年多来,他的粉丝数量一直在飙升,这并不奇怪。
我们非常热情地与他谈论抖音,基于 ML 的视觉效果的未来,任何人如何学习成为一名 deepfaker,以及更多关于他参与的其他项目。
Paperspace: 从一个非常基本的问题开始——什么是 deepfake?你最初是如何对 deepfakes 产生兴趣的?
理查兹:deep fake 就是用 AI 把一个现有人的脸放到别人身上。我第一次进入 deepfakes 是在看了一个由 ctrl-shift-face 制作的 Youtube 视频之后。我向他寻求更多关于他的过程的信息,他向我展示了他使用的程序!
Paperspace: 你能告诉我们更多关于你的背景吗?我们现在知道你是如何对 deepfakes 感兴趣的,并且知道你在电影行业。你能告诉我们你故事的其余部分吗?
理查兹: 我从幼儿园到高中都在内布拉斯加州的奥马哈长大。在整个高中和初中,我总是到处制作小视频。毕业后,我在爱荷华州苏中心的 Dordt 大学获得了计算机科学学士学位。
在多特的最后一个夏天,我飞到了洛杉矶,继续制作视频。在我遇到一些著名的 Vine 明星之前,我在那里制作了一些视频。在建立了这些联系并看到我可以以此谋生后,我完成了在 Dordt 的最后一个学期,然后前往洛杉矶,并在这里呆了三年。在此期间,我主要为品牌、有影响力的人、职业运动员和电影明星制作特效视频。就是这个去年 2019 年 7 月,我开始了解 deepfakes。
我做的前几个 deepfakes 绝对很糟糕。直到大约 2020 年 1 月,我的 deepfakes 才开始变得更好看,然后我利用隔离期间的时间继续钻研 deepfake 的工作。目前,我一直在为大型工作室和一些社交媒体制作 deepfakes。最近我和杰森·德鲁罗一起做了一个抖音!我刚刚和威尔·史密斯一起拍摄了几个特效视频,应该不错!我现在就在那里。
Paperspace : 迄今为止,你最喜欢的项目是什么?你最具挑战性的项目是什么,为什么?
理查兹: 我最喜欢的一个项目叫做“迈克尔的噩梦是什么”。我把托比的脸印在办公室里所有的人物上,然后剪辑成最恐怖的片段。将我的编辑技巧和 deepfakes 结合在一起非常有趣。
我最具挑战性的项目是做“办公室重启”在那段视频中,我找了十个年轻的明星(包括哈里·斯泰尔斯、蒂莫西·柴勒梅德和艾玛·斯通,仅举几个例子),把他们安排在办公室里,就好像十年后它会重新启动一样。花了很长时间才收集到所有的数据并使之发挥作用。
View this post on Instagram[【T 38]
What will happen if they restart the office with a new role in 2030? Who will be most excited to see it? # Office # Restart
A post
shared by](https://www.instagram.com/p/CD1cSF9lwfG/?utm_source=ig_embed&utm_campaign=loading) Deepbaker (@ iamdeepfaker) on August 13th, 2020 at 8:47 am Pacific Time
DCGANs 入门
原文:https://blog.paperspace.com/getting-started-with-dcgans/
生成对抗网络(GANS) 的发展是一项革命性的成就。虽然在深度学习领域已经有了许多突破性的进展,但是以前的生成方法产生的结果都不令人满意。GANs 成为第一个在大多数测试数据集上获得令人信服的高质量结果的方法。自 2014 年以来,GANs 一直是神经网络和深度学习研究中最受欢迎的方面之一。在我的上一篇文章“生成性对抗网络(GANs)完全指南”中,你可以通过下面的链接获得,我们涵盖了基本理解 GANs 所需的大部分基本概念。这些主题包括生成和判别模型、生成模型的类型、对它们的训练过程的深入理解以及它们在现代世界中的应用。
随着这些生成性对抗网络的流行,我们有许多 GAN 的变体,包括 DCGANs、SRGAN、Pix2Pix、Cycle GAN、ProGAN 等等。虽然我们将在未来的文章中查看其他 GAN 原型,但本节的重点将是深度卷积生成对抗网络(DC GAN)。在本文中,我们将向 DCGANs 介绍我们自己,并稍微深入一些他们复杂的方面。然后,我们将使用这些用于数字生成的 DCGANs 从头开始构建一个项目。下面提供的目录将引导您阅读本文的各个部分。虽然建议进一步详细检查每个方面,但是您可以随意跳到最吸引您的部分。
简介:
虽然 GANs 的受欢迎程度现在达到了顶峰,取得了巨大的成功,但情况和对这一概念的喜爱并不总是像人们现在所期望的那样特别。GANs 的可取之处在于缺乏启发式成本函数(一个很好的例子是像素独立均方损失),以及它是生成模型的较好方法之一。然而,缺乏计算要求,创新的想法,以及一个值得注意的事实,即 GANs 产生的结果大多没有意义。由于这些原因,卷积神经网络(CNN)主要用于构建与监督学习相关的任务,如分类问题,因为这些复杂问题可以在 CNN 的帮助下轻松实现和解决。
在现代,CNN 不局限于监督分类问题。随着 DCGANs 的引入,我们注意到 CNN 在许多任务中产生高质量结果的潜力越来越大。有了 DCGANs,无监督任务和生成模型的成功完成成为可能。对于 DCGANs 架构,我们将参考流行的“深度卷积生成对抗网络的无监督表示学习”研究论文,该论文广泛涵盖了这一主题。DCGANs 架构的主要目标是为一组约束的评估设置初始参数,以在大多数设置和技术场景中始终获得稳定的测试结果。一旦构建了初级模型,鉴别器的目的是在图像分类任务中表现出与其他流行的无监督学习算法相当的高度竞争性的性能。
一旦鉴别器被完全训练,我们就可以利用这个模型来将从生成器生成的图像分类为真的或假的。生成器模型的目标是生成如此逼真的图像,以至于可以绕过来自鉴别器的分类测试过程。在 DCGANs 原型中开发的生成器模型具有有趣的向量算术属性,这允许对生成的样本的许多语义质量进行操作。有了对 DCGANs 及其所有初始特性的基本介绍,让我们在本文的下一节看看它们的架构。
深入探究 DCGANs 的建筑:
如前所述,卷积神经网络在与分类或其他类似问题相关的监督任务上更为成功。随着 DCGANs 研究论文的推出,这种思维方式发生了变化,因为开发的架构在大规模数据上产生了非常令人满意的结果,使其甚至能够处理更高分辨率的图像。由于这种在许多重要数据集上成功实施的方法,理解核心概念变得至关重要,这些核心概念用于使 CNN 参与 GANs,使其表现出色。
实现的几个概念性想法包括使用卷积网络,跨越取代最大池结构。这种结构允许生成器模型在通过 Conv2D 转置的上采样的帮助下学习它自己的空间维度,同时使用跨距操作来允许网络也学习它各自的下采样。DCGAN 的第一层采用标记为\(Z\)的均匀分布值。输入层可能是唯一使用密集层的层。除了这个例外,其他层都不使用完全连接层或最大池层。
从上面的图像表示中,我们可以了解到,对于 LSUN 数据集,有一个 100 维的均匀分布的噪声,标记为\(Z\)。这种维度被投影到包含具有许多特征图的卷积表示的更小的空间扩展上。上图中描述的体系结构后面是一系列分数步长卷积的四个块,这有助于前面讨论的模型的空间维度学习。在开始时初始化的随机噪声将最终在通过分数步长卷积的连续上采样的帮助下学习显著的特征。
模型设计中涉及的其他关键方面包括批量标准化层的使用。这些批处理规范化层广泛用于生成器和鉴别器模型,以获得模型的稳定性。研究表明,通过应用批处理规范化过程,由于输入的初始化而出现的大多数训练问题都可以得到解决。通过将每个单元的输入归一化为零均值和单位方差,它还有助于更好地实现层中的梯度流。最后,在生成器模型中,除了包含 tanh 激活函数的输出层之外,所有层后面都是 ReLU 激活函数。鉴别器模型中的所有层将包含泄漏 ReLU 激活函数,因为它们被示出产生最佳结果。
使用 DCGANs 生成号码:
为了从头理解 DCG an 的工作原理,我们将在 MNIST 数据集的帮助下用 DCG an 构建一个数字生成项目。MNIST 数据代表修改后的国家标准与技术研究所数据库,这是最受欢迎的数据集之一,包括训练数据集上的 60,000 个大规模可用示例,以及由 10,000 个示例组成的测试集。对于任何有兴趣测试任何类型网络的人来说,这通常是一个很好的起点。我们将使用 DCGANs 的生成器和鉴别器模型从这个数据集中学习。一旦训练完成,我们将使用生成器模型来生成一些高质量的结果。
为了完成这个项目,我们将利用 TensorFlow 和 Keras 深度学习框架。如果你没有这两个库的完整知识,或者想快速更新你的基础知识,我建议你查看 TensorFlow 的以下链接和 Keras 的这个特定的链接。确保你有足够的知识继续前进。我们项目的第一步是导入所有必要的库。我们将使用 matplotlib 来可视化我们的数据,使用 numpy 库来访问数组形式的图像,使用 TensorFlow 和 Keras 深度学习框架来构建 DCGANs 架构,并使用其他库来处理它们的特定用例。
最重要的导入是 MNIST 数据集,它预先构建在核心 TensorFlow 和 Keras 库中,可供我们使用。我们将直接从框架中访问这个数据集,而不是执行外部安装。下面提供了所有必需导入的代码。
导入所需的库:
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import PIL
import glob
from tensorflow.keras.datasets.mnist import load_data
from tensorflow.keras.layers import Conv2D, Dense, Flatten, MaxPooling2D, BatchNormalization, Dropout, LeakyReLU
from tensorflow.keras.layers import Conv2DTranspose, Reshape
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.optimizers import Adam
import time
import imageio
from IPython import display
import os
加载数据:
现在我们已经导入了数字生成项目所需的所有库,让我们加载数据。由于 MNIST 数据集被分为图像以及分别用于训练和测试数据的相应标签,因此我们可以使用下面的代码块来相应地加载适当的数据。由于 MNIST 数据集是从 Keras 库中直接导入的,因此可以相对轻松地执行此操作。
(train_images, train_labels),(test_images, test_labels) = load_data()
可视化我们的数据:
我们将执行的下一步是数据集的可视化。我们将借助 matplotlib 库可视化数据的前 25 个元素。我们将绘制这些元素,并确保它们映射到二进制元素组合。二进制映射意味着这些图像被视为灰度图像。MNIST 数据集中提供的每幅影像都有\(28 × 28 × 1\)浮点数组的参数,用于表示灰度强度值,范围从\(0\)(黑色)到\(1\)(白色)。尺寸为 28 × 28 美元的宽度和高度,灰度图像的通道长度为 1 美元。下面的代码相应地绘制了适当的数据。
for i in range(25):
plt.subplot(5, 5, 1 + i)
plt.axis("off")
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.show()
Screenshot By Author
如果您想在不使用任何 cmap 函数的情况下可视化单个图像,您可以使用下面的代码行来实现。
plt.imshow(train_images[0])
Screenshot By Author
设置参数:
我们的下一步将是相应地规范化数据集,并为数字生成的特定任务设置一些初始参数。首先,我们将重塑我们的训练图像,使其适合通过 DCGANs 架构中的卷积神经网络。我们将把所有的数据转换成浮点型变量,并使数据正常化。规范化过程确保数据集中提供的所有数据都在 0 美元到 1 美元的范围内,而不是 0 美元到 255 美元的范围内。这样做将确保任务的计算稍微更快和更有效。我们还将相应地设置缓冲区大小和批处理大小。缓冲区大小将被设置为 60000 美元,这相当于训练示例的数量。可以根据硬件的便利性和限制来设置批量大小。我们将使用张量切片根据预先设置的参数来混洗训练数据集。
# Normalize The Training images accordingly
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5
BUFFER_SIZE = 60000
BATCH_SIZE = 256
# Batch and shuffle the data
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
在从头开始执行数字生成项目所需的所有初始步骤之后,包括导入基本库、加载适当的数据、可视化数据和设置初始参数,我们可以继续本文的下一步。在下一节中,我们将探讨如何为研究论文中描述的以下项目构建生成器和鉴别器模型。
构建发生器和鉴别器模型:
为了构建生成器和鉴别器模型,我们将利用之前讨论的 TensorFlow 和 Keras 深度学习框架。我们还将利用官方的 TensorFlow 生成模型部分作为构建我们两个架构的参考。DCGANs 体系结构的大部分核心设计流程与前面讨论的 DCGANs 体系结构相同。我们将相应地讨论一些细微的显著变化。让我们开始构建生成器和鉴别器模型。
发电机:
我们现在将使用顺序模型风格构建生成器架构。我们将定义初始模型,输入形状为 100,用于接收传入的随机噪声种子,它将通过具有跨层的卷积上采样的进一步层。正如在 DCGANs 架构中所讨论的,发生器模型主要由具有步长的卷积上采样层组成。该体系结构中没有使用最大池层。
每一层后面都有一个批处理归一化层,以实现稳定性和更快的梯度初始化。然而,稍微值得注意的变化是在 ReLU 激活函数上使用了泄漏的 ReLU 层,正如研究论文中提到的。您可以相应地研究这些因素,看看哪种方法产生的结果最好。无论如何,这两种方法的差别不应该太大。最后一层使用 tanh 激活,如研究论文中所述。生成器的目标是对其空间维度进行上采样,直到达到所需的图像大小 28 x 28 x 1 美元。
def The_Generator():
model = Sequential()
model.add(Dense(7*7*256, use_bias=False, input_shape=(100,)))
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Reshape((7, 7, 256)))
assert model.output_shape == (None, 7, 7, 256) # Note: None is the batch size
model.add(Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
assert model.output_shape == (None, 7, 7, 128)
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
assert model.output_shape == (None, 14, 14, 64)
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
assert model.output_shape == (None, 28, 28, 1)
return model
generator = The_Generator()
我们将发电机模型存储在“发电机”变量中。让我们研究一下这个生成器模型产生的模型概要和模型图。这样做将有助于我们从概念上理解所构建的模型架构的类型,并对其进行进一步的分析。
型号汇总:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 12544) 1254400
_________________________________________________________________
batch_normalization (BatchNo (None, 12544) 50176
_________________________________________________________________
leaky_re_lu (LeakyReLU) (None, 12544) 0
_________________________________________________________________
reshape (Reshape) (None, 7, 7, 256) 0
_________________________________________________________________
conv2d_transpose (Conv2DTran (None, 7, 7, 128) 819200
_________________________________________________________________
batch_normalization_1 (Batch (None, 7, 7, 128) 512
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU) (None, 7, 7, 128) 0
_________________________________________________________________
conv2d_transpose_1 (Conv2DTr (None, 14, 14, 64) 204800
_________________________________________________________________
batch_normalization_2 (Batch (None, 14, 14, 64) 256
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU) (None, 14, 14, 64) 0
_________________________________________________________________
conv2d_transpose_2 (Conv2DTr (None, 28, 28, 1) 1600
=================================================================
Total params: 2,330,944
Trainable params: 2,305,472
Non-trainable params: 25,472
_________________________________________________________________
模型图:
发电机图像的随机可视化:
既然我们已经建立了模型并分析了图和摘要,让我们也想象一下未经训练的发生器在随机噪声分布的情况下会产生什么样的输出。我们将使随机噪声通过发生器,并获得一个结果,即来自发生器的生成图像。
# Visualizing the random image generated by the generator
noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)
plt.imshow(generated_image[0, :, :, 0])
上图是正常情况下产生的彩色图像类型输出的代表。然而,我们知道我们的 MNIST 数据由灰度图像组成。因此,让我们尝试使用为灰度图像设置的参数来可视化生成的输出。
plt.imshow(generated_image[0, :, :, 0], cmap='gray')
鉴别器:
现在我们已经对发生器的工作过程和它们产生的输出类型有了一个简单的概念,我们也可以继续为 DCGANs 架构构建鉴别器模型。我们将为鉴别器使用顺序模型架构类型,这与生成器非常相似。我们将使用卷积层,然后是研究论文中描述的泄漏 ReLU 激活函数。但是,我们还添加了一个额外的层,以防止过拟合,并通过使用 dropout 获得更好的分类结果。最后,我们将展平架构,并使用包含一个节点的最终密集层来进行适当的预测。鉴别器的预测对真实图像输出正值,对伪图像输出负值。
def The_Discriminator():
model = Sequential()
model.add(Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1]))
model.add(LeakyReLU())
model.add(Dropout(0.3))
model.add(Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(LeakyReLU())
model.add(Dropout(0.3))
model.add(Flatten())
model.add(Dense(1))
return model
discriminator = The_Discriminator()
让我们探索鉴别器的模型概要和模型图,以帮助我们理解架构在视觉上看起来是怎样的。
型号汇总:
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 14, 14, 64) 1664
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU) (None, 14, 14, 64) 0
_________________________________________________________________
dropout (Dropout) (None, 14, 14, 64) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 7, 7, 128) 204928
_________________________________________________________________
leaky_re_lu_4 (LeakyReLU) (None, 7, 7, 128) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 7, 7, 128) 0
_________________________________________________________________
flatten (Flatten) (None, 6272) 0
_________________________________________________________________
dense_1 (Dense) (None, 1) 6273
=================================================================
Total params: 212,865
Trainable params: 212,865
Non-trainable params: 0
_________________________________________________________________
模型图:
做决定:
鉴别器的目的是分类图像输出产生的是真图像还是假图像。让我们看看鉴别器的单个决定提供给我们的输出类型。注意,模型被训练成正值意味着图像是真实的,而负值意味着图像是假的。
decision = discriminator(generated_image)
print(decision)
输出:
tf.Tensor([[0.0015949]], shape=(1, 1), dtype=float32)
现在我们已经构建了生成器和鉴别器架构模型,我们可以一起构建整个 DCGANs 模型,并相应地训练参数以获得最佳结果。
构建 DCGANs 模型:
在完成所有初始预处理步骤以及分别构建生成器和鉴别器模型之后,我们可以继续构建整个 DCGANs 模型,并对其进行相应的训练,以获得可能的最佳结果。文章的这一部分分为四个关键步骤,以实现模型的完美运行。我们将首先定义我们将用于模型编译的损失和优化器。之后,我们将创建一个检查点,以便在需要时可以重用模型。然后,我们将定义所有必要的参数,并设置@tf 函数,该函数将自动编译模型。最后,我们将训练模型,并通过 DCGANs 模型对生成的图像进行可视化。让我们从定义损失参数和优化器开始。
定义损失和优化器:
构建 DCGANs 模型的下一个重要步骤是定义损失函数和初始化优化器。我们将定义我们的损失函数,它将以二进制交叉熵的形式接受输出参数。我们将把 logits 属性设置为 true。logit 属性通知损失函数由模型生成的输出值未被归一化。这样做是有帮助的,因为我们知道鉴别器的工作是对输出进行分类。因此,负值代表伪图像,正值代表真实图像。在定义了鉴别器和发电机损耗之后,我们还将为它们定义优化器。我们将为这两个模型使用 Adam 优化器。让我们探索代码块,然后尝试进一步直观地理解它们做什么。
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
def discriminator_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
def generator_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
在上面的代码块中,真实输出指的是来自原始数据集的所有真实标签,而虚假输出指的是来自生成器模型输出的所有生成标签。以下代码块产生的总损耗将导致以下等式:
\[总损失= -log(真实输出)- log(1 -假输出)$ $
用于鉴别器的上述等式试图最小化损失函数以获得更好的结果。
### 定义检查点:
对于一个普通的 GPU,GANs 需要花费相当多的时间来训练。我们将定义检查点,以便如果我们希望在一组特定的时期后再次重新训练模型,我们可以恢复检查点以继续我们的训练。
```py
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
discriminator_optimizer=discriminator_optimizer,
generator=generator,
discriminator=discriminator)
```
### 设置基本参数并定义@tf.function:
下一步,我们将定义一些基本参数。我们将重用我们定义的种子,这样我们就可以更容易地看到生成的动画 GIF 随着时间推移的整体进度。我将运行该模型总共 100 个时期。您可以根据您的需求和系统限制选择做更多或更少的工作。
```py
EPOCHS = 100
noise_dim = 100
num_examples_to_generate = 16
seed = tf.random.normal([num_examples_to_generate, noise_dim])
```
我们的下一步是定义导致模型被编译的@tf.function。我们将利用 TensorFlow 中可用的 GradientTape()函数手动开始训练生成器和鉴别器模型。生成器的任务是生成好到可以绕过鉴别器的图像。鉴别器的任务是据此区分图像的真假。一旦生成器生成顶级的结果来绕过鉴别器的分类系统,我们就构建了一个高质量的 DCGANs 模型来成功完成特定的任务。
```py
@tf.function
def train_step(images):
noise = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training=True)
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
```
### 训练和生成图像:
随着所有其他步骤的完成,我们终于可以开始培训过程了。我们现在将定义训练函数,该函数将利用我们之前为模型定义的所有函数。我们还将打印运行每个单独时期所花费的时间,以及从开始生成的图像类型。这样做将有助于我们直观地看到模型的学习曲线,并更好地理解训练过程。
```py
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()
for image_batch in dataset:
train_step(image_batch)
# Produce the images for the GIF with each step
display.clear_output(wait=True)
generate_and_save_images(generator,
epoch + 1,
seed)
# Save the model every 15 epochs
if (epoch + 1) % 15 == 0:
checkpoint.save(file_prefix = checkpoint_prefix)
print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
# Generate after the final epoch
display.clear_output(wait=True)
generate_and_save_images(generator,
epochs,
seed)
```
您可以使用我们的 TensorFlow [参考文献](https://www.tensorflow.org/tutorials/generative/dcgan)中的以下代码块来生成和保存每个历元中生成的图像。
```py
def generate_and_save_images(model, epoch, test_input):
# Notice `training` is set to False.
# This is so all layers run in inference mode (batchnorm).
predictions = model(test_input, training=False)
fig = plt.figure(figsize=(4, 4))
for i in range(predictions.shape[0]):
plt.subplot(4, 4, i+1)
plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
plt.axis('off')
plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
plt.show()
```
现在我们已经完成了所有的主要步骤,让我们调用将开始训练模型的函数。一旦培训完成,我们就可以开始可视化产生的输出。
```py
train(train_dataset, EPOCHS)
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/739884dca55a5a3d3b3978394e1c58aa.png)
上面的图像表示是经过 100 个历元训练后的截图。这个项目的整个笔记本附在文章后面。请随意相应地探索它。
* * *
## 结论:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4cf596914bc7f48baca3469d4ed7cb9e.png)
Photo by [Dan Farrell](https://unsplash.com/@farreal?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)
生成性敌对网络生成虚假视觉、图像、文本或其他带有随机噪声的实体的能力是互补的。GANs 的一个变体是*深度卷积生成对抗网络(DCGANs)* ,它在测试的一些数据集上产生了奇妙的结果。根据我们之前对这篇[文章](https://blog.paperspace.com/complete-guide-to-gans/)中的 gan 的了解,以及我们对这篇文章中的 DCGANs 的理解,我们可以用深度学习框架构建许多精彩的项目,如 TensorFlow、Keras 和 PyTorch。
在本文中,我们简要了解了 DCGANs 的工作过程以及“深度卷积生成对抗网络的无监督表示学习”[研究论文](https://arxiv.org/abs/1511.06434)中采用的各种技术。然后,我们在 MNIST 数据集的帮助下进行数字生成项目。使用这个数据集,我们预处理了初始数据,并构建了适当的生成器和鉴别器模型来训练它们。在用 DCGANs 架构训练数据之后,我们能够在仅仅 50 个时期的训练之后产生相当不错的结果。随着进一步的训练和模型改进,有可能在数据集上取得更好的结果。
在生成对抗网络的后续部分中,我们将扩展 DCGANs 的应用,并研究如何用图像构建一个人脸生成项目,以生成不存在的人的真实人脸图像。我们还将在以后的文章中研究更多的 GAN 变体、项目和主题。在那之前,继续编码和探索吧!
# OpenAI 健身房入门:基本构建模块
> 原文:<https://blog.paperspace.com/getting-started-with-openai-gym/>
如果你想开始强化学习,OpenAI gym 无疑是最受欢迎的选择。在 OpenAI Gym 中,开箱即用的各种环境被用作证明任何新研究方法有效性的基准。此外,OpenAI gym 提供了一个简单的 API 来实现您自己的环境。
在这篇文章中,我将介绍 OpenAI 健身房的基本组成部分。以下是我在这篇文章中涉及的内容列表。
## 涵盖的主题
1. 装置
2. 环境
3. 间隔
4. 封装器
5. 矢量化环境
所以让我们开始吧。你也可以在 [Gradient 社区笔记本](https://ml-showcase.paperspace.com/projects/reinforcement-learning)上免费运行本教程中的所有代码。
## 装置
我们要做的第一件事是确保我们安装了最新版本的`gym`。
人们可以使用`conda`或`pip`来安装`gym`。在我们的例子中,我们将使用`pip`。
```py
pip install -U gym
```
## 环境
OpenAI Gym 的基础构件是`Env`类。它是一个 Python 类,基本上实现了一个模拟器,该模拟器运行您想要在其中训练您的代理的环境。开放式人工智能健身房配备了许多环境,例如你可以在山上移动汽车,平衡摆动的钟摆,在雅达利游戏中得分等等。Gym 也为你提供了创建自定义环境的能力。
我们从一个叫做`MountainCar`的环境开始,目标是开车上山。汽车在一维轨道上,位于两座“山”之间。目标是开车上右边的山;然而,这辆车的引擎不够强劲,不足以一口气爬上这座山。所以,成功的唯一方法就是来回开车造势。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1613a5a8d5593ea1269341511f2f72b6.png)
The goal of the Mountain Car Environment is to gain momentum and reach the flag.
```py
import gym
env = gym.make('MountainCar-v0')
```
环境的基本结构由 Gym `Env`类的`observation_space`和`action_space`属性描述。
`observation_space`定义了环境状态观测的结构和合法值。对于不同的环境,观察可以是不同的东西。最常见的形式是游戏截图。也可以有其他形式的观察,例如以矢量形式描述的环境的某些特征。
类似地,`Env`类也定义了一个名为`action_space`的属性,它描述了可以应用于环境的合法动作的数字结构。
```py
# Observation and action space
obs_space = env.observation_space
action_space = env.action_space
print("The observation space: {}".format(obs_space))
print("The action space: {}".format(action_space))
```
```py
OUTPUT:
The observation space: Box(2,)
The action space: Discrete(3)
```
山地汽车环境的观测值是代表速度和位置的两个数字的向量。以两座山的中点为原点,右为正方向,左为负方向。
我们看到,观察空间和动作空间分别由名为`Box`和`Discrete`的类表示。这些是由`gym`提供的各种数据结构之一,以便为不同种类的场景(离散动作空间、连续动作空间等)实现观察和动作空间。我们将在本文的后面对此进行深入探讨。
## 与环境互动
在这一节中,我们将介绍帮助代理与环境交互的`Env`类的函数。两个这样的重要功能是:
* `reset`:该函数将环境复位到初始状态,并返回初始状态对应的环境观察。
* `step`:这个函数把一个动作作为输入,并把它应用到环境中,这导致环境转换到一个新的状态。reset 函数返回四样东西:
1. `observation`:对环境状态的观察。
2. `reward`:执行作为`step`函数输入的动作后,你可以从环境中获得的奖励。
3. `done`:剧集是否已经结束。如果为真,您可能需要结束模拟或重置环境以重新开始剧集。
4. `info`:这提供了取决于环境的附加信息,例如剩余的生命数,或者可能有助于调试的一般信息。
现在让我们看一个例子来说明上面讨论的概念。我们首先从重置环境开始,然后我们检查一个观察。然后,我们应用一个动作并检查新的观察结果。
```py
import matplotlib.pyplot as plt
# reset the environment and see the initial observation
obs = env.reset()
print("The initial observation is {}".format(obs))
# Sample a random action from the entire action space
random_action = env.action_space.sample()
# # Take the action and get the new observation space
new_obs, reward, done, info = env.step(random_action)
print("The new observation is {}".format(new_obs))
```
```py
OUTPUT:
The initial observation is [-0.48235664 0.]
The new observation is [-0.48366517 -0.00130853]
```
在这种情况下,我们的观察不是正在执行的任务的截图。在许多其他环境中(像 Atari,我们将会看到),观察是游戏的截图。在任一场景中,如果您想要查看环境在当前状态下的样子,您可以使用`render`方法。
```py
env.render(mode = "human")
```
这将在一个弹出窗口中显示环境的当前状态。您可以使用`close`功能关闭窗口。
```py
env.close()
```
如果你想看到游戏截图的图像,而不是弹出窗口,你应该设置`render`函数的`mode`参数为`rgb_array`。
```py
env_screen = env.render(mode = 'rgb_array')
env.close()
import matplotlib.pyplot as plt
plt.imshow(env_screen)
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fe236b6cea549b200a901c7ad10dd375.png)
OUTPUT
收集到目前为止我们已经讨论过的所有小代码块,在`MountainCar`环境中运行代理的典型代码如下所示。在我们的例子中,我们只是采取随机的行动,但是你可以让一个代理根据你得到的观察做一些更智能的事情。
```py
import time
# Number of steps you run the agent for
num_steps = 1500
obs = env.reset()
for step in range(num_steps):
# take random action, but you can also do something more intelligent
# action = my_intelligent_agent_fn(obs)
action = env.action_space.sample()
# apply the action
obs, reward, done, info = env.step(action)
# Render the env
env.render()
# Wait a bit before the next frame unless you want to see a crazy fast video
time.sleep(0.001)
# If the epsiode is up, then start another one
if done:
env.reset()
# Close the env
env.close()
```
## 间隔
我们环境的`observation_space`是`Box(2,)`,`action_space`是`Discrete(2,)`。这些实际上意味着什么?`Box`和`Discrete`都是由 Gym 提供的称为“空间”的数据结构类型,用于描述对环境的观察和行动的合法值。
所有这些数据结构都来自于`gym.Space`基类。
```py
type(env.observation_space)
#OUTPUT -> gym.spaces.box.Box
```
`Box(n,)`对应的是`n`维的连续空间。在我们`n=2`的例子中,我们环境的观测空间是一个二维空间。当然,这个空间受到上限和下限的限制,上限和下限描述了我们的观察可以采用的合法值。我们可以使用观察空间的`high`和`low`属性来确定这一点。这些分别对应于我们环境中的最大和最小位置/速度。
```py
print("Upper Bound for Env Observation", env.observation_space.high)
print("Lower Bound for Env Observation", env.observation_space.low)
```
```py
OUTPUT:
Upper Bound for Env Observation [0.6 0.07]
Lower Bound for Env Observation [-1.2 -0.07]
```
您可以在定义空间和创建环境时设置这些上限/下限。
`Discrete(n)`框描述了具有`[0.....n-1]`个可能值的离散空间。在我们的例子中`n = 3`,意味着我们的行为可以取值为 0、1 或 2。与`Box`不同,`Discrete`没有`high`和`low`方法,因为根据定义,很清楚什么类型的值是允许的。
如果您试图在我们环境的`step`函数中输入无效值(在我们的例子中,比如说 4),就会导致错误。
```py
# Works
env.step(2)
print("It works!")
# Doesn't work.
env.step(4)
print("It works!")
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/599257013996aca3a5eb1a2f547e625b.png)
OUTPUT
有多个其他空间可用于各种用例,例如`MultiDiscrete`,它允许您使用多个离散变量作为您的观察和操作空间。
## 封装器
OpenAI Gym 中的`Wrapper`类为您提供了修改环境各部分以满足您需求的功能。为什么会出现这样的需求?也许你想正常化你的像素输入,或者也许你想剪辑你的奖励。虽然通常你可以通过创建另一个类来子类化你的环境`Env`类来完成同样的工作,但是`Wrapper`类允许我们更系统地完成这项工作。
但在我们开始之前,让我们切换到一个更复杂的环境,这将真正帮助我们欣赏`Wrapper`带来的效用。这个复杂的环境将成为雅达利游戏的突破口。
在我们开始之前,我们安装`gym`的雅达利组件。
```py
!pip install --upgrade pip setuptools wheel
!pip install opencv-python
!pip install gym[atari]
```
如果您对`AttributeError: module 'enum' has no attribute 'IntFlag'`的调整有错误,您可能需要卸载`enum`包,然后重新尝试安装。
```py
pip uninstall -y enum34
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d3e77ccb218123984dc30a5def988ff6.png)
Gameplay of Atari Breakout
现在让我们用随机动作运行环境。
```py
env = gym.make("BreakoutNoFrameskip-v4")
print("Observation Space: ", env.observation_space)
print("Action Space ", env.action_space)
obs = env.reset()
for i in range(1000):
action = env.action_space.sample()
obs, reward, done, info = env.step(action)
env.render()
time.sleep(0.01)
env.close()
```
```py
OUTPUT:
Observation Space: Box(210, 160, 3)
Action Space Discrete(4)
```
我们的观察空间是对应于相同大小的 RGB 像素观察的维度(210,160,3)的连续空间。我们的动作空间包含 4 个独立的动作(左、右、什么都不做、开火)
现在我们已经加载了我们的环境,让我们假设我们必须对 Atari 环境进行某些更改。在 Deep RL 中,我们通过将过去的`k`帧连接在一起来构建我们的观察是一种常见的做法。我们必须修改突破环境,使我们的`reset`和`step`函数返回串联的观察值。
为此,我们定义了一个类型为`gym.Wrapper`的类来覆盖分支`Env`的`reset`和`return`函数。顾名思义,`Wrapper`类是一个位于`Env`类之上的包装器,用于修改它的一些属性和功能。
`__init__`函数是用为其编写包装器的`Env`类以及要连接的过去帧的数量来定义的。请注意,我们还需要重新定义观察空间,因为我们现在使用级联帧作为我们的观察。(我们将观察空间从(210,160,3)修改为(210,160,3 * num_past_frames。)
在`reset`函数中,当我们初始化环境时,因为我们没有任何先前的观察值要连接,我们只是重复连接初始的观察值。
```py
from collections import deque
from gym import spaces
import numpy as np
class ConcatObs(gym.Wrapper):
def __init__(self, env, k):
gym.Wrapper.__init__(self, env)
self.k = k
self.frames = deque([], maxlen=k)
shp = env.observation_space.shape
self.observation_space = \
spaces.Box(low=0, high=255, shape=((k,) + shp), dtype=env.observation_space.dtype)
def reset(self):
ob = self.env.reset()
for _ in range(self.k):
self.frames.append(ob)
return self._get_ob()
def step(self, action):
ob, reward, done, info = self.env.step(action)
self.frames.append(ob)
return self._get_ob(), reward, done, info
def _get_ob(self):
return np.array(self.frames)
```
现在,为了有效地获得修改后的环境,我们将环境`Env`包装在刚刚创建的包装器中。
```py
env = gym.make("BreakoutNoFrameskip-v4")
wrapped_env = ConcatObs(env, 4)
print("The new observation space is", wrapped_env.observation_space)
```
```py
OUTPUT:
The new observation space is Box(4, 210, 160, 3)
```
现在让我们来验证一下这些观测值是否确实是连接在一起的。
```py
# Reset the Env
obs = wrapped_env.reset()
print("Intial obs is of the shape", obs.shape)
# Take one step
obs, _, _, _ = wrapped_env.step(2)
print("Obs after taking a step is", obs.shape)
```
```py
OUTPUT:
Intial obs is of the shape (4, 210, 160, 3)
Obs after taking a step is (4, 210, 160, 3)
```
除了普通的`Wrapper`类之外,`Wrappers`还有更多。Gym 还为您提供了针对特定环境元素的特定包装器,例如观察、奖励和行动。下一节将演示它们的用法。
1. `ObservationWrapper`:这有助于我们使用包装类的`observation`方法对观察进行修改。
2. `RewardWrapper`:这有助于我们使用包装类的`reward`函数对奖励进行修改。
3. `ActionWrapper`:这有助于我们使用包装类的`action`函数对动作进行修改。
让我们假设我们必须对我们的环境进行以下更改:
1. 我们必须将像素观测值归一化 255。
2. 我们必须在 0 和 1 之间削减奖励。
3. 我们必须防止滑块向左移动(动作 3)。
```py
import random
class ObservationWrapper(gym.ObservationWrapper):
def __init__(self, env):
super().__init__(env)
def observation(self, obs):
# Normalise observation by 255
return obs / 255.0
class RewardWrapper(gym.RewardWrapper):
def __init__(self, env):
super().__init__(env)
def reward(self, reward):
# Clip reward between 0 to 1
return np.clip(reward, 0, 1)
class ActionWrapper(gym.ActionWrapper):
def __init__(self, env):
super().__init__(env)
def action(self, action):
if action == 3:
return random.choice([0,1,2])
else:
return action
```
现在,我们在一行代码中将所有这些包装器应用到我们的环境中,以获得修改后的环境。然后,我们验证所有预期的更改都已应用到环境中。
```py
env = gym.make("BreakoutNoFrameskip-v4")
wrapped_env = ObservationWrapper(RewardWrapper(ActionWrapper(env)))
obs = wrapped_env.reset()
for step in range(500):
action = wrapped_env.action_space.sample()
obs, reward, done, info = wrapped_env.step(action)
# Raise a flag if values have not been vectorised properly
if (obs > 1.0).any() or (obs < 0.0).any():
print("Max and min value of observations out of range")
# Raise a flag if reward has not been clipped.
if reward < 0.0 or reward > 1.0:
assert False, "Reward out of bounds"
# Check the rendering if the slider moves to the left.
wrapped_env.render()
time.sleep(0.001)
wrapped_env.close()
print("All checks passed")
```
```py
OUTPUT: All checks passed
```
如果您想在应用包装器后恢复原来的`Env`,您可以使用`Env`类的`unwrapped`属性。虽然`Wrapper`类可能看起来像是`Env`的子类,但它确实维护了一个应用于基类`Env`的包装器列表。
```py
print("Wrapped Env:", wrapped_env)
print("Unwrapped Env", wrapped_env.unwrapped)
print("Getting the meaning of actions", wrapped_env.unwrapped.get_action_meanings())
```
```py
OUTPUT:
Wrapped Env :<ObservationWrapper<RewardWrapper<ActionWrapper<TimeLimit<AtariEnv<BreakoutNoFrameskip-v4>>>>>>
Unwrapped Env <AtariEnv<BreakoutNoFrameskip-v4>>
Getting the mean of actions ['NOOP', 'FIRE', 'RIGHT', 'LEFT']
```
# 矢量化环境
许多深度 RL 算法(如异步 Actor Critic 方法)使用并行线程,其中每个线程运行环境的一个实例,以加快训练过程并提高效率。
现在我们将使用另一个库,也是由 OpenAI 开发的,名为`baselines`。这个库为我们提供了许多标准深度 RL 算法的高性能实现,以便与任何新算法进行比较。除了这些实现,`baselines`还为我们提供了许多其他功能,使我们能够按照 OpenAI 实验中使用的方式来准备我们的环境。
其中一个特性包括包装器,它允许您使用一个函数调用并行运行多个环境。在开始之前,我们首先通过在终端中运行以下命令来安装基线。
```py
!git clone https://github.com/openai/baselines
!cd baselines
!pip install .
```
您可能需要重新启动 Jupyter 笔记本,以使已安装的软件包可用。
这里感兴趣的包装器称为`SubProcEnv`,它将以异步方式运行所有环境。我们首先创建一个返回我们正在运行的环境的函数调用列表。在代码中,我使用了一个`lambda`函数来创建一个返回体育馆环境的匿名函数。
```py
# get the neccasary stuff
import gym
from baselines.common.vec_env.subproc_vec_env import SubprocVecEnv
# list of envs
num_envs = 3
envs = [lambda: gym.make("BreakoutNoFrameskip-v4") for i in range(num_envs)]
# Vec Env
envs = SubprocVecEnv(envs)
```
这个`envs`现在作为一个单一的环境,在这里我们可以调用`reset`和`step`函数。然而,这些函数现在返回一组观察/动作,而不是单个观察/动作。
```py
# Get initial state
init_obs = envs.reset()
# We get a list of observations corresponding to parallel environments
print("Number of Envs:", len(init_obs))
# Check out of the obs
one_obs = init_obs[0]
print("Shape of one Env:", one_obs.shape)
# prepare a list of actions and apply them to environment
actions = [0, 1, 2]
obs = envs.step(actions)
```
```py
OUTPUT:
Number of Envs: 3
Shape of one Env: (210, 160, 3)
```
在矢量化的`envs`上调用`render`函数以平铺方式显示游戏的屏幕截图。
```py
# render the envs
import time
# list of envs
num_envs = 3
envs = [lambda: gym.make("BreakoutNoFrameskip-v4") for i in range(num_envs)]
# Vec Env
envs = SubprocVecEnv(envs)
init_obs = envs.reset()
for i in range(1000):
actions = [envs.action_space.sample() for i in range(num_envs)]
envs.step(actions)
envs.render()
time.sleep(0.001)
envs.close()
```
下面的屏幕显示出来。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/701c1a5ba0dbd78ccf5117a47afac64e.png)
`render` for the `SubProcEnv` environment.
你可以在这里找到更多关于矢量化环境的信息。
## 结论
第 1 部分到此为止。考虑到我们在这一部分所涉及的内容,你应该能够在 OpenAI Gym 提供的环境中开始训练你的强化学习代理。但是,如果您想要培训代理的环境在任何地方都不可用,该怎么办?如果是这样的话,你很幸运,原因有几个!
首先,OpenAI Gym 为您提供了实现自定义环境的灵活性。第二,这正是本系列的第二部分要做的事情。在那之前,享受使用开放式人工智能健身房探索强化学习的进取世界吧!别忘了查看完整代码,并从 [ML Showcase](https://ml-showcase.paperspace.com/projects/reinforcement-learning) 中免费运行。
## 进一步阅读
1. [打开 AI 健身房文档](https://gym.openai.com/docs/)
2. [基线 GitHub 页面](https://github.com/openai/baselines)
# PyPy 入门
> 原文:<https://blog.paperspace.com/getting-started-with-pypy/>
Python 编程语言是一种可以用多种方式实现的接口。一些例子包括使用 C 语言的 CPython,使用 Java 实现的 Jython 等等。
尽管是最流行的,CPython 并不是最快的。PyPy 是另一种 Python 实现,既兼容又快速。PyPy 依赖于实时(JIT)编译,这大大减少了长时间运行操作的执行时间。
在本教程中,将为初学者介绍 PyPy,以突出它与 CPython 的不同之处。我们还将讨论它的优点和局限性。然后我们将看看如何下载并使用 PyPy 来执行一个简单的 Python 脚本。PyPy 支持数百个 Python 库,包括 [NumPy](https://blog.paperspace.com/numpy-optimization-vectorization-and-broadcasting/) 。
具体来说,本教程涵盖以下内容:
* python 的快速概述
* PyPy 及其特性介绍
* PyPy 限制
* 在 Ubuntu 上运行 PyPy
* PyPy 与 CPython 的执行时间
让我们开始吧。
## **CPython 的快速概述**
在讨论 PyPy 之前,了解 CPython 的工作原理是很重要的。我之前的名为[用 Cython](https://blog.paperspace.com/boosting-python-scripts-cython) 提升 Python 脚本的教程详细介绍了 CPython 的工作原理,但是在这里快速回顾一下要点也无妨。下面你可以看到一个使用 CPython 实现的 Python 脚本的执行管道的可视化。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3fd2283b6c1a91769696fd1bfe957e04.png)
给定一个 Python `.py`脚本,首先使用 CPython 编译器将源代码编译成字节码。字节码被生成并保存在扩展名为`.pyc`的文件中。然后在虚拟环境中使用 CPython 解释器执行字节码。
使用编译器将源代码转换成字节码有很多好处。如果不使用编译器,那么解释器将通过逐行翻译成机器码来直接处理源代码。这样做的缺点是必须应用一些过程来将源代码的每一行翻译成机器代码,并且这些过程将对每一行重复。例如,语法分析将独立于其他行应用于每一行,因此解释器需要花费大量时间来翻译代码。编译器解决了这个问题,因为它能够一次处理所有代码,因此语法分析将只应用一次,而不是应用于每一行代码。因此,编译器生成的字节码很容易解释。注意,编译整个源代码在某些情况下可能没有帮助,我们将在讨论 PyPy 时看到一个明显的例子。
字节码生成后,由运行在虚拟机上的解释器执行。虚拟环境是有益的,因为它将 CPython 字节码与机器隔离开来,从而使 Python 跨平台。
不幸的是,仅仅使用编译器来生成字节码不足以加速 CPython 的执行。解释器的工作原理是每次执行代码时,将代码翻译成机器代码。因此,如果一个行`L`需要`X`秒来执行,那么执行它 10 次将会有`X*10`秒的开销。对于长时间运行的操作,这在执行时间上代价太高。
基于 CPython 的弊端,我们现在来看看 PyPy。
## **PyPy 及其特性介绍**
PyPy 是一个类似于 CPython 的 Python 实现,既兼容又快速。“兼容”意味着 PyPy 与 CPython 兼容,因为您可以在 PyPy 中使用几乎所有的 CPython 语法。有一些兼容性的差异,这里提到的[就是](https://pypy.org/compat.html)。PyPy 最强大的优势就是速度。PyPy 比 CPython 快很多;稍后我们将看到 PyPy 执行速度快 7 倍的测试。在某些情况下,它甚至可能比 CPython 快几十倍或几百倍。那么 PyPy 是如何实现它的速度的呢?
### **速度**
PyPy 使用实时(JIT)编译器,能够显著提高 Python 脚本的速度。CPython 中使用的编译类型是超前的(AOT),这意味着所有的代码在执行之前都会被转换成字节码。JIT 只是在运行时翻译代码,只是在需要的时候。
源代码可能包含根本不执行的代码块,但这些代码块仍在使用 AOT 编译器进行翻译。这导致处理时间变慢。当源代码很大并且包含数千行时,使用 JIT 会有很大的不同。对 AOT 来说,整个源代码都需要翻译,因此需要很多时间。对于 JIT,只执行代码中需要的部分,这样速度会快很多。
PyPy 翻译了一部分代码后,就会被缓存。这意味着代码只被翻译一次,以后再使用这个翻译。每次执行代码时,CPython 解释器都要重复翻译,这也是它运行缓慢的另一个原因。
### **毫不费力**
PyPy 不是提升 Python 脚本性能的唯一方法,但却是最简单的方法。例如,Cython 可以用来提高向变量分配 C 类型的速度。问题是 Cython 要求开发人员手动检查源代码并进行优化。这很烦人,而且随着代码大小的增加,复杂性也在增加。当使用 PyPy 时,您只需更快地运行常规 Python 代码,无需任何努力。
### **无堆叠**
标准 Python 使用 C 栈。这个堆栈存储相互调用的函数序列(递归)。因为堆栈的大小是有限的,所以函数调用的次数也是有限的。
PyPy 使用了[无栈 Python](https://wiki.python.org/moin/StacklessPython) ,这是一个**不使用 C 栈**的 Python 实现。相反,它将函数调用存储在对象旁边的堆中。堆的大小大于栈的大小,因此你可以做更多的函数调用。
无栈 Python 也支持微线程,比普通的 Python 线程要好。在单个无堆栈 Python 线程中,您可以运行数千个任务,称为“小任务”,所有这些任务都在同一个线程上运行。
使用微线程允许运行并发任务。并发意味着两个任务通过共享相同的资源同时工作。一个任务运行一段时间,然后停下来为第二个任务腾出空间。请注意,这与并行不同,并行涉及同时单独运行两个任务。
使用微线程减少了创建的线程数量,从而减少了操作系统管理所有这些线程的开销。因此,通过在两个线程之间交换来加速执行比在两个微线程之间交换更耗时。
使用无堆栈 Python 也为实现延续打开了大门。延续允许我们保存任务的状态,并在以后恢复它以继续它的工作。注意,无栈 Python 与标准 Python 没有什么不同;它只是增加了更多的功能。标准 Python 中可用的一切也将在无堆栈 Python 中可用。
在讨论了 PyPy 的好处之后,我们在下一节讨论它的局限性。
## **PyPy 限制**
虽然您可以在任何机器和任何 CPU 架构上使用 CPython,但是 PyPy 的支持相对有限。
下面是 PyPy ( [来源](https://pypy.org/features.html))支持和维护的 CPU 架构:
* x86 (IA-32)和 x86_64
* ARM 平台(ARMv6 或 ARMv7,带 VFPv3)
* aarh64 足球俱乐部
* PowerPC 64 位,小端和大端
* 系统 Z (s390x)
PyPy 不能在所有的 Linux 发行版上工作,所以你必须小心使用一个受支持的版本。在不受支持的发行版上运行 PyPy Linux 二进制文件将会返回错误。PyPy 只支持 Python 2 和 Python 3 的一个版本,分别是 PyPy 2.7 和 PyPy 3.6。
如果在 PyPy 中执行的代码是纯 Python,那么 PyPy 提供的速度通常是显而易见的。但是如果代码包含 C 扩展,比如 NumPy,那么 PyPy 实际上可能会增加时间。PyPy 项目正在积极开发中,因此将来可能会为 C 扩展提供更好的支持。
许多流行的 Python 框架都不支持 PyPy,比如 Kivy。Kivy 允许 CPython 在所有平台上运行,包括 Android 和 iOS。这意味着 PyPy 不能在移动设备上运行。
现在我们已经看到了 PyPy 的优点和局限性,让我们来看看如何在 Ubuntu 上运行 PyPy。
## **在 Ubuntu 上运行 PyPy**
您可以在 Mac、Linux 或 Windows 上运行 PyPy,但是我们将讨论在 Ubuntu 上运行它。再次提及 PyPy Linux 二进制文件仅在特定的 Linux 发行版上受支持是非常重要的。您可以在[这个页面](https://pypy.org/download.html)上查看可用的 PyPy 二进制文件及其支持的发行版。例如,PyPy(Python 2.7 或 Python 3.6)仅支持 Ubuntu 的三个版本:18.04、16.04 和 14.04。如果你有最新版本的 Ubuntu(19.10),那么你不能在上面运行 PyPy。尝试在不受支持的发行版上运行 PyPy 将返回以下错误:
`pypy: error while loading shared libraries ...`
我只是简单的用一个虚拟机运行 Ubuntu 18.04。
PyPy 二进制文件以压缩文件的形式出现。你需要做的就是解压你下载的文件。在解压缩的目录中有一个名为`bin`的文件夹,在其中可以找到 PyPy 可执行文件。我使用的是 Python 3.6,因此这个文件被命名为`pypy3`。对于 Python 2.7,它只是被称为`pypy`。
对于 CPython,如果想从终端运行 Python 3,只需输入命令`python3`。要运行 PyPy,只需发出命令`pypy3`。
在终端中输入`pypy3`命令可能会返回`Command 'pypy3' not found`消息,如下图所示。原因是 PyPy 的路径没有添加到 path 环境变量中。实际运行的命令是`./pypy3`,考虑到终端的当前路径在 PyPy 的`bin`目录中。点`.`表示当前目录,添加`/`是为了访问当前目录中的内容。发出`./pypy3`命令成功运行 Python,如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2d80b6bf9732f5f1de0fb5a20d0bec74.png)
现在,您可以像往常一样使用 Python,充分利用 PyPy 的优势。例如,我们可以创建一个简单的 Python 脚本,对 1,000 个数字求和,并使用 PyPy 执行它。代码如下。
```py
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)
```
如果这个脚本被命名为`test.py`,那么您可以简单地使用下面的命令运行它(假设 Python 文件位于 PyPy 的`bin`文件夹中,这与`pypy3`命令的位置相同)。
```py
./pypy3 test.py
```
下图显示了执行前面代码的结果。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/90521059775055ddb3f250353ce15de0.png)
## **PyPy 与 CPython 的执行时间**
为了比较 PyPy 和 CPython 对 1,000 个数字求和的运行时间,代码被更改为测量时间,如下所示。
```py
import time
t1 = time.time()
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)
t2 = time.time()
t = t2 - t1
print("Elapsed time is : ", t, " seconds")
```
与 CPython 的`0.0002`秒相比,PyPy 的时间接近`0.00045`秒(我在我的 Core i7-6500U 机器上运行代码@ 2.5GHz)。在这种情况下,与 PyPy 相比,CPython 花费的时间更少,这是意料之中的,因为这个任务实际上不是一个长时间运行的任务。如果代码改为添加 100 万个数字,而不是 1000 个,那么 PyPy 将最终获胜。在这种情况下,Pypy 需要`0.00035`秒,CPython 需要`0.1`秒。PyPy 的好处现在很明显。这应该让您了解 CPython 在执行长时间运行的任务时要慢多少。
## **结论**
本教程介绍了 PyPy,这是最快的 Python 实现。PyPy 的主要优点是它的实时(JIT)编译,它提供了对编译后的机器码的缓存以避免再次执行。PyPy 的局限性也得到了强调,主要的一点是它对于纯 Python 代码来说工作得很好,但是对于 C 扩展来说效率不高。
我们还看到了如何在 Ubuntu 上运行 PyPy,并比较了 CPython 和 PyPy 的运行时间,突出了 PyPy 对于长时间运行任务的效率。与此同时,对于短期运行的任务,CPython 仍可能击败 PyPy。在以后的文章中,我们将探索 PyPy、CPython 和 Cython 之间的更多比较。
# 强化学习入门
> 原文:<https://blog.paperspace.com/getting-started-with-reinforcement-learning/>
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cfa3528dff717d7152a2e92615a152f0.png)
Photo by [Visual Stories || Micheile](https://unsplash.com/@micheile?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)
人工智能(AI)是现代史上最具革命性的发展之一。曾经被认为不可能由计算机完成的任务现在可以由人工智能轻松完成,在某些情况下,甚至在某项特定任务上超过人类。随着技术的快速进步和大量数据的可用性,我们有可能开发出 AI 模型和算法来解决各种问题。人工智能是一个庞大的研究领域,由多个分支和子集组成。这些概念包括机器学习、深度学习、自然语言处理、计算机视觉、数学,以及本文关注的主题强化学习。
我们将看一个 2 部分的系列,在第一部分我们将获得对强化学习的直观理解,而在下一部分我们将构建几个关于强化学习的项目。对于本文,我们将在简要介绍这个研究领域之后理解强化学习的主要方面。然后,我们将讨论一些算法和策略,在强化学习的帮助下,我们可以利用它们来完成任务。最后,我们将开发一个具有深度强化学习的基础入门项目,以确保我们完美掌握所有的要点。
### 目录:
1. 强化学习简介
2. 理解强化学习的概念
3. 众多强化学习算法的讨论
4. 深度强化学习入门项目
1。大车杆子项目介绍
2。安装和获取所需的库
3。激活测试环境
4。创建我们的深度学习模型
5。构建我们的 RL 代理
6。保存和重新加载 RL 代理
5. 结论
## 强化学习简介:
人类通常倾向于从经验中学习他们的能力、限制和他们可以执行的行动类型。婴儿在成长过程中,开始学习走路。通常,这个过程似乎很自然,因为大多数人类是从慢慢爬行到站立,最后以婴儿的脚步行走。但是,在最终适应整个行走模拟过程之前,婴儿在学习阶段往往会摔倒很多。这种从失败中不断尝试的学习方法对于人类来说是一种常见的做法。然而,在人工智能领域也存在一个类似的研究分支,它处理被称为强化学习的方法。
在这个研究领域中,我们利用强化学习算法来训练期望的模型,以便从它们以前的错误中学习。一旦模型对它们的工作过程有了直观的理解,它们的结果就会相应地慢慢改善。近年来,人们提出了几种从失败中学习的算法,以应对众多任务,包括自动驾驶人工智能汽车、工业自动化、机器人模拟、征服游戏等等。由于这些强化算法对现实世界中的实际应用的影响,它们的效用在现代继续快速增长。在文章的下一部分,让我们习惯强化学习中的各种概念。
* * *
## 理解强化学习的概念:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b7d2fd43d35c377981c6fb510f8990a4.png)
[Image Source](https://commons.wikimedia.org/wiki/File:Reinforcement_learning_diagram.svg)
强化学习是我们可以训练模型学习的最独特的技术之一,因为它利用试凑法来达到预期的结果。构成强化学习核心构成的五个主要概念是主体、行动、环境、观察和奖励。让我们分别理解这些单独的元素,以及它们在强化学习项目中的意义。
代理是强化学习模型中最重要的组成部分,因为它代表了在环境中运行的核心组成部分,在该环境中,它遵循由特定操作任务和指定策略的原则所支配的一组规则。给予代理的规则允许它决定处理特定问题所需的路径。
另一方面,我们有一个代理可以操作和执行指定任务的环境。对于我们所有的强化学习项目,我们将利用 OpenAI gym,其中有几个预先构建的环境,允许研究人员自由利用和试验众多的强化学习模型和算法。我们将在下一篇文章中学习更多关于 OpenAI gym 的知识,但是让我们学习一些我们需要知道的基本功能。
在 OpenAI 健身房中,我们将利用的一些主要功能如下:
1. **env . reset()**–重置整个环境,获得观察的初始值。
2. **env . render()**–渲染显示工作设置可视化的环境。
3. **env . step()**–继续对选定的环境进行操作。
4. **env . Close()**–关闭环境的特定渲染帧。
动作是代理在相应环境中可以执行的操作。最好用几个例子来理解这个概念。如果你正在尝试编程一个机器人手臂模拟来举起和放下一个物体,那么有两个动作可以执行,即向上和向下运动。另一方面,如果您试图教授一个复杂的问题,如在跳棋、井字游戏或国际象棋等游戏中训练强化学习模型,模型可以考虑的每一个部分都有几种组合和计算。在我们将在本文后面部分讨论的例子中,我们有两个 cart 杆的动作,即左和右。我们将在接下来的章节中进一步理解这个概念。
最后,我们有观察空间或状态和奖励的概念。状态表示正在执行的任务的当前位置。这些状态包括相对于所执行任务的环境,您的座席当前所处的操作类型和位置。状态 s 是对世界状态的完整描述。没有任何关于世界的信息是对国家隐藏的。观察值 o 是状态的部分描述,它可能省略信息。奖励是在正确解释当前问题后奖励给模型的加分。对于代理在相关环境中执行的每个正确动作,代理被奖励一个奖励点或“奖赏”代理遵循的学习原则取决于用户实施的强化学习算法和策略的类型。在本文的下一部分,我们将尝试对一些强化学习算法有一个总体的了解。
* * *
## 众多强化学习算法的讨论;
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/43c2094ff853fa096ffab9a3e3592556.png)
[Image Source](https://spinningup.openai.com/en/latest/spinningup/rl_intro2.html)
强化学习算法是一种方法,在这种方法中,代理获得反馈以在环境中执行期望的动作,从而获得奖励并完成任务。有大量不同类型的强化学习算法,根据你选择询问的专家类型,答案可能会有所不同。我们将关注上面显示的图像表示,并讨论一些我们需要了解的更有用的算法。我还建议看看本文中没有提到的其他算法。
强化学习算法的两个主要分类包括无模型强化学习和基于模型的强化学习。两者之间需要注意的关键区别是,在无模型 RL 中,我们利用当前状态值来进行未来预测,而基于模型的 RL 方法也考虑未来状态来进行各自的预测。诸如推车杆问题的问题可以利用诸如 PPO 或 DQN 算法的无模型方法,而诸如象棋或 shogi 的问题将利用基于模型的方法来预测最佳可能结果。
在无模型强化学习中,我们主要使用两种主要技术来解决许多任务,即基于策略优化的方法和 Q 学习方法。首先,我们将讨论策略优化技术,它利用性能目标,并相应地调整和优化其值以适应策略。在近似策略优化(PPO)方法中,通过最大化替代目标函数,更新间接地最大化性能,替代目标函数给出了性能目标由于更新而改变多少的保守估计。
另一方面,我们有演员-评论家(A2C)强化学习算法,这是另一种无模型方法,执行梯度上升以直接最大化性能。在无模型方法中,Q-learning 是另一种更流行的技术,用于解决各种强化学习项目,如井字游戏。这种技术利用[贝尔曼方程](https://spinningup.openai.com/en/latest/spinningup/rl_intro.html#bellman-equations)和非策略方法来更新值,而不管训练的阶段。深度 Q 网络(DQN)算法是由 Deep Mind 提出的主要算法,也是解决众多问题的流行 Q 学习算法之一。
另一方面,对于基于模型的强化学习方法,我们有 [Alpha Zero](https://arxiv.org/abs/1712.01815) 。Alpha Zero 是强化学习算法所拥有的力量的最流行的例子之一。如果你是一个狂热的国际象棋爱好者,你很有可能会遇到术语国际象棋引擎和 Alpha Zero。拥有强化学习模型的主要优点是它允许代理提前计划。因此,像 Alpha Zero 这样的程序能够在获得高质量结果的同时,攻克诸如 shogi 和 chess 这样的复杂问题。它还能够通过从自我游戏中学习的白板强化学习,在围棋游戏中实现超人的表现。
对于您的特定模型,您还可以选择几个策略来操作。这些策略规定了如何构建您的模型,以及它们将如何在您创建的环境中进行交互和操作。我们将在下一篇文章中使用的稳定基线模块支持三种主要策略,即 MLP 策略、CNN 策略和多输入策略。我们将在下一篇文章中了解更多关于稳定基线 3 的内容。现在,我们将注意到,策略是代理将用来执行特定动作的计划或策略,充当代理状态和环境的功能。请随意探索关于这个主题的更多信息,并查看以下[链接](https://www.baeldung.com/cs/ml-policy-reinforcement-learning)以获得更详细的解释。
* * *
## 深度强化学习入门项目:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1308c8dda463f954fb46fea471266a6a.png)
[Image Source](https://gym.openai.com/envs/CartPole-v1/)
在本文的这一部分,我们将重点构建一个深度强化学习项目。我们将为这个特殊的深度强化学习项目进行一个手推车杆模拟。对于那些不熟悉推车杆子游戏的人,我会推荐你去一些在线网站,在那里你可以尝试自己玩这个游戏。我在下面的游戏中可以尝试的一个这样的网站就是 Flux 网站上的这个[链接](https://fluxml.ai/experiments/cartPole/)。
推车杆问题基本上是一个模拟棒或杆(称为杆),通过一个未驱动的关节连接到在无摩擦平面上移动的推车。这个游戏取决于你在推车上平衡这根杆子的能力。您可以使用左箭头键和右箭头键来平衡柱子的位置,柱子最初是直立的。玩家设法保持柱子直立的每一个动作都加 1 分。万一杆子塌到十五度以下的角度,游戏就结束了,根据计算的结果来评定你的总成绩。
我们将尝试借助深度强化学习来实现解决上述问题的解决方案。为了解决这个任务,我们将利用 OpenAI gym 来构建强化学习模型的环境。我们将在下一篇文章中更多地讨论这个工具,我们将在更好的库的帮助下更详细地讨论这个项目和另一个项目。构建这种深度强化学习模型的下一对要求是利用深度学习框架。
对于本文,我们将利用 TensorFlow 和 Keras 深度学习框架来实现预期的结果。请注意,我们将在下一篇文章中使用 PyTorch 环境,在这里我们将构建几个具有强化学习的项目。我在之前的文章中已经涵盖了与这些深度学习框架相关的所有概念。如果您还没有查阅它们或需要这些概念的简介,请查看以下内容-“[*tensor flow*](https://blog.paperspace.com/absolute-guide-to-tensorflow/)绝对指南”、“Keras 绝对指南”、“PyTorch 最终指南”我也强烈推荐通过这个[链接](https://www.youtube.com/c/NicholasRenotte/videos)查看下面的 YouTube 频道。大部分灵感和代码都参考了上述渠道和本 [GitHub](https://github.com/nicknochnack) 页面。现在让我们开始深度强化学习项目。
### 安装和获取所需的库:
构建深度强化学习项目的主要要求是利用环境来测试我们的特定项目,并利用深度学习框架来构建模型以解决适当的任务。我们还需要一个强化学习代理,它将利用我们期望的算法和策略,结合所有讨论过的元素来解决任务。除了 TensorFlow 和 Keras 深度学习框架之外,您需要安装的主要要求是 OpenAI gym 库和 Keras 强化学习库。确保您在安装了 TensorFlow 和 Keras 最新版本的虚拟环境中工作。在激活的同一个虚拟环境中,运行以下命令来满足该项目的所有必要要求。
```py
pip install gym
pip install keras-rl2
```
要了解这两个特定库的更多信息,我建议查看它们的官方文档,其中传达了大部分重要信息。我们还将在下一篇文章中更详细地查看 OpenAI gym 库,在那里我们将构建几个带有强化学习的项目。然而,你可以查看这个[链接](https://gym.openai.com/docs/)来了解更多关于 OpenAI 健身房的信息,以及这个[网站](https://keras-rl.readthedocs.io/en/latest/)来了解更多关于 Keras 强化学习的信息。
### 激活测试环境:
在我们项目的下一步,我们将构建合适的环境来测试 cart pole 问题。让我们导入几个基本的库,我们将在本文的这一部分使用它们。OpenAI gym 是为了创建和开发我们的测试环境而导入的,而 random 库将帮助我们输入一个随机数 0 或 1,用于 cart pole 的横向移动。
```py
import gym
import random
```
一旦我们导入了所需的库,让我们继续创建用于创建和测试强化学习模型的环境。在 make 函数的帮助下,我们将创建 CartPole-v0 环境。我们现在将解释状态,这是可能的观察数量,也解释可能的动作数量。注意,有四种状态,即小车的位置、小车的速度、小车的角度和角速度。动作的数量包括两个,即车杆的向左和向右运动。
```py
env = gym.make('CartPole-v0')
states = env.observation_space.shape[0]
actions = env.action_space.n
actions
```
在下一个代码块中,我们将在总共十集的环境中测试该模型,以查看它在没有任何训练的情况下如何执行。我们将实现左或右的随机点击,随机选择 0 和 1。对于每一步,我们都会计算分数。每向右移动一步,就会得到分数,而如果车杆未能保持直立 15 度角,游戏结束,并显示每集结束时计算的最终分数。检查下面的代码块以理解以下实现的过程。
```py
episodes = 10
for episode in range(1, episodes+1):
state = env.reset()
done = False
score = 0
while not done:
env.render()
action = random.choice([0,1])
n_state, reward, done, info = env.step(action)
score+=reward
print('Episode:{} Score:{}'.format(episode, score))
```
在接下来的部分中,我们将继续构建深度学习模型和适当的强化学习代理来处理这个项目。我们的目标是每集至少得 200 分。
### 创建我们的深度学习模型:
现在让我们着手构建一个简单的深度学习架构,我们将利用它来构建模型。第一步是导入该过程的所有必要需求。我们将导入一个序列模型,以及一个完全连接结构的密集层和一个展平层。我们还将利用 Adam 优化器。我建议尝试不同的方法和途径来测试哪种模型最适合你的培训模型。
```py
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.optimizers import Adam
```
下一步是创建一个函数,我们将在其中构建模型架构。我们将构建一个顺序模型,并将其展平到四个状态,从这四个状态开始,我们将通过两个完全连接层的隐藏节点,这些层具有 TensorFlow 和 Keras 的密集层。在最后一层,我们将利用线性激活函数并解释两种可能的输出,即左键或右键单击。一旦我们的模型被构建,我们将继续进行下一步来构建 RL 代理。
```py
def build_model(states, actions):
model = Sequential()
model.add(Flatten(input_shape=(1,states)))
model.add(Dense(24, activation='relu'))
model.add(Dense(24, activation='relu'))
model.add(Dense(actions, activation='linear'))
return model
```
### 构建我们的 RL 代理:
在构建我们的 RL 代理来处理这个项目之前,我们将导入这个任务所需的所有库。我们将利用 DQN 代理,你可以从[这里](https://keras-rl.readthedocs.io/en/latest/agents/dqn/)检查,也利用波尔兹曼 Q 策略与顺序存储器。所有必需的导入如下面的代码片段所示。
```py
from rl.agents import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory
```
在下一个代码块中,我们将构建一个用于构建 RL 代理的函数,类似于我们的模型构造。我们将定义基本参数,如策略、内存和处理问题的代理。下面的代码块说明了如何构造所需的函数。
```py
def build_agent(model, actions):
policy = BoltzmannQPolicy()
memory = SequentialMemory(limit=50000, window_length=1)
dqn = DQNAgent(model=model, memory=memory, policy=policy,
nb_actions=actions, nb_steps_warmup=10, target_model_update=1e-2)
return dqn
```
我们现在可以调用之前定义的模型函数,如下所示。
```py
model = build_model(states, actions)
model.summary()
```
```py
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_1 (Flatten) (None, 4) 0
_________________________________________________________________
dense_3 (Dense) (None, 24) 120
_________________________________________________________________
dense_4 (Dense) (None, 24) 600
_________________________________________________________________
dense_5 (Dense) (None, 2) 50
=================================================================
Total params: 770
Trainable params: 770
Non-trainable params: 0
_________________________________________________________________
```
### 注意:
```py
AttributeError: 'Sequential' object has no attribute '_compile_time_distribution_strategy'
```
如果要避免上述文本框中显示的以下错误,请确保模型始终在 Keras RL 导入后构建。否则,可能会出现顺序内存混合的情况,从而导致显示上述错误消息,并且您无法继续运行程序以获得预期的结果。
类似于调用我们的模型架构,我们也将使用各自的深度学习模型来构建所需的代理。一旦我们完成了用该模型构建我们的代理的过程,我们就可以继续编译和训练深度强化学习模型,并测试该模型的性能。
```py
dqn = build_agent(model, actions)
dqn.compile(Adam(lr=1e-3), metrics=['mae'])
dqn.fit(env, nb_steps=50000, visualize=False, verbose=1)
```
一旦训练过程完成,我们就可以加载每集的分数,并测试该模型的性能。我们应该能达到每集至少 200 分。
```py
scores = dqn.test(env, nb_episodes=100, visualize=False)
print(np.mean(scores.history['episode_reward']))
```
您还可以通过深度强化学习模型可视化您的结果,以检查 cart pole 的性能。
```py
_ = dqn.test(env, nb_episodes=15, visualize=True)
```
### 保存和重新加载 RL 代理:
我们将查看的最后一部分是经过训练的深度强化学习模型的权重的保存和重新加载。以 h5 或 h5f 格式保存与 TensorFlow 模型相似的权重,如下所示。
```py
dqn.save_weights('dqn_weights.h5f', overwrite=True)
```
如果您正在重新加载项目,并希望检查性能,这是非常简单的。重新加载健身房环境、模型结构、可能的动作和状态,最后编译模型。遵循下面的代码块表示来完成这个任务。
```py
env = gym.make('CartPole-v0')
actions = env.action_space.n
states = env.observation_space.shape[0]
model = build_model(states, actions)
dqn = build_agent(model, actions)
dqn.compile(Adam(lr=1e-3), metrics=['mae'])
```
最后一步是为我们的训练模型加载保存的权重。该步骤如下所示执行。
```py
dqn.load_weights('dqn_weights.h5f')
```
现在,您可以继续测试加载的训练模型,并直观地检查性能。
```py
_ = dqn.test(env, nb_episodes=5, visualize=True)
```
建议尝试代理、策略和模型架构的多种组合,看看您的项目如何相应地变化。在接下来的文章中,我们将查看构建 cart pole 项目的另一种方法,并了解如何使用强化学习解决自动驾驶汽车项目。
* * *
## 结论:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a1d573dbeb0188fd68b9c45169d7b91a.png)
Photo by [Element5 Digital](https://unsplash.com/@element5digital?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)
在强化学习的帮助下,我们可以构建各种各样的项目,其中 RL 代理学习如何适应特定的场景。在强化学习的所有概念的知识的帮助下,人们可以很容易地构建一个算法来完成像推车杆子问题这样的任务。强化学习算法在成败中学习,在成功中不断更新。这种实现方法在未来的许多应用中具有非常高的范围,值得学习。
在本文中,我们简要介绍了强化学习。我们理解了与这个主题相关的一些重要的概念。我们还简要地看了一些算法和策略,我们可以利用它们来构建许多项目和解决与强化学习相关的任务。最后,我们在 OpenAI gym 环境、TensorFlow 和 Keras 深度学习框架以及 Keras-RL 库的帮助下构建了一个深度强化学习项目。我建议尝试不同的变化,并检查模型的性能。我们将在下一篇文章中学习更多解决 RL 项目的方法。
在接下来的文章中,我们将看几个我们可以用强化学习构建的项目,让我们探索我们可以用机器学习领域完成的不同类型任务的无限可能性。在那之前,继续做新的独特的项目吧!
# scikit 入门-学习
> 原文:<https://blog.paperspace.com/getting-started-with-scikit-learn/>
机器学习领域正在以惊人的速度发展。这种发展最有趣的一个方面是围绕它产生的社区。仔细观察,我们可以看到 ML 社区可以分成几个小领域,对学科的不同方面感兴趣。例如,有些人对学习过程背后的数学和统计学感兴趣...
## 介绍
机器学习领域正在以惊人的速度发展。这种发展最有趣的一个方面是围绕它产生的社区。仔细观察,我们可以看到 ML 社区可以分成几个小领域,对学科的不同方面感兴趣。例如,有些人对学习过程背后的数学和统计学感兴趣。其他人对开发一个要求使用 ML 工具的想法感兴趣。如果你认定自己是后一种情况,你必须学会如何使用专业的 ML 库。在今天可用的 ML 库中, [scikit-learn](http://scikit-learn.org/stable/) 是最好的选择之一。
![enter image description here](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/18cb98c4bad5f1abd9dddf8fa2ea0bf0.png "scikit_learn_logo.png")
Scikit-learn 是一个 Python 开源库,旨在从头到尾解决机器学习问题。它被 Evernote 和 Spotify 这样的大公司使用并受到好评。此外,它还为典型的 ML 工作流程的所有步骤提供了工具。您可以使用它来加载数据,将数据集分为训练集和测试集,执行维度缩减和特征选择,训练几个众所周知和实施良好的算法,并使用模型选择微调您的超参数。最终结果是用于预测模型的健壮、有效和良好编码的解决方案。最棒的是,您将在一个快速的开发周期中完成所有这些工作,这是 Python 程序员/开发人员所习惯的。
为了向您介绍这个强大的库,我们将构建一个预测模型来解决 ML 中最常见也是最重要的问题:分类。我们将使用一个简单的 ML 工作流,其中我们加载一些数据集,解析它,预处理它,拟合模型并评估泛化误差。由于 *scikit-learn* 不是一个专门研究数据可视化的库,我们还将在工作流程的一些步骤中使用一点 *pandas* 和 *seaborn* 。
## 使用 scikit 分类-学习
### 加载、解析和可视化数据
我们启动机器学习项目首先需要的是**数据**。更具体地说,在我们的分类问题中,需要发现模式的几个有标记的例子。关于 *scikit-learn* 的第一件很酷的事情是它已经包含了一个名为 *sklearn.dataset* 的包,它可以帮助我们完成这项任务。这个包有几个“玩具数据集”,这是一个很好的方式来熟悉处理数据,并将它们提供给不同的 ML 算法。它还具有随机样本生成器,能够构建任意复杂程度和大小的数据集。最后,还有更高级的功能,可以帮助您获取现实世界问题中使用的真实数据集。
因为这是我们第一次使用 *scikit-learn* 的教程,所以让我们使用著名的 iris flower“玩具数据集”,它是由 Fisher 在 1936 年研究的。数据集具有以下属性:
* 基本描述:给出一种花的一些形态特征,确定它的种类
* 150 个样本
* 3 类:刚毛藻、杂色藻和海滨藻
* 4 个特点。所有特征都是真实和积极的
* 每类 50 个样本
要将该数据集加载到我们的程序中,并将其分为训练集和测试集,只需键入以下代码:
```py
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris_dataset = load_iris()
X, y = iris_dataset.data, iris_dataset.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=31)
```
执行上面的代码,我们为测试集分离出 25 %的原始数据集,而其余的进入训练集。此外,我们可以控制原始数据集的混洗,指定 *random_state* 参数。
既然我们已经将数据集加载到程序中,那么查看一些数据样本是很有趣的。我们可以选择打印一些样本的*numpy*n 数组,直接操作 X 和 y。然而,这是一个非常原始和丑陋的可视化,所以这种做法不应该被鼓励。更好的选择是使用*熊猫*图书馆。对于那些不知道它的人来说,*熊猫*是一个用于数据分析的库,它主要处理表格数据和时间序列。让我们用它随机打印出 10 %的数据集,并以表格的形式显示出来。
```py
import numpy as np
import pandas as pd
df = pd.DataFrame(
data=np.c_[X,y],
columns=iris_dataset['feature_names'] + ['target'])
df.sample(frac=0.1)
```
![enter image description here](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1dab537be46fdb22634c561e127b534e.png "pandas_df.png")
从表中,我们可以看到要素名称、测量单位,并确认上面提到的数据集的属性(4 个要素、3 个类、...).尽管对于这个“玩具数据集”来说是显而易见的,但这种做法在真实场景中非常有用,因为我们可能最终会处理一个没有详细描述的数据集。
### 预处理阶段
几乎每个原始数据集都需要一些预处理,然后我们才能成功地使用它来训练 ML 算法(除非有人已经为我们做了)。常见的预处理任务包括特征标准化、降维和特征选择。由于我们使用的是一个简单的数据集,其特征很少,因此我们将只进行标准化步骤。
```py
from sklearn import preprocessing
scaler = preprocessing.StandardScaler().fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
```
该代码保证所有特征的均值和单位方差为零,这是大多数最大似然算法正常工作的先决条件。
### 训练和评估泛化
现在到了这个过程中最重要的部分:训练机器,测试它是否已经将这个概念推广到了训练集之外。在我看来,这是 *scikit-learn* 最出彩的地方。这个 ML 库使用称为*估算器*的 Python 对象,这些估算器必须实现方法 *fit(X,y)* 和 *predict(T)* 。没错,您的训练是在一行代码中执行的,而测试代码的所有预测都是在一行代码中完成的!为了评估我们机器的通用性,我们使用包 *sklearn.metrics* 中定义的度量之一。下面的代码显示了一个例子,其中我们训练一个支持向量机来使用准确度分数对我们的数据进行分类。
```py
from sklearn import svm
from sklearn.metrics import accuracy_score
clf = svm.SVC(gamma=0.001, C=100.)
clf.fit(X_train, y_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
acc_train = accuracy_score(y_train, y_pred_train)
acc_test = accuracy_score(y_test, y_pred_test)
```
在上面的代码中,我们用一组固定的超参数 *gamma* 和 *C* 训练了一只 SVM。这将为我们的训练集和测试集提供大约 97 %的准确率,这是非常好的结果。为了获得关于我们机器性能的更多细节,我们可以计算并可视化混淆矩阵。而且,正如你可能已经怀疑的那样, *scikit-learn* 也有一个为我们计算矩阵的功能。
```py
#If you are using a Jupyter notebook, use the magic command below. Else, ignore it.
%matplotlib inline
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
confusion_matrix = confusion_matrix(y_test,y_pred_test) # Returns a ndarray
cm_df = pd.DataFrame(
confusion_matrix,
index = [idx for idx in ['setosa', 'versicolor', 'virginica']],
columns = [col for col in ['setosa', 'versicolor', 'virginica']])
plt.figure(figsize = (10,7))
sns.heatmap(cm_df, annot=True)
```
![enter image description here](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/94961608615e1ffcb804ba9e8f5aa042.png "cm.png")
在上面的代码中,我们再次使用了 *pandas* 的能力,以及可视化库 *seaborn* 来为我们在测试集中的结果生成一个漂亮而详细的可视化。从混淆矩阵中我们可以看出,唯一的错误是一只云芝被误认为是海滨锦鸡儿。
因此,我们已经在 *scikit-learn* 中完成了第一台机器的训练。恭喜你!但不要以为这就是结局。相反,在您的工作流程中有大量的模块需要发现、更改和组合,以获得新的更好的结果。此外,由于该库是开源的,我们甚至可以读取代码并修改它以针对特定的数据集。我们可以在我们的模型中做出的一个可能的改变是切换*估计器*,并使用费希尔线性判别分析代替 SVM。这种改变可以通过几行代码实现:
```py
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
clf = LinearDiscriminantAnalysis()
clf.fit(X_train, y_train)
```
使用*预测*方法,我们最终应该对训练集达到大约 98 %的准确率,对测试集达到 97 %的准确率。获得比那些更好的结果是不容易的。尽管如此,如果你想达到 100 %,下一步将是超参数调整。这将需要再分离一组用于超参数的验证或使用交叉验证。是的, *scikit-learn* 也为你想通了这一点。
## 结论
在本教程中,向您介绍了一个名为 *scikit-learn* 的 Python 机器学习库。沿着教程你了解到 *scikit-learn* :
* 是一个被专业人士和大公司称赞的开源 ML 库。
* 能够帮助您解决在构建预测模型时将要面临的大多数问题
* 用几行代码给出解决方案
* 应该与数据分析和数据可视化库结合使用,以充分利用它
* 有几个模块应该进行实验,连接,并在追求最佳结果的变化
对于接下来的步骤,你应该前往 *scikit-learn* [网页](http://scikit-learn.org/stable/auto_examples/index.html)中的示例页面,获取一些灵感。你也可以浏览它的[快速入门](http://scikit-learn.org/stable/tutorial/basic/tutorial.html)、[教程](http://scikit-learn.org/stable/tutorial/index.html)和[用户指南](http://scikit-learn.org/stable/user_guide.html)页面,更全面地了解图书馆提供的一切。对于那些对深度学习更感兴趣的人,你应该知道 *scikit-learn* 不具备创建严肃的深度学习神经网络的要求。不过有一个叫 [*skflow*](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/learn/python/learn) 的项目给了 TensorFlow 一个 *scikit-learn* 接口。
要开始使用您自己的 ML-in-a-box 或数据科学设置,[请在此注册。](https://www.paperspace.com/account/signup?utm-campaign=scikitblog)
# PyTorch 和 TensorFlow 中的 GhostNet (CVPR 2020)
> 原文:<https://blog.paperspace.com/ghostnet-cvpr-2020/>
2020 年 CVPR 奥运会带来了计算机视觉领域的新颖想法,以及 3D 视觉领域的许多有趣想法。在所有这些被探索的新想法中,一篇由[华为](http://www.noahlab.com.hk/#/home)、[悉尼大学](https://www.sydney.edu.au/)和[北京大学](http://english.pku.edu.cn/)的研究人员撰写的著名论文《GhostNet:廉价运营的更多特点 成功吸引了一些人的眼球。这个想法,尽管相当简单,却能够实现有竞争力和有趣的结果。
本文将从覆盖卷积神经网络中特征映射背后的基础开始,我们将观察这些映射中对 CNN 性能至关重要的一般模式。然后,我们将对 GhostNet 做一个回顾,并深入分析它的功能和缺点。
### 目录:
* 盘旋
* 特征映射模式冗余
* 幽灵网
* 深度方向卷积
* 密码
* 结果
* 缺点
* 参考资料和进一步阅读
## 盘旋
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/951a9d86764097abfa4d18947d1a3fea.png)
Standard 2-D Convolution
在我们深入管理 GhostNet 的概念之前,有必要简要回顾一下卷积算法。对标准卷积神经网络架构中使用的卷积算法有深刻理解的读者可以跳过这一部分。要更正式地介绍和深入剖析卷积,标题为[](https://arxiv.org/abs/1603.07285)*的文章是一个很好的起点。在这里,我们将只回顾足够了解 GhostNet 所需的知识。*
*总之,卷积是任何卷积神经网络(CNN)中涉及的最基本的过程。卷积源于基本的信号和图像处理,是一种简单的图像滤波过程,它使用内核来传播原始图像的某些基于纹理的信息。*
*![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/544892add30f5c3166b50b8292ca5f31.png)
Convolution using a predefined kernel on an input image*
*在图像处理中,要理解图像的语义,关键是增强或保留某些基于纹理的信息,这些信息更容易使用算法进行处理。传统技术包括检测图像中对象的边缘、勾勒轮廓、寻找角点等。计算机视觉(以及一般的深度学习)的发展为算法提供了使用可训练的卷积核来学习输入图像中的这些特征的能力,这形成了卷积神经网络架构的基础。*
*这些滤波器/内核也称为“权重矩阵”,包含应用于输入图像的数值,并通过反向传播进行调整,以捕捉给定输入图像的内在特征表示。一般来说,卷积层取一个 *C* 通道的输入张量,输出一个*C**'**通道的张量。这些 *C **'*** 通道由该层中存在的过滤器/内核的数量决定。**
*既然已经过了深度 CNN 中关于卷积的背景检查,那就来了解一下 GhostNet 论文的基础: ***特征图*** 。*
## *特征映射模式冗余*
*要素地图是通过应用标准卷积图层根据输入生成的空间地图。这些特征映射负责基于该层的学习卷积滤波器权重来保存/传播输入图像的某些特征丰富的表示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4cfe7b880d4fe93ee06fd7739649910c.png)
Feature Maps ([Source](https://debuggercafe.com/visualizing-filters-and-feature-maps-in-convolutional-neural-networks-using-pytorch/))
GhostNet 的作者分析了标准卷积层中的特征图,以了解它们是否表现出某种模式。在研究中,他们注意到在卷积层生成的整个特征映射集中,存在许多独特的内在特征映射的相似副本,这些副本变得多余,无法通过相当昂贵的卷积运算来生成。从上图所示的一组特征地图中可以看出,存在几个看起来相似的副本,如第 6 行、第 1 列和第 3 列中的副本。
作者创造了术语“幽灵特征地图”来指代这些冗余副本。这是本文的动机基础:我们如何减少生成这些冗余特征图的计算量?
# 幽灵网
减少参数,减少 FLOPs,获得接近基线的精度,这是一个绝对的三赢局面。这就是华为诺亚方舟实验室在 GhostNet 上的工作的动机,该工作在 CVPR-2020 上发表。尽管这种方法乍看起来可能很复杂,但它相当简单,易于解释和验证。本文旨在为标准深度卷积神经网络中使用的自然卷积层提供一种更廉价的替代方案。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/96dab50030821cf0641852672de3b702.png)
### 那么是什么想法呢?
正如我们之前看到的,卷积层接收由输入通道数量定义的输入张量 *C* ,并输出*C**'**通道的张量。这些通道本质上是上面讨论的特征映射,正如我们看到的,在那个集合中存在这些特征映射的冗余副本。GhostNet 不是通过标准卷积来创建所有的特征图,而是生成总输出特征图的 *x* %,而其余的是通过廉价的线性运算来创建的。这种廉价的线性运算导致参数和触发器的大量减少,同时保持与原始基线模型几乎相同的性能。廉价的线性运算被设计成类似于固有卷积的特性,并且通常应该是可学习的和依赖于输入的,使得它可以在使用反向传播的反向传递中被优化。*
在我们讨论 GhostNet 中使用的精确线性运算/变换之前,让我们花点时间来注意一下在神经网络中引入稀疏性或使其更有效的其他类似想法。最近提出了几种修剪神经网络的方法,它们显著降低了深度神经网络的复杂性。然而,这些方法中的大多数只能在训练后,即,在原始深度模型已经被训练之后被结合。因此,它们主要用于将训练模型压缩到最小尺寸,同时保持其性能。
该领域的一个突出想法,也称为*过滤器修剪*,是通过一个简单的过程完成的。一旦训练了模型,就获得了模型的任何层中的经训练的过滤器,并且计算了这些过滤器中的权重的相应绝对和。只有具有较高绝对权重和的 *top-x* 滤波器被保留,而剩余的滤波器被丢弃。
现在,回到 GhostNet 中的“廉价线性变换”。该论文使用*深度方向卷积*(通常表示为 DWConv)作为其廉价的线性变换。我们在前面几节讨论了什么是卷积。那么,什么是*深度方向卷积(DWConv)* ?
## 深度方向卷积
卷积是标准深度神经网络架构的圣杯,主要用于基于计算机视觉的问题。然而,卷积确实有某些缺点,这些缺点已经在多年来的许多著作中得到解决。有些包括在卷积层中使用的滤波器/内核之前添加一个结构,以便它们是上下文自适应的。其他包括改变控制卷积运算的架构过程。通常在深度神经网络中,卷积层从先前输出的激活中接收输入,该输出是表示为( *B,C,H,W* )的四维张量,也称为信道优先格式(否则为( *B,H,W,C* )格式)。这里, *B* 代表训练时使用的批量; *H* 和 *W* 表示输入的空间维度,即输入特征图的高度和宽度,最后 *C* 表示输入张量中的通道数。*(注:TensorFlow 通常遵循后一种格式指定张量的维数:(N,H,W,C),其中 N 与 B 相同,即批量。PyTorch 使用通道优先格式作为标准实践)*。因此,在计算多声道内在卷积时,将滤波器(与输入深度相同)应用于输入张量,以产生所需数量的输出声道。这增加了与输入声道数量和输出声道数量相对应的复杂性。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e5b9aa283f672bfc5ad798a33dd49047.png)
Depthwise Convolution for a 3-Channel Tensor
***深度方向卷积*** 被引入以解决由正常卷积引起的高参数和 FLOPs 的问题。不是在输入的所有通道上应用滤波器来生成输出的一个通道,而是将输入张量分割成单独的通道,然后仅在一个切片上应用滤波器;因此,术语“*深度方向*,基本上指的是每通道卷积。简单地说,2D 滤波器/核/窗与一个通道卷积,该通道是二维切片,以输出该层的输出张量的一个通道(也是 2-D)。这减少了自然卷积中线性增加的计算开销。然而,有一个警告:对于深度方向卷积来说,为了提供递增的速度和较低的复杂度,输入张量中的通道数和特定层中的结果输出张量应该匹配。因此,这是一个双重过程:1 .将 2-D 滤波器与输入张量的每个通道进行卷积,以生成 2-D 输出通道,然后 2。连接/堆叠这些二维通道以完成输出张量。(要进一步阅读不同类型的卷积以及对它们的深入分析,我建议浏览一下[这篇文章](https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning/#:~:text=Depthwise%20convolution&text=Split%20the%20input%20into%20channels,the%20output%20tensors%20back%20together.))。
因此,深度方向卷积导致大量的参数减少和减少的运算,使得计算更便宜,同时保持与固有卷积的密切关系。这就是 GhostNet 表演背后的“魔力”。虽然作者在他们的论文中没有明确提到深度方向卷积作为廉价的线性变换,但这纯粹是一种设计选择。这个问题甚至在该论文的官方知识库的这一期 GitHub 中被提出。
GhostNet 因此提出了深度神经网络架构中标准卷积层的基本独立的替换层,其中任何卷积层的输出张量现在都是通过两个操作的串行化来创建的。首先,通过三层的顺序堆栈为输出张量生成总通道的 *x* %:标准卷积,然后是批量归一化和非线性激活函数,该函数默认定义为校正线性单元(ReLU)。然后将其输出传递给第二个模块,第二个模块也是三层的顺序堆栈:深度方向卷积,然后是批量归一化和 ReLU。最后,来自第一顺序块的张量与来自第二顺序块的输出堆叠,以完成输出张量。
让我们通过一组简单的数学步骤来理解这一点:
假设 *x* 是深度神经网络架构中特定卷积层的维数( *B,C,H,W* 的输入张量。该层的输出预计为维度的*x[1]T7(*B,C[1] ,H[1] ,W[1]* )。以下操作定义了一个代替标准卷积使用的幻像卷积:*
**步骤 1(初级卷积)**:对输入张量 *x* 计算 *f(x)* 生成维数为( *B,C[1/2] ,H[1] ,W[1]* )的张量,其中 *f(x)* 代表标准卷积加批量归一化加 ReLU。这个新张量可以表示为*y[1]。
**第二步(二次卷积)**:在张量 *y[1]* 上计算 *g(x)* 生成维数为( *B,C[1/2] ,H[1] ,W[1]* )的张量其中 *g(x)* 代表深度方向卷积加批量归一化加 ReLU。这个新张量可以表示为*y[2]。
**第三步【堆栈】**:将 *y[1]* 和 *y[2]* 进行堆栈/串接,形成合成输出张量 *x[1]* 。**
在上面的步骤中,我们将初级和次级块生成的特征图的数量固定为张量应该具有的总输出特征图的 50%。但是,图层可以灵活地以不同的比率进行计算。50%反映了该层的一个参数,用 *s* 表示,默认值为 2。通常,论文以这一比率(称为“比率 2”)记录观察结果,然而,随着比率的增加,可以在速度和精度之间进行权衡,进一步减少参数。更高的 *s* 值实质上意味着更多的特征图由深度方向内核计算,而不是由主块中的标准卷积计算。这意味着更大的模型压缩和更高的速度,但精度较低。
作者对深度方向卷积滤波器的不同核大小进行了烧蚀实验。结果将在下一节 *GhostNet 结果*中讨论。
作者还提出了一种新的主干架构,称为 GhostNet,它本质上是一个 [MobileNet v3](https://arxiv.org/abs/1905.02244) ,其中的瓶颈被一个 *Ghost 瓶颈*所取代。Ghost 模块实质上构成了这些 Ghost 瓶颈的基础,它们遵循与标准 MobileNet v3 瓶颈相同的体系结构。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/de200026d11010570110aefbfbb6d38f.png)
GhostNet 是通过在输入层(标准卷积层)之后的一系列张量中堆叠这些具有递增通道的 ghost 瓶颈来构建的。(注意:输入卷积层,通常被称为模型的*主干*,没有被替换为虚卷积块)。重影瓶颈根据输入特征图的维度分阶段分组。所有 ghost 瓶颈都应用了 stride 1,除了最后一个瓶颈使用了 stride 2 设计,如上图所示。对于重影瓶颈中的一些剩余连接,作者甚至使用[挤压激励(SE)](https://arxiv.org/abs/1709.01507) 块来提供通道注意,从而以较小的计算开销提高精度。
## GhostNet 代码
让我们深入研究 Ghost 块和 Ghost 瓶颈的代码,它们可以简单地集成到任何基线卷积神经网络中。
我们将从基于 PyTorch 和 TensorFlow 的虚卷积代码开始,它可以用作标准卷积层的直接交换。
### 重影卷积(PyTorch)
```py
### Import necessary libraries and dependencies
import torch
import torch.nn as nn
import math
class GhostModule(nn.Module):
def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
super(GhostModule, self).__init__()
self.oup = oup
### Compute channels for both primary and secondary based on ratio (s)
init_channels = math.ceil(oup / ratio)
new_channels = init_channels*(ratio-1)
### Primary standard convolution + BN + ReLU
self.primary_conv = nn.Sequential(
nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),
nn.BatchNorm2d(init_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)
### Secondary depthwise convolution + BN + ReLU
self.cheap_operation = nn.Sequential(
nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),
nn.BatchNorm2d(new_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)
def forward(self, x):
x1 = self.primary_conv(x)
x2 = self.cheap_operation(x1)
### Stack
out = torch.cat([x1,x2], dim=1)
return out[:,:self.oup,:,:]
```
### GhostNet 卷积(张量流)
```py
### Import necessary dependencies and libraries
import tensorflow as tf
from tensorpack.models.common import layer_register
from tensorpack.utils.argtools import shape2d, shape4d, get_data_format
from tensorpack.models import BatchNorm, BNReLU, Conv2D
import math
import utils
### Depthwise convolution kernel weight initializer
kernel_initializer = tf.contrib.layers.variance_scaling_initializer(2.0)
### Secondary Depthwise Convolution layer
@layer_register(log_shape=True)
def MyDepthConv(x, kernel_shape, channel_mult=1, padding='SAME', stride=1, rate=1, data_format='NHWC',
W_init=None, activation=tf.identity):
in_shape = x.get_shape().as_list()
if data_format=='NHWC':
in_channel = in_shape[3]
stride_shape = [1, stride, stride, 1]
elif data_format=='NCHW':
in_channel = in_shape[1]
stride_shape = [1, 1, stride, stride]
out_channel = in_channel * channel_mult
if W_init is None:
W_init = kernel_initializer
kernel_shape = shape2d(kernel_shape) #[kernel_shape, kernel_shape]
filter_shape = kernel_shape + [in_channel, channel_mult]
W = tf.get_variable('DW', filter_shape, initializer=W_init)
conv = tf.nn.depthwise_conv2d(x, W, stride_shape, padding=padding, rate=[rate,rate], data_format=data_format)
if activation is None:
return conv
else:
return activation(conv, name='output')
def GhostModule(name, x, filters, kernel_size, dw_size, ratio, padding='SAME', strides=1, data_format='NHWC', use_bias=False,
activation=tf.identity):
with tf.variable_scope(name):
init_channels = math.ceil(filters / ratio)
### Primary standard convolution
x = Conv2D('conv1', x, init_channels, kernel_size, strides=strides, activation=activation, data_format=data_format,
kernel_initializer=kernel_initializer, use_bias=use_bias)
if ratio == 1:
return x #activation(x, name='output')
dw1 = MyDepthConv('dw1', x, [dw_size,dw_size], channel_mult=ratio-1, stride=1, data_format=data_format, activation=activation)
dw1 = dw1[:,:,:,:filters-init_channels] if data_format=='NHWC' else dw1[:,:filters-init_channels,:,:]
### Stack
x = tf.concat([x, dw1], 3 if data_format=='NHWC' else 1)
return x
```
现在让我们看看 Ghost 瓶颈的 PyTorch 实现,它被用作 GhostNet 的构建块。
### 幽灵瓶颈(PyTorch)
```py
### Squeeze Excitation Block
class SELayer(nn.Module):
def __init__(self, channel, reduction=4):
super(SELayer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channel, channel // reduction),
nn.ReLU(inplace=True),
nn.Linear(channel // reduction, channel), )
def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
y = torch.clamp(y, 0, 1)
return x * y
### DWConv + BN + ReLU
def depthwise_conv(inp, oup, kernel_size=3, stride=1, relu=False):
return nn.Sequential(
nn.Conv2d(inp, oup, kernel_size, stride, kernel_size//2, groups=inp, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)
### Ghost Bottleneck
class GhostBottleneck(nn.Module):
def __init__(self, inp, hidden_dim, oup, kernel_size, stride, use_se):
super(GhostBottleneck, self).__init__()
assert stride in [1, 2]
self.conv = nn.Sequential(
# pw
GhostModule(inp, hidden_dim, kernel_size=1, relu=True),
# dw
depthwise_conv(hidden_dim, hidden_dim, kernel_size, stride, relu=False) if stride==2 else nn.Sequential(),
# Squeeze-and-Excite
SELayer(hidden_dim) if use_se else nn.Sequential(),
# pw-linear
GhostModule(hidden_dim, oup, kernel_size=1, relu=False),
)
if stride == 1 and inp == oup:
self.shortcut = nn.Sequential()
else:
self.shortcut = nn.Sequential(
depthwise_conv(inp, inp, kernel_size, stride, relu=False),
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
)
def forward(self, x):
return self.conv(x) + self.shortcut(x)
```
### 幽灵网(PyTorch)
```py
__all__ = ['ghost_net']
def _make_divisible(v, divisor, min_value=None):
"""
It ensures that all layers have a channel number that is divisible by 8
It can be seen here:
"""
if min_value is None:
min_value = divisor
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
# Make sure that round down does not go down by more than 10%.
if new_v < 0.9 * v:
new_v += divisor
return new_v
class GhostNet(nn.Module):
def __init__(self, cfgs, num_classes=1000, width_mult=1.):
super(GhostNet, self).__init__()
# setting of inverted residual blocks
self.cfgs = cfgs
# building first layer
output_channel = _make_divisible(16 * width_mult, 4)
layers = [nn.Sequential(
nn.Conv2d(3, output_channel, 3, 2, 1, bias=False),
nn.BatchNorm2d(output_channel),
nn.ReLU(inplace=True)
)]
input_channel = output_channel
# building inverted residual blocks
block = GhostBottleneck
for k, exp_size, c, use_se, s in self.cfgs:
output_channel = _make_divisible(c * width_mult, 4)
hidden_channel = _make_divisible(exp_size * width_mult, 4)
layers.append(block(input_channel, hidden_channel, output_channel, k, s, use_se))
input_channel = output_channel
self.features = nn.Sequential(*layers)
# building last several layers
output_channel = _make_divisible(exp_size * width_mult, 4)
self.squeeze = nn.Sequential(
nn.Conv2d(input_channel, output_channel, 1, 1, 0, bias=False),
nn.BatchNorm2d(output_channel),
nn.ReLU(inplace=True),
nn.AdaptiveAvgPool2d((1, 1)),
)
input_channel = output_channel
output_channel = 1280
self.classifier = nn.Sequential(
nn.Linear(input_channel, output_channel, bias=False),
nn.BatchNorm1d(output_channel),
nn.ReLU(inplace=True),
nn.Dropout(0.2),
nn.Linear(output_channel, num_classes),
)
self._initialize_weights()
def forward(self, x):
x = self.features(x)
x = self.squeeze(x)
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
def ghost_net(**kwargs):
"""
Constructs a GhostNet model
"""
cfgs = [
# k, t, c, SE, s
[3, 16, 16, 0, 1],
[3, 48, 24, 0, 2],
[3, 72, 24, 0, 1],
[5, 72, 40, 1, 2],
[5, 120, 40, 1, 1],
[3, 240, 80, 0, 2],
[3, 200, 80, 0, 1],
[3, 184, 80, 0, 1],
[3, 184, 80, 0, 1],
[3, 480, 112, 1, 1],
[3, 672, 112, 1, 1],
[5, 672, 160, 1, 2],
[5, 960, 160, 0, 1],
[5, 960, 160, 1, 1],
[5, 960, 160, 0, 1],
[5, 960, 160, 1, 1]
]
return GhostNet(cfgs, **kwargs)
### Construct/ Initialize a GhostNet
model = ghost_net()
```
上面的代码可以用来创建本文中展示的 GhostNet 的不同变体。现在来看结果。
## GhostNet 结果
我们将把论文中的结果分成三个部分:
1. 结果展示了用幻影卷积层取代标准架构中的卷积层。(**鬼卷积**)
2. 结果展示了 GhostNet 在 ImageNet-1k 数据集上执行图像分类任务的性能,以及使用 GhostNet 作为检测器主干在 MS-COCO 上进行对象检测的性能。(**幽灵网**)
3. 进一步的消融研究展示了可变比率和内核大小对重影卷积模块次级模块中深度方向卷积的影响。我们还将观察由重影卷积生成的特征图与模型中标准卷积层的特征图之间的差异。(**消融研究**
### 1.重影卷积
#### CIFAR-10
| 模型 | 重量(百万) | 失败次数(百万) | 准确度(百分比) |
| --- | --- | --- | --- |
| VGG-16 | 15 米 | 313 米 | Ninety-three point six |
| ℓ1-VGG-16 | 5.4 米 | 206 米 | Ninety-three point four |
| SBP-VGG-16 战斗机 | - | 136 米 | Ninety-two point five |
| 幽灵-VGG-16 (s=2) | 7.7 米 | 158 米 | Ninety-three point seven |
| | | | |
| ResNet-56 | 0.85 米 | 125 米 | Ninety-three |
| CP-ResNet-56 | - | 63 米 | Ninety-two |
| ℓ1-ResNet-56 | 0.73 米 | 91 米 | Ninety-two point five |
| AMC-ResNet-56 | - | 63 米 | Ninety-one point nine |
| Ghost-ResNet-56 (s=2) | 0.43 米 | 63 米 | Ninety-two point seven |
#### ImageNet-1k
| 模型 | 重量(百万) | FLOPs(十亿) | 最高精度(百分比) | 前 5 名的准确性(百分比) |
| --- | --- | --- | --- | --- |
| ResNet-50 | Twenty-five point six | Four point one | Seventy-five point three | Ninety-two point two |
| Thinet-ResNet-50 | Sixteen point nine | Two point six | Seventy-two point one | Ninety point three |
| NISP-ResNet-50-B | Fourteen point four | Two point three | - | Ninety point eight |
| 多功能-ResNet-50 | Eleven | Three | Seventy-four point five | Ninety-one point eight |
| SSS-ResNet-50 | - | Two point eight | Seventy-four point two | Ninety-one point nine |
| Ghost-ResNet-50 (s=2) | Thirteen | Two point two | Seventy-five | Ninety-two point three |
| | | | | |
| Shift-ResNet-50 | Six | - | Seventy point six | Ninety point one |
| 泰勒-佛-BN-ResNet-50 | Seven point nine | One point three | Seventy-one point seven | - |
| 超薄型 ResNet-50 0.5 倍 | Six point nine | One point one | Seventy-two point one | - |
| MetaPruning-ResNet-50 | - | One | Seventy-three point four | - |
| Ghost-ResNet-50 (s=4) | Six point five | One point two | Seventy-four point one | Ninety-one point nine |
正如所观察到的,基于重影卷积的模型保持了接近基线网络的性能,但是参数和触发器的数量显著减少。
### 2.幽灵网
#### ImageNet-1k 上的图像分类
| 模型 | 重量(百万) | 失败次数(百万) | 最高精度(百分比) | 前 5 名的准确性(百分比) |
| --- | --- | --- | --- | --- |
| ShuffleNetV1 0.5× (g=8) | One | Forty | Fifty-eight point eight | Eighty-one |
| MobileNetV2 0.35× | One point seven | Fifty-nine | Sixty point three | Eighty-two point nine |
| ShuffleNetV2 0.5 倍 | One point four | Forty-one | Sixty-one point one | Eighty-two point six |
| MobileNetV3 小号 0.75× | Two point four | forty-four | Sixty-five point four | - |
| GhostNet 0.5× | Two point six | forty-two | Sixty-six point two | Eighty-six point six |
| | | | | |
| MobileNetV1 0.5× | One point three | One hundred and fifty | Sixty-three point three | Eighty-four point nine |
| MobileNetV2 0.6× | Two point two | One hundred and forty-one | Sixty-six point seven | - |
| ShuffleNetV1 1.0× (g=3) | One point nine | One hundred and thirty-eight | Sixty-seven point eight | Eighty-seven point seven |
| ShuffleNetV2 1.0× | Two point three | One hundred and forty-six | Sixty-nine point four | Eighty-eight point nine |
| MobileNetV3 大号 0.75× | Four | One hundred and fifty-five | Seventy-three point three | - |
| GhostNet 1.0× | Five point two | One hundred and forty-one | Seventy-three point nine | Ninety-one point four |
| | | | | |
| MobileNetV2 1.0× | Three point five | Three hundred | Seventy-one point eight | Ninety-one |
| ShuffleNetV2 1.5× | Three point five | Two hundred and ninety-nine | Seventy-two point six | Ninety point six |
| 铁网 1.0 倍 | Three point seven | Three hundred and one | Seventy-two point nine | - |
| FBNet-B | Four point five | Two hundred and ninety-five | Seventy-four point one | - |
| ProxylessNAS | Four point one | Three hundred and twenty | Seventy-four point six | Ninety-two point two |
| MnasNet-A1 | Three point nine | Three hundred and twelve | Seventy-five point two | Ninety-two point five |
| MobileNetV3 大型 1.0× | Five point four | Two hundred and nineteen | Seventy-five point two | - |
| GhostNet 1.3 倍 | Seven point three | Two hundred and twenty-six | Seventy-five point seven | Ninety-two point seven |
#### MS-COCO 上的目标检测
| 毅力 | 探测器 | 主干失败(百万) | 地图 |
| --- | --- | --- | --- |
| MobileNetV2 1.0× | RetinaNet | 300 米 | 26.7% |
| MobileNetV3 1.0× | RetinaNet | 219 米 | 26.4% |
| GhostNet 1.1× | RetinaNet | 164 米 | 26.6% |
| | | | |
| MobileNetV2 1.0× | 更快的 R-CNN | 300 米 | 27.5% |
| MobileNetV3 1.0× | 219 米 | 更快的 R-CNN | 26.9% |
| GhostNet 1.1× | 更快的 R-CNN | 164 米 | 26.9% |
同样,GhostNet 能够保持令人印象深刻的分数,考虑到该架构的轻量级,同时在不同的任务(如对象检测)中保持一致,这是其简单而有效的结构的证明。
### 3.消融研究
#### VGG-16 网络图像分类中可变比率对 CIFAR-10 数据集的影响
| *s* | 重量(百万) | 失败次数(百万) | 准确度(百分比) |
| --- | --- | --- | --- |
| 香草 | Fifteen | Three hundred and thirteen | Ninety-three point six |
| Two | Seven point seven | One hundred and fifty-eight | Ninety-three point seven |
| three | Five point two | One hundred and seven | Ninety-three point four |
| four | Four | Eighty | Ninety-three |
| five | Three point three | Sixty-five | Ninety-two point nine |
#### 在使用 VGG-16 网络的 CIFAR-10 数据集的图像分类中,可变核大小 *d* 对 Ghost 模块中深度方向卷积的影响
| *d* | 重量(百万) | 失败次数(百万) | 准确度(百分比) |
| --- | --- | --- | --- |
| 香草 | Fifteen | Three hundred and thirteen | Ninety-three point six |
| one | Seven point six | One hundred and fifty-seven | Ninety-three point five |
| three | Seven point seven | One hundred and fifty-eight | Ninety-three point seven |
| five | Seven point seven | One hundred and sixty | Ninety-three point four |
| seven | Seven point seven | One hundred and sixty-three | Ninety-three point one |
在原始论文中提供了对实验结果的进一步推导和分析,但是总体上我们可以推断,鬼卷积能够同时提供很好的推广和模型压缩。
此外,作者可视化了在由标准卷积层和幻影卷积层为 VGG-16 幻影网络中的第二层生成的特征图中观察到的差异,如下所示。重影卷积减少了空间冗余,并创建了一组具有更丰富表示的独特特征图。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/92ba381e82934b8b315f921e9500688b.png)
## 幽灵缺点
虽然这篇论文提供了非常有力的结果,但是这种方法有一些严重的缺点:
1. GhostNet 的结果与其他标准架构进行了比较,但是它们没有遵循完全相同的训练设置,也没有调整它们的初始学习率和批量大小,因此导致了不公平的比较。
2. 虽然使用深度方向卷积看起来合理有效,但并不那么简单。深度方向卷积因其非优化和硬件性能差而臭名昭著。进一步的细节在[本文](https://tlkh.dev/depsep-convs-perf-investigations/)中指出。因此,它并没有带来人们对这种压缩技术所期望的真正的速度提高。一些用户在官方知识库的[问题](https://github.com/huawei-noah/ghostnet/issues/26)中指出了这一点,作者回复道:
> Ghost 模块更适合 ARM/CPU,由于深度方向的 Conv,对 GPU 不友好。
3.由于 Ghost 模块是固有卷积和深度卷积的组合,这种方法不能用于压缩基于自然深度卷积的模型(如 MobileNet v2 ),因为使用 Ghost 模块代替深度卷积会导致巨大的参数和 FLOPs 增量,从而使其变得无用。
4.由于将单个函数分解成一系列函数,产生的中间张量导致记忆增加。这个[问题](https://github.com/huawei-noah/ghostnet/issues/18)也是用户在他们的知识库中提出的,作者回答说:
> 运行时 GPU 内存可能会增加,因为 ghost 模块需要缓存主功能图和 Ghost 功能图,然后将它们连接起来。
虽然这些缺点可能看起来像是一个交易破坏者,但这取决于用户自己通过尝试来判断神经网络中幽灵模块的影响。
感谢阅读。
## 参考资料和进一步阅读
* [深度方向可分离卷积:性能研究](https://tlkh.dev/depsep-convs-perf-investigations/)
* [GhostNet:更多来自廉价操作的特性](https://arxiv.org/pdf/1911.11907.pdf)、 [CVPR 版本](https://openaccess.thecvf.com/content_CVPR_2020/papers/Han_GhostNet_More_Features_From_Cheap_Operations_CVPR_2020_paper.pdf)、[储存库](https://github.com/huawei-noah/ghostnet)
* [深度学习卷积算法指南](https://arxiv.org/abs/1603.07285)
* [用于机器学习的深度方向可分离卷积](https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning/#:~:text=Depthwise%20convolution&text=Split%20the%20input%20into%20channels,the%20output%20tensors%20back%20together.)
* [挤压和激励网络](https://arxiv.org/abs/1709.01507)*
# 使用 OpenAI 的 GLIDE 从文本提示生成和编辑照片级真实感图像
> 原文:<https://blog.paperspace.com/glide-image-generation/>
今年早些时候,我们展示了 [PixRay 项目的 PixelDrawer](https://blog.paperspace.com/how-i-made-this-articles-cover-photo-with-vqgan-clip/) ,展示了我们如何在只有文本提示的情况下,生成一个精确到所提供描述的独特而有创意的作品。PixRay 还可以用于创建丰富多彩和细节丰富的艺术,尽管有点卡通和超现实主义。这是恐怖谷的问题:这些模型生成的图像在意义上与文本提示是准确的,但它们通常不像现实。
因此,目标是生成如此逼真的作品,以至于它们可以愚弄人工智能和人类演员,这是一个有趣的挑战,而准确和普遍地做到这一点以满足随机文本提示的潜在范围甚至更困难。解释一个字符串的含义,并把它转换成图像,这是一项连人类都难以完成的任务。这在很大程度上是为什么文本到视觉模型像[剪辑](https://github.com/openai/CLIP)往往会在这个主题的研究中如此频繁地出现。
在本文中,我们将研究 OpenAI 的 [GLIDE](https://github.com/openai/glide-text2im) ,这是许多令人兴奋的项目之一,旨在使用文本导向扩散模型生成和编辑照片级真实感图像。我们将首先分解 GLIDE 的基于扩散模型的框架如何在引擎盖下运行,然后通过代码演示在渐变笔记本上运行 GLIDE。
[https://www.youtube.com/embed/o8tfaOVBOew?feature=oembed](https://www.youtube.com/embed/o8tfaOVBOew?feature=oembed)
# 滑音
## 体系结构
GLIDE 架构可以归结为三个部分:一个烧蚀扩散模型(ADM ),用于生成 64 x 64 的图像;一个文本模型(transformer ),通过文本提示影响图像的生成;以及一个上采样模型,用于将较小的 64 x 64 图像转换为更易于理解的 256 x 256 像素。前两个组件相互作用,以指导图像生成过程准确地反映文本提示,而后者是使我们生成的图像更容易理解所必需的。
GLIDE 项目的根源在于[2021 年发布的另一篇论文](https://arxiv.org/pdf/2105.05233.pdf),该论文表明,在图像样本质量方面,ADM 方法可以胜过同时代流行的最先进的生成模型。GLIDE 作者为 ADM 使用了与 Dhariwal 和 Nichol 相同的 ImageNet 64 × 64 模型,但选择将模型宽度增加到 512 通道。这为 ImageNet 模型产生了大约 23 亿个参数。然而,与 Dhariwal 和 Nichol 不同的是,GLIDE 团队希望能够更直接地影响图像生成过程,因此他们将视觉模型与注意力激活转换器配对。
通过处理文本输入提示,GLIDE 能够对图像生成过程的输出进行某种程度的控制。这是通过在足够大的数据集(与在 [DALL-E](https://arxiv.org/abs/2102.12092) 项目中使用的相同)上训练 transformer 模型来完成的,该数据集由图像及其相应的标题组成。为了以文本为条件,文本首先被编码成一个由 *K 个*标记组成的序列。然后,这些令牌被输入到一个转换器模型中。变压器的输出有两种用途。首先,最终的令牌嵌入被用来代替 ADM 模型的类嵌入。第二,令牌嵌入的最后一层——特征向量序列——被分别投影到 ADM 模型中每个注意力层的维度,并连接到每个注意力层的注意力上下文。在实践中,这允许 ADM 模型使用其学习到的对输入单词及其相关图像的理解,以独特且逼真的方式从相似文本标记的新颖组合中生成图像。这个文本编码转换器使用 24 个宽度为 2048 的残差块,大约有 12 亿个参数。
最后,上采样器扩散模型也具有大约 15 亿个参数,并且与基本模型的不同之处在于其文本编码器更小,具有 1024 和 384 个基本通道的宽度。顾名思义,这个模型有助于扩大样本的规模,以提高机器和人类参与者的可解释性。
### 扩散模型
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5b50ae9cbf524a38d9aef4aeea52548f.png)
[Source](https://lilianweng.github.io/posts/2021-07-11-diffusion-models/)
GLIDE 利用自己的 ADM 实现(ADM-G 代表“guided”)来执行图像生成。ADM-G 可以被认为是扩散 U-net 模型的一种改进。
扩散 U 网模型明显不同于更流行的基于 VAE、GAN 和变压器的图像合成技术。他们创建了扩散步骤的马尔可夫链,将随机噪声逐步引入数据,然后他们学习逆转扩散过程,并仅从噪声中重建所需的数据样本。
它分两个阶段工作:向前扩散和向后扩散。给定来自样本实际分布的数据点,前向扩散过程在预定义的一组步骤中向样本添加少量噪声。随着步长变大并接近无穷大,样本将失去其所有可区分的特征,并且序列将开始模仿各向同性高斯曲线。在反向扩散过程中,扩散模型顺序地学习反转添加的噪声对图像的影响,并通过试图逼近原始输入样本分布来引导生成的图像朝向其原始形式。一个完美的模型将能够从一个真正的高斯噪声输入和一个提示中做到这一点。
ADM-G 与上述过程的不同之处在于,模型(CLIP 或专用转换器)使用输入的文本提示标记来影响反向扩散步骤。
### 剪辑与无分类器的指导
作为 GLIDE 图像合成器开发的一部分,研究人员试图创建一种新颖、改进的方法来影响生成过程。通常,“首先在有噪声的图像上训练分类器,并且在扩散采样过程中,来自分类器的梯度用于将样本导向标签”([源](https://arxiv.org/pdf/2112.10741.pdf))。这就是通常使用 CLIP 之类的模型的地方。CLIP 从自然语言监督中学习视觉概念,并可以将这些学习到的概念传授给图像合成过程。然而,代价是使用裁剪在计算上是昂贵的。同时,[何&萨利曼(2021)](https://openreview.net/pdf?id=qw8AKxfYbI) 展示了不使用分类器进行引导的先例。他们证明了可比较的图像质量可以通过“在有标签和无标签的扩散模型的预测之间进行插值”来实现(“T4”来源)。这被称为无分类器制导。
```py
# Create a classifier-free guidance sampling function
def model_fn(x_t, ts, **kwargs):
half = x_t[: len(x_t) // 2]
combined = th.cat([half, half], dim=0)
model_out = model(combined, ts, **kwargs)
eps, rest = model_out[:, :3], model_out[:, 3:]
cond_eps, uncond_eps = th.split(eps, len(eps) // 2, dim=0)
half_eps = uncond_eps + guidance_scale * (cond_eps - uncond_eps)
eps = th.cat([half_eps, half_eps], dim=0)
return th.cat([eps, rest], dim=1)
```
根据这些发现,GLIDE 的分类器自由导航作为一个梯度函数,其行为类似于模型(见上面的代码)。这是`p_sample_loop`(训练)函数的一个参数,可用于强制扩散模型使用其对输入的学习理解来影响图像生成程序。
总的来说,GLIDE 的作者发现无分类器指导比使用 CLIP 更有效。这有两个原因。首先,单个模型可以使用它自己的知识来指导合成,而不是必须通过解释单独的分类模型来这样做。第二,当以难以用分类器预测的信息(例如文本)为条件时,它简化了指导。因此,当进行人工质量测试时,与没有引导或使用剪辑生成的图像相比,使用无分类器引导生成的图像通常被描述为质量更高,对字幕更准确。
作者还推测,公开可用的剪辑模型没有在充分噪声的数据上训练,并且在采样期间遇到的噪声中间图像对他们来说是不分布的。这可能会导致两种方法在感知能力上的一些差异。
可以用`clip_guided.ipynb`和`text2im.ipynb`笔记本自己测试一下他们的能力!
## 能力
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2fdf388f1743b2b02a6f9b17c31a4fe9.png)
[Source](https://arxiv.org/pdf/2112.10741.pdf)
### 图象生成
GLIDE 最流行和最常见的用例无疑是它在图像合成方面的潜力。虽然这些图像很小,并且在动物/人的形状上滑行很困难,但是一次成像的潜力几乎是无限的。它不仅可以生成动物、名人、风景、建筑等更多的图像,而且还可以以不同的艺术风格或逼真的方式来实现。正如你从上面的例子中所看到的,论文的作者认为 GLIDE 能够理解各种各样的文本输入并将其转化为图像格式。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4dcbd53a54ac25e6015eec31e2578450.png)
[Source](https://arxiv.org/pdf/2112.10741.pdf)
### 修补
可以说,GLIDE 更有趣的应用是它的自动照片修复。GLIDE 能够接受现有的图像作为输入,在需要编辑的区域使用文本提示对其进行处理,并可以轻松地对图像中的这些区域进行添加。为了获得更好的结果,GLIDE 需要与编辑模型结合使用,比如 [SDEdit](https://github.com/ermongroup/SDEdit) 。将来,利用这种能力的应用程序可能是创建无代码图像编辑方法的关键。
# 代码演示
为了您的方便,我们已经创建了一个包含演示笔记本的 GLIDE repo 的[分支,这些演示笔记本已经过优化,可以在 Gradient 上运行,请务必查看它们!您还可以直接从 Gradient 访问和派生演示笔记本。回购的笔记本目录中有三个笔记本,每个笔记本专注于不同的任务。对于图像生成的无分类器指导:使用`text2im.ipynb`,对于剪辑指导:使用`clip_guided.ipynb`,对于修复现有照片:使用`inpaint.ipynb`。](https://github.com/gradient-ai/glide-text2im/)
一旦你设置好了一切,让我们从进入`inpaint.ipynb`文件开始。这是一个笔记本,你可以用它来输入你自己的图像,然后由修复模型进行编辑。我们将在这个演示中演练修复笔记本,因为它可以说是本文中展示的 ADM-G 的最新颖的用途。其他的笔记本不会在这篇博文中讨论,但是应该很容易理解,因为它们和这篇博文中讨论的笔记本有很高的相似度。
让我们开始吧。
```py
!pip install git+https://github.com/gradient-ai/glide-text2im
```
确保运行第一个单元。这将把 Github repo 安装为 Python 包,供我们在演示中使用。如果你在 Gradient 上,确保你在安装中使用 gradient-ai Github fork 来运行代码。
```py
from PIL import Image
from IPython.display import display
import torch as th
from glide_text2im.download import load_checkpoint
from glide_text2im.model_creation import (
create_model_and_diffusion,
model_and_diffusion_defaults,
model_and_diffusion_defaults_upsampler
)
```
这是我们的进口货。您刚刚安装的 glide_text2im 库已经为我们创建模型和开始使用提供了很多有用的功能。
```py
# This notebook supports both CPU and GPU.
# On CPU, generating one sample may take on the order of 20 minutes.
# On a GPU, it should be under a minute.
has_cuda = th.cuda.is_available()
device = th.device('cpu' if not has_cuda else 'cuda')
```
运行此单元以确保 PyTorch 在培训过程中使用了您的 GPU。
```py
# Create base model.
options = model_and_diffusion_defaults()
options['inpaint'] = True
options['use_fp16'] = has_cuda
options['timestep_respacing'] = '100' # use 100 diffusion steps for fast sampling
model, diffusion = create_model_and_diffusion(**options)
model.eval()
if has_cuda:
model.convert_to_fp16()
model.to(device)
model.load_state_dict(load_checkpoint('base-inpaint', device))
print('total base parameters', sum(x.numel() for x in model.parameters()))
```
在这里,我们正在加载修复基础模型,它将执行修复。首先,我们使用提供的函数`model_and_diffusion_defaults()`为基本模型创建配置设置。接下来,`create_model_and_diffusion()`使用这些参数创建要使用的 ImageNet 和扩散模型。`model.eval()`然后将模型置于评估模式,这样我们就可以使用它进行推理,而`model.to(device)`确保模型的操作将由 GPU 执行。最后,`model.load_state_dict()`加载 OpenAI 为基线修复模型提供的检查点。
```py
# Create upsampler model.
options_up = model_and_diffusion_defaults_upsampler()
options_up['inpaint'] = True
options_up['use_fp16'] = has_cuda
options_up['timestep_respacing'] = 'fast27' # use 27 diffusion steps for very fast sampling
model_up, diffusion_up = create_model_and_diffusion(**options_up)
model_up.eval()
if has_cuda:
model_up.convert_to_fp16()
model_up.to(device)
model_up.load_state_dict(load_checkpoint('upsample-inpaint', device))
print('total upsampler parameters', sum(x.numel() for x in model_up.parameters()))
```
与上面类似,我们现在需要加载上采样模型。该模型将获取基本模型生成的样本,并将其上采样到 256 x 256。我们使用“fast27”作为时间步长,从样本中快速生成放大的照片。就像基线一样,我们使用参数来创建上采样模型和上采样扩散模型,将上采样模型设置为评估模式,然后将修补上采样模型的检查点加载到我们的`model_up`变量。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a0d8713cc1244789304a95d10f188bb6.png)
这是我们将在演示中使用的示例“grass.png”。这个图像将被你输入的任何提示所覆盖。我们的样本输入将是“着火的小房子”,但是您可以随意更改。您也可以上传您自己的图像,并通过改变下面所示的`source_image_256`和`source_image_64`变量赋值函数中的路径来测试它们。
```py
# Sampling parameters
prompt = "a small house on fire"
batch_size = 1
guidance_scale = 5.0
# Tune this parameter to control the sharpness of 256x256 images.
# A value of 1.0 is sharper, but sometimes results in grainy artifacts.
upsample_temp = 0.997
# Source image we are inpainting
source_image_256 = read_image('grass.png', size=256)
source_image_64 = read_image('grass.png', size=64)
# The mask should always be a boolean 64x64 mask, and then we
# can upsample it for the second stage.
source_mask_64 = th.ones_like(source_image_64)[:, :1]
source_mask_64[:, :, 20:] = 0
source_mask_256 = F.interpolate(source_mask_64, (256, 256), mode='nearest')
# Visualize the image we are inpainting
# show_images(source_image_256 * source_mask_256)
```
此单元格包含设置 GLIDE 图像生成的最后步骤。在这里,我们建立我们的`prompt`、`batch_size`和`guidance_scale`参数。值得注意的是,`prompt`决定了修补者将试图从图像中添加或移除什么,而`guidance_scale`影响了分类器自由引导将如何强烈地试图作用于图像。最后,我们生成我们的源掩码。这些用于识别照片中要修补的区域。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2de3279a9ed3fb2be86ecbd2832ad27d.png)
grass.png with the mask
在`inpaint.ipynb`中,我们还需要加载一个 64 x 64 和 256 x 256 的样本图像。然后,我们可以使用 64 x 64 版本来创建我们的蒙版,以确定在两种尺寸的图像中我们的样本中需要修复的区域。
```py
##############################
# Sample from the base model #
##############################
# Create the text tokens to feed to the model.
tokens = model.tokenizer.encode(prompt)
tokens, mask = model.tokenizer.padded_tokens_and_mask(
tokens, options['text_ctx']
)
# Create the classifier-free guidance tokens (empty)
full_batch_size = batch_size * 2
uncond_tokens, uncond_mask = model.tokenizer.padded_tokens_and_mask(
[], options['text_ctx']
)
# Pack the tokens together into model kwargs.
model_kwargs = dict(
tokens=th.tensor(
[tokens] * batch_size + [uncond_tokens] * batch_size, device=device
),
mask=th.tensor(
[mask] * batch_size + [uncond_mask] * batch_size,
dtype=th.bool,
device=device,
),
# Masked inpainting image
inpaint_image=(source_image_64 * source_mask_64).repeat(full_batch_size, 1, 1, 1).to(device),
inpaint_mask=source_mask_64.repeat(full_batch_size, 1, 1, 1).to(device),
)
# Create an classifier-free guidance sampling function
def model_fn(x_t, ts, **kwargs):
half = x_t[: len(x_t) // 2]
combined = th.cat([half, half], dim=0)
model_out = model(combined, ts, **kwargs)
eps, rest = model_out[:, :3], model_out[:, 3:]
cond_eps, uncond_eps = th.split(eps, len(eps) // 2, dim=0)
half_eps = uncond_eps + guidance_scale * (cond_eps - uncond_eps)
eps = th.cat([half_eps, half_eps], dim=0)
return th.cat([eps, rest], dim=1)
def denoised_fn(x_start):
# Force the model to have the exact right x_start predictions
# for the part of the image which is known.
return (
x_start * (1 - model_kwargs['inpaint_mask'])
+ model_kwargs['inpaint_image'] * model_kwargs['inpaint_mask']
)
# Sample from the base model.
model.del_cache()
samples = diffusion.p_sample_loop(
model_fn,
(full_batch_size, 3, options["image_size"], options["image_size"]),
device=device,
clip_denoised=True,
progress=False,
model_kwargs=model_kwargs,
cond_fn=None,
denoised_fn=denoised_fn,
)[:batch_size]
model.del_cache()
# Show the output
show_images(samples)
```
现在设置已经完成,这个单元格包含了创建 64 x 64 修复图像所需的所有内容。首先,我们创建文本标记,从提示输入到模型中,并使用`batch_size`为我们的模型生成条件文本标记和掩码。接下来,我们创建一个空的标记序列,作为无分类器的引导文本标记和相应的掩码。然后,我们在`model_kwargs`字典中连接两组标记和遮罩,以及我们的修补图像及其对应的图像遮罩。然后使用`model_kwargs`,连同图像的输入张量和我们输入的步数,创建`model_fn`。`model_fn`用于通过从扩散模型采样来执行修复,以生成图像,但是在每个采样步骤之后,用来自背景的噪声版本的样本来替换图像的已知区域。这是通过对模型进行微调以擦除训练样本的随机区域,并将剩余部分输入到具有掩蔽通道的模型中作为附加条件信息来实现的([源](https://arxiv.org/pdf/2112.10741.pdf))。在实践中,生成器学习将文本提示标记所建议的信息添加到图像的屏蔽部分。
一旦这个单元完成运行,它将输出一个 64 x 64 生成的图像,其中包含在您的提示中指定的修复更改。然后,上采样模型使用该图像来创建我们最终的 256 x 256 图像。
```py
##############################
# Upsample the 64x64 samples #
##############################
tokens = model_up.tokenizer.encode(prompt)
tokens, mask = model_up.tokenizer.padded_tokens_and_mask(
tokens, options_up['text_ctx']
)
# Create the model conditioning dict.
model_kwargs = dict(
# Low-res image to upsample.
low_res=((samples+1)*127.5).round()/127.5 - 1,
# Text tokens
tokens=th.tensor(
[tokens] * batch_size, device=device
),
mask=th.tensor(
[mask] * batch_size,
dtype=th.bool,
device=device,
),
# Masked inpainting image.
inpaint_image=(source_image_256 * source_mask_256).repeat(batch_size, 1, 1, 1).to(device),
inpaint_mask=source_mask_256.repeat(batch_size, 1, 1, 1).to(device),
)
def denoised_fn(x_start):
# Force the model to have the exact right x_start predictions
# for the part of the image which is known.
return (
x_start * (1 - model_kwargs['inpaint_mask'])
+ model_kwargs['inpaint_image'] * model_kwargs['inpaint_mask']
)
# Sample from the base model.
model_up.del_cache()
up_shape = (batch_size, 3, options_up["image_size"], options_up["image_size"])
up_samples = diffusion_up.p_sample_loop(
model_up,
up_shape,
noise=th.randn(up_shape, device=device) * upsample_temp,
device=device,
clip_denoised=True,
progress=False,
model_kwargs=model_kwargs,
cond_fn=None,
denoised_fn=denoised_fn,
)[:batch_size]
model_up.del_cache()
# Show the output
show_images(up_samples)
```
最后,现在我们已经生成了我们的初始图像,是时候使用我们的扩散上采样器来获得 256 x 256 的图像了。首先,我们使用`model_up`重新分配令牌和掩码。然后我们打包代表我们生成的图像的`samples`变量;令牌和面具,修复图像,和修复面具一起作为我们的`model_kwargs`。然后,我们使用我们的`diffusion_up`模型对“快速”27 步的图像(现在在 kwargs 中存储为`low_res`)进行上采样。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3f7e6b19917b6aa1d1c9f46fb00a3fa7.png)
在渐变笔记本上运行所有这些步骤后,我们能够生成这个 256 x 256 的修复。正如你所看到的,它成功地抓住了提示的精神(“着火的小房子”),通过添加一个透过窗户闪耀的火焰的小屋外观部分。它似乎还确定了原始样本是朝着地面倾斜的。模型被训练的 COCO 数据集可能没有这样角度的房屋图像。为了补救这一点,增加了一块更平的草地,供小屋站立。
如你所见,GLIDE 并不是没有问题。虽然它可以创建逼真的图像,但可以制作的图像种类有限。在我们的测试中,我们发现 GLIDE 在处理真人图像时有些吃力。GLIDE 尤其难以处理人脸,它经常在修复任务中变形和扭曲人脸,而不是做出建议的改变。其他问题包括在真实图像中偶尔出现的伪像。最后,仅对于 256 x 256 的图像,运行模型的成本相对较高。有理由认为,如果我们进一步上采样或使用昂贵的 CLIP 来指导这一过程,这一数字只会呈指数级增长。
# 结束语
现在我们已经完成了整个过程,您应该了解了 GLIDE 的基本工作原理,了解了它在图像生成和图像内编辑方面的能力范围,并且现在可以在渐变笔记本上实现它了。我们鼓励你尝试一下这个教程,看看你能从这个整洁的模型中得到什么有趣的图像。
这是一个关于文本引导图像生成的非正式系列的一部分。如果艺术和图像生成是一个感兴趣的主题,请务必查看实现 [PixRay 的 CLIPit-PixelDraw](https://blog.paperspace.com/how-i-made-this-articles-cover-photo-with-vqgan-clip/) next 的演练。
# 解释了全球背景网络
> 原文:<https://blog.paperspace.com/global-context-networks-gcnet/>
非局部网络已经为用于计算机视觉的深度神经网络架构中的许多现代注意机制提供了强大的直觉和基础。
在这篇文章中,我们将回顾一项杰出的工作,该工作从[非局域网络](https://arxiv.org/abs/1711.07971)和[压缩-激发网络](https://blog.paperspace.com/channel-attention-squeeze-and-excitation-networks/)中获得灵感,构建了一个注意力机制模型,使网络能够以相当低的成本捕捉长程相关性。这被称为[全球背景网络](https://arxiv.org/pdf/1904.11492.pdf),它于 2019 年在 ICCV 研讨会上被接受。
首先,在概述压缩和激发网络之前,我们将简单地回顾一下非局域网络。然后,我们将深入讨论全局上下文建模,然后提供其代码并观察本文中呈现的结果。最后,我们将考虑 GCNet 的一些缺点。
## 目录
* 抽象概述
* 重访非本地网络
* 再谈压缩和激发网络
* 全球背景网络
* PyTorch Code
* 结果
* 缺点
* 参考
### 抽象概述
> 然而,通过严格的实证分析,我们发现,对于图像中不同的查询位置,非局部网络所建模的全局上下文几乎是相同的。在本文中,我们利用这一发现创建了一个基于查询独立公式的简化网络,它保持了 NLNet 的准确性,但计算量明显减少。我们进一步观察到这种简化的设计与压缩激励网络(SENet)具有相似的结构。因此,我们将它们统一为一个三步通用框架,用于全局上下文建模。在通用框架内,我们设计了一个更好的实例化,称为全局上下文(GC)块,它是轻量级的,可以有效地建模全局上下文。
## 重访非本地网络
非本地网络通过将特定于查询的全局上下文聚集到每个查询位置,采取了一种有效的方法来捕获长期依赖性。简单来说,非局部网络负责通过聚合其周围像素的关系信息来建模单个像素的注意力地图。这是通过使用少量的置换操作来实现的,以允许用焦点查询像素来构造注意力图。这种方法在抽象的意义上有点类似于[萨根](https://arxiv.org/abs/1805.08318)中提出的**自我关注机制**。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d3c35b1df68e555246ea854452e51707.png)
如上图所示,NL 块主要接收输入张量,并首先将其从$ C \ x H \ x W \ x C \ x HW $ dimension 格式进行置换。这个排列后面是三个分支。在其中一个分支中,它简单地经历$ 1 \乘以 1$逐点空间保持卷积。在其他两个分支中,它也经历类似的卷积运算,但通过置换其中一个输出,它们乘以叉积,之后所得输出通过 SoftMax 激活层,该激活层输出$ HW \乘以 HW$整形张量。然后,将该输出乘以第一个分支的输出的叉积,得到结果$ C \乘以 HW$输出,然后将其置换为$ C \乘以 H \乘以 W$的形状,然后像剩余连接一样按元素添加到块的原始输入中。
GCNet 论文提供了非本地块的简化和一般化形式,如上图所示。此简化块的输入并行通过两个$ 1 \乘以 1$卷积运算符,其中一个保留通道和空间维度,而另一个仅将通道维度减少到$1$。然后,第一个卷积的输出从$ C \乘以 H 乘以 W$置换为$ C \乘以 HW$,类似于经典的非局部块,而第二个卷积的输出从$ 1 \乘以 H \乘以 W$重新整形为$ HW \乘以 1 \乘以 1$。这两个张量随后使用叉积相乘,得到形状为$ C \乘以 1 \乘以 1$的输出张量。这个输出张量然后类似地被添加到原始输入,就像剩余连接一样。
## 再谈压缩和激发网络
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ea96edd19774aa50ad0fcc3e6818ca61.png)
挤压-激励网络包含一个通道注意机制,该机制主要由三个组件组成:挤压模块、激励模块和缩放模块。
挤压块负责使用全局平均池(GAP)将输入特征映射$(C \乘以 H \乘以 W)$减少到单个像素,同时保持通道数量不变$(C \乘以 1 \乘以 1)$。然后,*压缩的*张量被传递到激励块,这是一个多层感知器瓶颈(MLP ),负责学习通道注意力权重。最后,这些奇异权重通过 sigmoid 激活,然后与非调制输入张量中的相应通道逐元素相乘。
要阅读关于挤压和激励网络的深入评论,你可以在这里阅读我的文章。
## 全球背景网络
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fe6d42dfa3f1290c1f4966a68c54c32e.png)
GCNet 论文提出了一个统一的简单模型,可以推广压缩和激发网络和非局域网络。这个模块被称为全局上下文建模框架,它包含三个值得注意的部分:上下文建模、转换和融合。上下文建模用于建立查询像素的长程相关性,而变换通常表示归因于通道注意中的注意向量的维度变化,并且最终变换本质上将注意向量与原始输入融合。
如上图所示,该框架可以概括简化的非本地块,其中上下文建模负责捕获长期依赖关系。类似地,在挤压和激发网络的情况下,上下文建模表示通过间隙对输入张量的分解。接下来是构建注意力权重的变换,在这种情况下,这是一个 MLP 瓶颈。
全局上下文网络在全局上下文建模框架内结合了简化的 NL 块和挤压-激励块的最佳部分。上下文建模与简化的 NL 块中的相同,而转换是一个类似于挤压和激励块的瓶颈结构,唯一的区别是 GCNet 在瓶颈中使用了一个额外的`LayerNorm`。
### PyTorch Code
```py
import torch
from torch import nn
from mmcv.cnn import constant_init, kaiming_init
def last_zero_init(m):
if isinstance(m, nn.Sequential):
constant_init(m[-1], val=0)
m[-1].inited = True
else:
constant_init(m, val=0)
m.inited = True
class ContextBlock2d(nn.Module):
def __init__(self, inplanes, planes, pool, fusions):
super(ContextBlock2d, self).__init__()
assert pool in ['avg', 'att']
assert all([f in ['channel_add', 'channel_mul'] for f in fusions])
assert len(fusions) > 0, 'at least one fusion should be used'
self.inplanes = inplanes
self.planes = planes
self.pool = pool
self.fusions = fusions
if 'att' in pool:
self.conv_mask = nn.Conv2d(inplanes, 1, kernel_size=1)
self.softmax = nn.Softmax(dim=2)
else:
self.avg_pool = nn.AdaptiveAvgPool2d(1)
if 'channel_add' in fusions:
self.channel_add_conv = nn.Sequential(
nn.Conv2d(self.inplanes, self.planes, kernel_size=1),
nn.LayerNorm([self.planes, 1, 1]),
nn.ReLU(inplace=True),
nn.Conv2d(self.planes, self.inplanes, kernel_size=1)
)
else:
self.channel_add_conv = None
if 'channel_mul' in fusions:
self.channel_mul_conv = nn.Sequential(
nn.Conv2d(self.inplanes, self.planes, kernel_size=1),
nn.LayerNorm([self.planes, 1, 1]),
nn.ReLU(inplace=True),
nn.Conv2d(self.planes, self.inplanes, kernel_size=1)
)
else:
self.channel_mul_conv = None
self.reset_parameters()
def reset_parameters(self):
if self.pool == 'att':
kaiming_init(self.conv_mask, mode='fan_in')
self.conv_mask.inited = True
if self.channel_add_conv is not None:
last_zero_init(self.channel_add_conv)
if self.channel_mul_conv is not None:
last_zero_init(self.channel_mul_conv)
def spatial_pool(self, x):
batch, channel, height, width = x.size()
if self.pool == 'att':
input_x = x
# [N, C, H * W]
input_x = input_x.view(batch, channel, height * width)
# [N, 1, C, H * W]
input_x = input_x.unsqueeze(1)
# [N, 1, H, W]
context_mask = self.conv_mask(x)
# [N, 1, H * W]
context_mask = context_mask.view(batch, 1, height * width)
# [N, 1, H * W]
context_mask = self.softmax(context_mask)
# [N, 1, H * W, 1]
context_mask = context_mask.unsqueeze(3)
# [N, 1, C, 1]
context = torch.matmul(input_x, context_mask)
# [N, C, 1, 1]
context = context.view(batch, channel, 1, 1)
else:
# [N, C, 1, 1]
context = self.avg_pool(x)
return context
def forward(self, x):
# [N, C, 1, 1]
context = self.spatial_pool(x)
if self.channel_mul_conv is not None:
# [N, C, 1, 1]
channel_mul_term = torch.sigmoid(self.channel_mul_conv(context))
out = x * channel_mul_term
else:
out = x
if self.channel_add_conv is not None:
# [N, C, 1, 1]
channel_add_term = self.channel_add_conv(context)
out = out + channel_add_term
return out
```
由于将非局部网络和挤压-激励网络结合到单个统一框架中,GCNet 能够捕获查询像素的长程相关性,同时在全局邻域上提供良好的注意力表示,这使得网络对局部空间区域的变化更加鲁棒。
## 结果
### 图像分类
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7b9bddaa4f1a98d9ad3e0041d683686d.png)
Here baseline represents a ResNet-50
### MS-COCO 上的实例分割和对象检测
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4d929058971fbc7927edafffa2193ac2.png)
Here baseline represents a Mask R-CNN with ResNet-50 backbone and FPN
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e70f2189e01eadd006ecfebc82cd9219.png)
正如所观察到的,GCNet 提供了非常强的结果,并且相对于基线对应物有相当大的性能改进。这归因于 GCNet 能够模拟像素级的长程相关性,同时映射通道式注意力。
## 缺点
1. 就参数复杂性而言,GCNets 相当昂贵。开销相当大,并且主要归因于 MLP 瓶颈和上下文建模块,这两者都以通道数量 C 的数量级添加了额外的参数,这在数值上相当高。
2. 由于它使用了在压缩和激发网络中使用的 MLP 瓶颈结构,由于瓶颈中的维数减少,有相当大的信息损失。
总的来说,GCNet 是一个非常强大的执行注意机制,并提供了一个显着的性能增益。它还在 ICCV 2019 神经建筑师研讨会上获得了最佳论文奖。
## 参考
1. [GCNet:非局域网络与压缩激发网络相遇并超越](https://arxiv.org/pdf/1904.11492.pdf)
2. [非局部神经网络](https://arxiv.org/abs/1711.07971)
3. [挤压和激励网络](https://arxiv.org/abs/1709.01507)
4. [GC net 的 PyTorch 正式实施](https://github.com/xvjiarui/GCNet)
5. [自我关注生成对抗网络](https://arxiv.org/abs/1805.08318)
# 卷积神经网络中的全局池
> 原文:<https://blog.paperspace.com/global-pooling-in-convolutional-neural-networks/>
一段时间以来,池操作一直是卷积神经网络的支柱。虽然像最大池和平均池这样的过程经常占据更多的中心位置,但它们不太为人所知的表亲全局最大池和全局平均池已经变得同等重要。在本文中,我们将探讨这两种常见的池技术的全局变量是什么,以及它们之间的比较。
```py
# article dependencies
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets as Datasets
from torch.utils.data import Dataset, DataLoader
import numpy as np
import matplotlib.pyplot as plt
import cv2
from tqdm.notebook import tqdm
import seaborn as sns
from torchvision.utils import make_grid
```
```py
if torch.cuda.is_available():
device = torch.device('cuda:0')
print('Running on the GPU')
else:
device = torch.device('cpu')
print('Running on the CPU')
```
### 经典卷积神经网络
许多计算机视觉的初学者经常被介绍到卷积神经网络作为图像数据的理想神经网络,因为它保留了输入图像的空间结构,同时从它们学习/提取特征。通过这样做,它能够学习图像中相邻像素和对象位置之间的关系,从而使它成为一个非常强大的神经网络。
多层感知器也可以在图像分类环境中工作,但是与它的 convnet 对应物相比,它的性能会严重下降,因为它通过展平/矢量化立即破坏了图像的空间结构,从而去除了相邻像素之间的大部分关系。
#### 特征提取器和分类器组合
许多经典的卷积神经网络实际上是 convnets 和 MLPs 的组合。例如,看看 [LeNet](https://blog.paperspace.com/writing-lenet5-from-scratch-in-python/) 和 [AlexNet](https://blog.paperspace.com/alexnet-pytorch/) 的架构,人们可以清楚地看到,他们的架构只是一对卷积层,最后附有线性层。
这种配置很有意义,它允许卷积层做他们最擅长的事情,即提取二维空间数据中的特征。之后,提取的特征被传递到线性层上,因此它们也可以做它们擅长的事情,找到特征向量和目标之间的关系。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/78db420ffe1e844afcd7092e0f79a298.png)
#### 设计中的缺陷
这种设计的问题是线性图层非常容易过度适应数据。辍学管制的引入有助于缓解这一问题,但这仍然是一个问题。此外,对于一个以不破坏空间结构而自豪的神经网络来说,经典的 convnet 仍然做到了这一点,尽管在网络中更深,程度更低。
### 一个经典问题的现代解答
为了防止 convnets 中的这种过拟合问题,尝试 dropout 正则化后,合乎逻辑的下一步是完全消除所有线性图层。如果要排除线性图层,则需要寻找一种全新的方法来对特征地图进行下采样,并生成与所讨论的类别数量相等的矢量表示。这正是全球统筹的用武之地。
考虑一个 4 类分类任务,1 x 1 卷积图层将有助于对要素地图进行下采样,直到它们的数量为 4,而全局池将有助于创建一个 4 元素长的矢量表示,然后损失函数可以使用它来计算梯度。
### 全球平均池
仍然在上面描述的相同分类任务中,想象一个场景,其中我们感觉我们的卷积层处于足够的深度,但是我们有 8 个大小为`(3, 3)`的特征图。我们可以利用 1×1 卷积层来将 8 个特征映射下采样到 4 个。现在我们有 4 个大小为`(3, 3)`的矩阵,而我们实际需要的是一个 4 个元素的向量。
从这些特征图中导出 4 元素向量的一种方法是计算每个特征图中所有像素的平均值,并将其作为单个元素返回。这基本上是全球平均池所需要的。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4499a51aa5970d1496de020f2df93cf5.png)
### 全局最大池
就像上面的场景一样,我们希望从 4 个矩阵中产生 4 个元素的向量,在这种情况下,我们不是取每个特征图中所有像素的平均值,而是取最大值,并将其作为感兴趣的向量表示中的单个元素返回。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a5a2b8057acabeb938a4777cb87ba9c6.png)
### 基准全球统筹方法
这里的基准测试目标是基于两种全局池技术在用于生成分类向量表示时的性能来比较它们。用于基准测试的数据集是 FashionMNIST 数据集,它包含常见时尚物品的 28 像素乘 28 像素图像。
```py
# loading training data
training_set = Datasets.FashionMNIST(root='./', download=True,
transform=transforms.ToTensor())
# loading validation data
validation_set = Datasets.FashionMNIST(root='./', download=True, train=False,
transform=transforms.ToTensor())
```
| 标签 | 描述 |
| --- | --- |
| Zero | t 恤 |
| one | 裤子 |
| Two | 套衫 |
| three | 连衣裙 |
| four | 外套 |
| five | 凉鞋 |
| six | 衬衫 |
| seven | 运动鞋 |
| eight | 包 |
| nine | 踝靴 |
#### 使用全球平均池进行转换
下面定义的 convnet 使用 1 x 1 卷积层与全局平均池,而不是线性层,来产生 10 个元素的矢量表示,而无需正则化。关于 PyTorch 中全局平均池的实现,所有需要做的就是利用常规的平均池类,但是使用大小等于每个单独特征图大小的内核/过滤器。举例来说,从第 6 层出来的特征映射的大小为`(3, 3)`,因此为了执行全局平均池,使用大小为 3 的内核。*注意:简单地取每个特征图的平均值会得到相同的结果。*
```py
class ConvNet_1(nn.Module):
def __init__(self):
super().__init__()
self.network = nn.Sequential(
# layer 1
nn.Conv2d(1, 8, 3, padding=1),
nn.ReLU(), # feature map size = (28, 28)
# layer 2
nn.Conv2d(8, 8, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # feature map size = (14, 14)
# layer 3
nn.Conv2d(8, 16, 3, padding=1),
nn.ReLU(), # feature map size = (14, 14)
# layer 4
nn.Conv2d(16, 16, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # feature map size = (7, 7)
# layer 5
nn.Conv2d(16, 32, 3, padding=1),
nn.ReLU(), # feature map size = (7, 7)
# layer 6
nn.Conv2d(32, 32, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # feature map size = (3, 3)
# output layer
nn.Conv2d(32, 10, 1),
nn.AvgPool2d(3)
)
def forward(self, x):
x = x.view(-1, 1, 28, 28)
output = self.network(x)
output = output.view(-1, 10)
return torch.sigmoid(output)
```
#### 具有全局最大池的 Convnet
另一方面,下面的 ConvNet_2 用 1 x 1 卷积层替换线性层,与全局最大池协同工作,以便产生 10 个元素的向量,而无需正则化。与全局平均池类似,要在 PyTorch 中实现全局最大池,需要使用常规的最大池类,其内核大小等于此时的特征映射大小。*注意:简单地导出每个特征图中的最大像素值会产生相同的结果。*
```py
class ConvNet_2(nn.Module):
def __init__(self):
super().__init__()
self.network = nn.Sequential(
# layer 1
nn.Conv2d(1, 8, 3, padding=1),
nn.ReLU(), # feature map size = (28, 28)
# layer 2
nn.Conv2d(8, 8, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # feature map size = (14, 14)
# layer 3
nn.Conv2d(8, 16, 3, padding=1),
nn.ReLU(), # feature map size = (14, 14)
# layer 4
nn.Conv2d(16, 16, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # feature map size = (7, 7)
# layer 5
nn.Conv2d(16, 32, 3, padding=1),
nn.ReLU(), # feature map size = (7, 7)
# layer 6
nn.Conv2d(32, 32, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2), # feature map size = (3, 3)
# output layer
nn.Conv2d(32, 10, 1),
nn.MaxPool2d(3)
)
def forward(self, x):
x = x.view(-1, 1, 28, 28)
output = self.network(x)
output = output.view(-1, 10)
return torch.sigmoid(output)
```
#### 卷积神经网络类
下面定义的类包含用于训练和利用 convnets 的训练和分类函数。
```py
class ConvolutionalNeuralNet():
def __init__(self, network):
self.network = network.to(device)
self.optimizer = torch.optim.Adam(self.network.parameters(), lr=3e-4)
def train(self, loss_function, epochs, batch_size,
training_set, validation_set):
# creating log
log_dict = {
'training_loss_per_batch': [],
'validation_loss_per_batch': [],
'training_accuracy_per_epoch': [],
'validation_accuracy_per_epoch': []
}
# defining weight initialization function
def init_weights(module):
if isinstance(module, nn.Conv2d):
torch.nn.init.xavier_uniform_(module.weight)
module.bias.data.fill_(0.01)
# defining accuracy function
def accuracy(network, dataloader):
total_correct = 0
total_instances = 0
for images, labels in tqdm(dataloader):
images, labels = images.to(device), labels.to(device)
predictions = torch.argmax(network(images), dim=1)
correct_predictions = sum(predictions==labels).item()
total_correct+=correct_predictions
total_instances+=len(images)
return round(total_correct/total_instances, 3)
# initializing network weights
self.network.apply(init_weights)
# creating dataloaders
train_loader = DataLoader(training_set, batch_size)
val_loader = DataLoader(validation_set, batch_size)
for epoch in range(epochs):
print(f'Epoch {epoch+1}/{epochs}')
train_losses = []
# training
print('training...')
for images, labels in tqdm(train_loader):
# sending data to device
images, labels = images.to(device), labels.to(device)
# resetting gradients
self.optimizer.zero_grad()
# making predictions
predictions = self.network(images)
# computing loss
loss = loss_function(predictions, labels)
log_dict['training_loss_per_batch'].append(loss.item())
train_losses.append(loss.item())
# computing gradients
loss.backward()
# updating weights
self.optimizer.step()
with torch.no_grad():
print('deriving training accuracy...')
# computing training accuracy
train_accuracy = accuracy(self.network, train_loader)
log_dict['training_accuracy_per_epoch'].append(train_accuracy)
# validation
print('validating...')
val_losses = []
with torch.no_grad():
for images, labels in tqdm(val_loader):
# sending data to device
images, labels = images.to(device), labels.to(device)
# making predictions
predictions = self.network(images)
# computing loss
val_loss = loss_function(predictions, labels)
log_dict['validation_loss_per_batch'].append(val_loss.item())
val_losses.append(val_loss.item())
# computing accuracy
print('deriving validation accuracy...')
val_accuracy = accuracy(self.network, val_loader)
log_dict['validation_accuracy_per_epoch'].append(val_accuracy)
train_losses = np.array(train_losses).mean()
val_losses = np.array(val_losses).mean()
print(f'training_loss: {round(train_losses, 4)} training_accuracy: '+
f'{train_accuracy} validation_loss: {round(val_losses, 4)} '+
f'validation_accuracy: {val_accuracy}\n')
return log_dict
def predict(self, x):
return self.network(x)
```
### 基准测试结果
#### ConvNet_1(全局平均池)
ConvNet_1 使用全局平均池来产生分类向量。设置感兴趣的参数和 60 个时期的训练产生如下分析的度量日志。
```py
model_1 = ConvolutionalNeuralNet(ConvNet_1())
log_dict_1 = model_1.train(nn.CrossEntropyLoss(), epochs=60, batch_size=64,
training_set=training_set, validation_set=validation_set)
```
从获得的日志来看,在模型训练过程中,训练和验证的准确性都有所提高。验证准确度在大约 66%开始,然后在第 28 个时期稳定地增加到略低于 80%的值。然后在第 31 个时期观察到急剧增加到低于 85%的值,最终在第 60 个时期达到大约 87%的峰值。
```py
sns.lineplot(y=log_dict_1['training_accuracy_per_epoch'], x=range(len(log_dict_1['training_accuracy_per_epoch'])), label='training')
sns.lineplot(y=log_dict_1['validation_accuracy_per_epoch'], x=range(len(log_dict_1['validation_accuracy_per_epoch'])), label='validation')
plt.xlabel('epoch')
plt.ylabel('accuracy')
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/deca9be9c84691fc482b73505552ed68.png)
#### ConvNet_2(全局最大池)
ConvNet_2 利用全局最大池而不是全局平均池来产生 10 个元素的分类向量。保持所有参数不变并训练 60 个时期会产生下面的度量日志。
```py
model_2 = ConvolutionalNeuralNet(ConvNet_2())
log_dict_2 = model_2.train(nn.CrossEntropyLoss(), epochs=60, batch_size=64,
training_set=training_set, validation_set=validation_set)
```
总的来说,在 60 个时期的过程中,训练和验证的准确性都增加了。验证准确度在波动之前开始时略低于 70%,而到第 60 个时期时稳定地增加到略低于 85%的值。
```py
sns.lineplot(y=log_dict_2['training_accuracy_per_epoch'], x=range(len(log_dict_2['training_accuracy_per_epoch'])), label='training')
sns.lineplot(y=log_dict_2['validation_accuracy_per_epoch'], x=range(len(log_dict_2['validation_accuracy_per_epoch'])), label='validation')
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.savefig('maxpool_benchmark.png', dpi=1000)
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5f3338cd9b15d731c2a0e8d84a6addca.png)
#### 比较性能
比较两种全局池技术的性能,我们可以很容易地推断出全局平均池的性能更好,至少在我们选择使用的数据集(FashionMNIST)上。这似乎真的很符合逻辑,因为全局平均池产生代表每个特征图中所有像素的一般性质的单个值,而全局最大池产生孤立的单个值,而不考虑特征图中存在的其他像素。然而,为了得出更具结论性的结论,应该对几个数据集进行基准测试。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7643322a2ebda907054bcd408a120e39.png)
### 引擎盖下的全球统筹
为了直观地了解为什么全局池实际工作,我们需要编写一个函数,使我们能够可视化卷积神经网络中中间层的输出。很多时候,神经网络被认为是黑盒模型,但至少有某些方法可以尝试撬开黑盒,以了解里面发生了什么。下面的函数就是这么做的。
```py
def visualize_layer(model, dataset, image_idx: int, layer_idx: int):
"""
This function visulizes intermediate layers in a convolutional neural
network defined using the PyTorch sequential class
"""
# creating a dataloader
dataloader = DataLoader(dataset, 250)
# deriving a single batch from dataloader
for images, labels in dataloader:
images, labels = images.to(device), labels.to(device)
break
# deriving output from layer of interest
output = model.network.network[:layer_idx].forward(images[image_idx])
# deriving output shape
out_shape = output.shape
# classifying image
predicted_class = model.predict(images[image_idx])
print(f'actual class: {labels[image_idx]}\npredicted class: {torch.argmax(predicted_class)}')
# visualising layer
plt.figure(dpi=150)
plt.title(f'visualising output')
plt.imshow(np.transpose(make_grid(output.cpu().view(out_shape[0], 1,
out_shape[1],
out_shape[2]),
padding=2, normalize=True), (1,2,0)))
plt.axis('off')
```
为了使用该功能,应正确理解参数。该模型是指一个卷积神经网络实例化相同的方式,我们在这篇文章中,其他类型将不会与此功能。在这种情况下,数据集可以是任何数据集,但最好是验证集。Image_idx 是所提供的第一批数据集中图像的索引,该函数将一批图像定义为 250 个图像,因此 image_idx 的范围可以是 0 - 249。另一方面,Layer_idx 并不是指卷积层,而是指 PyTorch sequential 类定义的层,如下所示。
```py
model_1.network
# output
>>>> ConvNet_1(
(network): Sequential(
(0): Conv2d(1, 8, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU()
(2): Conv2d(8, 8, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(3): ReLU()
(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(5): Conv2d(8, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(6): ReLU()
(7): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(8): ReLU()
(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(10): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU()
(12): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(13): ReLU()
(14): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(15): Conv2d(32, 10, kernel_size=(1, 1), stride=(1, 1))
(16): AvgPool2d(kernel_size=3, stride=3, padding=0)
)
)
```
#### 为什么全球平均池有效
为了理解全局平均池的工作原理,我们需要在完成全局平均池之前可视化输出层的输出,这对应于第 15 层,因此我们需要抓取/索引第 15 层,这意味着 layer_idx=16。使用 model_1 (ConvNet_1 ),我们产生了下面的结果。
```py
visualize_layer(model=model_1, dataset=validation_set, image_idx=2, layer_idx=16)
# output
>>>> actual class: 1
>>>> predicted class: 1
```
当我们在全局平均汇集之前可视化图像 3 的输出(索引 2)时,我们可以看到模型已经正确地预测了它的类别为类别 1(裤子),如上所示。观察可视化,我们可以看到,与其他特征图相比,索引 1 处的特征图平均具有最亮的像素。换句话说,就在全局平均汇集之前,convnet 已经学会了通过在感兴趣的特征图中“打开”更多像素来对图像进行分类。然后,当进行全局平均池化时,最高值的元素将位于索引 1 处,因此它被选为正确的类。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b6171c9bcad1e4b03d3c5100ac853044.png)
Global average pooling output.
#### 为什么全球最大池有效
保持所有参数不变,但在本例中使用 model_2 (ConvNet_2 ),我们获得以下结果。同样,convnet 正确地将该图像分类为属于类别 1。查看生成的可视化,我们可以看到索引 1 处的特征图包含最亮的像素。
在这种情况下,convnet 已经学会通过在全局最大值汇集之前“打开”感兴趣的特征图中最亮的像素来对图像进行分类。
```py
visualize_layer(model=model_2, dataset=validation_set, image_idx=2, layer_idx=16)
# output
>>>> actual class: 1
>>>> predicted class: 1
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ad85b530d0f6d6d57384e55c0ac2b80c.png)
Global max pooling output.
### 结束语
在本文中,我们探讨了全局平均池和最大池的含义。我们讨论了它们为什么会被使用,以及它们如何相互比较。我们还通过对我们的神经网络进行活组织检查和可视化中间层,对它们的工作原理有了一种直觉。
# 超越火炬视觉模型
> 原文:<https://blog.paperspace.com/going-beyond-torchvision-models/>
ResNets、DenseNets 和 Inception networks 无疑是用于执行图像分类和对象识别的一些最强大的模型。这些模型已经在 ImageNet 大规模视觉识别挑战(ILSVRC)中显示出一些有希望的结果,并且已经达到了超越人类的程度。
Pytorch,脸书用于研究和生产的深度学习基础设施,有一个名为 Torchvision 的库,主要用于计算机视觉任务,它为我们提供了所有这些在 ImageNet 数据集上训练的令人难以置信的模型。
我们可以利用这些现有的规范模型,使用一种称为迁移学习的技术来执行图像分类和检测,以适应我们的问题。查看这些模型的评估指标,我们可以发现这些模型虽然强大,但离完美的准确性还有一些距离。计算机视觉研究人员真正推动了尽可能精确的建筑模型的界限,甚至超越了 ResNets 和 DenseNets,但我们还没有看到 Torchvision 模型模块的任何更新。这就是本文试图解决的问题——访问尚未添加到 Torchvision 框架中的模型。
非常感谢 GitHub 知识库的作者[https://github.com/Cadene/pretrained-models.pytorch](https://github.com/Cadene/pretrained-models.pytorch)在 Pytorch 中实现所有这些在 torchvision 框架中不可用的模型方面所做的伟大工作。下面是整篇文章的快速概述。
1. 安装必要的库
2. 获取我们的模型。
3. 使用迁移学习在 cifar-10 数据集上训练其中一个模型
4. 将我们的模型与类似的火炬视觉模型进行评估和比较。
### 装置
有两种方法可以安装所需的模块——从 GitHub 库下载或使用 pip install。我们将首先通过 pip 安装来安装该模块。这比你想象的要简单得多。只需启动您的终端并输入命令:
```py
pip install pretrainedmodels
```
仅此而已。让我们看看如何通过克隆到存储库中来安装 pretrainedmodels 模块。也很简单。只需启动 git cmd 或任何其他终端,并使用以下命令将这些模型的实现克隆到 GitHub 存储库中:
```py
git clone https://github.com/Cadene/pretrained-models.pytorch
```
在终端中,进入克隆的目录并输入命令:
```py
python setup.py install
```
这应该会安装 pretrainedmodels 模块。要验证这一点,请打开任何 python IDE 或更好的 Jupyter notebook,并导入 pretrainedmodels 模块,代码如下:
```py
import pretrainedmodels
```
如果我们没有得到任何错误,我们的模块已经正确安装。我们应该注意,该模块不包括模型的重量。当我们获得模型时,重量将被自动下载。
### 获取我们的模型
在我们为分类选择首选模型之前,让我们看看 pretrainedmodels 模块中可供我们选择的无穷无尽的模型列表。让我们看看实现这一点的代码。
```py
print(pretrainedmodels.model_names)
```
这应该会打印出 pretrainedmodels 模块中所有可用模型的单一列表。我们还可以通过运行包含以下代码的 Jupyter 笔记本单元来查看每个型号的配置:
```py
print(pretrainedmodels.pretrained_settings["name of the model"])
```
这会以字典形式打印出一些关于模型的信息,比如模型权重的 URL 路径、用于归一化输入图像的平均值和标准偏差、输入图像大小等等。在本文中,我们将使用 se_resnet50 模型。如果你想了解更多关于这些模型的架构和性能,那么你应该看看这篇文章:[https://arxiv.org/abs/1709.01507](https://arxiv.org/abs/1709.01507)。几乎每个机器学习模型都需要数据来进行训练。在本文中,我们将使用 torchvision 框架中包含的 Cifar-10 数据集。我们将通过一个简单的管道从 torchvision 框架加载数据,但在此之前,我们需要导入一些重要的库。
```py
import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
import copy
import time
```
导入所需的库后,我们可以继续创建管道来加载数据集。
```py
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize(mean = (0.5, 0.5, 0.5), std = (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=16,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=16,
shuffle=False, num_workers=2)
dataset_sizes = {
'train' : len(trainset),
'val' : len(testset)
}
dataloaders = {
'train': trainloader,
'val' : testloader
```
在接下来的步骤中,我们将构建和训练我们的模型。我们将使用面向对象的编程风格,这是一种构建 PyTorch 模型的传统方式。在您继续下面的部分之前,我建议您在 Pytorch 官方页面上学习这个 60 分钟的 blitz 教程:[https://py torch . org/tutorials/beginner/deep _ learning _ 60min _ blitz . html](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)但是如果您仍然选择继续,不要担心,我会尽最大努力在代码中的评论中解释代码的每一点。让我们看看这样做的代码。
```py
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
# Obtain the desired model from the pretrainedmodels library
self.model = pretrainedmodels.__dict__['se_resnet50']()
# Build our classifier and since we are classifying the images into 10
# classes, we return a 10-dimensional vector as the output.
self.classifier = nn.Sequential(
nn.Linear(self.model.last_linear.in_features,10),
nn.LogSoftmax(dim=1))
# Requires_grad = False denies the se_resnet50 base model the ability to
# update its parameters hence make it unable to train.
for params in self.model.parameters():
params.requires_grad = False
# We replace the fully connected layers of the base model (se_resnet model)
# which served as the classifier with our custom trainable classifier.
self.model.last_linear= self.classifier
# Every model which inherits from nn.Module requires that we override the forward
# function which defines the forward pass computation performed at every call.
def forward(self, x):
# x is our input data
return self.model(x)
```
每次调用 init 函数时,都会创建我们的模型。现在我们已经准备好了模型,我们可以开始训练它了。在 Model 类中,我们可以定义另一个名为 fit 的函数,该函数将调用我们在一批图像上覆盖的 forward 函数,然后通过模型反向传播我们的错误以进行权重更新。让我们构建软件管道来执行这个向前传播和向后传播任务。
```py
def fit(self, dataloaders, num_epochs):
# We check whether a gpu is enabled for our environment.
train_on_gpu = torch.cuda.is_available()
# We define our optimizer and pass in the model parameters (weights and biases)
# into the constructor of the optimizer we want.
# More info: https://pytorch.org/docs/stable/optim.html
optimizer = optim.Adam(self.model.last_linear.parameters())
# Essentially what scheduler does is to reduce our learning by a certain factor
# when less progress is being made in our training.
scheduler = optim.lr_scheduler.StepLR(optimizer, 4)
# Criterion is the loss function of our model.
# We use Negative Log-Likelihood loss because we used log-softmax as the last layer of our model.
# We can remove the log-softmax layer and replace the nn.NLLLoss() with nn.CrossEntropyLoss()
criterion = nn.NLLLoss()
since = time.time()
# model.state_dict() is a dictionary of our model's parameters. What we did here
# is to deepcopy it and assign it to a variable
best_model_wts = copy.deepcopy(self.model.state_dict())
best_acc = 0.0
# We check if a gpu is enabled for our environment and move our model to the gpu
if train_on_gpu:
self.model = self.model.cuda()
for epoch in range(1,num_epochs+1):
print('Epoch {}/{}'.format(epoch, num_epochs))
print('-' * 10)
# Each epoch has a training and validation phase.
# We iterate through the training set and validation set in every epoch.
for phase in ['train', 'val']:
# we apply the scheduler to the learning rate in the training phase since
# we don't train our model in the validation phase
if phase == 'train':
scheduler.step()
self.model.train() # Set model to training mode
else:
self.model.eval() #Set model to evaluate mode to turn off features like dropout
running_loss = 0.0
running_corrects = 0
# Iterate over batches of train and validation data.
for inputs, labels in dataloaders[phase]:
if train_on_gpu:
inputs = inputs.cuda()
labels = labels.cuda()
# clear all gradients since gradients get accumulated after every
iteration.
optimizer.zero_grad()
# track history if only in training phase
with torch.set_grad_enabled(phase == 'train'):
outputs = self.model(inputs)
_, preds = torch.max(outputs, 1)
# calculates the loss between the output of our model and ground-truth
labels
loss = criterion(outputs, labels)
# perform backpropagation and optimization only if in training phase
if phase == 'train':
# backpropagate gradients from the loss node through all the
parameters
loss.backward()
# Update parameters(Weighs and biases) of our model using the
gradients.
optimizer.step()
# Statistics
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / dataset_sizes[phase]
epoch_acc = running_corrects.double() / dataset_sizes[phase]
print('{} Loss: {:.4f} Acc: {:.4f}'.format(
phase, epoch_loss, epoch_acc))
# Deep copy the model if we obtain a better validation accuracy than the previous one.
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(self.model.state_dict())
time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(
time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))
# Load best model parameters and return it as the final trained model.
self.model.load_state_dict(best_model_wts)
return self.model
# We instantiate our model class
model = Model()
# Run 10 training epochs on our model
model_ft = model.fit(dataloaders, 10)
```
对 se_resnet50 进行 10 个历元的训练,我们达到了 86.22%的验证准确率。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0be368bfe7ad856afdeaa3b2172567a3.png)
best validation accuracy for se_resnet50
我们还拟合了 torchvision 提供的 resnet50 模型,其中 se_resnet50 是 cifar-10 数据集上 10 个时期的变体。在训练结束时,我们获得了 34.90%的准确率,这是非常差的。很明显,我们可以看到 resnet50 模型并不适合这个问题,但它的一个变体(已经在 pretrainedmodels 库中实现)在这个问题上表现得非常好。
### 后续步骤
下一步,我鼓励读者尝试 pretrainedmodels 库中提供的所有模型,看看是否有任何 pretrainedmodels 能够带来性能上的改进。
### 关于作者
我是一名本科生,目前在读电气电子工程。我也是一个深度学习爱好者和作家。我的工作主要集中在计算机视觉在医学图像分析中的应用。我希望有一天能打入自动驾驶汽车领域。你可以在推特(@henryansah083)上关注:[https://twitter.com/henryansah083?s=09](https://twitter.com/henryansah083?s=09)LinkedIn:[https://www.linkedin.com/in/henry-ansah-6a8b84167/](https://www.linkedin.com/in/henry-ansah-6a8b84167/)
# 对比:谷歌的 AI 平台笔记本和 Paperspace 的渐变笔记本
> 原文:<https://blog.paperspace.com/google-cloud-platform-notebooks/>
谷歌云平台提供了一套名为 [AI Platform](https://cloud.google.com/ai-platform/) 的机器学习工具,其中包括标记数据、创建管道(通过 Kubeflow)、运行作业、部署模型以及创建可共享的基于云的笔记本的工具。
今天,我们将关注人工智能平台笔记本电脑——这是一款与来自其他公共云的企业笔记本电脑直接竞争的产品,如 [Azure 的机器学习笔记本电脑](https://blog.paperspace.com/azure-machine-learning-jupyter-notebooks-comparison-alternative/)和 AWS 的 SageMaker 笔记本电脑——我们将把它与 Paperspace Gradient 进行比较,paper space Gradient 是一款在可用性和功能方面进行竞争的产品。
# 谷歌在超大规模笔记本领域的地位
谷歌在机器学习领域拥有并运营着大量公司。需要明确的是,我们今天比较的目标是谷歌云平台的人工智能笔记本产品——而不是 Kaggle Kernels 或谷歌 Colab——尽管我们将在稍后的日期更深入地研究这些产品。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f289bd07f8894f53345d530625b83a8e.png)
AI Notebooks are part of Google GCP's AI Project – and are a different product entirely than Google Colab or Kaggle Notebooks
**GCP 人工智能笔记本**(今天的对比)面向需要在云(GCP)上托管完整 JupyterLab 实例的企业客户,这些实例具有基于角色的访问控制和合规性保证等企业功能。
与此同时,Google Colab 是 JupyterLab 的“轻量级”版本,通常用作 ML 工程师进行探索工作的便笺本,并与合作者和公众共享最新的库和工具。
与此同时,Kaggle 内核是 Kaggle 社区的以数据科学为中心的“轻量级”JupyterLab 风格的 IDE 版本,它也支持 r。
尽管 Colab 和 Kaggle 内核各有利弊,但 AI 笔记本是谷歌在云中提供的唯一“完整”版本的 JupyterLab。谷歌笔记本产品之间的其他一些差异如下:
| | Kaggle 笔记本 | Google Colab | GCP 人工智能笔记本 |
| --- | --- | --- | --- |
| 谷歌做的? | 是 | 是 | 是 |
| 自由层 | 是 | 是 | 不 |
| 自由层 GPU | 1 个英伟达特斯拉 P100 | 1 x NVIDIA K80 | 不适用的 |
| 自由层会话限制 | 9 小时(60 分钟后重启) | 12 小时(90 分钟后重启) | 不适用的 |
| 语言支持 | Python 河 | Python,Swift | Python 河 |
| 下载笔记本? | 不 | 不 | 是 |
| 费用 | 自由的 | 免费或每月 9.99 美元 | 实例定价 |
| JupyterLab or IDE | 仅限 IDE | 仅限 IDE | JupyterLab only |
| API 访问? | 不 | 是 | 是 |
| 链接分享? | 是 | 是 | 不 |
# Paperspace 渐变如何比较?
与谷歌相比,Paperspace 是一家年轻的公司,但在三个数据中心区域拥有近 50 万云 GPU 用户。2018 年初推出的【Paperspace Gradient 笔记本已经是最受欢迎的云笔记本之一,产品[由 Fast.ai](https://course.fast.ai/start_gradient) 作为云笔记本提供商正式推荐。
Paperspace Gradient 笔记本提供了谷歌人工智能平台笔记本的一些专业吸引力(如强大的 GPU 实例、团队协作和从您自己的容器中构建),但具有 Kaggle 内核和谷歌 Colab 用户喜欢的许多可用性功能-如能够在几秒钟内启动笔记本,并通过按下按钮来邀请合作者。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/944df7cd55c8933cb3cd4e22cfae853f.png)
Console view of a Paperspace Gradient notebook running the latest version of Fast.ai's **[Practical Deep Learning for Coders](https://course.fast.ai/start_gradient)**
# TL;博士;医生
谷歌人工智能平台笔记本电脑是企业级笔记本电脑,最适合那些有合规要求的人,那些需要从 BigQuery 等 GCP 来源获取数据的人,以及那些已经在 GCP 生态系统中并可以利用现有计算实例的人。
缺点是,人工智能平台笔记本需要大量的设置时间,需要 GCP 实例来为笔记本提供资金,并且有一些令人困惑的界面怪癖,使其难以快速启动和运行——甚至难以完成一些基本任务。
# 深度看 GCP 上的云 AI 笔记本
谷歌为机器学习提供软件工具全生命周期的努力被称为 AI 平台。
AI 平台被宣传为“端到端的机器学习生命周期”,包含以下组件:
* 准备-[big query](https://cloud.google.com/bigquery/)、[云存储、](https://cloud.google.com/storage/)、[数据标注服务](https://cloud.google.com/ai-platform/data-labeling/docs/)
* build-[AutoML](https://cloud.google.com/automl/), [AI 项目笔记本](https://cloud.google.com/ai-platform/notebooks/docs/)
* 验证-[人工智能解释](https://cloud.google.com/explainable-ai/)、[假设分析工具](https://pair-code.github.io/what-if-tool/)、[维兹尔](https://cloud.google.com/ai-platform/optimizer/docs/overview)
* 部署-[预测](https://cloud.google.com/ai-platform/prediction/docs/overview)、[汽车视觉边缘](https://firebase.google.com/docs/ml/automl-image-labeling)、[腾迅企业](https://cloud.google.com/tensorflow-enterprise/)
* MLOps-[管道](https://cloud.google.com/ai-platform/pipelines/docs)
用谷歌的话说,人工智能项目笔记本是“构建”步骤的一部分。该产品不同于其他谷歌支持的笔记本选项,如 Kaggle Notebooks 或 Colab,因为这些笔记本由特定的(可能比你在 Kaggle 上获得的 P100 或 Colab 的 K80 更强大)GCP 实例支持。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a26705c5c02f0e42bdc066b4c50508aa.png)
Notebooks view in GCP AI Platform Notebooks
如前所述,谷歌有 3 款笔记本产品:
1. 人工智能平台笔记本
2. Google Colab
3. Kaggle 笔记本
谷歌人工智能平台笔记本相对于谷歌 Colab 和 Kaggle 笔记本来说,功能更加全面。
人工智能平台笔记本电脑是为企业用户设计的。事实上,Kaggle 有几个提示鼓励你升级到 AI 平台笔记本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b043dc75ca4e689e6783aec28ee203d3.png)
所以 AI 平台笔记本是谷歌在笔记本方面最高的产品。
Google Colab 和 Kaggle 笔记本的用途要广泛得多,但是没有享受到 AI 笔记本的健壮性和可扩展性。
例如,如果您需要创建不可抢占的笔记本,或者可以在多个 K80 GPU 上运行的笔记本,或者任何数量的任何其他场景,您将需要使用 AI 项目笔记本来满足这些要求。
# 特征比较
总的来说,梯度笔记本和 AI 平台笔记本提供了一个全功能和受管版本的 JupyterLab,具有一些关于数据摄取、计算管理等的附加功能。
让我们比较一下 Paperspace Gradient 和 AI 平台笔记本:
| | 谷歌人工智能平台笔记本 | 图纸空间渐变笔记本 |
| --- | --- | --- |
| **成本** | 新用户可获得 300 美元的免费积分 | 免费的 CPU 和 GPU 笔记本电脑 |
| **资源** | 任何 GCP 实例 | 任何图纸空间实例 |
| **从零要求开始** | 信用卡,GPU 批准 | 免费的 CPU 和 GPU,无需信用卡或批准 |
| **启动时间** | 计算需要几分钟来初始化 | 计算需要几秒钟来初始化 |
| **自动关机** | 不* | 是 |
| **Jupyter 笔记本选项** | 是 | 是 |
| **JupyterLab Option** | 是 | 是 |
| **从容器构建** | 是 | 是 |
**有许多需要自定义代码的变通方法。一个例子使用 Google 的云调度器,另一个例子需要编写一个 cronjob,通过 API 访问实例。更多关于[堆栈溢出](https://stackoverflow.com/questions/58830867/gcp-auto-shutdown-and-startup-using-google-cloud-schedulers)的信息。*
# 成本比较
谷歌人工智能平台笔记本运行在 GCP 实例上,定价非常难以预测。
以下是基于 GPU 类型的谷歌人工智能平台笔记本价格的尝试:
| 实例类型 | 图纸空间渐变笔记本 | 实例类型 | 谷歌人工智能平台笔记本 |
| --- | --- | --- | --- |
| 免费(M4000) | 每小时 0.00 美元 | A100 | $2.93/GPU/hr |
| 免费(P5000) | 每小时 0.00 美元 | T4 | $0.35/GPU/hr |
| P4000* | 每小时 0.51 美元 | P4 | $0.60/GPU/hr |
| P5000* | 每小时 0.78 美元 | V100 | $2.48/GPU/hr |
| P6000* | 每小时 1.10 美元 | P100 | $1.46/GPU/hr |
| V100* | 每小时 2.30 美元 | K80 | $0.45/GPU/hr |
| P5000 x4* | 每小时 3.12 美元 | - | - |
| P6000 x4* | 每小时 4.40 美元 | - | - |
Paperspace 的付费实例需要订阅计划,而 GCP AI 笔记本不需要订阅。梯度订购层级如下:
| 梯度订阅类型 | 费用 | 细节 |
| --- | --- | --- |
| 自由的 | 0 美元/月 | -仅免费实例
-笔记本是公共的
-限制 1 台并发笔记本
-每次会话最多限制 12 小时
- 5GB 永久存储 |
| G1(个人) | 8 美元/月 | -免费和付费实例
-私人笔记本
-限制 5 个并发笔记本
-无限会话长度
- 200GB 永久存储 |
| G2(个人) | 24 美元/月 | -免费和付费实例
-私人笔记本
-限制 10 个并发笔记本
-无限会话长度
- 1TB 永久存储 |
| T1(团队) | 12 美元/用户/月 | -免费和付费实例
-私有笔记本
-限制 10 个并发笔记本
-无限会话长度
- 500GB 持久存储
-私有团队协作
-私有托管集群 |
| T2(团队) | 49 美元/用户/月 | -免费和付费实例
-私有笔记本
-限制 50 个并发笔记本
-无限会话长度
- 1TB 持久存储
-私有团队协作
-私有托管集群 |
# 入门指南
## 使用谷歌 GCP 的人工智能平台设置 Jupyter 笔记本:
* 如果需要,创建一个 GCP 帐户(您可以使用现有的谷歌帐户)
* 导航到 GCP 控制台([链接](https://console.cloud.google.com))并为该帐户启用计费
* 同意条款并添加信用卡
* 一旦你的信用卡再次通过验证,请访问 GCP 控制台
* 从 GCP 工具条中,选择人工智能>人工智能平台>笔记本
* 创建一个新实例,并选择要运行的计算。您将根据所选实例的大小付费。
* *注意:GCP 目前为新账户提供 300 美元的信贷*
## 在图纸空间渐变中设置 Jupyter 笔记本
若要开始使用渐变笔记本:
* 创建一个 Paperspace 帐户([链接](https://console.paperspace.com/signup))
* 导航到渐变>笔记本,并选择创建笔记本
* 输入笔记本的名称、运行时(可选),并选择一个实例
* 如果你选择了一个自由的 CPU 或自由的 GPU 实例,选择开始笔记本,就是这样!(付费实例需要信用卡。)
* *注意:Paperspace 提供对自由层 CPU 和 GPU 支持的笔记本电脑的无限制使用*
# 启动时间
任何云提供商都需要一些时间来启动 CPU 或 GPU 实例。GCP 人工智能平台创建第一个资源大约需要 3 分钟,而 Paperspace 大约需要 30 秒。
# 结论
谷歌有三款云笔记本产品——Google Colab、Kaggle 笔记本和 AI 平台笔记本——每一款都有不同的优势,从社交(例如 Kaggle)到强大(AI 平台笔记本)。
AI 平台笔记本电脑是一种企业产品,专为那些拥有监控访问控制和资源消耗的 IT 功能的公司而设计。该产品最适合已经充分利用 GCP 资源的公司——因为与 GCP 计算实例和 BigQuery 等 GCP 数据管理工具的集成是人工智能平台笔记本可用性的关键。
同时,Paperspace Gradient 笔记本电脑还提供可扩展性(通过 Paperspace 云或已配置的公共云中的其他集群)和更多功能,让您更快地启动和运行,包括链接共享、轻松的协作者管理、共享您的工作的公共“梯度运行”链接、简单的计费等。
我们希望您喜欢这个比较!
要阅读该比较系列的更多内容,请查看[比较:Azure ML 笔记本和渐变笔记本](https://blog.paperspace.com/azure-machine-learning-jupyter-notebooks-comparison-alternative/)或访问 [Paperspace Learn](https://learn.paperspace.com/) 。
# GPT-NeoX:梯度多 GPU 上的 200 亿参数 NLP 模型
> 原文:<https://blog.paperspace.com/gpt-neox-20-multi-gpu/>
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/afd9280a53a8050d583c151a1999c974.png)
GPT-NeoX's logo, from EleutherAI's [project page](https://www.eleuther.ai/projects/gpt-neox)
[GPT-NeoX](https://blog.eleuther.ai/announcing-20b/) 是来自 [EleutherAI](https://www.eleuther.ai/) 的最新自然语言处理(NLP)模型,于 2022 年 2 月发布。它是迄今为止最大的开源 NLP 模型,包含 200 亿个参数。
在这里,我们展示了 Gradient 的多 GPU 能力允许这个模型用于文本生成和模型评估,而不需要用户首先进行设置。
## 什么是 GPT-奈奥斯?
GPT-NeoX,或者更具体地说,GPT-NeoX-20B,是过去几年中发布的一系列越来越大的 NLP 模型的一部分。这是在发现模型越大,这种模型的性能越好之后。因此,研究小组受到激励,不断突破界限。
不幸的是,生产最大模型的很多能力存在于大型科技公司的研究团队中。因此,除了通过 API 或 app 来使用已经训练好的模型之外,这些模型仍然是闭源的,并且不可用于一般社区。
这促使 EleutherAI 和其他人发布了他们自己的一系列大型 NLP 模型的开源版本,以便更广泛的社区可以使用它们进行研究。GPT-NeoX-20B 是其中最新的一个,在 96 个 A100 GPUs 上训练。
## 梯度上的 GPT-NeoX-20B
> 总参数:20,556,201,984
该模型可在 [EleutherAI 的 GitHub 库](https://github.com/EleutherAI/gpt-neox)上获得。虽然回购表现良好且易于访问,但单独来看,这需要用户有一个能够运行模型的 GPU 设置。特别是在编写的时候,它需要至少 2 个 GPU 才能运行。这不仅仅是指总内存,还包括数值。除了设置 GPU 和 CUDA 等常见任务之外,这还会在硬件和软件方面向用户引入多 GPU 设置要求。,来运行机器学习工作负载。
Gradient 消除了 GPU 设置开销,允许用户直接使用 repo 并运行模型。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/df21ba8fd224c34142eeec882389c322.png)
Setting up a multi-GPU machine on Gradient: Click the one you want and ... that's it
设置模型在坡度上运行: **(1)**
* 注册一个允许访问多 GPU 机器的订阅。目前这是[增长水平](https://www.paperspace.com/pricing)或更高。
* 使用 A6000x2 机器(2 个 Nvidia Ampere A6000 GPUs)创建一个新项目和笔记本
* 在笔记本的*高级选项*下,设置容器为`nvcr.io/nvidia/pytorch:21.10-py3`。
* 还将工作区 URL 设置为 EleutherAI repo: [`https://github.com/EleutherAI/gpt-neox`](https://github.com/EleutherAI/gpt-neox) 。这会将该回购安装到您的笔记本中,以便您可以立即访问它。
* 在笔记本中打开一个终端窗口,安装其依赖项:`pip install -r requirements/requirements.txt`
* 然后下载模型: **(2)**
`wget --cut-dirs=5 -nH -r --no-parent --reject "index.html*" [https://mystic.the-eye.eu/public/AI/models/GPT-NeoX-20B/slim_weights/](https://mystic.the-eye.eu/public/AI/models/GPT-NeoX-20B/slim_weights/) -P 20B_checkpoints`
* 这会将它放在当前目录的目录`20B_checkpoints/`中。你也可以把它放在你笔记本的持久`/storage`目录中,或者把它做成一个渐变数据集(见我们的[数据](https://docs.paperspace.com/gradient/data/)文档)。 **(3)**
* 编辑`configs/20B.yml` YAML 文件,将其`"pipe-parallel-size": 4`设置从 4 改为 1,因为我们使用的是 2 个 GPU 而不是 8 个。
* 如果您的`20B_checkpoints/`目录不在您当前的目录中,编辑同一个 YAML 文件中的`vocab-file`、`save`和`load`设置以指向它的位置。
既然模型已经准备好运行,我们将继续让它生成一些文本,然后评估它的性能。
***(1)** 这些说明假设你知道使用渐变的基本知识。如果你不太熟悉渐变,那么[文档](https://docs.paperspace.com/gradient/)中的笔记本教程是一个很好的起点。*
***(2)** 型号大:39GB。将来,我们可能会将其添加到梯度公共数据集,使其比外部站点的通用`wget`更容易访问。*
***(3)** 梯度数据集是文件的通用版本化集合,因此它们可以包括诸如 NLP 模型检查点的文件。*
## 文本生成
展示模型运行的一个简单方法是让它生成一些文本。您可以通过无条件生成来运行它,或者提供一个包含文本提示的文件,然后模型从该文件中提供一个延续。
对于无条件生成,运行
`./deepy.py generate.py ./configs/20B.yml`
第一次运行大约需要半个小时,因为必须加载 39GB 大小的模型。默认输出放入`samples.txt`。
如果您在脚本运行时运行`nvidia-smi`,您可以看到它在 2 个 GPU 上占用了大约 40GB 的总 GPU RAM,如 repo 中所述。当脚本完成时,它很好地将 GPU 清除回零内存使用。
当我们运行它时,生成的文本是(减去 JSON 格式):
```py
Q:
How to get the value of a variable in a function in a different file?
I have a file called "main.py" and a file called "functions.py".
In "main.py" I have:
import functions
def main():
print(functions)
```
对于条件生成,您可以运行,
`./deepy.py generate.py ./configs/20B.yml -i prompt.txt -o sample_outputs.txt`
我们的输入在`prompt.txt`中,这次我们把输出放在`sample_outputs.txt`中。
当我们使用三个提示运行它时:
```py
Paperspace Gradient is
The second prompt is
This is the last line
```
```py
Paperspace Gradient is a new type of gradient that is based on the paper-folding technique. It is a gradient that is created by folding a piece of paper into a specific shape. The shape is then cut out and used as a gradient.
The gradient is created by folding a piece of paper into a specific shape. The
```
```py
The second prompt is to enter the name of the file you want to create.
The third prompt is to enter the name of the file you want to create.
The fourth prompt is to enter the name of the file you want to create.
The fifth prompt is to enter the name of the file you want to
```
和
```py
This is the last line of the file.
A:
You can use the following regex:
^.*?\
(.*?)\
(.*?)\
(.*?)\
(.*?)\
(.*?)\
(.*?)\
(.*?)\
(.*?)\
(
```
第一个看起来还可以,尽管后两个可能需要一些工作。这可能是因为输入是相当不寻常的文本或者引用了带有“Gradient”的专有名词。如上所述,输出长度受限于用户提供的或默认的字符数,这就是它被截断的原因。
因为提示可以是任意的文本,并且模型具有代码生成和文本等功能,所以它的使用范围几乎是无限的。
## 模型评估
除了生成的文本有多好的定性外观之外,模型生成的内容也可以进行定量评估。有很多方法可以做到这一点。
使用回购协议中的一个简单示例,我们可以运行
`./deepy.py evaluate.py ./configs/20B.yml --eval_tasks lambada piqa`
这告诉它使用与上面相同的预训练模型,即`20B.yml`中描述的 GPT-NeoX-20B,在数据集`lambada`和`piqa`上运行评估任务。`lambada`数据由`evaluate.py`脚本下载。
***注意:**在我们的设置中似乎出现了一个错误,其中 [lm_eval](https://www.eleuther.ai/projects/lm-eval/) `lambada.py`中的参数似乎与`best_download` Python 模块相反,导致输出文件被命名为其校验和值,然后找不到。不清楚这是回购中的错误还是库版本不匹配,但我们通过直接获取数据来解决这个问题:`wget [http://eaidata.bmk.sh/data/lambada_test.jsonl](http://eaidata.bmk.sh/data/lambada_test.jsonl) -O data/lambada/lambada_test.jsonl`,然后重新运行 evaluate。(而且没错是 [JSONL](https://jsonlines.org/) ,不是 JSON。)*
不幸的是,回购协议和白皮书没有明确提到什么是`lambada`和`piqa`,但可以推测它们对应于之前在 EleutherAI 的[博客](https://blog.eleuther.ai/)和其他地方提到的 LAMBADA 和 PIQA 文本数据集。它们是包含训练和测试部分的文本数据集,因此适合于评估 NLP 模型。
当我们运行评估时,它给出了结果(再次减去一些格式):
```py
'lambada':
'acc': 0.7209392586842616
'acc_stderr': 0.006249003708978234
'ppl': 3.6717612629980607
'ppl_stderr': 0.07590817388828183
'piqa':
'acc': 0.7742110990206746
'acc_norm': 0.780739934711643
'acc_norm_stderr': 0.009653357463605301
'acc_stderr': 0.009754980670917316
```
这意味着它得到了 72.09 +/- 0.62%和 77.4 +/- 0.98%的精度,这与[原始白皮书](http://eaidata.bmk.sh/data/GPT_NeoX_20B.pdf)中的值一致。所以我们的结果看起来是合理的。
## 未来的工作
到目前为止,明显缺失的部分是模型微调训练。
虽然文本生成和模型评估是从 NLP 中获得价值的关键功能,但对于给定的业务问题或其他项目,获得最大价值的正常方法是采用提供的预训练模型,然后对其执行微调训练,以根据您的特定任务或领域定制模型。
这需要带来自己的额外相关训练数据,并对模型进行几个时期的训练,以使其在期望的领域中表现良好。
例如,在 EleutherAI repo 中,他们提供了一系列微调数据集,默认为几年前众所周知的争议中的安然电子邮件数据。与一般的训练模型相比,对此模型进行微调将提高其在生成新的安然式电子邮件方面的性能。也许你的项目不需要更好的安然电子邮件,但是如果你提供你所需要的数据,这个想法就很清楚了。
用于微调训练的数据通常比用于从头开始训练的原始文本小得多。这里,最初提供的 GPT-NeoX-20B 是在 800GB 的[堆](https://pile.eleuther.ai/)文本数据集上训练的,但微调数据集可以是几十 MB,只要示例是高质量的。
然而,报告指出,与我们上面看到的需要大约 40G GPU RAM 的文本生成和模型评估相比,总内存需要“明显更多的训练”。在 Forefront Technologies 的这篇博客文章之后,我们预计它可以在 8 个 A100 GPUs 的设置上工作,但这里我们将这一点推迟到未来的博客文章中。
回购协议还指出,他们使用[权重&偏差](https://wandb.ai)来跟踪他们的模型训练。在[最近在我们的博客上发布了](https://blog.paperspace.com/weights-biases-with-gradient/)之后,我们也计划展示这一点。
## 结论
我们已经展示了迄今为止发布的最大的开源自然语言处理(NLP)模型 GPT-NeoX-20B:
* 在 Gradient 上运行,不需要用户自己设置任何 GPU 基础架构
* 是否满足了至少有 2 个 GPU(多 GPU)和 40GB+总 GPU RAM 的要求
* 成功生成外观合理的文本
* 达到预期的精度(模型评估)
* 任何 Gradient 用户都可以运行 Growth 订阅或更高版本
由于这是 Gradient 的通用笔记本+工作流+部署数据科学+ MLOps 功能的一部分,因此用户可以以任何方式扩展运行该模型的项目。
在未来的帖子中,我们计划在比这里使用的 2 个更多的 GPU 上进行微调训练,并通过 Weights & Biases 监控训练。
### 后续步骤
* [报名梯度](https://console.paperspace.com/signup)试用 GPT-NeoX-20B
* 在 [EleutherAI 的 GitHub 知识库](https://github.com/EleutherAI/gpt-neox)上阅读更多信息
* 查看他们最初的[白皮书](http://eaidata.bmk.sh/data/GPT_NeoX_20B.pdf)
# Agisoft Photoscan 中的 GPU 加速
> 原文:<https://blog.paperspace.com/gpu-acceleration-in-agisoft-photoscan/>
## 如何在 Photoscan 中启用 GPU 加速
借助 Paperspace 强大的 GPU 和 Photoscan 的 GPU 加速工作流,大型图像数据集的处理可以在几小时内完成,而不是几天。本演练将介绍使用 Photoscan 和 Paperspace 进行 GPU 加速的强大功能。
想要更多曝光?
如果您希望您的摄影测量工作流程以 Paperspace 为特色,请给我们发电子邮件至 hello@paperspace.com,或发推特给我们,包括标签#PoweredByPaperspace
## **教程大纲**
* [GPU 加速](#gpu)
1. 观察
* [启用和禁用](#enable)
1. 偏好;喜好;优先;参数选择
* [CPU vs GPU](#benchmark)
1. 中央处理器
* [结论](#conclusion)
### **1。** GPU 加速
Agisoft Photoscan 能够在处理图像数据集时利用机器的 CPU 或 GPU。借助他们的 GPU 加速功能,通常需要近几天才能完成的工作现在只需几个小时。
这种速度的提高在很大程度上取决于项目、图像数量、图像大小和图像内容。根据经验,具有高重叠、高图像内容以及大量关键点的项目从加速中获益更多。
那么,我们如何在图纸空间中实现这一点呢?
### **2。**启用和禁用 GPU 处理
可以在处理选项中启用或禁用 GPU 的使用:
1. 在菜单栏中,点击*首选项*
2. 选择 *GPU* 。![Select GPU](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1a6e48c12fb019e2259f7d0e1fd43c27.png)
3. 在 GPU 部分,选择或取消选择 GPU 设备。![Select GPU](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2dd4f165b7fdeac6b3bce28528006fbc.png)
### **3。** CPU vs GPU
我们在 Paperspace 的 P5000 上收集了一些有用的 CPU 和 GPU 性能指标评测。
我们使用 Agisoft 提供的基准文件,以便在超高设置上构建密集云模型。
**基准模型统计数据**
* 84 台摄像机(图像)
* 图像尺寸:5184 x 3456
![Benchmark Model](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/64e1957df3f22f48cdac73124a2ef9f6.png)
**基准测试步骤**
1. 在菜单栏中,点击*工作流>构建密云* ![Photoscan on Paperspace CPU](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9e22db6e6e57464360dc1104af106345.png)
2. 选择超高质量。![Photoscan on Paperspace CPU](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f417a2815584a4232682235260bc437e.png)
3. 选择确定。
**P5000 - CPU**
注:为了测试 CPU - GPU 应该被禁用。
![Photoscan on Paperspace CPU](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e9399156685d606ee9feea7da53c5d46.png)
这个模型估计需要 32 个小时来完成处理。
**P5000 - GPU + CPU**
注意:确保在 GPU 选项卡中也启用了 CPU。
![Photoscan on Paperspace GPU](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/db34b37db5b6e6bc600696d195ac959a.png)
这个模型估计需要 1.75 小时来完成处理。
### **4。**结论
Photoscan 的 GPU 加速是一个强大的功能,可以利用 Paperspace 的 NVIDIA GPU 基础设施。在 CPU 和 GPU 之间的简单基准测试中,我们展示了 Paperspace 的基础设施可以为 Photoscan 中的 GPU 加速摄影测量带来的巨大收益。我们希望您能在[hello@paperspace.com](mailto:hello@paperspace.com)与我们分享自己的成果和经验。
您可以从他们的[用户手册中了解更多关于 Agisoft Photoscan 和图像捕捉最佳实践的信息。](http://www.agisoft.com/downloads/user-manuals/)
尽情享受吧!
要用 Photoscan 建立自己的摄影测量工作室,请在此注册。
我们需要你的帮助!
我们正在寻找专注于 IT 工作流和机器学习的内容作家、爱好者和研究人员,以帮助建立我们的社区。给 hello@paperspace.com 发电子邮件,附上写作范例和教学想法
# Gradient 上的免费 GPU 有多厉害?
> 原文:<https://blog.paperspace.com/gpu-benchmarking/>
在选择平台、实例类型或 GPU 来进行机器学习时,根据手头的情况优化您的硬件非常重要。首先,大多数阅读本文的人关心三个因素:功率、可访问性和成本。
获得足够强大的 GPU 对于完成许多涉及大数据、机器学习和深度学习的任务至关重要。持续轻松地访问这些强大的 GPU 可确保用户能够完成他们需要 GPU 完成的任务。成本是所有用户在计划一项需要高计算费用和易于访问的任务时必须考虑的平衡。
梯度上也是如此。通过让我们的免费层用户免费访问 Quadro M4000,许多用户可以获得他们需要的电源。我们的 Pro plan 用户每月支付 8 美元,甚至有更好的好处:所有 Pro plan 用户都可以免费获得带有 RTX4000、P4000、RTX5000 和 P5000 GPUs 的实例。最后,用户可以访问更强大的 GPU 实例,如 A100,只需支付订阅费用和额外的每小时费用(在本例中为 3.09 美元/小时)。
本文旨在为寻求最佳平衡的用户提供指导。使用以下关于可用机器的见解和事实,帮助您做出关于渐变项目所需实例的明智决策。
## 快速事实:
| GPU 类型 | 免费? | 内存带宽(GB/秒) | FP32 带宽(TFLOPS) | CUDA 核心 | CPU 核心的数量 | 内存大小(千兆字节) |
| 免费 M4000 | 是 | One hundred and ninety-two | Two point six | One thousand six hundred and sixty-four | eight | eight |
| P4000 | 是的,在专业计划上 | Two hundred and forty-three point three | Five point three | One thousand seven hundred and ninety-two | eight | eight |
| RTX4000 | 是的,在专业计划上 | Four hundred and sixteen | Seven point one | Two thousand three hundred and four | eight | eight |
| P5000 | 是的,在专业计划上 | Two hundred and eighty-eight | Eight point nine | Two thousand five hundred and sixty | eight | Sixteen |
| RTX5000 | 是的,在专业计划上 | Four hundred and forty-eight | Eleven point one five | Three thousand and seventy-two | eight | Sixteen |
| A100 | 不 | One thousand five hundred and fifty-five | Nineteen point five | Six thousand nine hundred and twelve | Twelve | Forty |
以上是之前列出的可供免费用户使用的 GPU 的细分,以及最强大的单个 GPU A100 的基准数据。
通过比较每个单元的 RAM 和 CUDA 内核数量,您可以看到,底层架构会影响每个 GPU 的整体处理速度。这一点最明显的体现在 CUDA 核心数量和内存带宽上:RTX 系列 GPU 的 CUDA 核心数量明显更多,与相应的 Pascal 系列 GPU 相比,在相同的内存级别上实现了更快的内存带宽。
现在,我们已经了解了可用的硬件是如何变化的,让我们看看它们在一系列基准测试中的实际表现
## 基准测试
#### 测试本身
[tf-metal-experiments](https://github.com/tlkh/tf-metal-experiments) 是由用户 tlkh 创建的 GitHub repo,旨在帮助他们比较苹果 M1 系列芯片与传统 GPU 的机器学习性能。测试本身旨在评估 TensorFlow CNN 或 HuggingFace Transformer 模型成功训练所需的时间,并输出结果以衡量每秒可处理的样本数量。
那些试图利用这个测试的人需要确保他们进行测试的环境安装了 TensorFlow 和 HuggingFace 的变形金刚库。这些将用于执行基线测试。
对于下面的这个基准测试示例,我将使用与原作者相同的 4 个测试模型框架:ResNet50、MobileNetv2、DistilBert 和 BertLarge。由于我们正在测试的最弱 GPU Quadro m 4000 的性能相对较弱,我们必须限制所有测试的批量大小,以便每台机器都能够在不耗尽内存的情况下完成每个基准测试。
除了可用的最强大的单个 GPU A100 之外,所有测试都相继完成,并且分析了所有 free 和 free for Pro 实例类型。
*注:这些结果是我自己的,并且是使用渐变进行的。如果您想复制这些结果,请遵循本文底部的指南。*
要查看我们在 Paperspace 上提供的其他 GPU 类型,请查看我们的[文档页面](https://docs.paperspace.com/gradient/more/instance-types)中的指南。
#### 结果呢
| GPU 类型 | ResNet50,批量= 32(样品/秒) | MobileNetv2,批量= 32(样本/秒) | 蒸馏仪,批量= 8(样品/秒) | BertLarge,批量= 4(样本/秒) |
| 免费 M4000 | 58, 56.6, 56.7 | 67.2, 65.7, 65.9 | 32.4, 32.6, 32.6 | 5.2, 5.1, 5.1 |
| P4000 | 105, 105.1, 104.4 | 159.5, 159.3, 158.5 | 63.6, 63.4, 63.6 | 9.9, 8.8, 8.8 |
| RTX4000 | 153.5, 154.3, 154 | 242.2, 243, 241.9 | 108.7, 107, 106.3 | 16.7, 16.3, 16.4 |
| P5000 | 133.2, 133.5,133.2 | 185.3, 187.5,184.6 | 80.6, 80.5, 80.6 | 15.2, 15.2, 15.1 |
| RTX5000 | 182.5, 181.3, 181.2 | 263.8, 262.8, 261.4 | 135.2, 134.3, 133.8 | 21.4, 21.4, 21.3 |
| A100 | 561.3, 616, 618 | 276.9, 273, 276.2 | 510.7, 507.8, 507.5 | 81.4, 81.3, 80.7 |
正如预期的那样,训练率的结果因实例类型而异。表现最差的不出所料是 M4000,它是我们列表中最老、最弱的 GPU。作为每个类别中表现最差的人,我们也可以将其作为一个很好的比较点。例如,在两个 BERT transformer 基准测试中,A100 的性能都比 M4000 快 16 倍,尽管可用内存只有 m 4000 的 5 倍。这在很大程度上是基础架构一代又一代进步以及 CUDA 内核随之增加的结果。
在专业级用户无需支付额外费用的 GPU 中,RTX5000 是每个类别中的明显赢家。尽管在 CUDA 内核和 RAM 方面存在显著差异,但 RTX5000 在 MobileNetV2 基准测试中的表现甚至与 A100 不相上下。这表明,我们的专业层用户在使用 MobileNetv2 等特别轻的型号时,除了订阅 Paperspace 之外,可以免费获得几乎与 A100 相同的功率。这有一些直观的意义;RTX 图形处理器是基于图灵微架构,安培的直接前身。
总的来说,RTX5000 在其价格点上表现非常好,我建议我们的专业用户尽可能使用这种设备。
## 复制这些基准
虽然上述指标为用户应该为渐变选择什么 GPU 提供了很好的见解,但我们自己测试这些结果总是更好。如果您想重现上述测试的结果,请使用以下指南。
首先,登录到 Paperspace Gradient 并创建一个笔记本。对于您的运行时,选择 TensorFlow。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/44c68e1264f8d54dd630dcd0adaba4fa.png)
Here is what set up should look like if you wanted to run a benchmark on a M4000
其次,选择您有兴趣测试的实例。鉴于上面的结果可供比较,我建议您从列出的“免费 GPU”(m 4000)之一开始。
第三,切换页面底部的高级选项,粘贴“[https://github.com/tlkh/tf-metal-experiments](https://github.com/tlkh/tf-metal-experiments)”作为工作区的 URL。
第四,推出笔记本。
第五,当实例运行时,在渐变笔记本中创建一个新的 iPython 笔记本。请注意,以下步骤是在 ipynb 单元中执行的,以便于记录基准测试的分数,但它们也可以直接在终端中运行。
第六,在一个新的单元格中粘贴以下内容:
```py
# run each time you make a new instance
!python3 -m pip install --upgrade regex --no-use-pep517
!pip install transformers ipywidgets
```
最后,选择您想要运行的测试,并将其粘贴到要运行的单元格中。如果您想准确地重现我的测试,请在单个单元格中运行以下命令:
```py
!python tf-metal-experiments/train_benchmark.py --type cnn --model resnet50 --bs 32
!python tf-metal-experiments/train_benchmark.py --type cnn --model mobilenetv2 --bs 32
!python tf-metal-experiments/train_benchmark.py --type transformer --model distilbert-base-uncased --bs 8
!python tf-metal-experiments/train_benchmark.py --type transformer --model bert-large-uncased --bs 4
```
您的结果将以每秒样本数的形式输出:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4fc9311fddad996611c911c8337b35a2.png)
## 结论
甚至 Gradient 上免费提供的 GPU 也足够强大,可以为 ML/DL 运行大型复杂的训练,例如 BertLarge 架构所需的训练。对于这样一项任务,运行如此昂贵的培训过程的成本是显而易见的:M4000 的培训时间将是 A100 的近 16 倍,但培训仍然完成了。
这个基准测试练习旨在展示使用更快的 GPU 有多大优势,不是通过展示它们对于适当复杂的任务是必需的,而是通过展示正确的硬件可以让处理计算成本高昂的任务快多少。因此,我鼓励这篇文章的读者考虑一下他们的时间相对于 GPU 成本的价值。花一个小时等待一个模型在你的 GPU 上训练远不如花一个小时验证和测试那个模型有价值。
# GPU 内存带宽
> 原文:<https://blog.paperspace.com/gpu-memory-bandwidth/>
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3408ea5379ac7a1349e666ab163d3f08.png)
Photo by [Rafael Pol](https://unsplash.com/@rapol?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)
这篇博客分解了一个最容易被忽视的 GPU 特性:内存带宽。我们将深入什么是 GPU 内存带宽,并看看为什么它应该被视为一个 ML 专家应该在机器学习平台中寻找的品质之一。
理解机器学习的内存需求是模型开发过程的重要组成部分。然而,这有时很容易被忽视。
## 基本的 GPU 剖析
与主板一样,显卡也是一块印刷电路板,其中包含处理器、内存和电源管理单元。它还有一个 BIOS 芯片,可以保留卡的设置,并对内存、输入和输出执行启动诊断。
图形卡上的图形处理单元(GPU)有点类似于计算机主板上的 CPU。另一方面,GPU 被设计为进行图形渲染或其他机器学习相关应用所需的复杂数学和几何计算。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c9626048affbf0dc542cb3d996e76519.png)
Nvidia GTX 780 PCB Layout, [Source](https://www.flickr.com/photos/83620044@N06/8800935020)
对于显卡来说,计算单元(GPU)通过称为内存接口的总线连接到内存单元(VRAM,视频随机存取内存的缩写)。
在整个计算机系统中,有许多内存接口。内存接口是内存总线的物理位宽,因为它与 GPU 相关。数据在每个时钟周期(每秒数十亿次)被发送到卡上存储器和从卡上存储器接收。每个时钟周期适合总线的物理位数是该接口的宽度,通常被描述为“384 位”或类似的东西。384 位存储器接口允许每个时钟周期传输 384 位数据。因此,在 GPU 上建立最大内存吞吐量时,内存接口也是内存带宽计算的一个重要部分。因此,NVIDIA 和 AMD 更有可能在其显卡中采用标准化的串行点对点总线。例如,POD125 标准由 A4000、A5000 和 A6000 NVIDIA Ampere 系列显卡使用,您可以找到适用于 [Paperspace 用户](https://blog.paperspace.com/new-ampere-gpus/)的显卡,它本质上描述了与 GDDR6 vRAMs 的通信协议。
说到内存带宽,延迟是第二个要考虑的因素。最初,实现了 VMEbus 和 S-100 总线等通用总线,但当代的存储器总线设计为直接连接到 VRAM 芯片以减少延迟。
在 GDDR5 和 GDDR6 存储器的情况下,这是 GPU 存储器标准的最新形式之一。每个存储器由两个芯片组成,每个芯片都有一个 32 位总线(两个并行的 16 位),允许同时进行多个存储器访问。因此,具有 256 位内存接口的 GPU 将具有八个 GDDR6 内存芯片。
存储器类型的另一个标准是 HBM 和 HBM2(高带宽存储器 v1 和 v2),根据这些标准,每个 HBM 接口都是 1024 位,通常提供比 GDDR5 和 GDDR6 更高的带宽。
主板和显卡之间的外部 PCI-express 连接不要与此内部内存接口混淆。这种总线的特点还在于它的带宽和速度,尽管它要慢几个数量级。
## 什么是 GPU 内存带宽?
GPU 的内存带宽决定了它将数据从内存(vRAM)移动到计算核心的速度。是比 GPU 内存速度更有代表性的指标。它取决于内存和计算内核之间的数据传输速度,以及这两部分之间的总线中独立并行链路的数量。
自 20 世纪 80 年代初的家用电脑以来,消费电子设备中的绝对内存带宽增加了几个数量级(约 1MB/s),但可用的计算资源增加得更快,避免不断触及带宽限制的唯一方法是确保工作负载和资源在内存大小和带宽方面具有相同的数量级。
让我们来看一个最先进的面向 ML 的 GPU,NVIDIA RTX A4000:
它配备了 16 GB 的 GDDR6 内存,256 位内存接口(GPU 和 VRAM 之间的总线上的独立链接数量)和数量惊人的 CUDA 内核,达到 6144 个。凭借所有这些与内存相关的特性,A4000 可以达到 448 GB/s 的内存带宽。
其他面向 [Gradient](https://docs.paperspace.com/gradient/machines/) 用户的 GPU 也提供了一些高性能内存特性:
| 国家政治保卫局。参见 OGPU | 视频随机存取存储器 | 存储器接口宽度 | 存储带宽 | |
| --- | --- | --- | --- | --- |
| P4000 | 8GB GDDR5 | 256 位 | 243 GB/秒 | |
| P5000 | 16GB GDDR5X | 256 位 | 288 GB/秒 | |
| P6000 | 24GB GDDR5X | 384 位 | 432 GB/秒 | |
| V100 | 32GB HBM2 | 4096 位 | 900 GB/秒 | |
| RTX4000 | 8GB GDDR6 | 256 位 | 416 GB/秒 | |
| RTX5000 | 16GB GDDR6 | 256 位 | 448 GB/秒 | |
| A4000 | 16GB GDDR6 | 256 位 | 448 GB/秒 | |
| A5000 | 24GB GDDR6 | 384 位 | 768 GB/秒 | |
| A6000 | 48GB GDDR6 | 384 位 | 768 GB/秒 | |
| A100 | 40GB HBM2 | 5120 位 | 1555 GB/秒 | |
## 为什么机器学习应用需要高内存带宽?
内存带宽的影响本质上并不明显。如果速度太慢,系统将出现瓶颈,这意味着所有成千上万的 GPU 计算核心在等待内存响应时都将处于空闲状态。此外,根据 GPU 的应用类型,GPU 可以重复处理数据块(称之为 T 次),因此外部 PCI 带宽必须是 GPU 内部带宽的 1/Tth。
GPU 最常见的用途证明了上述限制。例如,模型训练程序会将训练数据加载到 GDDR RAM 中,并为计算核心中的神经网络层运行几次,每次运行几个小时。所以 PCI 总线带宽与 GPU 内部带宽之比可以达到 20 比 1。
所需的内存带宽完全取决于您正在进行的项目的类型。例如,如果您正在进行一个深度学习项目,该项目依赖于在内存中输入、重新处理和持续恢复大量数据,您将需要更宽的内存带宽。对于一个基于视频和图像的机器学习项目,对内存和内存带宽的要求不像自然语言处理或声音处理项目那么低。对于大多数普通项目来说,一个较好的大概数字是 300 GB/s 到 500 GB/s。这并不总是如此,但通常有足够的内存带宽来适应各种各样的视觉数据机器学习应用。
我们来看一个深度学习内存带宽需求验证的例子:
如果我们考虑具有超过 2500 万个权重参数的 50 层 ResNet,并且如果我们使用 32 位浮点来存储单个参数,则它将占用大约 0.8GB 的存储空间。因此,在以 32 为例的小批量并行计算期间,我们将需要在每次模型传递期间加载 25.6GB 的内存。使用像 [A100](https://blog.paperspace.com/new-ampere-gpus/) 这样能够处理 19.5 万亿次浮点运算的 GPU,并考虑到 ResNet 模型在一次传递中使用 497 万亿次浮点运算(对于特征大小为 7 x 7 x 2048 的情况),我们将能够每秒完成大约 39 次完整传递,这将导致 998 GB/s 的带宽需求。因此带宽为 1555 GB/s 的 [A100](https://blog.paperspace.com/new-ampere-gpus/) 将能够有效地处理这种模型,并远离瓶颈。
## 如何优化模型以降低内存带宽使用率?
一般的机器学习算法,特别是计算机视觉领域中的深度神经网络,会导致大量的存储器和存储器带宽占用。一些技术可以用于在资源受限的环境中或者甚至在强大的云 ML 服务中部署 ML 模型,以减少成本和时间。以下是一些可以实施的策略:
**部分拟合:**如果数据集太大,无法在一次处理中拟合。此功能允许您分阶段地在数据上拟合模型,而不是一次性地在数据上拟合模型。因此,它获取一段数据,拟合它以获得一个权重向量,然后继续处理下一段数据,拟合它以获得另一个权重向量,以此类推。不用说,这降低了 VRAM 的使用,同时增加了训练持续时间。最大的缺陷是,并不是所有的算法和实现都利用部分拟合,或者在技术上可以调整到这样做。然而,只要有可能,就应该考虑到这一点。
**降维:**这不仅对减少训练时间很重要,对减少运行时的内存消耗也很重要。一些技术,如主成分分析(PCA)、线性判别分析(LDA)或矩阵分解,可以显著降低维数并产生具有较少特征的输入变量子集,同时保留原始数据的一些重要质量。
**稀疏矩阵:**处理稀疏矩阵时,只存储非零项可以节省大量内存。根据非零项的数量和分布,可以使用不同的数据结构,与基本技术相比,可以节省大量的内存。代价是访问单个组件变得更加困难,并且需要额外的结构来无歧义地检索原始矩阵,这需要使用更多的核心计算来换取更低的存储器带宽利用率。
## 结论
了解机器学习的内存带宽要求是模型构建过程的关键部分。通过阅读本文,您现在知道了什么是内存带宽。在回顾了相关性以及如何评估存储器带宽需求之后。我们讨论了一些减少带宽使用和降低成本的方法,方法是选择功能较弱的云包,同时保持时间和准确性标准。
# GPU 渲染序列背后的精彩
> 原文:<https://blog.paperspace.com/gpu-rendering-with-core/>
在本文中,我们将讨论 GPU 渲染幕后的魔力以及被称为 GPU 渲染的 3 大 GPU 渲染引擎。这篇文章中的信息旨在帮助你做出明智的职业选择。
## GPU 渲染:简介
单词**‘rendering’**来源于图形领域,其中渲染是艺术家对新结构的表现。计算机辅助设计(CAD)中的渲染是将 3D 模型的某个角度转换为逼真的图像。它既包括像美食家阴影这样的基本照明技术,也包括模拟阴影、反射和折射的更复杂的效果。它可能还包括给表面不同的纹理。
> **快速提示:GPU 是连接到计算机主板的专用单元-** 在当代系统中,GPU 用于渲染、人工智能、机器和深度学习、大数据应用、游戏和数据挖掘。
作为一个术语,渲染指的是简单地赋予事物生命。有不同的方法可以进行渲染,其中一种是使用图形处理单元(GPU)。渲染过程受益于 GPU 中数千个微小的低功耗内核执行的并行数据计算。由于其并行处理能力,GPU 可以快速轻松地计算大量数据。
现在你明白了什么是 GPU 和渲染,我们可以说: **GPU 渲染是使用图形处理器,使用计算机程序从模型中自动创建二维或三维图像的过程。**
图形要求苛刻的应用程序拖累了 CPU 并影响了处理性能,导致了 GPU 的引入。GPU 渲染不使用 CPU,而是使用显卡来渲染数据。由于 GPU 是专为快速图像渲染而构建的,因此这种技术可以显著加快渲染速度。
## 为什么使用 GPU 进行渲染
GPU 加速渲染是各种应用程序的热门选择,包括建筑、动画、电影和产品设计等领域的照片级渲染。其他应用包括 GPU 加速分析、3D 模型图形、游戏中的神经图形处理、虚拟现实和人工智能创新。
虽然 CPU 和 GPU 处理数据的方式基本相似,但 GPU 更强大,可以更快地执行有限的任务。这与 CPU 处理各种活动的能力形成对比。
只有在某些工作中,GPU 明显比 CPU 快。由于使用相同的图形卡进行渲染和显示时的交互性问题,或者由于内存不足,GPU 生成复杂场景的能力可能会受到限制。
以下是使用 GPU 渲染工作负载的理由:
* 与 CPU 相比,GPU 渲染技术使用更少的能量
* 速度提升——许多当代渲染系统与 GPU 硬件和软件兼容,这些系统专为大规模并行工作负载而构建,可以提供更高的整体性能
* 由于计算能力的提高,硬件变得更便宜
* 与 CPU 相反,为计算机添加额外的 GPU 要简单得多,并且会提高性能(在一定程度上)。因此,从这个意义上来说,它们具有难以置信的适应性和可扩展性
* 通过 Paperspace Core 等在线服务轻松访问
在讨论 4 大 GPU 渲染引擎之前,我们先来讨论一下渲染的特性和组件。
## GPU 渲染的独特功能和方面
以下是基于 GPU 的渲染系统的显著特征:
* **快速**-GPU 专为并行计算而生,受益于几个内核的性能。当处理成千上万的微内核时,可以获得相当大的速度。模型在 GPU 上的渲染速度比在 CPU 上快得多。这种速度有助于快速迭代和渲染的实时可视化。
* **效率-** 制造商在设计 GPU 时考虑了特定的功能。不同的 GPU 适用于不同的任务,如渲染、设计和游戏。为渲染而设计的 GPU 可以更快地给出可靠的结果,这使得它们更省时。他们也很好地渲染场景。
* **新技术的出现-** 自诞生以来,GPU 取得了显著进步。GPU 的性能随着每次后续迭代呈指数级增长。由于 GPU 比 CPU 更频繁地接收更新,渲染性能逐年提高。
* **合理的低成本-** 像 Threadripper 3990x 这样的高端 CPU 比 RTX 3090 这样的高端 GPU 更贵。此外,GPU 易于升级,使得在单个系统上组合一个或多个 GPU 变得简单,每个 GPU 都专用于执行特定的任务。GPU 较低的入门成本使更多人可以使用它,使他们能够在预算有限的情况下开始渲染。对于租用 GPU 的远程访问来说尤其如此,其成本可以用每小时的美分来衡量。
* **视口性能** -视口是一个窗口,可让您查看根据您的输入设置所做的调整。快速的工作流程源于快速的视口可视化。既然 GPU 擅长一次处理多个作业,这将导致输入的快速执行,从而加速视口渲染。修改几乎是实时可见的。
## 前三大 GPU 渲染引擎
如上所述,GPU 渲染概念是在多个内核上对大量数据执行,将并行处理集中在一个特定的活动上。由于 GPU 渲染时间的减少,有许多不同的渲染引擎使用 GPU 渲染。
特别是,在多个 GPU 上同时渲染比在单个 GPU 上渲染快 5 倍、10 倍或 20 倍。因此,世界上一些顶级品牌主要致力于创建增强多 GPU 性能的解决方案。
Redshift、Octane 和 Vray 是当今使用的一些流行的多 GPU 渲染引擎。这些引擎已经取得了巨大的进步,并且在受欢迎程度和渲染速度方面正在迅速超越基于 CPU 的渲染引擎。
### 红移
强大的 GPU 加速渲染器 Redshift 旨在满足当今大规模生产渲染的独特需求。Redshift 提供了一系列强大的功能,与业界广泛使用的 CG 应用程序相连接,旨在为各种规模的创意个人和工作室提供服务。
#### 主要特征
* **除了基本的几何和纹理-** Redshift 的有效内存管理支持渲染具有数 TB 纹理数据和数亿多边形的场景。
* **通用照明-** 使用基于偏置点的 GI 方法和强力 GI 来提供闪电般的间接照明。
* **代理-** 红移代理文件,可以很容易地被其他场景引用,可以被用户用来导出对象和灯光组。代理为生产中经常需要的着色器、遮罩和可见性标志提供了强覆盖。
* **命令行渲染-** 用户可以使用 Redshift CMD Line 工具导出他们的场景,并独立于他们的 3D 应用程序进行渲染。
[Application Manager - MaxonOur Application Manager allows you to manage the installation, licensing, upgrades, and updates for all your Maxon products.![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6a17fcb8ba8b194ce5f53ce92ce4c4ba.png)Maxon![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dcfaf0f27db8bdc8cd77512931138750.png)](https://www.maxon.net/en/try)
Try Redshift on your Paperspace Core machine today!
### 辛烷
简单地说,Octane 是一个 GPU 渲染引擎,它采用了一种计算最终生成的图片的方法,这种方法寻求照片真实感。采用 GPU 技术,类似阿诺德。Octane 是世界上第一个也是最快的物理精确、无偏见的 GPU 加速渲染器。目前,它被用于主要的电影,广告,甚至建筑渲染。一旦 Octane 可以在云上使用,预计它将更多地融入这些企业的创意流程。
#### 主要特征
* 与 CPU 渲染相比,GPU 渲染技术的一个好处是你可以多快地渲染一张图片。如果您目前正在使用 Cinema 的物理或传统渲染,您会意识到即使对于一个简单的场景,渲染一帧也可能需要几分钟。简单的序列像黄油一样被辛烷分割,变成秒。
* **借助实时查看器,Octane 将加速您的工作流程-** 交互式预览区域是使用任何第三方渲染引擎(IPR)的主要优势。IPR 的时髦术语是 LiveViewer。用户可以非常即时地查看渲染的场景。尤其是考虑到 Octane 使用 GPU 执行渲染。当一个对象被修改,一个光被添加,或者一个纹理属性被改变,IPRs 立即更新。太棒了。
* octane 有一个很大的社区- 有很多平台可以找到 Octane 用户,并在遇到任何问题时提供帮助。
[Octane 2022 Preview is Here with KitBash3D, Greyscalegorilla Plus and more!OTOY® announces the next-generation of the industry’s first and fastest unbiased GPU renderer Octane 2022 with monthly KitBash3D Kits and full commercial access to Greyscalegorilla Plus, World Creator, EmberGenFX, Architron, LightStage MetaFace scans, and Render.![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6ee0df16e2b48741e4693fb1f504e333.png)OTOY![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/84ad6a5ee0949a2bcfd5ce3c1d1d963e.png)](https://home.otoy.com/render/octane-render/demo/)
Try Octane on your Paperspace Core machine today!
### Vray GPU
V-Ray GPU 生成的输出等同于其他渲染器的输出,同时通过利用所有的 GPU 和/或 CPU 来提高交互性能。现在,V-Ray Next GPU 可以通过利用 NVIDIA RTX 级 GPU 中的专用光线跟踪硬件来加速产品渲染。V-Ray GPU 支持任何支持 CUDA 的 GPU,包括过去 7 年的大多数 NVIDIA GPUs。
#### 主要特点
* **GPU + CPU 渲染-** 在 CPU 和 NVIDIA GPUs 上,V-Ray GPU 渲染产生相同的结果。添加 CPU 有助于减少 13%和 25%的渲染时间。速度的提高是好的,而不是让这些强大的 CPU 闲置。
[V-Ray GPU | GPU rendering by ChaosV-Ray® GPU is full-featured 3D GPU rendering software that delivers fast, photorealistic results and instant feedback while you work.![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3a3e9c13e4fd6f9d3fd70b60e860c63a.png)GPU rendering by Chaos![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a2c3cf1adfbc449441f182311abdf6d6.png)](https://www.chaos.com/vray-gpu#free-trial)
Try Vray on your Paperspace Core machine today!
## 总结
你正在通过使用 GPU 引擎进入未来。使用 GPU 获得的速度提升是难以否认的,即使仍然有许多充分的理由利用 CPU 渲染引擎。
与计算机的任何其他组件相比,GPU 的升级也要简单得多。在使用 GPU 几年后,随着技术的进步,你可以打开 PC 的侧面,用新卡替换旧卡。如果你想要最新、最快的 CPU,你不必总是创建一个全新的系统。你现在可以把钱存起来,花在必需品上。
# 在云中使用 Octane 进行 GPU 渲染
> 原文:<https://blog.paperspace.com/gpu-rendering-with-octane-in-the-cloud/>
GPU 渲染正在成为视觉效果、动画和广告领域的行业标准。通过利用当今的高端图形卡,计算机可以看到使用针对 GPU 优化的渲染系统的性能提高了几个数量级。
Alek Pluta 设计的卡尔马艺术博物馆
GPU 渲染正在成为视觉效果、动画和广告领域的行业标准。通过利用当今的高端图形卡,计算机可以看到使用针对 GPU 优化的渲染系统的性能提高了几个数量级。OctaneRender 是最受欢迎的系统之一,因为它集成了多种软件和材料库。在本文中,我们将讨论 GPU 渲染的一些优势和历史。
想要突出你的作品吗?
如果你想在 Paperspace 上展示你的作品,请给我们发一封电子邮件到 hello@paperspace.com,或者给我们发一张图片,并附上#MadeOnPaperspace 的标签
**教程大纲**
* [辛烷是什么?](#what)
* [无偏与有偏渲染器](#UvsB)
* [Gpu vs Cpu](#GPUvsCPU)
* [优点&缺点](#Advantages)
* [辛烷渲染](#Rendering)
* [独立版 vs 插件版](#PluginVsStandalone)
* [渲染模式](#RenderModes)
* [采样选项](#Sampling)
* [材料设置](#Material)
* [Paperspace 的 GPU 选项](#Gpus)
* Gpu+
* P500(测试版)
* * *
## 辛烷是什么?
Octane 是世界上第一个也是最快的 GPU 加速、无偏见、物理正确的渲染器。Octane 是由一家名为 Refractive Software,Ltd .的新西兰公司开发的,后来在 2012 年被 OTOY 公司收购。现在它正被用于故事片、商业广告,甚至建筑渲染。由于能够在云端运行 Octane,它可能会更加融入这些行业的创意流程。
![Octane render from OTOY's website](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/04f192bc7eeddf89615ba930e1e6b3dc.png)
## 无偏与有偏渲染
* **偏向渲染**是视觉效果和动画世界的中流砥柱。因为偏向渲染会提前执行某些计算,所以在实际渲染过程中会节省大量处理能力。偏向渲染本质上是“欺骗”,通过做出允许更少计算的假设来加速渲染过程。换句话说,“偏见”是为了提高效率而故意引入的。就在 5 年前,有偏见的渲染引擎,如 Mental Ray、Vray 和 Renderman,成为了行业标准,因为它们看起来像照片一样真实,但渲染时间只占“物理精确”的一小部分。然而,这些有偏差的引擎通常会导致奇怪的伪像(称为偏差误差),需要优化和微调以确保最终的图像正确无误。
* **无偏渲染**尽可能接近物理精度,因为每个像素都被推过现实生活中光线粒子在场景中传播的路径。这种严格的过程会在无偏渲染的初始阶段导致大量的混乱(想想噪音)。经过几次处理后,图像开始变得平滑,误差率(噪声)随着时间的推移而降低。当开发有偏差的渲染时,无偏差的渲染通常是有偏差的渲染与之比较的标准,以确保图像计算是正确的。
> 有趣的事实: *虽然一个无偏见的渲染引擎会产生卓越的质量和真实感,远远优于一个有偏见的渲染引擎,但一个无偏见的渲染永远不会“完成”。它将永远呈现,直到你简单地保存它并调用它完成。*
![Octane Render](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/aa715a3736c8e1babf77144a0107a348.png)
*辛烷渲染[RunHarry.com](http://www.runharry.com/2013/07/10/octane-render-vs-mental-ray/)*
![Mental Ray](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/72f01ea7b6e49bffcc48066f2ce36794.png)
*精神射线渲染[RunHarry.com](http://www.runharry.com/2013/07/10/octane-render-vs-mental-ray/)*
* * *
## GPU vs CPU
TL;CPU 可以非常快速地进行一次计算,但只能同时进行几次计算。CPU 上的并行处理受到内核数量的限制,在现代机器上通常是 4 到 16 个。GPU 拥有大量低功耗内核,允许并行进行大量小型计算。一个典型的 GPU 有几百到几千个内核。
由于偏向渲染器的速度优势(在 VFX 和 Mograph 世界中,时间=金钱),偏向渲染器已经统治了相当一段时间。但是随着 CUDA 和多处理器显卡的出现,图形世界中的许多人清楚地认识到,可能有更好的方法来进行无偏见的渲染。尽管图形处理器中的内核比传统 CPU 内核的带宽低得多,但在并行工作时(由于渲染器与图形处理器的集成),图形卡处理像素信息的速度比 CPU 高得多。
![GPU vs CPU](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9b4fb4bf3127ccf6a48a3f37766755f7.png)
BOXX 做了一篇关于 GPU 和 CPU 渲染差异的[大对比文章](http://blog.boxx.com/2014/10/02/gpu-rendering-vs-cpu-rendering-a-method-to-compare-render-times-with-empirical-benchmarks/)。发现使用 CPU 和 GPU 来产生相似质量的图像需要截然不同的渲染时间。
> 在使用标准硬件(如 3.4 Ghz 英特尔至强处理器和具有 2,688 个 CUDA 内核的 NVIDIA GPU)运行的测试中,他们发现 GPU 的性能比 CPU 快 6.2 倍。
### GPU 的优势和劣势
### **优点**
###### 1.GPU 拥有比 CPU 多得多的核心处理器
When utilized correctly by software, GPU rendering can be much faster than CPU rendering.
###### 2.GPU 比 CPU 消耗更少的能量
It is expected that soon a 14 nm fabrication technology will be used in manufacturing GPU and that will make the cost of power consumption even smaller. In the future, you can expect to see many GPUs coming equipped with larger sized memories which will enable larger resolution sized images to get rendered faster.
###### 3.降低硬件成本
Put simply, the power of a GPU can be equivalent to anywhere from five to twenty CPUs. This means it would take only one machine to do the job of five or twenty CPU machines put together. This is the reason GPUs produce results faster.
###### 4.潜在增长的未来
The future of GPU processing is bright. As CPUs near the limit for growth due to size limitations, GPUs may be the next logical step in continuing computing power growth.
**
### 不足之处
**
###### 1.GPU 内存限制
Because a user's 3D file will have to be loaded into the graphics card's memory, there is a limitation on how big your file can be, although this limitation seems to be slowly becoming irrelevant as the memory on graphics cards is always expanding. The software developers also seem to be working on ways to circumvent this limitation as the new version of Octane may not have any memory limitations beyond your system's ram.
###### 2.每个内核的带宽限制
Due to the fact that Graphics cards have so many cores, the actual bandwidth of each core is much lower than that of a traditional CPU core.
###### 3.严重依赖软件效率
The ability to utilize GPU cores is fairly new, and the development side is still in flux, however, the potential for growth here is tremendous.
* * *
## 辛烷渲染
Octane 易于安装,学习曲线低。对于本教程,我们将使用 4D 电影院辛烷插件。
##### 独立版本与插件版本
在 Paperspace 上使用 Octane 有两种方式。有一个[独立应用](https://home.otoy.com/render/octane-render/purchase/)和[插件支持多种软件](https://home.otoy.com/render/octane-render/purchase/)。你需要决定哪个版本最适合你。
单机版要求你在 Alembic(.abc)格式或将单个几何图形导出为。obj 文件。
##### 渲染模式
There are 4 different render modes that octane has. ![](http://i.imgur.com/ucHSiS7.jpg)
### 信息渠道
Info Channels mode allows you to see certain passes that are used for compositing. ![Info Channels](http://i.imgur.com/MJ3Km3c.png)
### 直接照明
Direct Lighting is very useful for working quickly and realtime manipulation of materials. While it is not unbiased, it is useful when creating quick animations or renders. ![Direct Lighting](http://i.imgur.com/i0t0WO5.png)
### 路径跟踪
Path Tracing is used when a photorealistic render is required. It can have some difficulties with small light sources and proper caustics (for which pmc is better suited). ![Path Tracing](http://i.imgur.com/6ATgdvx.png)
### 先天性肌强直病
PMC is a unbiased render kernel written specifically for GPUs. It allows for complex caustics and lighting to be resolved but often has the longest render times. ![PMC Render](http://i.imgur.com/VsX8UY4.jpg)
##### 采样选项
Each render kernal has similar settings but some settings are specific to that kernal.
![Direct Lighting Sample Settings](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4892b8e7904fdbd417d3f348912c0cd5.png)
* 最大样本数(Max Samples)-此设置控制最终图像的噪点。采样越高,噪波越低,但渲染时间越长。
* **胃肠模式** -胃肠取样有两种选择。
* 环境遮挡:标准环境遮挡。这种模式通常可以提供逼真的图像,但没有渗色。
* 漫射:间接漫射,配置为设置要包括的间接漫射反弹的数量。这提供了介于环境遮挡和路径跟踪之间的 GI 质量,没有焦散和相当真实的质量(比 AO 好),但比路径跟踪/PMC 快得多。非常适合快速的决赛和动画。它在某些方面类似于其他发动机中的间接 GI。
* 镜面/光泽/漫射深度 -这控制了光线在终止前会经过多少次反弹。设置越高,高光材质越亮,但是渲染时间会随着设置的增加而增加。
* **射线ε**-这是偏移新射线的距离,这样它们就不会与原始几何体相交。在渲染中出现同心圆的情况下,增加光线ε可以使这些伪像消失。
* **过滤尺寸** -该设置决定了过滤的半径。通过增加此值,图像会变得柔和,噪点会减少,但如果设置过高,图像的清晰度会降低。对渲染时间的影响很小,因为该设置通常不应高于 4。
* **AO 距离** -通过增加或减少该值,相交表面之间的暗度会相应增加或减少。对于小对象,该值可以减小,而对于较大的场景,该值可以增大。
* **阿尔法阴影** -该设置允许任何具有透明度的物体(镜面材质、具有不透明设置和阿尔法通道的材质)投射出合适的阴影,而不是表现为实体。
* 阿尔法通道(Alpha Channel)-该选项移除背景并将其渲染为透明。如果用户希望在另一个图像上合成渲染,并且不希望出现背景,这将非常有用。
* **保持环境** -该选项与 Alpha 通道设置结合使用。它允许背景以零 alpha 进行渲染,但在最终渲染中仍然可见。这使得合成图像更加灵活。
* **路径终止功率** -该值调整渲染速度与收敛(噪声消失的速度)。增加它将导致内核保持路径更短,在黑暗区域花费的时间更少(这意味着它们保持噪声的时间更长),但可能会增加每秒采样数很多。减小该值将导致内核平均跟踪更长的路径,并在黑暗区域花费更多的时间。当前的默认值 0.3 在大多数场景下都很好,但是调整它,看看它是否能加速你的渲染。
* **相干比率** -这可以提高渲染速度,但可能会在第一个样本/像素期间导致一些“闪烁”,应该主要用于最终渲染,如果你计划渲染 500 个样本/像素或更多的话。
* * *
## 材料设置
![Material Settings](/conteimg/2017/03/Screen-Shot-2017-03-19-at-9.54.01-PM.png)
###### 材料类型
![Material Types](/conteimg/2017/03/Screen-Shot-2017-03-19-at-10.39.34-PM.png)
* 漫射材料在表面上传播反射,就像混凝土、砖块和粘土一样。这是粗糙表面的理想选择。
* 有光泽的 -有光泽的材料是金属、车漆和闪亮的塑料。
* **镜面** -镜面材质用于透明表面,如玻璃或水。
##### 材料选项
### 传播
The diffuse channel is where you can define a base surface color. ![Diffuse](/conteimg/2017/03/Diffuse.jpg)
### 镜子的
Specular highlights represent a form of reflectivity that is affected by the surface roughness.
### 粗糙
Roughness is useful when trying to create surfaces like Brushed Steel or matte plastic. ![Roughness](/conteimg/2017/03/Roughness.jpg)
### 反射
The Reflection value determines the glossiness of the mesh.
### 胶片宽度
This controls the thickness of a optical, thin film on the material. This is useful in creating rainbow or oil slick effects.
### 电影索引
This controls the Index of Refraction of the thin film.
![Thin Film example](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fba9a343cd7c406dd378e0ebbf59cc21.png)
### 凹凸/法线凹凸和法线通道都可以加载图像来控制凹凸贴图和法线贴图的数量。)凹凸通道应该设置为 floatimage 来加载凹凸贴图。法线通道应设置为图像数据类型,以加载全色法线贴图。![](/conteimg/2017/03/Screen-Shot-2017-03-19-at-11 . 30 . 57-pm . png)
### 位移
位移贴图是一种应用于纹理的技术,目的是为了在三维空间中为二维对象提供一定的深度。与凹凸贴图和法线贴图相反,置换贴图会影响对象的轮廓,并且实际上会扭曲曲面。!【位移】(https://docs . otoy . com/manuals/WP-content/uploads/2016/05/resim 69 . png)
### 不透明度
决定一个表面的透明程度。
### 折射率
这里控制[折射率](https://en.wikipedia.org/wiki/Refractive_index)。这会影响光线通过曲面的曲率。这里有一个方便的折射率列表。
* * *
## Paperspace 的 GPU 选项
Paperspace 为高性能 GPU 计算机提供了几个可以被 Octane 利用的选项。
###### GPU+
paper space 提供的 GPU+机器包括一台 NVIDIA Quadro M4000,配备 8gb 专用内存,使用 1,664 个 CUDA 内核可以达到 192 GB/s。这是 Octane 的理想设置,还包括 30GB 的 RAM 和 8 个 vCPUs。
###### P5000
*Beta*
P5000 机器拥有 2,560 个并行 CUDA 内核和 16GB 的 GPU 内存,让您可以推动高达 288 GB/s 和惊人的 9 TeraFLOPs。
* * *
## 结论
We hope this overview has shown you some of the ways to utilize A GPU renderer like Octane in the cloud. In future tutorials, we will be going over ways to optimize your renders, how to get the best image possible, and how to use some special features like hair and volumetrics!
要开始使用你自己的渲染管道,[在这里注册。](https://www.paperspace.com/account/signup?utm-campaign=octaneblog)
# 梯度增强树和自动建模
> 原文:<https://blog.paperspace.com/gradient-boosted-trees-automl-h2o/>
尽管深度学习很受欢迎,但许多 AI 团队都有最好用深度学习以外的方法解决的问题。这可能是因为可解释性、真实世界数据的鲁棒性、法规要求、可用的计算能力或时间、批准的软件、可用的专业知识或任何数量的其他原因。
在这个项目中(以及伴随的 [GitHub 库](https://github.com/gradient-ai/Gradient-Boosted-Trees-and-AutoML)),我们将利用[梯度增强决策树](https://docs.paperspace.com/machine-learning/wiki/gradient-boosting) (GBTs)来训练一个模型,然后将该模型部署到生产中。与其应用深度学习技术,我们将会看到许多 ML 问题可以用一个调整良好的 GBT 来解决。
我们为这个项目选择的软件是一个最先进的开源库,叫做 [H2O](https://h2o.ai) 。它包括 GBT 模型、 [XGBoost](https://github.com/dmlc/xgboost) 模型和[集合方法](https://docs.paperspace.com/machine-learning/wiki/machine-learning-models-explained#algorithms)的高级功能,例如通过 [AutoML](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/automl.html) 超参数调整进行叠加。
一旦模型被训练,我们将展示如何使用 [Gradient](https://gradient.paperspace.com/) 将其作为 REST API 端点部署到生产中,并向其发送推断数据。
在附带的 GitHub repo 中,我们还将展示如何在项目的探索阶段使用[渐变笔记本](https://gradient.paperspace.com/notebooks),以及在生产阶段使用[渐变工作流程](https://gradient.paperspace.com/machine-learning-pipelines)。
我们开始吧!
## 内容
* 数据和模型
* 渐变笔记本
* 渐变工作流
* 模型部署
* 结论
* 后续步骤
## 伴随材料
* ML 展示区
* GitHub 知识库
## 数据和模型
我们这个项目的目的是在梯度上展示端到端的[经典机器学习](https://docs.paperspace.com/machine-learning/wiki/machine-learning-models-explained#classical-machine-learning)(而不是深度学习)。
因为我们想要解决一个典型的商业问题,非常适合像 GBT 这样的方法,所以我们想要混合数据类型的表格数据,而不是文本或计算机视觉数据。
因此,我们将使用著名的 [UCI 人口普查收入数据集](https://archive.ics.uci.edu/ml/datasets/census+income)。下面显示了一个典型的数据片段。我们的目标将是执行二元分类来预测一个人的收入是低还是高。这些分别标记为`<=50K`和`>50K`。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c2c58205322a97e14cf5469383094c57.png)
UCI census income dataset used for model training
我们看到数据集由混合数据类型的人口统计信息列组成:数字、分类、零等。幸运的是,对于这种类型的数据,决策树往往是健壮的,我们不需要准备步骤,如标准化或一次性编码。
分析的其他细节在下面的章节中给出。
## 渐变笔记本
我们在梯度笔记本中的目标是从最初的原始数据集到准备部署的已保存模型。
H2O 作为 Java 服务器运行,所以我们将从笔记本单元初始化它:
```py
import h2o
h2o.init()
```
在更大的规模上,这允许它在计算集群以及单台机器上运行。
由于我们的数据很小,我们可以使用导入方法将它们作为 CSV 导入到 H2O 的数据框中(类似于熊猫):
```py
df = h2o.import_file(path = "income.csv")
```
由于梯度笔记本允许任意 Python 代码,其他探索性数据分析和准备可以添加到这里。我们暂时不考虑这一点,因为这不是我们关注的焦点,而且这个模型正在正确地运行。
数据被分成特征列`x`和标签列`y`,并被分成用于训练、验证和测试的非重叠随机子样本。然后,他们会接受模特培训。
我们将使用 H2O 的 AutoML:
```py
from h2o.automl import H2OAutoML
aml = H2OAutoML(max_models=20, seed=1)
aml.train(x=x, y=y, training_frame=train)
```
尽管 AutoML 很简单,但它适用于许多问题,包括高级用户的使用。这是因为它搜索了广泛的模型和超参数——相当于甚至超过了专家的搜索范围。
AutoML 的梦想是,它可以生成一个模型,在手工调整模型所需时间的一小部分内提供足够的商业价值。
然而,AutoML 并不能解决所有问题。特别是,许多真实的业务问题都有可能得不到支持的业务逻辑条件或异常指标。
因此,这里需要注意的重要一点是,完全的手动调优(以及任何其他需要的任意代码)可以用与这个项目在 Gradient 上相同的方式来完成。要手动调整模型,我们只需用手动调整方法(如`H2OGradientBoostingEstimator`)替换 AutoML 位。
AutoML 探索的一些模型包括:
* 常规 GBT(也称为 GBM 或梯度推进机)
* 具有超参数值网格的 XGBoost 模型
* 深度学习模型
* 随机森林
* 模型的堆叠集合(堆叠=将模型输出输入下一个模型输入)
有点令人惊讶的是,我们实际上在这里包括了深度学习技术!然而,这个项目的重点是经典 ML,所以这是我们坚持的。
最适合该数据集的模型是堆叠集合。这是许多场景的常见结果,在这些场景中,经过良好调整的 GBT 从模型集合中受益。虽然集合可以提高准确性,但它也会增加计算时间并降低可解释性——因此它最终只在某些情况下有用。
一旦训练完成,大量的信息就会显露出来。例如,模型“排行榜”显示了各种模型的表现:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5d4a4c2b27b53f81c35a5c597d316958.png)
H2O AutoML model leaderboard
还提供模型指标:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/734cad4d565718fd032788e29622db2f.png)
Some model metrics from the AutoML run
这里我们使用了二元分类的默认度量:曲线下面积,或 AUC。这是由真阳性率对假阳性率的图包围的区域。最可能的值是 1,这里我们在验证集上得到大约 0.92,所以模型表现得很好。
我们还可能从完整的排行榜中看到,深度学习任务花费的时间是其他方法的 20 倍以上,并且表现更差。这不一定是一个公平的比较,因为它没有被广泛地探索,然而我们看到,对于解决问题的快速分析,经典的 ML 在这种情况下是足够的。
H2O 记录了大量关于模型的信息,包括度量标准。Gradient 能够在实验和生产过程中记录这些指标。指标可以按原样使用,也可以经过后期处理成为具有更大商业价值的东西,比如预期收入。
在训练之后,我们可以在看不见的测试数据上测量模型的性能,以通常的方式检查其泛化能力。
最后,我们可以保存该模型,以便将其部署到生产环境中。`get_genmodel`参数输出模型部署所需的`h2o-genmodel.jar` Java 依赖项。
```py
modelfile = model.download_mojo(path="/storage", get_genmodel_jar=True)
```
在 Gradient 目前的开发状态下,它可以与 TensorFlow 或 ONNX 集成模型生产。因此,我们直接使用 Java 来部署模型(参见下面的模型部署)。我们认为这是值得展示的,因为(1)端到端数据科学应该包括部署到生产中,( 2)这种方法适用于任何梯度 H2O 模型,它包含了人们希望用经典 ML 解决业务问题的大部分内容。
## 渐变工作流
Gradient 的两个主要组件是笔记本和工作流。
笔记本电脑旨在帮助您快速启动和运行数据探索、模型实验以及访问 GPU 等硬件加速器。
工作流旨在使用现代 MLOps 原则(如 Git、Kubernetes、微服务和 YAML)将项目投入生产。用户不需要 DevOps 专业知识就可以使用 Gradient 作为编排平台。
在这个项目中,我们展示了如何在笔记本和工作流程中完成上述分析。
有一条蟒蛇。包含工作流规范的 YAML 文件调用的 py 脚本。这给出了步骤的完整版本化描述,Python 和 YAML 可以在不损害任何特定 IDE 或环境的情况下开发。
工作流 YAML 规范如下所示:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/63686f13f9659c30455c50995d4f3662.png)
Part of the Gradient Workflow YAML specification
工作流是通过从命令行调用以下类型的命令来运行的:
```py
gradient workflows run \
--id abc123de-f567-ghi8-90jk-l123mno456pq \
--path ./Workflow/gbt_automl.yaml \
--apiKey ab12cd34ef56gh78ij90kl12mn34op
```
API 也可以存储在您的配置中,而不是被提供。将来,GUI 和 SDK 将支持工作流调用。完成后,工作流和输出数据集在 GUI 中可见:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2784cdb2c0e8801ba95320f9932ef86d.png)
Gradient Workflow to run AutoML model training
工作流被表示为有向非循环图(DAG)。虽然这个例子很简单,但是扩展步骤也很简单,例如添加一个单独的数据准备工作、多个模型等等。
我们还可以查看工作流中的其他信息,例如以前的运行和版本化的输出数据集。
工作流的输出与笔记本的输出相同——一个训练有素的模型,随时可以部署!
## 模型部署
Gradient 的目标之一是使模型部署尽可能容易,即使存在硬件加速。
虽然深度学习模型与 TensorFlow 有相当广泛的集成,但目前 H2O 经典 ML 模型是使用 Java 部署的。然而,由于上面提到的原因(端到端数据科学,H2O/Java 非常普遍),这是有价值的,并且随着项目的发展,Gradient 的集成将在未来增加。
这个设置是基于 Spikelab 的这个[博客条目](https://medium.com/spikelab/building-a-machine-learning-application-using-h2o-ai-67ce3681df9c)中的例子,做了一些修改。
笔记本和/或工作流项目部分在 MOJO 中输出 H2O 模型(模型对象,优化)。MOJO 是[保存模型的首选格式](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/productionizing.html),因为它是可移植的,并且比 POJO(普通旧 Java 对象)更通用。
该模型命名如下:
`StackedEnsemble_AllModels_AutoML_20210621_203855.zip`
并且有一个单独关联的 Java 依赖:`h2o-genmodel.jar`。
我们使用简单的 web 框架 [Javalin](https://javalin.io/) 用 Maven 构建一个 Java 项目,其中包含模型(`Model.java`)和一个将模型设置为 REST 端点的应用(`App.java`)。
我们使用渐变容器中的命令行创建项目,而不是使用 IntelliJ 或 Eclipse 之类的 IDE GUI。我们也使用 3.32.1.3 H2O 的稍新版本。
Java 代码从在线回归示例修改为我们的二元分类示例。这就是为什么我们改变了被调用的类和数据类型。我们已经将 H2O 的 artifactID 从`h2o-genmodel`更改为`h2o-genmodel-ext-xgboost`以适应 XGBoost 型号。推理数据包含分类和数字数据类型。
如何部署的细节在 GitHub repo 项目中,但是在 Gradient 上的终端中,部署是通过 Java 启动的,并监听一个端口:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/700922fea598d21c117391576365e721.png)
H2O model deployed to REST endpoint
推断数据可以通过 curl 发送。这里,我们提交来自收入数据集的一行数据,模型返回其响应,即该行的预测标签。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b0645219c7070d4fa4ab5b1d7d71ae6f.png)
Deployed model correct response to inference data
在这种情况下,该人被预测为处于较低收入阶层,`<=50K`。
显然,这不是全面的企业生产部署,但让我们看看我们在这里做什么。我们在 REST 端点上有一个模型,推理数据被发送给它,模型返回响应。
Gradient 拥有扩展这一功能并与其他工具(如模型监控)集成的基础设施。
## 结论
我们已经表明,Gradient 能够支持深度学习所代表的领域子集之外的高级机器学习(ML)模型。正如我们所发现的,哪种方法是最好的——深度学习还是经典的 ML——完全取决于具体情况。
这是我们所做的:
* 我们使用著名的开源 ML 库 H2O 来训练梯度增强决策树和其他模型
* 我们使用其 AutoML 自动化机器学习功能来搜索大量模型超参数组合和附加设置,例如通过堆叠进行模型组装
* 我们通过 Java 将结果模型作为 REST 端点部署到生产中
我们使用了以下工具:
* 我们使用梯度笔记本,它对应于数据科学项目的探索或实验阶段
* 我们使用了一个梯度工作流,它对应于一个更大的项目,需要更严格和生产级的实践
然后,我们通过命令行在 Gradient 上部署。
## 后续步骤
笔记本、工作流和部署中显示的端到端设置可以适用于各种业务问题和其他应用程序。
以下是我们在本教程中提到的一些资源:
* [项目 GitHub 库](https://github.com/gradient-ai/Gradient-Boosted-Trees-and-AutoML)
* [项目展示区](https://ml-showcase.paperspace.com/projects/gradient-boosted-trees-and-automl)
* [纸空间梯度](https://gradient.paperspace.com/)
* [梯度文件](https://docs.paperspace.com/gradient/)
* [联系销售](https://info.paperspace.com/contact-sales)
这里有一些额外的链接供进一步阅读:
* [H2O](https://h2o.ai) 开源 ML 库
* H2O 汽车
* [H2O MOJO](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/productionizing.html) 车型格式
* [Javalin](https://javalin.io/) web 框架
* [Spikelab 博客条目](https://medium.com/spikelab/building-a-machine-learning-application-using-h2o-ai-67ce3681df9c)Java H2O 部署示例
* [UCI 人口普查收入](https://archive.ics.uci.edu/ml/datasets/census+income)数据集
感谢您的关注!如果您有任何问题或意见,请通过[联系](https://twitter.com/hellopaperspace)告诉我们!
# 分类中的梯度推进:不再是黑箱!
> 原文:<https://blog.paperspace.com/gradient-boosting-for-classification/>
机器学习算法需要的不仅仅是拟合模型和进行预测来提高准确性。大多数在行业或竞赛中获胜的模型都使用了集合技术或特征工程来表现得更好。
与特征工程相比,集成技术由于其易用性而特别受欢迎。有多种集成方法已被证明在与高级机器学习算法一起使用时可以提高准确性。一种这样的方法是**梯度增强**。虽然梯度增强经常被当作一个黑盒来讨论,但在本文中,我们将一步一步地、直观地、广泛地揭开梯度增强的秘密,这样你就能真正理解它是如何工作的。
在本文中,我们将讨论以下主题:
* 什么是梯度增强?
* 分类中的梯度推进
* 直观的理解:可视化梯度增强
* 数学上的理解
* Python 中梯度推进的实现
* 比较和对比 AdaBoost 和梯度增强
* 梯度增强的优点和缺点
* 结论
## 什么是梯度增强?
先来简单回顾一下**系综学习**。顾名思义,集成学习涉及通过使用“较弱”模型的集合(或“集成”)来构建强模型。梯度提升属于提升方法的范畴,其从每个弱学习器迭代学习以建立强模型。它可以优化:
* 回归
* 分类
* 等级
本文的范围将特别限于分类。
助推背后的想法来自直觉,薄弱的学习者可以被修改,以变得更好。 [AdaBoost](https://blog.paperspace.com/adaboost-optimizer/) 是第一个 boosting 算法。 [Leo Breiman (1997)](https://statistics.berkeley.edu/sites/default/files/tech-reports/486.pdf) 首先将 AdaBoost 和相关算法纳入统计框架,这为其他研究人员如 [Jerome H. Friedman](https://statweb.stanford.edu/~jhf/ftp/trebst.pdf) 将这项工作修改为开发回归的梯度推进算法奠定了基础。随后,许多研究人员为机器学习和统计的更多领域开发了这种 boosting 算法,远远超出了回归和分类的最初应用。
梯度增强中的术语“梯度”指的是同一函数有两个或多个导数的事实(我们将在后面更详细地讨论这一点)。梯度提升是一种*迭代函数梯度算法*,即通过迭代选择指向负梯度的函数来最小化损失函数的算法;无力的假设。
## 分类中的梯度推进
多年来,梯度推进已经在各种技术领域中得到应用。该算法起初看起来可能很复杂,但在大多数情况下,我们只使用一个预定义的配置进行分类,一个用于回归,当然可以根据您的要求进行修改。在这篇文章中,我们将重点放在分类问题的梯度推进。我们将从直观和数学的角度来看这个算法是如何在幕后工作的。
梯度增强有三个主要部分:
* **损失函数** -损失函数的作用是评估模型在给定数据下进行预测的能力。这可能因手头的问题而异。例如,如果我们试图根据一些输入变量预测一个人的体重(回归问题),那么损失函数将有助于我们找到预测体重和观察体重之间的差异。另一方面,如果我们试图根据一个人的个性来对他是否喜欢某部电影进行分类,我们将需要一个损失函数来帮助我们了解我们的模型在对喜欢或不喜欢某些电影的人进行分类时有多准确。
* 弱学习者(Weak Learner)-弱学习者是指对我们的数据进行分类,但做得很差的人,可能比随机猜测好不了多少。换句话说,它的错误率很高。这些是典型的[决策树](https://blog.paperspace.com/decision-trees/)(也称为决策树桩,因为它们没有典型的决策树复杂)。
* **加法模型** -这是一种迭代和连续的方法,一次一步地添加树(弱学习者)。在每次迭代之后,我们需要更接近我们的最终模型。换句话说,每次迭代都应该减少损失函数的值。
### 直观的理解:可视化梯度增强
我们先来看一个最常见的二分类机器学习问题。它旨在根据一些特征预测泰坦尼克号上乘客的命运:他们的年龄、性别等。为了方便起见,我们将只选取数据集的一个子集,并选择某些列。我们的数据集看起来像这样:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c663b088890182a1c0400c1025c5fa77.png)
Titanic Passenger Data
* Pclass 或乘客等级是明确的:1、2 或 3。
* 年龄是泰坦尼克号上乘客的年龄。
* *票价*是客运票价。
* *性别*是人的性别。
* *生还*指人是否在坠机中幸存;如果没有,则为 0,如果有,则为 1。
现在我们来看看梯度提升算法是如何解决这个问题的。
我们从一个叶节点开始,它预测每个乘客的初始值。对于一个分类问题,将是目标值的 log(odds)。log(odds)相当于分类问题中的平均值。由于我们的案例中有 4 名乘客幸存,2 名乘客未能幸存,因此一名乘客幸存的可能性为:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/981d47dff07d5616398aa9c48069ce34.png)![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/68f8cb3521b0c1a173ba50ba9c70c2a5.png)
这成为我们最初的叶子。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4796e64ff0268490dde1ba208527d34a.png)
Initial Leaf Node
使用 log(odds)进行分类的最简单方法是将其转换为概率。为此,我们将使用以下公式:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bf09db9bd3e8a650d8be27b3b697fda6.png)
> 注意:请记住,我们在这里将所有数据四舍五入到小数点后一位,因此对数(赔率)和概率是相同的,这可能并不总是如此。
如果幸存的概率大于 0.5,那么我们首先将训练数据集中的每个人都归类为幸存者。(0.5 是用于基于概率做出分类决策的常见阈值;请注意,阈值很容易被当成其他东西。)
现在我们需要计算**伪残差**,即观察值和预测值之间的差值。让我们在图上画出残差。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/88a6ae203f77ac39f94db463f66fb28a.png)
蓝色和黄色的点是观察值。蓝点是没有幸存的概率为 0 的乘客,黄点是幸存的概率为 1 的乘客。这里的虚线表示预测概率为 0.7
我们需要找到剩余部分:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d4e0ff3456e5934f394eb6b9ae61b126.png)![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/902c4bad283c56db747abde115f0cc2b.png)
这里,1 表示是,0 表示否。
我们将使用这个残差来得到下一棵树。我们考虑的是剩余价值而不是实际价值,这似乎很荒谬,但我们应该向前看。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ba953c8cb3719d8be9cce65f3e00e2d3.png)
Branching out data points using the residual values
我们在这里使用两个叶子的限制来简化我们的示例,但实际上,梯度增强的范围在 **8 个叶子到**32 个叶子之间。
由于叶子的限制,一个叶子可以有多个值。预测是根据对数(赔率)进行的,但这些树叶是从导致差异的概率中得出的。所以,我们不能仅仅把之前得到的单叶和这棵树相加来得到新的预测,因为它们来自不同的来源。我们必须使用某种转换。用于分类的梯度增强中最常见的变换形式是:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/af7efedd8055b48a3368d4b22db6d675.png)
这个方程的分子是特定叶子的残差的和。
分母是(每个残差的先前预测概率)* (1 -相同的先前预测概率)的和。
这个公式的推导将在本文的数学部分解释。
现在,让我们将公式付诸实践:
第一片叶子只有一个残差值 0.3,因为这是第一棵树,所以前一个概率将是来自初始叶子的值,因此,对所有残差值都是一样的。因此,
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1b9881f19bc5195a75e1a80295af7a90.png)
对于第二片叶子,
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/30cef3d953902cc59a46431624e84103.png)
同样,对于最后一片叶子:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f8d9495d93053423e52879c8c263f489.png)
现在,转换后的树看起来像这样:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e2f1cac1428528e75b47f47c2fabca9c.png)
Transformed tree
现在,我们已经转换了它,我们可以用我们的新树添加我们的初始线索,学习率。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a819e82cfec6af7a427a50739b7b9e4c.png)
**学习率**用于衡量新树的贡献。这使得预测朝着正确的方向前进了一小步。经验证据已经证明,与第一步中的完美预测相比,在正确的方向上采取许多小步骤会导致使用测试数据集(即模型从未见过的数据集)进行更好的预测。学习率通常是 0.1 这样的小数字
我们现在可以计算新的对数(赔率)预测,从而计算新的概率。
例如,对于第一个乘客,老树= 0.7。对于所有记录保持不变的学习率等于 0.1,并且通过缩放新树,我们发现其值为-0.16。因此,在公式中代入我们得到:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5c4d74a33b9c05337d8ba876863eee02.png)
类似地,我们替换并找到每个乘客的新日志(赔率),从而找到概率。使用新的概率,我们将计算新的残差。
这个过程一直重复,直到我们指定了最大数量的树或者残差变得非常小。
### 数学上的理解
既然我们已经直观地了解了梯度推进算法如何处理分类问题,那么填补我们在上一节中留下的大量空白将非常重要,这可以通过数学地理解该过程来完成。
我们将一次一步地完成每一步,并试图理解它们。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6a24abb52bb92f7cb2747df9665cca73.png)
Xi——这是我们输入模型的输入变量。
这是我们要预测的目标变量。
给定预测概率,我们可以预测数据的对数似然性
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dd829f93da9bb03d52740fef70eccda0.png)
yi 是观察值(0 或 1)。
p 是预测的概率。
目标是最大化对数似然函数。因此,如果我们使用 **log(likelihood)** 作为损失函数,其中较小的值代表更好的拟合模型,则:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a4156e61947f7b48adc954bccac9d6f6.png)
现在对数(可能性)是预测概率 p 的函数,但我们需要它是预测对数(几率)的函数。所以,让我们试着转换公式:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c07c992b6c16310b2394a8f5bb91554a.png)
我们知道:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/34bada9e409bbce7876b6c9d925160f3.png)
替代,
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f119b8672886ef2615e308385b850646.png)
现在,
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/532486af278a7248a7059b453e48802b.png)
因此,
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5544760ab70fd3d4c6ffbca67b994d7f.png)
现在我们已经把 p 转换成 log(odds),这就成了我们的**损失函数**。
我们必须证明这是可微的。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9193a45cd9557a34a895d3232a1855e2.png)
这也可以写成:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fcf7489fe0e4cafb326fefae134a7a5b.png)
现在我们可以进入模型构建的实际步骤。
**步骤 1:用常量值初始化模型**
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/57cdcb45b668132965a83efa7beffd75.png)
这里,yi 是观察值,L 是损失函数,γ是 log(赔率)的值。
我们对损失函数求和,即我们对每个观察值的损失函数求和。
arg min/gamma 意味着我们需要找到一个 log(odds)值来最小化这个和。
然后,我们对每个损失函数求导:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5ed846878ca2a1b54268170dd7ee9989.png)
...诸如此类。
**第二步:对于 m = 1 到 M:**
(一)
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/847499bd096f1628177131ae80204da7.png)
这一步需要你用给定的公式计算残差。我们已经发现损失函数为:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fcf798f0964317c54e5560a6ac53f798.png)
因此,
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8a534feb32448184af1262c3b5397c8e.png)
(B)将回归树拟合到残差值并创建终端区域
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cf33a0a949e9a4b3b85b74dfb0c43b95.png)
因为一个分支的叶子是有限的,所以在一个特定的末端区域可能有不止一个值。
在我们的第一棵树中,m=1,j 将是每个终端节点的唯一编号。所以 R11,R21 等等。
(三)
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/607906911c48c2f7dd595b20954e2e04.png)
对于新树中的每一片叶子,我们计算输出值 gamma。求和应该只针对那些生成该叶的记录。理论上,我们可以找到关于伽马的导数,以获得伽马的值,但这可能是非常令人厌倦的,因为我们的损失函数中包含大量变量。
代入上述等式中的损失函数和 i=1,我们得到:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1f5214235ed27246c4d21454c55afaef.png)
我们使用二阶泰勒多项式来近似这个损失函数:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/950eb8600fa1aa08b30c7260d1ba81a7.png)
在我们的近似值中有三项。对γ求导得到:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3ea5685a74021a86160cbb0068201e39.png)
等于 0,从两边减去一阶导数。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9c0e1d6f170e1a502b7f147048b5001b.png)
那么,伽马将等于:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/555d7c381bdae5f9f4846704c74469e4.png)
伽马方程可能看起来很庞大,但简单来说,它就是:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/669e51dedb99551ff2ac7e8827fb8728.png)
我们将只替换损失函数的导数的值
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ac411e7c5ee493c21ef2b5904a361c4f.png)
现在我们将求解损失函数的二阶导数。经过一些繁重的计算,我们得到:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/891b6df3572891c16e011b7c239876dc.png)
我们简化了分子和分母。最终的伽玛解决方案看起来像:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/40098a934f97293841b97cedd3988be1.png)
我们试图找到伽马值,当加到最近的预测日志(赔率)时,使我们的损失函数最小化。当我们的终端区域只有一个残差值,因此只有一个预测的概率时,这个伽玛是有效的。但是,回想一下我们上面的例子,由于梯度增强中的受限叶,一个末端区域可能有许多值。那么概括的公式将是:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8f25a2414e8b8069a0c6f6529ac512d9.png)
因此,我们已经计算了树中每个叶子的输出值。
(四)
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c1472ef19337813b9cfc8c2b1958f0f3.png)
这个公式要求我们现在更新我们的预测。在第一遍中,m =1,我们将替换 F0(x),所有样本的共同预测,即初始叶值加上 nu,nu 是从我们先前构建的树到输出值的学习速率。求和是针对单个样本在多个叶中结束的情况。
现在,我们将使用这个新的 F1(x)值来获得每个样本的新预测。
新的预测值应该会让我们更接近实际值。要注意的是,与我们考虑的一棵树相反,梯度增强构建了许多树,M 可以大到 100 或更大。
这就完成了第 2 步中的 for 循环,我们为梯度增强的最后一步做好了准备。
**第三步:输出**
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e496bd1a20d499e9eb41cac3a9abc552.png)
如果我们得到一个新的数据,那么我们将使用这个值来预测乘客是否幸存。这将为我们提供这个人幸存的概率。将其代入“p”公式:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d447c8597a5180dbd46583a775523f22.png)
如果结果值高于我们的阈值,那么这个人就活了下来,否则就不行。
## 使用 Python 实现梯度增强
我们将使用 [Kaggle](https://www.kaggle.com/c/titanic/data) 中可用的完整 Titanic 数据集。为了方便起见,数据集已经分为训练集和测试集。
第一步是导入我们在这个过程中需要的库。
```py
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
import numpy as np
from sklearn import metrics
```
然后,我们将加载我们的训练和测试数据
```py
train = pd.read_csv("train.csv")
test= pd.read_csv("test.csv")
```
让我们打印出每一列的数据类型
```py
train.info(), test.info()
```
```py
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId 891 non-null int64
Survived 891 non-null int64
Pclass 891 non-null int64
Name 891 non-null object
Sex 891 non-null object
Age 714 non-null float64
SibSp 891 non-null int64
Parch 891 non-null int64
Ticket 891 non-null object
Fare 891 non-null float64
Cabin 204 non-null object
Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId 418 non-null int64
Pclass 418 non-null int64
Name 418 non-null object
Sex 418 non-null object
Age 332 non-null float64
SibSp 418 non-null int64
Parch 418 non-null int64
Ticket 418 non-null object
Fare 417 non-null float64
Cabin 91 non-null object
Embarked 418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
```
将 PassengerID 设置为我们的索引
```py
# set "PassengerId" variable as index
train.set_index("PassengerId", inplace=True)
test.set_index("PassengerId", inplace=True)
```
我们生成训练目标集和训练输入集,并检查形状。除了“幸存”列之外的所有变量都成为输入变量或特征,只有“幸存”列成为我们的目标变量,因为我们试图基于乘客的信息来预测乘客是否幸存。
联接训练和测试数据集以获得训练测试数据集
```py
train_test = train.append(test)
```
下一步是在将数据输入模型之前对其进行预处理。
我们进行以下预处理:
1. 删除“姓名”、“年龄”、“SibSp”、“船票”、“船舱”、“烤盘”等栏目。
2. 用 pandas.get_dummies 将对象转换为数字。
3. 用值 0.0 填充空值,这是分类变量中最常见的情况。
4. 用 MinMaxScaler()方法转换数据。
5. 将训练集随机分成训练和验证子集。
```py
# delete columns that are not used as features for training and prediction
columns_to_drop = ["Name", "Age", "SibSp", "Ticket", "Cabin", "Parch"]
train_test.drop(labels=columns_to_drop, axis=1, inplace=True)
```
```py
train_test_dummies = pd.get_dummies(train_test, columns=["Sex"])
train_test_dummies.shape
```
检查数据中缺少的值:
```py
train_test_dummies.isna().sum().sort_values(ascending=False)
```
```py
Embarked 2
Fare 1
Sex_male 0
Sex_female 0
Pclass 0
dtype: int64
```
让我们来处理这些缺失的值。对于“已装船”,我们将估算最常发生的值,然后创建虚拟变量,对于“票价”,我们将估算 0。
```py
train_test_dummies['Embarked'].value_counts()
train_test_dummies['Embarked'].fillna('S',inplace=True) # most common
train_test_dummies['Embarked_S'] = train_test_dummies['Embarked'].map(lambda i: 1 if i=='S' else 0)
train_test_dummies['Embarked_C'] = train_test_dummies['Embarked'].map(lambda i: 1 if i=='C' else 0)
train_test_dummies['Embarked_Q'] = train_test_dummies['Embarked'].map(lambda i: 1 if i=='Q' else 0)
train_test_dummies.drop(['Embarked'],axis=1,inplace=True)
```
```py
train_test_dummies.fillna(value=0.0, inplace=True)
```
最后一次检查我们是否处理了所有丢失的值。
```py
train_test_dummies.isna().sum().sort_values(ascending=False)
```
```py
Embarked_Q 0
Embarked_C 0
Embarked_S 0
Sex_male 0
Sex_female 0
Fare 0
Pclass 0
dtype: int64
```
所有丢失的值似乎都被处理了。
之前,我们已经生成了我们的目标集。现在我们将生成我们的特征集/输入集。
```py
X_train = train_test_dummies.values[0:891]
X_test = train_test_dummies.values[891:]
```
在我们拟合我们的模型之前,是时候进行最后一步了,这将是转换我们的数据,使一切都达到一个特定的比例。
```py
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train_scale = scaler.fit_transform(X_train)
X_test_scale = scaler.transform(X_test)
```
我们现在必须将数据集分为训练和测试两部分。训练来训练我们的模型,测试来检查我们的模型与数据集的吻合程度。
```py
from sklearn.model_selection import train_test_split
X_train_sub, X_validation_sub, y_train_sub, y_validation_sub = train_test_split(X_train_scale, y_train, random_state=0)
```
现在,我们训练我们的梯度增强算法,并检查从 0 到 1 的不同学习速率下的准确性。
```py
learning_rates = [0.05, 0.1, 0.25, 0.5, 0.75, 1]
for learning_rate in learning_rates:
gb = GradientBoostingClassifier(n_estimators=20, learning_rate = learning_rate, max_features=2, max_depth = 2, random_state = 0)
gb.fit(X_train_sub, y_train_sub)
print("Learning rate: ", learning_rate)
print("Accuracy score (training): {0:.3f}".format(gb.score(X_train_sub, y_train_sub)))
print("Accuracy score (validation): {0:.3f}".format(gb.score(X_validation_sub, y_validation_sub)))
```
```py
('Learning rate: ', 0.05)
Accuracy score (training): 0.808
Accuracy score (validation): 0.834
('Learning rate: ', 0.1)
Accuracy score (training): 0.799
Accuracy score (validation): 0.803
('Learning rate: ', 0.25)
Accuracy score (training): 0.811
Accuracy score (validation): 0.803
('Learning rate: ', 0.5)
Accuracy score (training): 0.820
Accuracy score (validation): 0.794
('Learning rate: ', 0.75)
Accuracy score (training): 0.822
Accuracy score (validation): 0.803
('Learning rate: ', 1)
Accuracy score (training): 0.822
Accuracy score (validation): 0.816
```
这就完成了我们的代码。此处使用的参数的简要说明。
* **n_estimators :** 要执行的升压阶段的数量。梯度增强对过度拟合相当稳健,因此较大的数量通常会产生较好的性能。
* **learning_rate :** 学习率将每棵树的贡献缩小`learning_rate`。在 learning_rate 和 n_estimators 之间有一个折衷。
* **max_features :** 寻找最佳分割时要考虑的特征数量。
* **max_depth :** 单个回归估计量的最大深度。最大深度限制了树中节点的数量。调整此参数以获得最佳性能;最佳值取决于输入变量的相互作用。
* **random _ state:**random _ state 是随机数生成器使用的种子。
超调这些参数以获得最佳精度。
## 比较和对比 AdaBoost 和 GradientBoost
AdaBoost 和 Gradient Boost 都是从弱学习者集合中顺序学习的。从这些弱学习者的加法模型中获得强学习者。这里的主要焦点是从迭代中每一步的缺点中学习。
AdaBoost 要求用户指定一组弱学习器(或者,它会在真正的学习过程之前随机生成一组弱学习器)。它增加错误预测实例的权重,并减少正确预测实例的权重。因此,弱学习者更关注困难的例子。在被训练之后,弱学习者根据其表现被添加到强学习者中(所谓的阿尔法权重)。它表现得越高,对学习能力强的人的贡献就越大。
另一方面,梯度增强不会修改样本分布。弱学习者训练强学习者的剩余错误,而不是训练新采样的分布。这是另一种方式给予困难的例子更多的重视。在每次迭代中,计算伪残差,并将弱学习器拟合到这些伪残差。然后,弱学习器对强学习器的贡献不是根据其在新分布的样本上的性能来计算的,而是使用梯度下降优化过程。计算出的贡献是最小化强学习者的整体误差的贡献。
**Adaboost** 更多的是关于“**投票** **权重****渐变** **boosting** 更多的是关于“**添加** **渐变** **优化**”。
## 梯度增强的优点和缺点
梯度增强的优点是:
* 通常提供无可匹敌的预测准确性。
* 大量的灵活性-可以优化不同的损失函数,并提供了几个超级参数调整选项,使函数拟合非常灵活。
* 不需要数据预处理——通常适用于分类值和数值。
* 处理缺失数据-不需要插补。
很棒,对吧?让我们也看看一些缺点。
* 梯度推进模型将继续改进,以尽量减少所有误差。这可能会过分强调异常值,导致过度拟合。
* 计算成本高-通常需要很多树(> 1000),这会耗费大量时间和内存。
* 高灵活性导致许多参数相互作用并严重影响该方法的行为(迭代次数、树深度、正则化参数等)。).这需要在调优期间进行大范围的网格搜索。
* 本质上较少解释,尽管这可以通过各种工具轻松解决。
## 结论
本文提出了梯度推进算法的理论和实践方法。梯度推进已被反复证明是在分类和回归中建立预测模型的最强有力的技术之一。因为分级提升算法很容易在训练数据集上过度拟合,所以可以利用不同的约束或正则化方法来增强算法的性能并对抗过度拟合。惩罚学习、树约束、随机抽样和收缩可以用来对抗过度拟合。
许多现实生活中的机器学习挑战已经通过梯度推进得到解决。
希望这篇文章鼓励你深入探索梯度推进,并开始将它们应用到现实生活中的机器学习问题中,以提高你的准确性!
### 参考
1. [使用 Scikit 学习 Python 中的梯度增强分类器](https://stackabuse.com/gradient-boosting-classifiers-in-python-with-scikit-learn/)
2. [使用 AdaBoost 和梯度增强进行增强——数据科学家的成就](https://medium.com/diogo-menezes-borges/boosting-with-adaboost-and-gradient-boosting-9cbab2a1af81)
3. [梯度推进第一部分:回归主要思路](https://www.youtube.com/watch?v=3CC4N4z3GJc)
4. [梯度推进机](http://uc-r.github.io/gbm_regression)
5. [使用 AdaBoost 和梯度增强进行增强——数据科学家的成就](https://medium.com/diogo-menezes-borges/boosting-with-adaboost-and-gradient-boosting-9cbab2a1af81)
6. [3.2.4.3.6。巩恩.合着.梯度助推器-scikit-learn 0 . 22 . 2 documentation](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingRegressor.html)
7. [回归问题的梯度推进示例|回归算法基础](https://acadgild.com/blog/gradient-boosting-for-regression-problems)
8. [渐变增强的简单介绍](http://www.ccs.neu.edu/home/vip/teach/MLcourse/4_boosting/slides/gradient_boosting.pdf)
9. [机器学习基础知识——梯度推进& XGBoost](https://shirinsplayground.netlify.com/2018/11/ml_basics_gbm/)
10. [了解梯度增压机](https://towardsdatascience.com/understanding-gradient-boosting-machines-9be756fe76ab)
# Paperspace 渐变社区笔记本指南
> 原文:<https://blog.paperspace.com/gradient-community-notebook-guide/>
[2021 年 12 月 2 日更新:本文包含关于梯度实验的信息。实验现已被弃用,渐变工作流已经取代了它的功能。[请参见工作流程文档了解更多信息](https://docs.paperspace.com/gradient/explore-train-deploy/workflows)。]
[渐变社区笔记本](https://gradient.paperspace.com/free-gpu)允许用户在免费的 GPU 上创建、运行和共享 Jupyter 笔记本。在这篇文章中,将介绍渐变社区笔记本,并详细讨论入门步骤,因此您可以在 GPU 或 CPU 上轻松创建一个免费的 Jupyter 笔记本,并与公众分享。
我们将涵盖:
* 渐变入门
* 渐变社区笔记本
* 选择容器
* 选择机器
* 笔记本选项
* 管理笔记本
* 第一次打开你的笔记本
* 左侧工具栏
* 文件浏览器
* 共享文件和文件夹
* 共享笔记本
* 上传和下载文件
* 读取和写入文件
* 导出笔记本
让我们开始吧。
## **渐变入门**
Gradient 是 Paperspace 用于构建机器学习(ML)和深度学习(DL)项目的平台。它帮助您完成构建和部署 ML/DL 模型的整个过程。笔记本电脑配有预配置环境,可消除任何工具问题。只需点击几下鼠标,您就可以启动并运行 Keras、TensorFlow、PyTorch 和 Scikit-Learn 等流行的库。
[上手渐变](https://docs.paperspace.com/gradient/get-started/quick-start)就是这么简单。你需要做的就是创建一个 Paperspace 账户(你可以在这里[做](https://www.paperspace.com/account/signup))。您有三种不同的注册选项:
1. 您的 Google 帐户
2. 您的 GitHub 帐户
3. 常规电子邮件注册
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dacc0476c37bd050faaf9da0b66df16b.png)
登录后,您将进入控制台页面。入门产品主要有两种:[渐变](https://gradient.paperspace.com/)和[核心](https://www.paperspace.com/core)。在这篇文章中,我们关注的是渐变。该产品提供了构建 ML/DL 项目的工具,从探索到模型部署。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/452f10ad17a804f699e74c7d8d15efb1.png)
在开始使用 Gradient 及其免费服务之前,让我们检查一下您的个人资料。在控制台页面的右上角,您可以找到您的个人资料徽标(最初设置为默认图标)。将鼠标悬停在图标上,点击`Profile`选项。在那里你可以看到你的账户的基本信息。下图显示了我的个人资料页面。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/46a77b3fa8024123a724782c4233318e.png)
在页面的左下角有一个`EDIT PROFILE`按钮,你可以点击它来输入你的详细信息。您也可以单击个人资料照片进行更改。您的`User Handle`将用于您的账户页面 URL。我使用了`ahmed`,因此我的 Paperspace 配置文件 URL 是 https://www.paperspace.com/ahmed。最后,点击`SAVE`按钮保存您的详细信息。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4222c36625f45a67521e73f689731f0a.png)
现在让我们回到控制台页面。在页面的右上角有一个链接,可以将您转到控制台。你总能在 https://www.paperspace.com/console 找到控制台页面。
本帖将详细讨论如何使用[渐变社区笔记本](https://gradient.paperspace.com/free-gpu),这将在下一节介绍。
## **渐变社区笔记本**
2019 年 10 月,Paperspace 推出了测试版的[渐变社区笔记本](https://gradient.paperspace.com/free-gpu)。这些允许你创建运行在自由 CPU 和 GPU 实例上的 Jupyter 笔记本。创建帐户后(根据上一节中的说明),您可以登录并开始免费创建笔记本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1132f317a11e246d49bd9cbecf83aeaa.png)
从 Paperspace [控制台页面](https://www.paperspace.com/console)点击左侧导航栏上的`Gradient`。然后你将被引导至[梯度控制台](https://www.paperspace.com/console/gradient)。建议的第一步是:
1. **[运行](https://www.paperspace.com/console/notebooks/create)** :创建并运行一个`Jupyter Notebook`。
2. **[训练](https://www.paperspace.com/console/projects)** :创建一个`Gradient Project`来训练你的模型。
3. **[部署](https://www.paperspace.com/console/deployments)** :使用`Gradient Deployments`将模型推向生产。
根据[文档](https://docs.paperspace.com/gradient/projects/about),梯度项目定义如下:
> 渐变项目是您或您的团队运行实验和作业、存储模型等工件以及管理部署(已部署的模型)的工作空间。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b216bc41a2a1ffedfad7c808dec7dcd0.png)
因为我们对创建 Jupyter 笔记本感兴趣,我们将点击 **[运行 Jupyter 笔记本](https://www.paperspace.com/console/notebooks/create)** 选项,这将把您重定向到[这一页](https://www.paperspace.com/console/notebooks/create)。当你将鼠标悬停在左边工具栏上的**渐变**上时,你也可以点击**笔记本**。
在笔记本页面,我们可以找到创建 Jupyter 笔记本的三个步骤:
1. 选择容器
2. 选择机器
3. 笔记本选项
我们将在接下来的 3 个部分中讨论这 3 个步骤。
## **选择容器**
Paperspace Gradient 在一个“容器”中运行 Jupyter 笔记本,容器是一个准备好运行笔记本所需的所有依赖项的环境。总共有 **18** 个预配置的容器可用,你也可以使用一个自定义的。访问[本页](https://docs.paperspace.com/gradient/notebooks/notebook-containers#base-containers)了解更多关于支持的容器的信息。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/919bb57ee600db89dbcab5a69e72a403.png)
在这 18 个容器中,有 7 个因受欢迎而被推荐。这些是:
1. [Fast.ai](https://github.com/Paperspace/fastai-docker)
2. [多功能一体机](https://github.com/ufoym/deepo)
3. [TensorFlow 2.0](https://hub.docker.com/r/tensorflow/tensorflow)
4. [英伟达急流](https://hub.docker.com/r/rapidsai/rapidsai/tags)
5. [指针](https://hub.docker.com/r/pytorch/pytorch)
6. [TensorFlow 1.14](https://hub.docker.com/r/tensorflow/tensorflow)
7. [变形金刚+ NLP](https://blog.paperspace.com/introducing-paperspace-hugging-face/)
如果您对使用或构建自定义容器感兴趣,请查看本指南了解更多信息。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/da100863fc7023d4675c13cd5ec7a264.png)
“All-in-one”是一个很好的开始容器,因为它预先安装了所有主要机器学习/深度学习框架的所有依赖项。选择容器后,我们需要选择一台机器。
## **选择机器**
下图显示了您可以从中选择运行笔记本的计算机的列表。总共有 16 个**实例可供选择,它们的功耗和成本各不相同。有关可用实例的更多信息,请查看[页面](https://docs.paperspace.com/gradient/instances/instance-types)。**
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/aab8eb03f57bba8fd6672e15907054b4.png)
在这 16 台机器中,有 3 个可用的免费实例。这些是:
1. **自由 CPU** ,提供 2 个 vCPU 和 2gb RAM
2. **Free-GPU+** ,提供一个 **NVIDIA M4000 GPU** ,带 8 个 vCPUs 和 30gb RAM
3. **Free-P5000** ,提供一个 **NVIDIA P5000 GPU** ,带 8 个 vCPUs 和 30gb RAM
免费实例还包括 5 GB 的持久存储空间。自由实例共有三个限制:
1. 每次会话 6 小时后,笔记本电脑将自动关机
2. 一次只能运行 1 台笔记本电脑
3. 该笔记本将被设置为公共
尽管笔记本电脑将在 6 小时后关闭,但您可以启动的会话数量没有限制。您可以在 6 小时后开始新的会话。为了防止在达到 6 小时限制时丢失正在运行的进程所取得的进展,您可以将进程(如果可能)拆分到不同的会话中。您也可以在 6 小时的会话结束前将您的进度保存到永久存储器中,然后在下一个会话中再次加载它以从您停止的地方继续。
如果您的流程必须不间断地执行 6 个小时以上,那么您可能应该升级您的订阅。
现在,选择你感兴趣的免费实例(关于免费实例的更多信息,请查看[这一页](https://docs.paperspace.com/gradient/instances/free-instances))。现在我们来看看创建渐变社区笔记本的第三步,也是最后一步:设置笔记本选项。
## **笔记本选项**
创建笔记本的最后一步是根据下图指定一些选项。这两个选项是:
1. 自动关机限制
2. 笔记本是公共的还是私人的
这两个选项对于自由实例是不可编辑的,如下图所示。在自由实例上运行的笔记本将始终设置为公共,并在 6 小时后自动关闭。对于付费实例,您可以自由地更改两者。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e773e270c233c155514b4f81642019b9.png)
现在只需点击页面左下角的`CREATE NOTEBOOK`按钮即可创建您的笔记本。下一节将讨论如何使用它。
## **管理笔记本**
点击`CREATE NOTEBOOK`按钮后,您将被引导至[笔记本页面](https://www.paperspace.com/console/notebooks),在这里您可以管理您的笔记本。在设置过程中,笔记本会处于`Pending`状态几秒钟。这将变成下面显示的`Running`阶段,这表明你可以访问你的笔记本。在此页面中,您还可以看到笔记本的以下详细信息:
* 笔记本所有者姓名。
* 笔记本名称。笔记本会被分配一个默认名称,即`My Notebook`,但是您可以点击它并将其更改为另一个名称。
* 容器名称。在这种情况下,选择的容器是`TensorFlow 2.0`容器。
* 在`Choose Machine`步骤中选择的机器类型。在这种情况下,我选择了提供 **NVIDIA M4000 GPU** 的**免费 GPU+** 机器。
* 笔记本 ID,直接位于机器类型后面。这种情况下,ID 为 **nlc7r4dn** 。
* 笔记本的创建日期和运行时间。
* 状态。目前这应该是`Running`。其他潜在的地位是`Shutting down`或`Stopped`。
* 多个动作:`OPEN`、`SHARE`、`STOP`等。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2f60f08f2ff31f668a90fb39a0305203.png)
当笔记本的**状态**为**运行**时,你可以在它的正下方找到**复制笔记本令牌**的链接。单击它将复制与笔记本相关联的令牌。下面是一个令牌的例子:`2ac47522e520429189b9ba572cbd5e1582b4b3942e138c02`。此令牌用于将相关存储中的文件和文件夹共享链接到笔记本。因此,不公开它是很重要的。
在操作中,您可以点击`SHARE`按钮来共享您的笔记本。这将弹出一个窗口,如下所示。在窗口底部,你可以点击链接复制笔记本的公共 URL,在本例中是[https://www.paperspace.com/ahmed/notebook/pr1qzg6wz](https://www.paperspace.com/ahmed/notebook/pr1qzg6wz)。由于**公开**,任何有链接的人都可以查看笔记本,甚至不需要创建一个 Paperspace 账户。只要访问它的链接,并享受它!有关查看笔记本的更多信息,请访问[本页](https://docs.paperspace.com/gradient/notebooks/public-notebooks)。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6cb2282c2b9ad53c4000b383ee4fbb23.png)
从操作中,您也可以单击`STOP`按钮来停止笔记本。笔记本状态将从`Running`变为`Stopped`,如下图所示。停止后,会出现一些附加动作:`START`、`FORK`、`DELETE`。因此,当笔记本状态为`Running`时,您不能派生或删除笔记本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d0bb5a99bd0d8db3011ac83efcd6254e.png)
您可以点击`START`按钮运行笔记本。点击此按钮后,将出现一个新窗口,您可以在其中选择或更改以下内容:
1. **笔记本名称**
2. **实例类型**
3. **自动关机限制**(选择自由实例时不能更改)
在窗口底部,只需点击`START NOTEBOOK`按钮即可启动笔记本。在笔记本状态变为`Running`后,你可以在**经典**或者**测试**版本中打开它。既然您的 Jupyter 笔记本已经启动并运行,接下来的部分将探索您可以在这个环境中做什么,以及一些附加的特性。
## **第一次打开笔记本**
下图是第一次在测试版中打开笔记本后的结果。因为这是你第一次打开笔记本,一个**启动器**会打开,询问你是否要打开笔记本、控制台、终端等等。在这种情况下,我们对创建 Python 笔记本感兴趣,因此选择了第一个选项。请注意,仅支持 Python 3。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3734c2ba9e03ad97fdb525983c532bba.png)
下图显示了笔记本创建后的结果。默认情况下,它被命名为`Untitled.ipynb`。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/931cccc9b8c43310acce8cdc4422e8a6.png)
在下一部分,我们将讨论工具栏,你可以在上面的图片左侧看到。
## **工具栏**
在上图主窗口的最左侧,您可以找到 5 个图标,分别表示以下内容(从上到下):
1. **文件浏览器**:查看存储文件和文件夹。
2. **运行终端&内核**:查看和关闭 Python 笔记本和终端。
3. **命令**:创建和管理笔记本的命令列表。例如,将笔记本导出为 PDF 或 markdown、关闭笔记本、更改字体大小、清除输出等等。
4. **笔记本工具**:打开笔记本时出现此图标。
5. **打开标签页**:查看当前打开的笔记本列表。
您可以轻松地将鼠标悬停在图标上来查看其名称。
通过点击这些图标中的任何一个,其关联的窗口将从隐藏状态变为未隐藏状态,反之亦然。下一节将讨论文件浏览器。
## **文件浏览器**
**文件浏览器**可用于浏览与笔记本相关的存储器。除了你的 Jupyter 笔记本(。ipynb)文件,还有另外两个文件夹。第一个被称为**数据集**,它包含许多预加载的 ML/DL 数据集,您可以在下面看到。这为您节省了自己下载这些文件的时间和精力,并且这些存储包含在您的免费环境中。您可以轻松浏览每个子文件夹以查看其内容。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/380516dcf6a03e494813d63e08dbfd60.png)
第二个文件夹是您的持久存储库。关闭笔记本后,保存您想要保留的任何内容。
在文件资源管理器的顶部有四个图标,下面突出显示。这些是(从左至右):
1. **新启动器**:打开我们之前第一次打开笔记本时看到的启动器。可用于创建新文档。
2. **新建文件夹**:在当前目录下新建一个文件夹。
3. **上传文件**:上传文件到当前目录。
4. **刷新文件列表**:刷新文件列表,查看最新变化。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b5d4989f23ddde88358c9b583429a7f0.png)
现在让我们再次打开发射器,并打开控制台。下图显示我们现在有两个活动选项卡:第一个用于笔记本,第二个用于控制台。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5431d9b42758183d8dbb7d825ebb68f2.png)
您也可以右键单击文件来打开附加选项列表。下图显示了右键单击笔记本文件后的结果。您可以找到包括打开、删除、复制、剪切、下载和重命名在内的选项。您也可以右键单击文件夹来查看更多类似的选项。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/10c61d152b9131457b950e74e9e77082.png)
注意,在这个列表中有一个选项**复制可共享链接**。我们现在就讨论这个。
## **共享文件和文件夹**
通过点击**复制可共享链接**选项,可以获得文件的可共享链接。请注意,文件名出现在链接中,因此链接会根据当前名称而变化。
下图显示了带有该链接的人打开它的结果。如果您已经有了令牌,只需粘贴它并单击`Log in`按钮。之后,您可以查看与该链接相关联的文件或文件夹。这是超级酷,因为它使发送文件给他人非常容易!
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cb795db5988b5702fb1cebd07f82aaa5.png)
下一节将讨论如何通过公共 URL 共享笔记本。
## **分享笔记本**
与他人共享笔记本的公共 URL 将允许他们以 HTML 页面的形式查看上次停止的笔记本版本。下图是[这个笔记本网址](https://www.paperspace.com/ahmed/notebook/pr1qzg6wz)被公开访问后的结果。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/afcbc482fa69c6a9cb6ccd2109f9c2d2.png)
根据 [Paperspace 文档](https://docs.paperspace.com/gradient/notebooks/public-notebooks),下面是查看当前正在运行的笔记本的工作方式。
> 当您访问社区笔记本时,您将始终看到该笔记本的最后停止版本。因此,如果笔记本电脑所有者当前正在运行它,您将会看到一条消息,内容如下:
> `Note: You are viewing the static version of this notebook. Run the notebook to see the interactive live version.`
下一节讨论将文件上传到笔记本`/storage`目录。
## **上传和下载文件**
点击**文件浏览器**窗口中的**上传文件**图标,用户可以从本地存储器中选择文件并上传到笔记本存储器。下图显示了一个图像被上传到当前目录,并在一个新的选项卡中打开。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/84f05a4e04c388b9ec421c6bff04dce1.png)
请注意,该文件上传到笔记本存储的根目录。根据 [Paperspace 文档](https://docs.paperspace.com/gradient/data/storage),这里有一个关于`/storage`目录的说明:
> 您存储在`/storage`目录中的任何内容都可以在给定存储区域的多次实验、作业和笔记本中进行访问。
因此,我们可以将文件移动到这个目录,以便利用在不同笔记本之间共享文件的能力。您可以简单地将文件移动到`/storage`目录,方法是剪切文件并粘贴到那里,或者简单地将文件拖放到`/storage`目录中。
您也可以轻松下载文件。右键单击该文件,然后单击**下载**选项,如下图所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/610daa2f1db96d49be02a79c1215fa2f.png)
在下一节中,我们将讨论读写文件。
## **读写文件**
在本节中,我们将在笔记本单元格中编写代码,并了解如何运行它。只需打开笔记本文件并编写 Python 代码。在下图中,打印语句被输入到一个单元格中。要运行它,只需点击标有红圈的**运行**按钮图标,或者按键盘上的 **shift + enter** 。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d2f1e70f95c07b72a02483eed97abbe3.png)
要从代码中访问一个文件,首先你需要获取它的路径。为此,只需右击该文件并点击**复制路径**选项。对于之前上传的名为`image.png`的图像文件,移动到`/storage`目录后,其路径为`/storage/image.png`。使用这个路径,我们可以根据下一个代码块读取和显示图像。
```py
import matplotlib.image
import matplotlib.pyplot
im = matplotlib.image.imread("/storage/image.png")
matplotlib.pyplot.imshow(im)
matplotlib.pyplot.show()
```
下图显示了执行前面代码的结果。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/974a67d105ae0e3c69939b060d820c7f.png)
与读取文件类似,您也可以通过指定路径来写入文件。接下来的代码块将我们刚刚读取的图像除以 2,并作为`image.png`写入`/storage`目录。
```py
import matplotlib.image
import matplotlib.pyplot
im = matplotlib.image.imread("/storage/image.png")
im = im / 2
matplotlib.pyplot.imshow(im)
matplotlib.pyplot.show()
matplotlib.pyplot.imsave("/storage/new_image.png", im)
```
下图显示图像成功保存到`/storage`目录。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f75af7a4119fb27c10fdae33016a7e5b.png)
下一节讨论将笔记本导出为文档。
## **导出笔记本**
完成工作后,您可能会对保存笔记本的静态版本感兴趣。你可以用渐变笔记本轻松做到这一点。按照下图,进入**文件**菜单,点击**导出笔记本为**。您可以找到不同的选项来导出笔记本,包括 Markdown、PDF、LaTex 和 HTML。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3209fcc923089735aeb58024f293ec47.png)
## **结论**
这篇文章讨论了如何开始使用 Paperspace 的免费[渐变社区笔记本](https://gradient.paperspace.com/free-gpu)。我们从引入[梯度](https://gradient.paperspace.com)开始,这是 Paperspace 的平台,用于构建轻松的机器学习和深度学习产品和管道。它提供了与各种工具打包在一起的不同实例和容器,这样用户就不会陷入依赖关系或工具问题的困扰。
2019 年 10 月,Paperspace 增加了免费启动和共享 Jupyter 笔记本的免费 GPU 和 CPU 实例。这篇文章讲述了创建、运行和共享免费笔记本的步骤。还讨论了一些附加功能,如共享文件和文件夹,上传和下载文件,以及向`/storage`目录读写文件。
在这篇文章结束时,你可以很容易地开始使用运行在 GPU 或 CPU 上的免费 Jupyter 笔记本。
# 介绍 GradientCI 我们新的友好的 CI/CD 机器人,用于机器学习和人工智能管道
> 原文:<https://blog.paperspace.com/gradient-continuous-integration-github/>
***更新**:这个帖子过时了。我们建议查看[文档页面](https://docs.paperspace.com/gradient/projects/gradientci),其中包含更多信息和 GradientCI 入门的分步指南。*
* * *
我们很高兴推出 GradientCI,这是我们新的 GitHub 集成,使您的 ML 工作比以往任何时候都更容易。
安装在你的私人 github repos 这里:[https://github.com/apps/gradientci](https://github.com/apps/gradientci)
## 它是如何工作的
*注意:这是一个早期版本,还没有准备好投入生产*
GradientCI 确实是新的,所以它还没有完整的功能。如果你渴望测试出血边缘,下面是你今天可以尝试的方法!
就像您可以测试和部署代码的 webapps 的持续集成构建一样,您现在可以针对 GitHub repo 中的任何变化运行渐变作业。
使用 GradientCI for GitHub,您不再需要为每次 git 提交手动运行作业。这一切都是简化和自动化的,无需添加任何额外的配置。
### 1.在您的私人回购上安装 GradientCI 应用程序
转到[https://github.com/apps/gradientci](https://github.com/apps/gradientci)并配置对私有回购的访问。
![Pasted-image-at-2018_06_28-09_05-PM](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0c61042e4e7894350698a6836ebe4b13.png)
GitHub 首先会让你选择想要安装 GradientCI 的位置。如果是 GitHub 组织的成员,它会提示您选择个人帐户或组织帐户。
下一步是选择想要安装 GradientCI 应用程序的存储库。您不需要让我们访问与您的帐户相关的所有组织和存储库,您可以选择您希望 GradientCI 运行的单个存储库。
![Screenshot_6](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d8ae911803f0d9dd08f6cdb1d6901af3.png)
### 2.在您的 repo 中,添加一个图纸空间配置文件
如果您已经使用`paperspace project init`从 paperspace CLI 初始化了您的 repo,您的目录将已经有一个`.ps_project`文件夹。在这个文件夹中有一个名为`config.json`的文件,其中包含您的工作参数。
您的回购应该如下所示:
* 项目目录
* 。ps _ 项目
* config.json
* main.py
* run.sh
* ...
您的`config.json`应该是这样的:
```py
{
"project": "paperspace/nvidia-smi",
"container": "tensorflow/tensorflow:1.8.0-gpu",
"command": "nvidia-smi",
"machineType": "K80",
}
```
### 3.将 API 密钥添加到您的`config.json`
这是今天试用 GradientCI 的临时步骤。我们通常不建议将您的私有 API 密钥放入回购中,这个要求将很快被取消。 ***重要:确保你的回购是私人的!你不希望你的 Paperspace API 密匙在互联网上到处传播***
```py
{
"project": "dte/nvidia-smi",
"container": "tensorflow/tensorflow:1.8.0-gpu",
"command": "nvidia-smi",
"machineType": "K80",
"apiKey": "abc123" <---- add your API key here
}
```
### 4.推点代码!
配置 GradientCI bot 后,每个新的 repo 推送都将运行一个作业。在未来,我们将使其可配置,以便它只发生在拉请求,或当您手动指定它。
一旦你`git commit && git push`你的工作将开始运行!
如果作业已提交,您的提交将被标记为一条`success`消息,如果您的配置有任何问题,将被标记为`error`。
![image--2-](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b8be0d12a8ff0c15a302da6ccb71a1ec.png)
## 结论
我们即将推出一些非常令人兴奋的新工具来帮助构建生产 ML 管道。请务必订阅我们的时事通讯以获得通知!
# Gradient Health 和 Paperspace 携手推进医学成像
> 原文:<https://blog.paperspace.com/gradient-health-and-paperspace-team-up-to-advance-medical-imaging/>
[Gradient Health](https://gradienthealth.ai/) 是一家为医学成像构建开放研究平台的医疗技术公司,它选择 Paperspace 作为其 MLOps 平台,为其机器学习管道带来可再现性和确定性。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5736097bb1683fb1a46b3e04e0cc0c82.png)
Lung segmentations image set made available by Gradient Health
Gradient Health 由杜克大学的学生和毕业生创建,使用最新的机器学习技术为成像算法的开发者建立关键的基础设施。这些开发人员将尖端科学研究带入现实世界,为放射领域构建新颖的成像工作流程,造福于医生和科学家。
Paperspace 的高级机器学习架构师 Misha Kutsovsky 表示:“Gradient Health 的使命是帮助医学成像社区利用最新最伟大的 ML 技术进行创新。我们很高兴能够支持他们的机器学习工作,并与他们合作开展这项关键工作。”
凭借 Paperspace 的 MLOps 平台(恰好与 Gradient 同名),Gradient Health 为其开发人员提供了一个可互操作的安全临床环境。拥有一个紧密的机器学习反馈循环是交付有价值产品的关键。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f1805b595540ded8b6b8142d292ffe68.png)
“通过与 PACS 供应商的合作,我们的目标是为所有研究人员提供安全的访问权限,以便在生产场景医疗数据集上训练和验证模型。Paperspace 为我们提供了一种更好地与研究人员交流的方式;使我们能够专注于构建工具和广泛适用于该领域的模型。”
渐变健康首席执行官黄
通过合作,Gradient Health 和 Paperspace 将比以往任何时候都更容易将领先的科学研究转化为现实世界的机器学习算法,从而改善患者的结果。
[![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/aeec1d8e2e83833c814b56f6201c4293.png)](https://gradienthealth.ai/)
要了解更多关于梯度健康的信息,请访问: [www.gradienthealth.ai](https://gradienthealth.ai/)
# 比较 Gradient 和 Kaggle 的免费 GPU 访问和可用资源
> 原文:<https://blog.paperspace.com/gradient-kaggle-notebook-comparison/>
# 介绍
Kaggle 是 Google 的一个流行的代码和数据科学工作区,支持大量数据集和公共数据科学笔记本。
Paperspace Gradient 是一个用于构建和扩展真实世界机器学习应用程序的平台,Gradient Notebooks 是一个基于 web 的 Jupyter IDE,带有免费的 GPU。
在这篇博文中,我们将看看 Google Kaggle 和 Paperspace Gradient,并根据使用案例确定每个产品的优势和劣势。
我们开始吧!
# 什么是机器学习的好工作空间?
好的机器学习管道的关键是可访问性。这包括获得良好的信息,一个强大的处理环境,以及一个可靠的传播结果和训练模型的方法。
机器学习工程师和数据科学家经常在数据科学探索的一个或多个阶段遇到挑战。
Kaggle 在提供可访问的公共数据集方面取得了巨大成功。Kaggle 擅长维护丰富的数据集,并为数据科学竞赛提供基础。
Gradient 已经成功地为 GPU 提供了加速的计算实例,并为在该平台上生产项目提供了可行的途径。
这篇博客将试图分析两种方法之间的差异 Kaggle 的方法和 Gradient 的方法——在他们试图建立一个功能齐全的机器学习探索和 MLOps 平台的过程中。
本文的每一节都将比较和对比这两种产品的不同方面或特性,您可以使用右边的目录导航到每一节。
# 绘图处理器
Kaggle 和 Gradient 都提供免费的 GPU。让我们来看看免费的 GPU 类型。
## 可用的免费类型
| | 卡格尔 | 渐变笔记本(免费) | 渐变笔记本(付费) |
| --- | --- | --- | --- |
| 类型 | P100 | M4000 | P5000 |
| 核心 | Two | eight | eight |
| 内存(GB) | Thirteen | eight | Sixteen |
#### 卡格尔:
GPU: TESLA P100,带 2 个 CPU 内核和 13 GB 内存
TPU: TPU v3-8,带 4 个 CPU 内核和 16 GB 内存
#### 渐变:
GPU:空闲层:带有 8 个 CPU 内核和 8 GB RAM 的 QUADRO M4000
Pro tier(用户以每月 8 美元的价格免费使用更多 GPU):带有 8 个 cpu 内核和 8 GB RAM 的 QUADRO P4000,带有 8 个内核和 16 GB RAM 的 QUADRO P5000,以及带有 8 个 CPU 内核和 8 GB Ram 的 Quadro RTX4000
TPU:不适用于坡度
## 易接近
#### 卡格尔:
对于 Kaggle 上的每种类型的处理器,GPU/TPU 访问被限制在每周 30 小时。由于高需求,也有可用性问题,所以你可能会被放在一个队列中,要求在他们的笔记本电脑平台的 GPU。
#### 渐变:
GPU 访问仅受可用性限制。
## 免费 GPU 可用性
#### 卡格尔:
每个笔记本编辑会话在被中断之前有 9 个小时的可用执行时间,只有 20 分钟的空闲时间(这意味着 20 分钟的不活动将导致内核关闭)。
#### 渐变:
GPU 访问仅受可用性限制。每个 free-GPU 实例将在运行 6 小时后关闭,但在此期间有无限的空闲时间。
# 定价
#### 卡格尔:
Kaggle 在实际的 Kaggle 平台上是完全免费的。然而,你可以在谷歌云项目的付费版本上访问 Kaggle 笔记本,这就是 Kaggle 用户如何使用不同的 GPU、docker 容器等来访问更多可定制的环境。然而,实际的设置过程可能非常复杂且耗时,因此我们建议您遵循[本指南](https://www.kaggle.com/getting-started/234090)。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/01b198374239e7680225794e78697d0f.png)
Gradient Pricing
#### 渐变:
Gradient 为个人客户提供了三个价格等级,每个价格等级的免费 GPU 数量和质量都在增加。[第一个付费层以每月 8 美元的价格扩展到 3 种新的免费 GPU 类型和 15 GB 存储。](https://gradient.run/pricing)
# 资源
### 卡格尔:
竞赛:基于社区的产品,竞赛允许 Kaggle、第三方和用户创建和参与 ML 相关的竞赛。众所周知,这里是这类比赛最受欢迎的地方之一,也是世界著名的 Netflix 奖的举办地。
笔记本:类似 IDE 的 Jupyter 笔记本,带有代码和 markdown 单元、一个终端和一个可定制的环境(仅 CPU、GPU 或 TPU)。作为用户可以对易于访问的 Kaggle 数据集进行 ML 和数据分析的地方。
### 渐变:
笔记本:类似 IDE 的 Jupyter 笔记本,有代码和 markdown 单元、终端、日志、版本和非常可定制的环境。用户可以在这里探索自己的数据,对自己的数据或[在梯度存储](https://docs.paperspace.com/gradient/data/data-overview/private-datasets-repository/public-datasets-repository)中公开存储的数据进行 ML 和数据分析,并准备建立 ML 管道。
[工作流](https://gradient.run/workflows):一旦探索和初始分析完成并确定了工作流,工作流允许用户通过连接到 GitHub 来动态更新他们的建模管道。这允许随着进展进行模型的版本化开发。
[部署](https://gradient.run/deployments):一旦最终确定的模型被确定并保存到 [Gradient 的持久存储](https://docs.paperspace.com/gradient/explore-train-deploy/workflows/gradient-actions#model-create),部署资源允许用户通过几个简单的步骤将其新模型部署为 API 端点,从而避免了 Kubernetes、Docker 和框架设置的许多令人头疼的问题。
# 储存;储备
#### 卡格尔:
用户大多被限制使用 Kaggle 的内置存储。他们可以通过他们的客户端连接到 AWS s3 或类似的产品,但与仅使用 Kaggle 数据集功能相比,这是笨重的。
所有笔记本电脑的工作数据总量不得超过 20 GB。
#### 渐变:
在 Gradient 上,用户可以使用大量的存储服务,包括 Gradient 自带的持久存储、AWS s3、[等等](https://docs.paperspace.com/gradient/data/data-overview/private-datasets-repository/storage-providers)。
自由层用户在 Gradient 的永久存储上将被限制为每台笔记本电脑有 5 GB 的自由存储空间(在任何给定时间最多可进行五个项目和运行一台笔记本电脑)。超额部分将按每 GB 存储 0.29 美元收取费用。
# 笔记本功能
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3c65d13dfcd0306f6beece454b1bb79c.png)
A Kaggle Notebook
#### 卡格尔:
优点:
* 即时启动速度(然而,一旦代码运行,您必须等待会话开始)
* 笔记本启动页面上的灵感建议项目库
* 类似 Jupyer 的 IDE
* 笔记本日程安排
* 私人和公共笔记本
* Kaggle 社区允许协作和轻松的工作共享
* 9 小时执行时间限制
* 比无梯度层更高的 GPU RAM (13 GB)
* 完全自由
* 所有笔记本电脑都有 20 GB 存储空间
缺点:
* 难以定制笔记本电脑环境(容器、工作区等)。).通过谷歌云项目可以做到这一点,但迁移到一个新的平台[和](https://www.kaggle.com/getting-started/234090) [n 设置](https://www.kaggle.com/getting-started/234090)笔记本电脑既费时又费力
* 20 分钟的短暂空闲时间使得在没有持续关注的情况下很难在大数据上训练模型。
* GPU 驱动的笔记本电脑每周限制 30 小时
* 如果不迁移到单独的平台,就无法访问更好或不同的 GPU
#### 渐变:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/25374f11803e25c1c4b56338f08264fa.png)
A Gradient Notebook
优点:
* Jupyter-like IDE
* 私人和公共笔记本
* Python、R、JavaScript 等等都可以在笔记本上工作
* 项目和团队格式便于公司或团队内部的协作
* 从工作空间到容器再到 GPU 类型的高度可定制的设置非常简单
* 没有空闲时间限制(但在 GPU 驱动的笔记本电脑上有 6 小时的执行时间限制)
* 访问梯度持久存储&能够连接到 AWS s3 等外部存储提供商
* 能够升级到更高层并访问更好的 GPU
* 空闲层实例上的 CPU 内核比 Kaggle 多(8 对 2)
* 轻松切换到付费版本,获得更强大的资源
缺点:
* 免费 GPU 的 6 小时执行时间限制
* 免费层的 5 GB 永久存储限制(能够以 0.29 美元/GB 的价格支付更多费用)
* 无法访问自由层中的笔记本电脑终端
# 结论
这两个平台都提供了对有价值的计算能力和 GPU 可访问性的免费访问,一个使用该计算进行 ML/DL 工作的强大环境,以及一个集成到笔记本电脑中的有用的存储系统,以方便所有这一切。
虽然 Kaggle 在免费 GPU 的 RAM (13 GB 与免费层的 8 GB 相比)、免费可用存储容量(20 GB 与 5 GB)以及调度笔记本电脑的能力方面具有明显的优势,但 Gradient 的多功能性似乎使其超越了竞争对手。梯度笔记本电脑在其自由层实例中提供了更多数量的 CPU 内核,对 GPU 访问时间没有每周限制,更可定制的笔记本电脑环境,以及由于其超长的空闲时间而更加用户友好的 UX。此外,其他两个 Gradient 资源,工作流和部署,将 Gradient 的功能与 Kaggle 分隔得更远。
# 引入渐变公共配置文件📣
> 原文:<https://blog.paperspace.com/gradient-public-profiles/>
Gradient 中的公共配置文件旨在更容易地在 Gradient 平台上发布和发现机器学习项目。
一如既往,我们渴望听到您的反馈。今天就要求你的用户或团队处理,让我们知道你的想法。我们很期待看到你的作品!🙌
## 要求您的用户或团队处理!
您可以为您的个人帐户以及您管理的任何共享团队创建自定义句柄。这使您能够轻松地与全世界共享您的公共笔记本!
您的公开个人资料将在[https://www.paperspace.com/](https://www.paperspace.com/){**your _ handle**可见
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/95d80cbf9f18a264c55b3f0b650ca6d3.png)
## 共享您的个人资料
一旦你公开了你的个人资料,你就可以开始分享你的作品了。目前,公共配置文件仅支持公共笔记本,但很快您将能够共享项目、模型、数据集等。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2a979a4fe8ec9e89011df328234cb565.png)![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/926b416c5afc5f64c23df7092d15e11b.png)
## 在文档中了解更多信息
[查看文档](https://docs.paperspace.com/gradient/public-profiles/gradient-public-profiles)了解更多关于 Gradient public profiles 的信息以及我们下一步的发展方向。
[Gradient Public ProfilesPublic profiles in Gradient are designed to make it easier to publish and discover machine learning projects on the Gradient platform. Currently they support sharing public notebooks.![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/978b75f7fd6953bee4d60d4e29691e50.png)Gradient Docs![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/198f5a89167c8d5545e05a8baef2d5c9.png)](https://docs.paperspace.com/gradient/public-profiles/gradient-public-profiles)
## 想看看一些示例公共笔记本吗?
前往我们新的 ML 展示区,在那里我们收集公共笔记本和项目样本。
[Gradient - ML ShowcaseA collection of interactive Machine Learning projects curated by Paperspace Gradient.![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d67eec5444413bae6edc989ff166040c.png)ML Showcase![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1af3ec88982d89254204e06bf30ae2db.png)](https://ml-showcase.paperspace.com/)![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8128f52c545711899d317a9989b408d4.png)
# 渐变更新(2019 年 2 月 22 日)
> 原文:<https://blog.paperspace.com/gradient-update-2-22-2019/>
[Gradient](http://paperspace.com/gradient) 已经更新,以回应来自社区的大量反馈。以下是我们最近添加的一些内容的综述:
# 新的系统和定制指标!
现在,无论何时运行作业,您都可以实时观察主机指标,包括 GPU 利用率(%)、使用的 GPU 内存、温度和负载。您还可以查看 CPU 和 RAM 利用率,以确保渐变作业按预期执行。
此外,您可以创建自己的自定义指标。在[文档](https://docs.paperspace.com/gradient/jobs/graphing-custom-metrics)中了解更多信息。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/707b9369829cf35bf062cadb93d8757c.png)
# 动态构建 docker 文件
给我们一个 Dockerfile 文件并将`--useDockerfile`命令传递给 CLI,我们将在运行它之前构建映像!也可以选择推送到公共或私有 docker 注册表。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/72a397780cf910aafb154e57917e9d70.png)
在[文档](https://docs.paperspace.com/gradient/jobs/create-a-job#new-run-jobs-from-dockerfiles)中了解更多信息。或者克隆一个[样本任务](https://github.com/Paperspace/tf-dockerfile?utm_source=newsletter_charts-docker-stylegan&utm_medium=email&utm_campaign=build-dockerfiles-btn)到你自己的账户中!
# 新的示例项目- StyleGAN 人脸生成器!
你可能已经看到了 https://thispersondoesnotexist.com 周围的流言蜚语。在幕后,它是建立在 NVIDIA 的革命性的新风格。
只需点击一下鼠标,即可使用作业构建器和 StyleGAN 示例项目测试渐变作业。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7a5c50ea441faec7f5e54ee1672c9c8e.png)
StyleGAN in Gradient
下面是一个显示 GAN 输出的公共作业:[https://www . paper space . com/console/jobs/jqtl 5 zat 16 r 6/metrics](https://www.paperspace.com/console/jobs/jqtl5zat16r6/metrics)
# SAML 单一登录(SSO)
现在,将 Gradient 与您现有的企业部署集成在一起比以往任何时候都容易。点击了解更多[。](https://blog.paperspace.com/introducing-single-sign-on-sso/)
# 新急流笔记本容器
[www.rapids.ai](www.rapids.ai) 是一个令人兴奋的新项目,旨在将 GPU 的强大功能引入传统(非深度学习)机器学习和数据科学世界。借助 NVIDIA Rapids,您可以在 GPU 上执行数据科学和分析管道。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e7813109a6e17d52899dce431ecb7df2.png)
# 渐变更新(2018 年 7 月 18 日)
> 原文:<https://blog.paperspace.com/gradient-update-july/>
[Gradient](http://paperspace.com/gradient) 已经更新,以回应来自社区的大量反馈。以下是我们最近添加的一些内容的综述:
产品发布说明可以在[这里找到](https://paperspace.zendesk.com/hc/en-us/articles/217560197-Release-Notes),API 发布说明可以在[这里找到](https://github.com/Paperspace/paperspace-node/blob/master/releasenotes.md)
## 职务页面更新
渐变作业界面已更新,包括一些新项目。最引人注目的是,你会看到这些新的彩色块,对应于界面中每个独特的 UUID。这些块给你一个快速的方法来区分界面中不同的独特元素,并使移动更加容易。
![image--6-](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6645d3f75d04e5c6d4a20c19ba08a0fb.png)
其他更新包括:
* 更好地报告工作中的错误
* 用于公开工作的更清晰的界面
* 更快的记录
## 作业存储
在新的`/data`选项卡下,我们还可以更轻松地查看您的梯度存储选项及其当前利用率。我们正在努力使存储管理变得更加简单,这是将可视性添加到纸张空间生态系统的这一部分的第一步。
![Screenshot_7b](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9d2b8015225662a76882c4615b0e4a42.png)
## 笔记本电脑改进
笔记本电脑是我们最受欢迎的功能之一。除了一些新的模板和对 Jupyter 实验室更好的支持,我们还推出了一些小的修复来解决用户的反馈。
例如,您现在可以直接在笔记本列表中找到正在运行的作业的 URL,从而更容易共享您的笔记本并在多个浏览器选项卡中工作。
![image--7-](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8bc4b81a5301e93ffad4b9eddce91b00.png)
## 新机器类型(支持多 GPU!)
在此版本中,我们现在支持更多的机器类型。其中包括新的机器类型,如 V100,以及多 GPU 机器实例。
![Screenshot_7asd](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a093849fa5d668d36ab0861719bbfdc7.png)
在[定价页面](https://www.paperspace.com/pricing)了解更多信息
## 新计划类型
越来越多的团队正在使用 Gradient 构建他们的 ML/AI 工作流,为了支持这些团队,我们添加了额外的计划类型,以支持更多的作业、笔记本和存储。
![Screenshot_7ba](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5068a0ea6c4427de224b3d82cae8c532.png)
## 更好的可视性
最后,现在比以往任何时候都更容易检查您的账单选项卡下的梯度利用率。你可以在月底结账前看到你到底用了多少钱。
![image--5-](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/38f686a9d9f40f28433439c57fe02d78.png)
## 对存储数据集的 linux 访问
我们添加了一些新的数据集,包括[可可](http://cocodataset.org/#home),并使直接从您的虚拟机、笔记本电脑和作业中访问`/storage`和`/datasets`变得前所未有的简单。所有新的 linux 虚拟机都会自动挂载这些目录,这使得在您的机器学习项目中处理大型数据集变得前所未有的简单。
## 更便宜的实例!
深度学习可能会变得昂贵,所以我们一直在降低一些最受欢迎的实例类型的价格。例如,P100 现在只需 1.72 美元/小时,包括您的梯度持久存储!
# Paperspace Gradient 与 Google Kaggle:选择深度学习的最佳平台
> 原文:<https://blog.paperspace.com/gradient-vs-kaggle-2022/>
最好的 ML 平台通常是平台的 IDE、硬件和成本的函数。考虑到这些因素,我们将比较 Kaggle 笔记本和 Paperspace 的渐变笔记本。
在本指南中,我们的目标是展示每个平台的优势,并为何时使用其中一种产品提供清晰的论据。
#### 图纸空间渐变笔记本
[Gradient](https://gradient.run/) 是一个 MLOps 平台,旨在帮助用户扩展真实世界的机器学习应用。[渐变笔记本](https://gradient.run/notebooks)拥有一个由 [Jupyter](https://jupyter.org/) 支持的笔记本工作区,用户可以在其中执行代码单元格。笔记本对于处理和运行直接上传到工作目录的数据、上传到团队数据集卷并装载到笔记本中的数据,或者来自所提供的公共数据集之一的装载卷的数据非常有用。
#### Kaggle 笔记本
[Kaggle](https://kaggle.com/) 是一个受欢迎的平台,供用户查找和发布数据集,在数据科学环境中创建和测试模型,通过社交媒体功能与其他用户协作和交流,以及参加比赛以解决数据科学挑战。用户可以在基于网络的平台上运行的 IPython 笔记本上操作,并使用 Kaggle 笔记本来执行代码和 markdown 单元格,以处理用户上传到平台的数据。Kaggle 笔记本还有一个控制台窗口,如果需要在笔记本之外运行任何命令的话。
### 它们总体上有什么相似之处?
渐变笔记本和 Kaggle 笔记本共享许多功能。熟悉 Jupyter 笔记本的用户在这两种环境下都会感到舒适。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4ea6111060e8cf36ba065aeba6ff5e55.png)
The Kaggle and Gradient Notebook IDEs
首先,这两种 ide 都允许用户执行代码和标记单元格。Markdown 单元格使得在笔记本中提供上下文散文、标签、描述性语句和注释变得容易,而 code 单元格则支持在笔记本中执行代码。这种类似 Jupyter 的行为使得这两个平台都非常适合以描述性和可重复的方式探索数据或试验新模型。
<https://blog.paperspace.com/content/media/2022/05/exit-this.mp4>
The interactive session allows you to start and stop kernels at will.
这两个平台都具有访问在 Docker 容器中运行的交互式会话的功能,该容器带有预安装的软件包。这种容器化的会话运行允许用户使用期望的包启动会话。然而,在这两种 ide 中,容器的定制范围有很大的不同。我们将在下面更详细地介绍这一点。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/72711d0b58dd9e311d047856c1660042.png)
每个平台都能够装载版本化数据源并在平台内访问它们。这使用户能够通过安装的卷在笔记本电脑中处理上传到主机平台的文件。同样,这里功能的范围变化很大,但是两个平台都能够在导入的数据上执行代码。
最后,每个平台都允许用户访问可定制的计算资源,如 GPU——但是 Kaggle 和 Gradient 处理 GPU 硬件分配和可用性的方式不同,Gradient 提供更多选项和更高端的硬件。我们将在下面详细讨论这些和其他的区别。
* * *
# 对比 Kaggle 笔记本和渐变笔记本
## 五金器具
| | 卡格尔 | 梯度 |
| --- | --- | --- |
| 国家政治保卫局。参见 OGPU | 是的,P100 | 有,M4000、P4000、RTX4000、A4000、P5000、RTX5000、A5000、A6000、V100 - 16GB、V100 - 32GB、A100 - 40GB、A100 - 80GB |
| TPU | 是 | 不 |
| 中央处理器 | 是 | 是 |
### 机器种类和性能
Kaggle 笔记本能够运行 CPU、GPU 或 TPU 驱动的实例,而 Gradient 只能选择 CPU 或 GPU 机器。如果看重 TPUs,Kaggle 是更好的选择。然而,如果你重视对 CPU 和 GPU 硬件有更多的选择,Gradient 是一个更好的选择。
Kaggle 只提供 P100 GPU,这是一种 Pascal 系列 GPU,现已有两代历史。与 Gradient 上提供的 V100 和 A100 实例相比,P100 的效率将相形见绌,尽管这些 GPU 只能通过 Gradient 上的订阅获得。
TPU 是谷歌开发的人工智能 ASIC,用于加速深度学习应用程序的张量计算。这主要是为了与深度学习库 TensorFlow 配合使用,软件和硬件的开发是为了优化彼此的使用。
<https://blog.paperspace.com/content/media/2022/05/vid-1.mp4>
Gradient's GPU selection from the Notebook create page
Gradient 的可用[GPU](https://docs.paperspace.com/gradient/machines/)及其[基准](https://blog.paperspace.com/best-gpu-paperspace-2022/)的完整列表可以在这些链接中找到。
### 机器选择
对 Kaggle 不利的最大因素之一是机器选择。众所周知,在给定的时间框架内完成深度学习任务的能力取决于硬件以及操作员。
对于 Kaggle,没有选择不同机器类型的选项。对于平台上的大多数爱好者、学生和爱好者来说,这不是问题。P100 足以完成许多深度学习任务。然而,随着深度学习领域的发展,P100 无法处理许多越来越普遍的大型数据问题。企业级用户会很快发现 P100 不足,这种影响只会因 Kaggle 上 GPU 笔记本的 30 小时限制而加剧。
梯度不存在这个问题。在所有 Paperspace Core 和 Gradient 实例上,用户可以以不同的价格访问各种 Maxwell、Pascal、Volta、Turing 和 Ampere GPUs。这使得用户能够对使用多少数据以及深度学习算法处理梯度数据的速度有更多的控制。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d0c19ccd9c76858ad43d9a097abee42e.png)
Multi-GPU machines on Gradient
此外,Gradient 拥有多个 GPU 实例,可以支持 Kaggle 用户无法使用的整个库和并行化范例,如 rise 或 DeepMind。
## 连接
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/42c6986380581f16b3f4e71108f5d0e9.png)
由于与 Jupyter 共享遗产,渐变笔记本 IDE 和 Kaggle 笔记本界面的相似性大于差异,但也有明显的差异。
这两个平台都是编译 Python 或 R 代码的优秀环境,拥有完整的编辑器和控制台,并且可以通过。ipynb 文件和。py 脚本虽然它们都是健壮的编码环境,但它们的主要区别实际上与用户体验的质量有关。这些差异在于额外的特性:度量、日志和定制。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d2f97ee41d47a126482534b0f08fe624.png)
Monitoring your GPU power draw or total memory used is easy with the Metrics tab
在 Kaggle 中,很难显示代码中发生了什么。对于日志记录和获取关于实例的各种度量,这是一个双重问题。您需要设置自己的专有日志记录应用程序来记录笔记本电脑上正在执行的操作或查找错误代码,并且通常需要第三方应用程序来识别有关机器本身的指标,如内存消耗或温度。幸运的是,有了渐变,这些事情就变得简单了。只需使用任何笔记本左侧的按钮,即可访问日志记录、指标、版本历史记录等等。
## 其他功能
在这一节中,我们将了解区分这两个平台的许多其他特性。
### Github 集成
虽然 Kaggle 有一个 API 和一个流行的 Github 动作来推送数据集内核,但它目前缺乏与 Github 的任何进一步集成。这是合理的;Kaggle 笔记本主要是为在玩具数据集上进行快速实验而设计的,而不是用于迭代 Github repo 的工作空间。
然而,Gradient 的特点是与 Github 的强大集成,以便让用户能够管理他们的笔记本工作区,并通过工作流更新 repos。通过将 Gradient account 与 Github Gradient 应用程序集成在一起,Gradient 可以用于在使用 Gradient 工作流完成工作时迭代地更新存储库。实际上,这允许用户在应用程序开发过程中对代码进行版本控制,并确保任何同事和合作者都将获得最佳体验。在较低的集成级别上,Gradient 还支持使用 Github URL 作为笔记本创建的工作区 URL。通过这样做,用户可以自定义笔记本中可用的启动文件。
### S3 数据集
对于 ML ops 平台用户来说,访问不同的存储位置和类型非常重要。在 Kaggle 上,这完全是在 Kaggle 生态系统内处理的,这是无法改变的。然而,在 Gradient 上,用户可以选择将数据存储在 Gradient Managed 上或连接到 S3 存储桶。通过一个[f](https://docs.paperspace.com/gradient/data/)T2【ew】的配置步骤,任何 Gradient 用户都可以将一个 S3 桶直接连接到他们的账户。这将允许用户设置他们自己的数据存储限制,并克服用户可能招致的超过梯度管理存储限制的任何限制或超额费用。对于任何希望使用大数据的用户来说,这是必须的。
### 运行时间限制
Kaggle 笔记本只能用于执行代码,CPU 和 GPU 笔记本会话执行时间为 12 小时,TPU 笔记本会话执行时间为 9 小时。此外,每个用户每周最多只能使用 30 小时的 GPU 和 20 小时的 TPU 时间。这个限制无法克服,所有用户都必须遵守。
另一方面,在渐变笔记本中,唯一具有设定时间限制的笔记本是免费的 GPU 笔记本。这些在免费层上最多只能运行 6 个小时。然而,用户可以为梯度笔记本的 GPU 访问付费,从几个小时到几天到没有运行时间限制。这使得梯度更可取,每当你有一个计算昂贵或长时间的训练任务要完成。
### 公共和团队数据集
Kaggle 最大的特点是它可以访问用户提交和管理的海量数据。在 Kaggle 笔记本中,用户可以轻松地访问这些数据集的不可变版本,以用于他们的代码,并且可以使用 Kaggle API 更新这些版本。出于这个原因,Kaggle 目录也是新数据科学家开始玩具项目工作的一个非常常见的地方。
Gradient 还让用户可以访问一系列公共数据集,其中包含越来越多的流行机器和深度学习数据集。目前,这些数据集比 Kaggle 数据集更加有限,但它们允许用户几乎立即将数据安装到笔记本上,并包括像 MS-COCO 这样的流行大型数据集。用户还能够通过团队数据集上传和使用他们自己的数据。
总而言之,虽然 Kaggle 目前是平台内快速访问数据的首选,但 Gradient 正在通过其数据集功能快速接近用户的生存能力。Kaggle 数据集的优势也受到其 API 的严重阻碍,因为它使任何用户都可以在很短的时间内以 Paperspace 数据中心的互联网速度轻松下载任何 Kaggle 数据集。
## 结论
在这篇博文中,我们试图分析 Kaggle 和 Gradient 之间的区别。我们试图展示 Kaggle 是如何成为学生和业余爱好者的理想起点的,这要归功于它简单的 IDE、数据共享工具和缺乏术语定制选项。与此同时,我们还展示了 Gradient 提供了许多相同的功能,并增加了企业级可伸缩性、优于 Jupyter 的生活质量改善和部署能力。
出于这些原因,我们建议只有早期职业用户和那些需要使用 Kaggle 笔记本电脑的 TPU 工作。Gradient 应该是企业级用户的首选,也有很强的理由作为任何寻求进行深度学习的用户的首选。
# 引入图纸空间渐变
> 原文:<https://blog.paperspace.com/gradient/>
## 云机器学习和人工智能开发者的新工具
[https://www.youtube.com/embed/bK6DLlYjY7E](https://www.youtube.com/embed/bK6DLlYjY7E)
当我们开始构建 Paperspace 时,我们知道 GPU 将为云计算能够做的事情开启一个全新的可能性世界。在过去的几年里,我们看到了专门为并行计算而构建的软件和工具的爆炸式增长,随之而来的是一整套全新的工作流。
可以轻描淡写地说,深度学习和人工智能的兴起不仅为云计算,而且为整个世界开辟了新的前沿。每天都在创造新的企业,现代深度学习是商业模式的核心,对于每个行业的现有企业来说,深度学习正在成为未来战略越来越重要的一部分。
一年前,我们宣布了对机器学习、人工智能和数据科学的全面支持,推出了我们强大的专用 GPU 实例和我们的一揽子机器学习模板。成千上万的用户已经使用这些工具来构建从基因组学到癌症检测,再到无人驾驶汽车的各种东西。
今天,我们很高兴地宣布我们迄今为止最大、最具雄心的产品——[纸空间渐变](https://www.paperspace.com/gradient)。
Gradient 是一套工具,旨在加速云人工智能和机器学习。它包括一个强大的作业运行程序(甚至可以在新的 Google TPUs 上运行!),对容器和 Jupyter 笔记本的一流支持,以及一组新的语言集成。
凭借我们对语言集成的重视,您可以在最先进的 GPU 基础设施上无缝运行现有的 python 项目,而无需离开您的代码。
我们很期待看到你的作品!今天就在[www.paperspace.com/gradient](https://www.paperspace.com/gradient)报名吧
呼唤
首席产品官
-
##### 阅读人们在说什么:
[TechCrunch: Paperspace 走向无服务器化,以简化人工智能在云中的部署](https://techcrunch.com/2018/03/21/paperspace-goes-serverless-to-simplify-ai-deployment-in-the-cloud/)
[福布斯:Paperspace 推出无服务器人工智能平台 Gradient](https://www.forbes.com/sites/janakirammsv/2018/03/21/paperspace-launches-gradient-a-serverless-artificial-intelligence-platform/#34664bb72ad8)
[BetaNews:新的开发者工具有助于人工智能应用的部署](https://betanews.com/2018/03/21/ai-developer-tool/)
[SiliconAngle: Paperspace 推出 Gradient,这是一项在云端训练人工智能模型的新服务](https://siliconangle.com/blog/2018/03/21/paperspace/)
[ChannelBuzz: Paperspace 推出 Gradient suite,为开发人员带来 GPU 计算和机器学习](http://www.channelbuzz.ca/2018/03/paperspace-launches-gradient-suite-to-bring-gpu-compute-and-machine-learning-to-developers-25076/?utm_source=website&utm_campaign=wordtwit&utm_medium=web)
[CRN: Paperspace 为希望在云中配置 GPU 的人工智能开发人员推出工具包](https://www.crn.com/news/cloud/300101006/paperspace-introduces-toolkit-for-ai-developers-looking-to-provision-gpus-in-the-cloud.htm)
[App Developer Magazine:paper space 推出的企业 AI 工具](https://appdevelopermagazine.com/5926/2018/3/22/enterprise-ai-tools-launched-by-paperspace/)
[SDTimes:梯度发射推动下一代云人工智能](https://sdtimes.com/ai/gradient-launches-fuel-next-generation-cloud-ai/)
# 在 Paperspace 上启动和运行 Graphcore IPUs
> 原文:<https://blog.paperspace.com/graphcore-ipu-jupyter-tutorial/>
Graphcore 为机器学习和人工智能开发硬件加速器。与 Paperspace 的全新集成使得在数据科学笔记本环境中旋转 IPU 变得前所未有的简单。
# 入门指南
第一步是前往 Paperspace 并登录控制台。如果你还没有注册 Paperspace,它是免费的,大约需要 60 秒。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/734377d9932325a135c9ef0a7ef6645e.png)
从控制台,我们将前往梯度和创建一个新的项目。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5f7a366c63109ae51d22899f882af598.png)
接下来,我们将使用预先配置的 Graphcore 运行时之一创建一个新的笔记本。这些运行时会自动将一些启动文件下载到您的笔记本中。它们还指定了 Graphcore IPU 机器类型和预先配置的容器,该容器已经包含了在 IPU 上运行工作负载所需的所有依赖项。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e9d2cfc57696260ad706d6d20d46649c.png)
在我们启动笔记本电脑后,大约需要一分钟来克隆回购和配置机器。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f5d8bd3f35f4159cef5230720900f898.png)
机器现在正在运行!
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2fd26fe1a14af36ed83cbfcdca4a45fd.png)
让我们开始运行一些代码吧!
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b0ca70692f3522b715246efd01d0e858.png)
太棒了。我们现在在 Paperspace 上运行一个 Graphcore IPU。
如果我们想查看更多的入门项目,Paperspace 提供了三种不同的入门运行时,包括 HuggingFace、PyTorch 和 TensorFlow。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/81f21805aa3dba808137cc6788f27a48.png)
有关 Paperspace 上可用的特定 Graphcore IPUs 的更多信息,请务必[阅读文档](https://docs.paperspace.com/gradient/machines/)!
# 选择最佳云虚拟机服务的指南
> 原文:<https://blog.paperspace.com/guidelines-in-choosing-the-best-cloud-vm-service/>
## 介绍
大多数时候,当提到虚拟机或云虚拟化的话题时,人们会感到非常困惑。通常,当这些话题被提起时,人们心中会有很高程度的怀疑。听到“云是什么?”这样的问题并不少见或者“什么是虚拟机?”或者‘什么是云虚拟机?’在外行人中间。
在本文中,我们将逐一介绍这些术语,以帮助您更好地理解它们的含义。之后,我们将讨论以下主题:
→什么是虚拟机?
→使用虚拟机的优势
→什么是云虚拟机?
→使用云虚拟机的优势
→普通虚拟机和云虚拟机之间的区别
→云虚拟机的类型
→选择最佳云虚拟机服务的指南
在本教程结束时,我们将最终能够看到资源选择*隧道*尽头的*之光。*我们将能够根据我们项目的需求,放心地选择云虚拟机服务。
## 什么是虚拟机?
让我们想象一个没有虚拟机的世界。设想一个假设的情况,我们想要在一台 Windows 计算机上构建一个可以在不同操作系统上使用的应用程序。两个月后,我们完成了应用程序的构建。我们在我们的电脑上测试它,它工作得很完美。现在,我们想在 Mac OS 上测试它,但是我们遇到了一个问题。我们没有苹果电脑。
我们能想到的最佳解决方案是去亚马逊商店花 1500 美元买一台 Macbook。现在,我们有了 Macbook,我们在那里测试我们的应用程序。现在,如果我们想在 Linux 操作系统上测试应用程序,我们该怎么做呢?我们将不得不再次重复这个循环,去一些电脑商店购买 Linux 设备。
这个解决方案有什么问题?
显然,这种解决方案远非成本有效,更不用说耗时了。这样做的原因是,当我们想在多个设备上测试我们的应用程序时,我们必须购买这些设备,配置它们,并在它们上安装我们的依赖项,然后才能成功地测试它们。
这种解决方案的另一个问题是可伸缩性。当我们将应用程序推向生产,用户数量增加时会发生什么。我们现在需要再买一台更大规格的电脑吗?正如我们所看到的,在这样的物理限制下进行生产开发是很有压力的。
## 虚拟机在哪里发挥作用
现在,想象一个世界,我们可以在一台计算机中得到这些不同的机器。想象一下,我们可以从一个 Windows 操作系统开始,当我们想在 Linux 上测试它时,我们可以很容易地在同一个工作空间中切换到 Linux 操作系统。让我告诉你们一些好消息。没必要想象。时间已经到了。
有了虚拟机,我们不需要在任何时候测试应用程序都需要单独的硬件。我们可以在单台计算机上获得这些设备的虚拟图像。我们可以通过各种服务使用 windows 上的 Linux,mac 上的 Linux,mac 上的 windows 或者任意组合。
虚拟机的核心是虚拟机管理程序。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c8c104ddce7fbc4738286b88b6576c7b.png)
借助虚拟机管理程序,我们可以将多台虚拟计算机托管到一台物理计算机中。虚拟机管理程序的例子有 VirtualBox、VMware 和许多其他虚拟机管理程序。
关于虚拟机的一个非常有趣的事实是,它们不知道自己是虚拟机。虚拟机不需要互相查看,它们是完全隔离的。
### 使用虚拟机的优势
* 虚拟机是分布式的。这意味着,如果虚拟机出现故障,不会影响主机。
* 虚拟机是完全隔离的,不会危及我们的主操作系统
* 有了虚拟机,我们可以轻松地在多个操作系统中测试我们的应用程序。
## 云虚拟机
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/607b2192690ea37d4495a9be6c2a4359.png)
到目前为止,我们已经看到了虚拟机是多么迷人。云虚拟机(也称为云虚拟化)就是云上的虚拟机。它们充当可以在云中运行的物理机器的数字版本。就像我们的物理计算机一样,它可以运行操作系统,连接到多个网络,以分布式方式存储我们的数据,并执行许多其他任务。提供云虚拟机的服务示例包括 Paperspace、AWS 和 Azure
### 使用云虚拟机的优势
有了云虚拟机,我们可以用最佳优化的资源来构建我们的完美计算机。例如,如果我们需要一台具有高内存的计算机,我们可以简单地让云为我们完成这项工作。有了云虚拟机,我们可以用我们需要的资源来构建我们非常完美的计算机,并根据需要进行更改。
* **低成本**:在云中创建虚拟机比购买物理机更容易、更便宜。还记得吗,当我们想在 safari 上测试我们的应用程序时,我们去商店买了一台 1500 美元的电脑。借助云虚拟化,我们可以以更低的价格获得更多资源。
* **可扩展性**:我们可以根据负载轻松扩展我们的云虚拟机架构。在使用云虚拟机时,我们只为我们需要的东西付费。例如,让我们想象我们想要构建一个游戏软件。对于这个软件,我们将需要大量的 CPU 和 GPU。让我们想象我们不需要很多内存。我们可以购买一台拥有大量 CPU 和 GPU 的虚拟机,这样我们就能得到它。因此,通过这一点,我们可以获得优化的资源,并为我们需要的东西付费。如果我们需要更多这样的东西,我们可以很容易地支付更多的钱,获得更多的资源。
* 易维护性:还记得我们假设去商店买了那台 1500 美元的苹果电脑吗?购买后,我们需要对其进行配置。可想而知,买了这样一台电脑之后,要配置一台满足我们需求的电脑,压力会有多大。有了云虚拟机,设置和维护变得简单多了。这有助于我们快速开始。有了这一点,我们可以轻松地加速我们的增长,并让我们的软件快速上市。
* **灾难恢复**:借助云虚拟化,我们可以在将软件投入生产的同时高枕无忧。我们的云提供商以分布式方式提供容错能力。我们不需要软件的副本,以防出现故障或软件宕机。云为我们提供了这一点。
### 常规虚拟机和云虚拟机的区别
* **自动化**:在常规虚拟机中,我们需要与虚拟机管理程序交互来创建虚拟机。我们还需要自己管理我们的资源。所有这些都是通过使用云虚拟机来自动化的。云虚拟机不需要人类。它使用 API(应用程序可编程接口)与虚拟机管理程序进行通信。这减少了人工干预,使我们能够获得最好的资源。
* **可扩展性**:与云虚拟机相比,常规虚拟机的可扩展性不好。
* **灾难恢复**:云虚拟机依赖于多台机器,而常规虚拟机依赖于我们的外围设备。云虚拟机以分布式方式托管多个设备。这样做是为了容错。这样做的原因是为了防止多台机器中的一台损坏,另一台机器从那里拾取任务并运行它。因此,通过这一点,我们确信我们的软件将是容错的。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bf00bd20b66aeab88583d85b681ada8d.png)
* **灵活性**:与常规虚拟机相比,云虚拟机非常灵活
* **资源**:在常规虚拟机中,资源是共享的。假设我们有一台 8gb 内存的计算机。我们想在 Linux 机器上测试我们的应用程序,所以我们安装了一个虚拟机。如果我们的计算机使用 4gb 的 ram,并且它将 4gb 的 ram 分配给 Linux 虚拟机,我们将无法添加另一个虚拟机,因为没有更多的 ram 可供分配。因此,在常规虚拟机中,资源是从我们的外围设备共享的。然而,在云虚拟机中,情况并非如此。云虚拟机根据策略、需求和规模自动放置机器。
* **存储**:云虚拟机提供无限的存储,而常规虚拟机依赖于我们物理设备的存储
### 云虚拟机服务的示例
* 纸空间核心
* 亚马逊网络服务(AWS)
* 谷歌云
* 微软 Azure 虚拟机
* VMWare Horizon 云
* V2 云
* 腾讯云
* 阿里云
## 选择最佳云虚拟机服务的指南
### 可量测性
这是增加或减少 IT 资源以满足不断变化的需求的过程。使用云虚拟机服务的众多好处之一是可伸缩性。数据存储、网络、处理能力、计算资源等参数都可以扩展。使用云虚拟机服务,可以快速、轻松地完成扩展,不会出现中断或停机。原因是云提供商已经拥有了所有的基础设施。我们需要做的就是随时扩展。
云虚拟机的一个很好的优势是可伸缩性的速度。在物理虚拟机服务上扩展应用程序通常需要数周甚至数月时间。有两种缩放类型;垂直缩放和水平缩放。
垂直扩展(也称为向上/向下扩展)是增加或减少现有云 ram、存储或处理能力的过程。在本地计算中,这意味着购买额外的 ram 或额外的存储,并将其添加到我们的应用程序中。当内存或存储耗尽时,我们需要购买更高的内存并不断添加。
水平扩展(扩大/缩小)是向我们的系统添加资源(如服务器)以分散工作负载的过程。这进而提高了性能、处理能力和存储。当我们需要最小的停机时间时,水平扩展更为重要。
#### 我们如何知道何时扩展?
* 测试:知道我们的应用程序何时需要伸缩的方法之一是简单地测试它。通过测试,我们检查响应时间、用户数量、应用程序收到的请求数量、应用程序性能以及其他许多方面。通过测试应用程序,如果我们注意到加载时间太长或者执行一个查询需要很长时间,我们可能需要扩展它。同样,如果我们拥有的资源数量远远超过用户数量,我们就不能很好地利用资源,我们必须缩减我们的应用程序。
* 自动化:这有助于管理我们的扩展需求。自动化系统可以检测对更多资源的需求,并在需要时自动将它们拉进来。一些服务提供自动伸缩功能,根据负载的增加或减少,自动在[托管实例组(MIG)](https://cloud.google.com/compute/docs/instance-groups#managed_instance_groups) 中添加或删除虚拟机实例。这比直接测试更不可靠,但是可以节省时间。
### 监控工具
这是选择云虚拟机服务时要注意的另一件事。我们要选择的云虚拟机有监控工具吗?
首先,什么是监控工具?监控工具有助于监控、故障排除和提高应用程序性能。它自动捕获指标,并允许我们在应用程序中定义自定义指标。
它还允许我们执行分析、创建图表、查看相关性、创建自定义仪表板、创建警报等等。有很多云服务为我们的软件产品提供监控。一些例子是 Paperspace Gradient Notebook 的 Metrics window(用于深度学习)、Google Cloud LogicMonitor、Site 24X7 虚拟化监控、Veeam One、Quest Foglight、Amazon Cloudwatch、Microsoft Azure Monitor 等等。
在选择云虚拟化服务时,我们必须检查它是否支持监控。
### 数据准备工具
我们还应该看看我们想要选择的云虚拟机是否支持数据准备工具。数据准备是发现、丰富、预处理和转换数据的过程。我们的应用程序是数据驱动的吗?然后,我们需要看看我们的云服务是否支持数据准备工具。检查我们选择的服务是否可以访问这些工具来获取数据,以及是否有专门的数据存储区来存储数据。
### 深度学习支持
我们的应用需要深度学习支持吗?为此,我们需要寻找一种支持深度学习的云虚拟化服务。在深度学习支持方面,Paperspace Core 是一个非常高效的平台。借助 Core,我们可以使用他们的 ML-in-a-box 模板来创建虚拟机映像。这预装了 TensorFlow、PyTorch、Keras 等等。所有的 ML-in-a-box 实例都在 GPU 机器上运行。我们可以利用 Core 的内置可扩展性轻松升级我们的 GPU、vRAM 和存储,而无需担心软件兼容性。
### 可靠性
服务的可靠性对其成功至关重要。服务器端和客户端都有很多可能出错的地方,这会影响云虚拟机的成功运行。最好的云虚拟机服务将确保我们的机器在我们需要时始终可用,并且我们不会遇到像随机机器关闭这样的问题。
同样,各种类型的机器始终具有可靠的可用性也很重要。例如,由于高需求,具有强大 GPU(如 A100s 或 V100s)的流行实例在许多服务上经常不可用。像 Paperspace 这样的公司总是确保每种类型都有最大数量的机器可用,并且总是购买更多的硬件和升级来满足这种需求。
### 储存;储备
选择云虚拟化服务时,存储是我们最需要考虑的因素之一。在选择虚拟化服务时,我们需要一种能够在我们的虚拟机上提供高存储容量选项的服务。我们这样做是为了应对可能需要更多存储空间的情况,比如在扩展时。我们不希望出现因为虚拟化服务中存储不足而导致应用开发失败或中断的情况。
我们还寻求具有备份冗余的存储虚拟化服务。这意味着无论发生什么,我们的应用程序都能够提供服务的连续性。
在选择我们的存储虚拟化服务时,我们还希望减少成本节约和停机时间。我们不想因为我们选择的存储虚拟化服务而耗尽资金。
满足所有这些存储要求的良好虚拟化服务是 Paperspace Core。借助 Paperspace Core,我们获得了一个可变的高可用性存储系统,这是一种备份冗余存储服务,机器数据可在实例停机时保存,与竞争对手相比,存储成本更低,停机时间最短。
## 结论
在本文中,我们了解了物理虚拟机、云虚拟机、它们之间的区别、云虚拟机的优势,以及在选择云虚拟机服务时需要注意的一些准则。Paperspace 是一家云虚拟机公司,在可扩展性、容错性、对数据处理工具的支持、对深度学习的支持、存储和许多其他好处方面得分最高。
现在,我们已经看到了我们可以使用云虚拟机做的许多事情,并且可以自信地知道在为我们的应用程序选择最佳云虚拟机服务时应该注意什么。
感谢我们的阅读。
# 你好呀
> 原文:<https://blog.paperspace.com/hello-ycombinator/>
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4cd99f8ec5a53b7df065bd9ea24b53fd.png)
###### 我们很高兴地宣布,Paperspace 将加入 YCombinator 的 2015 年冬季系列!
我们有很多工作要做,但要知道,我们周围会有一些最聪明的人。期待山景城接下来的 3 个月!
特别感谢 Justin Kan、Garry Tan、Kat Menalac 和 Alexis Ohanian。
# 在云中托管 QuickBooks
> 原文:<https://blog.paperspace.com/hosting-quickbooks-in-the-cloud/>
QuickBooks 是超过 3000 万小型企业的首选会计软件。这仅仅发生在美国。最明显的趋势是越来越多的企业在云端托管 QuickBooks。在本教程中,我们将配置 QuickBooks Desktop 和一个在 Paperspace 上运行的公司数据文件,这一切只需几分钟。
# 为什么要在云端运行?
首先,你可能想知道在云中运行与在办公室或使用 QuickBooks Online 相比有什么好处。以下是公司转向托管解决方案的几个原因(请随意跳到教程):
## 随时随地访问
在云中运行应用程序的主要好处之一是,您不会被单一设备或复杂的 VPN 所束缚。Paperspace 提供了一个从任何配备 web 浏览器的设备进入 QuickbBooks 环境的门户。
## 多用户协作
与客户、合作伙伴和您的团队协作至关重要,在基于云的环境中添加或删除用户只需点击一下鼠标。QuickBooks Online 一次只允许一个人访问。增加更多的人需要升级,因此成本更高。使用托管解决方案,多达 30 人可以同时访问同一文档,无需额外费用。
## 降低 IT 成本
如果你在办公室运行 QuickBooks,你要支付昂贵的 IT 支持和服务器维护费用。Paperspace 为您处理基础设施的正常运行,完全消除了对本地 IT 成本的需求。此外,您只需为您使用的内容付费。
## 自动备份
Paperspace 提供连续的自动化备份。在界面中,您可以轻松设置备份计划和备份数量。只需单击一下,您就可以回滚到任何时间点。再也不用担心误删了。
## 可量测性
在云环境中运行的主要优势之一是,您只需为您需要的东西付费,并且只需点击一个按钮,就可以随时扩展您的资源。您可以轻松地动态扩展存储或 RAM 和 CPU。
## 安全性
最常见的数据泄露是针对内部网络的攻击。在 Paperspace,您的信息安全地存放在我们配备了最高安全标准的一流数据中心。安全措施包括 256 位数据加密、24/7 网络监控和自动威胁检测。
## 功能齐全的 QuickBooks 桌面
QuickBooks Desktop 比 Quickbooks Online 功能更加全面。更重要的是,您熟悉界面和功能。迁移到托管解决方案不需要学习新软件。
## 超高速固态硬盘存储
在我们经过微调的固态硬盘支持的存储上运行,将会大大提高办公室设置和在线 QuickBooks 的性能。
* * *
# 装置
## 先决条件
* 最少两台机器
* 一个[专用网络](https://paperspace.zendesk.com/hc/en-us/articles/216115938-Dedicated-Private-Networks)这样他们就可以互相交流了
让我们直接开始吧。在本次演示中,我们将使用多用户模式,这样您就可以大致了解我们如何设置公司数据文件以及将通过您的网络访问该数据文件的计算机。我首先创建了两台机器,分别叫做 QB1 和 QB2。QB1 将托管公司数据文件,我网络上的每个人都可以访问,QB2 将成为 QuickBooks 桌面应用程序机器。您可以根据需要添加任意数量的 QuickBooks 桌面计算机。
## 下载 QuickBooks
***5 分钟***
在本教程中,我们将使用 QuickBooks Desktop Pro 2016,并在 Window Server 2016 上运行。在 Intuit [支持](https://community.intuit.com/articles/1364927-system-requirements-for-quickbooks-desktop-2017-and-enterprise-solutions-17-0)的许多平台上,体验是非常相似的,所以如果您运行的是不同的版本,也不用担心。你可以通过[登录](https://camps.intuit.com/app/selfservice/index.html)到你的账户[下载](https://support.quickbooks.intuit.com/Support/ProductUpdates.aspx)并注册你的副本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a800253ec1aaccc5116adef590138903.png)
## 准备公司数据文件和网络共享
***4 分钟***
我们将从在 QB1 上运行 QuickBooks 安装程序开始。
小费
如果您担心出错,可以在安装过程中拍摄快照,以便回滚。
选择*自定义和网络选项*
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bc0f44ea6ffe63093844bb227620d4c4.png)
选择*我不会在这台电脑上使用 QuickBooks...*
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ecc16f1a9d4bb2329520fe66577ea428.png)
完成安装
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4e5d9c0560e8451e27a6d32f154e81af.png)
接下来,我们将设置公司数据文件。我将使用 QuickBooks 安装中提供的一个示例。该文件名为*sample _ service-based business . qbw*,尽管您很可能会带来一个现有的公司数据文件或创建一个新文件。
在 c:\下为数据文件创建一个名为 *QB* 的文件夹。确保数据文件被添加到 QB 文件夹。
右键单击 QB 文件夹,然后单击属性>共享给>特定的人
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ed25c55ff5689872319f24625441ae63.png)
在这个例子中,为了简单起见,我将选择每个人。在这一步,您可以随意设置更细粒度的权限。点击*添加*。
然后设置*读/写*权限,点击*分享*
我将通过右键单击 QB 文件夹,选择*共享*选项卡,找到该公司文件的网络路径。在*网络文件和文件夹共享*部分你会看到网络路径。在本例中,它使用我的主机名后跟文件夹名。注意:我们也可以使用在 Paperspace 控制台中可见的 IP 地址。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f7e2c0626cb5c00509ba6d600d64dd7e.png)
此时,我们可以跳转到 QB2,以确认我们可以从其他机器访问该公司数据文件。我们将把 QB 文件夹作为网络驱动器挂载到 QB2 上。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8ec5307fce4adbc7dc769ee00ed182fe.png)
它会提示您输入凭证。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d305031503214a2baf5a8769e4da96ed.png)
看起来我能够成功地与 QB1 沟通。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f8be469faeed8b5dbe6a9bf8f8b6d937.png)
## 安装 QuickBooks 桌面
***4 分钟***
下一步是在 QB2 上安装 QuickBooks Desktop。再次运行安装程序,只需选择快速安装选项或选择*我将在这台计算机上使用 QuickBooks*。
因为这是桌面版,我可以在最后一步直接进入 QuickBooks。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/07cbe07afe509b497ce9a3d89fcb1657.png)
最后,我们将在 QB2 上激活 QuickBooks(QB1 上不需要激活,因为它只存放公司文件)。
## 完成设置
我们需要验证公司数据文件可以从其他机器上打开。
在 QB2 上打开 QuickBooks 时,选择*打开或恢复现有公司*,点击*打开*。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e65daa48340ede5fcfd4f6d9d58ec590.png)
选择网络共享文件夹下的公司数据文件。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0d5023d9a90f076d0a6c437d9fa66c90.png)
好了,文件打开了!
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/33712caee0ea94c4f8a0b6230ab7ab34.png)
若要开始设置您自己的 Quickbooks,[请在此注册。](https://www.paperspace.com/account/signup?utm-campaign=quickbooksblog)
# 我如何用 VQGAN-CLIP 制作这篇文章的封面照片
> 原文:<https://blog.paperspace.com/how-i-made-this-articles-cover-photo-with-vqgan-clip/>
[https://www.youtube.com/embed/Z_9iGP9fsDk?feature=oembed](https://www.youtube.com/embed/Z_9iGP9fsDk?feature=oembed)
Video tutorial of VQGAN-CLIP. Be sure to go to advanced options and set your workspace url to [https://github.com/gradient-ai/ClipIt-PixelDraw.git](https://github.com/gradient-ai/ClipIt-PixelDraw.git) and your container name to paperspace/clip-pixeldraw:jupyter
使用深度学习来生成艺术或智能修改现有图像并不是一个新概念。像 Alexander Mordvintsev 的 DeepDream architecture 这样的著名项目已经存在了五年,该项目使用 CNN 的通过[算法](https://en.wikipedia.org/wiki/Algorithm) [pareidolia](https://en.wikipedia.org/wiki/Pareidolia) 对输入的图像进行变形,以发现并强化视觉数据中的模式,或者使用 [Pix2Pix](https://phillipi.github.io/pix2pix/) 对黑白绘图的轮廓进行填充和着色。这些技术有着巨大的潜力,安全、营销、购物和一般生活的用例都来自于上述突破。当计算机视觉和深度学习的进步的影响开始渗透到 ML 社区之外的商业世界时,艺术世界中 DL 技术的存在也变得越来越受欢迎。NFT 和虚拟影响者的出现,[人工智能产生了音乐](https://www.youtube.com/watch?v=sgdTHbgzLr8&t=49s)和艺术作品,以及更多提供了明确的证据,表明深度学习技术正在产生巨大的文化影响。
在本文中,我们将着眼于最有前途的图像生成器之一,VQGAN-CLIP,并了解它如何与 NLP 和 Gradient 一起使用,使用单一提示生成新颖的剪贴画。
### 什么是 VQGAN-CLIP?
简而言之,VQGAN-CLIP 是两种神经网络架构(VQGAN & CLIP)之间的交互,这两种架构协同工作,从文本提示中生成新颖的图像。这两个工具协同工作,生成并限定 PixRay 的像素艺术,VQGAN 生成图像,CLIP 评估图像与输入文本的对应程度。
> 我们的方法使用卷积 VQGAN 来学习上下文丰富的视觉部分的码本,其组成随后用自回归变换器架构建模。离散码本提供了这些架构之间的接口,基于补丁的鉴别器支持强压缩,同时保持高感知质量。该方法将卷积方法的效率引入到基于变换的高分辨率图像合成中。
[报价来源](https://compvis.github.io/taming-transformers/)
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/32defdde39c603b2c1b97ffe2916847c.png)
A visualization of the flow of VQGAN image processing with a CNN discriminator, as used in the original paper. [Source](https://compvis.github.io/taming-transformers/)
**VQGAN (** ***矢量量化生成对抗网络*** **)** : VQGAN 是一种 GAN 架构,可以用来学习并基于之前看到的数据生成新颖的图像。它最初是由埃塞尔、罗姆巴赫和奥姆尔在论文[《驯服变形金刚》(2021)中介绍的。其工作原理是首先将图像数据直接输入到 GAN,以对图像可视部分的特征图进行编码。然后,该图像数据被矢量量化:一种信号处理形式,将矢量分组编码成可由标记质心的代表矢量访问的簇,该代表矢量被称为“码字”。一旦编码,矢量量化数据被记录为码字字典,也称为码本。码本作为图像数据的中间表示,然后作为序列输入到变换器。然后,变换器被训练以将这些编码序列的合成建模为作为生成器的高分辨率图像。](https://compvis.github.io/taming-transformers/)
VQGAN 的主要创新——也是它如此令人兴奋的原因——是能够通过自回归码本编码序列将图像数据直接输入转换器。在实践中,变换器以自回归方式在从码本输入的量化记号序列上被训练:它学习如何基于先前记号的序列来预测下一个记号的分布。 [1](https://www.casualganpapers.com/vqvae-discrete-vision-transformer/VQGAN.html) 该步骤极大地降低了生成这种图像的预期成本,并且允许更快地处理图像数据。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/02b089271bf728bfe3402846d5c72374.png)
How a sliding-window works with image data [Source](https://www.researchgate.net/figure/Moving-Sliding-Window-Algorithm-diagram-using-3X3-pixel-area-The-algorithm-for-Mean_fig3_320564070)
转换器还通过滑动窗口将图像生成的上下文限制为像素的“补丁”。这允许转换器仅考虑补丁的本地上下文:在生成图像和提高资源效率时,仅“查看”相邻补丁的信息。
最后,转换器输出无条件生成的高分辨率图像,或者随着训练的迭代进行,如 VQGAN-CLIP go by,有条件地基于生成事件的上下文。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6fdb056ed44bca3ff4272684fa512469.png)
[Source](https://github.com/openai/CLIP)
**CLIP(对比语言-图像预训练)**:简单来说, [CLIP](https://arxiv.org/abs/2103.00020) 是一个经过训练的模型,用来评估一个字幕相对于一组中其他字幕对图像的适合度。CLIP 能够进行零触发学习,使其即使在未知数据上也能表现良好。当在 VQGAN-CLIP 中应用时,CLIP 能够与用户输入的字幕相比评估生成的图像的质量,并且输出的分数可以用作权重来“指导”VQGAN 的学习,以通过递归迭代更准确地匹配主题。²
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e3709204fb616e5ae9024e97029717d4.png)
An example image generated using PixelDraw on Gradient.
**组装起来**:
当一起使用时,VQGAN-CLIP 创建一系列模型,这些模型可用于从一串文本生成图像。通过使 VQGAN 首先生成随机噪声图像来创建这些图像,该随机噪声图像被矢量量化并在码本中编码,然后该码本被用作从编码信号生成新图像的变换器的输入,然后该输出被用于评估图像对通过 CLIP 输入的提示的准确性,然后该得分被发送回 VQGAN 以更新图像生成模型来更接近地反映提示。
## PixelDraw 是什么?
[渐变笔记本](https://console.paperspace.com/signup?gradient=true)【**注**:分叉附送笔记本目前无法工作。按照文章顶部的视频说明来创建您自己的图像,当问题解决后,我们将删除此注释]我们用来创建图像的脚本由用户 Dribnet 领导的项目团队编写,用于开源项目 [PixRay](https://github.com/dribnet/pixray) 。PixRay 于 2021 年 9 月推出,是一个使用 VQGAN-CLIP、感知引擎、CLIPDraw 和采样生成网络从用户提交的文本提示中创建新颖像素艺术的库。您可以在这里访问该项目的 GitHub,也可以在这里访问该项目的公开演示笔记本。这个笔记本设置为使用 clip it(PixRay 的前身)和 PixRay 的组件脚本 PixelDraw 生成像素艺术。PixelDraw 专门用来生成 PixelArt。
## 笔记本
### 环境和要求
PixRay 经过优化,可以在 GPU 环境中运行,您必须在本地机器上安装一个,或者远程访问一个。如果您可以在本地访问 GPU,那么您可以使用 Cog 通过 Docker 运行 PixRay。
我们在这个项目中使用的笔记本电脑运行在 P6000 GPU 上,训练和图像生成大约需要 10 分钟。我们建议您按照本文顶部视频中的说明来设置自己的笔记本。**注意:**请务必进入高级选项,将您的工作区 url 设置为 https://github.com/gradient-ai/ClipIt-PixelDraw.git,将您的容器名称设置为 paper space/clip-pixeldraw:jupyter。
要在 Gradient 上访问此项目,[单击此链接。](https://console.paperspace.com/signup?gradient=true)
### 代码:
```py
git clone https://github.com/openai/CLIP
git clone https://github.com/CompVis/taming-transformers.git
git clone https://github.com/dribnet/clipit
```
首先,我们需要安装相关的库。为了让 PixelDraw 正常工作,这意味着 CLIP、clipit 和驯服变形金刚。转到您的终端,输入上面的代码片段。然后打开“pixeldraw.py”文件。
```py
#Setup Paths
import sys
sys.path.append("clipit")
sys.path.append("/clipit")
sys.path.append("CLIP")
sys.path.append("/CLIP")
sys.path.append("diffvg")
sys.path.append("/diffvg")
sys.path.append("taming-transformers")
sys.path.append("/taming-transformers")
```
当设置 pixeldraw.py 脚本本身时,我们必须首先设置环境中库的相关路径。完成初始设置后,我们可以开始对 PixelDrawer.py 脚本本身进行设置。以下是我们使用的一些设置
```py
# Settings: these are variables that can be used to adjust the output of the model
# prompts: the sentence the model will try to interpret to produce an image
prompts = "Humans use Machine Learning to make art #pixelart" #@param {type:"string"}
# aspect: the shape/size of the outputted .png
aspect = "widescreen" #@param ["widescreen", "square"]
# use_pixeldraw: set to True to ensure ClipIt uses PixelDraw rather than other options, clipdraw or vqgandraw
use_pixeldraw = True #@param {type:"boolean"}
```
* 其中最重要的是**提示**。该提示是一个文本字符串,它将指导我们生成图像,因为 CLIP 使用该提示作为测试标题来评估生成图像的质量。
* 接下来要考虑的设置是**方面**。长宽比用于决定我们生成的图像的形状,可以设置为“宽屏”,表示宽的矩形图像,也可以设置为方形,表示图像在两个维度上的大小相等。
* 最后,对于这个例子,我们希望确保使用正确的绘图脚本,所以我们将 **use_pixeldraw** 设置为等于 True。其他选项是 clipdraw 和 vqgandraw,它们将从 PixelDrawer.py 生成类似但最终不同的图像。
我们可以应用的其他设置包括**质量**和**比例**,它们分别影响图像的分辨率和大小。**迭代**影响训练迭代的次数,而 **display_every** 告诉 ipython 小部件在 *n* 训练周期结束时显示图像。
既然我们已经分配了设置,那么就需要实例化 clipit 并随后应用设置。
```py
# Import clipit to access relevant functions
import clipit
# reset_settings() ensures we are using defaults
clipit.reset_settings()
# add_settings creates a dictionary of the settings we defined above, and make other adjustments to output
clipit.add_settings(prompts=prompts, aspect=aspect)
clipit.add_settings(quality="better", scale=2.5)
clipit.add_settings(use_pixeldraw=use_pixeldraw)
# Use iterations and display_every params to adjust training time and information display throughout
# This is the example of how to run longer with less frequent display
clipit.add_settings(iterations=300, display_every=50)
# apply_settings() to implement the settings from the add_settings() dict
settings = clipit.apply_settings()
# Run and create the image
clipit.do_init(settings)
clipit.do_run(settings)
```
为了将设置应用到 clipit,我们首先导入库。
* 然后,我们首先使用 **reset_settings()** 方法来确保没有预设参数,并且我们使用的是默认参数。
* 接下来,我们使用 **add_settings()** 将我们的设置输入到 clipit 中。这将准备在下一步中完全应用的设置。
* 然后,我们使用 **apply_settings()** 创建一个字典,其中包含我们在训练运行期间要应用的设置。
* 最后,我们可以通过连续输入 **clipit.do_init(settings)** 和 **clipit.do_run(settings)** 来运行模型并生成图像,以使用我们的用户输入设置来运行 clipit。
要实际运行这个脚本,只需将它输入到笔记本或您的终端中。如果成功完成,图像生成训练应该开始,并运行输入的训练迭代次数。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/37c739af2fba2c2887c6af3176196ca1.png)
An example of pixeldraw running for 300 iterations to generate this articles cover photo.
该图像将输出为 500 x 280 像素的 png 文件,并将作为“output.png”直接保存到您的文件中
## 总结发言
从一个看似平凡的来源创造出新颖艺术的能力永远是一件有趣的事情。今天,我们看了用户如何只用 GPU 和文本输入就能生成像素艺术。这种技术的使用案例几乎是无限的,但是我将列出一些:
* 自动化内容生成(营销和设计):未来的团队将能够使用简单的文本输入为他们的内容生成有趣的新作品。这可以为品牌和艺术资产创造节省数百个小时。
* 自动内容生成(艺术家):这也延伸到创意,因为艺术家可以利用人工智能艺术生成作为一种新的工具。不能低估文本到图像技术的创造潜力。
* 自动照片处理:用户可以选择性地选择输入的照片。如果起始照片已经具有显著的设计/图案而不是随机噪声,则该照片可以用作照片处理的指导模板。理论上,一旦这一点得到充分优化,我们可以使用类似的架构对现有图像进行适当的编辑,而不是手动进行。例如,如果我们给模型一个小丑的图像,并带有文本提示“没有帽子的小丑”,我们可以预期模型会尝试摘掉那些帽子。不过这还有很长的路要走。
感谢您阅读 PixRay 的 PixelDrawer 使用指南!期待更多像这样的教程和我们的新指南,看看我们如何在渐变上创建 [Adoro 应用程序。](https://blog.paperspace.com/adoro-deepfake-your-selfie-first-order-motion-model/)
## 视频的代码片段
**进口**
```py
!pip install ftfy regex tqdm omegaconf pytorch-lightning
!pip install kornia
!pip install imageio-ffmpeg
!pip install einops
!pip install torch-optimizer
!pip install easydict
!pip install braceexpand
!pip install git+https://github.com/pvigier/perlin-numpy
```
### 学分:
1: @casual_gan,“24: VQGAN 解释”。[https://www . casualganpapers . com/vqvae-discrete-vision-transformer/vqgan . html](https://www.casualganpapers.com/vqvae-discrete-vision-transformer/VQGAN.html)
2: [亚历克·拉德福德](https://openai.com/blog/authors/alec/),[伊利亚·苏茨基弗](https://openai.com/blog/authors/ilya/),[琼·金旭](https://openai.com/blog/authors/jong/),[格雷琴·克鲁格](https://openai.com/blog/authors/gretchen/),[桑迪尼·阿加瓦尔](https://openai.com/blog/authors/sandhini/),《剪辑:连接文字和图像》[https://openai.com/blog/clip/](https://openai.com/blog/clip/)
# 如何在 Keras 中建立一个可变的自动编码器
> 原文:<https://blog.paperspace.com/how-to-build-variational-autoencoder-keras/>
本教程介绍了变分自动编码器(VAE)神经网络,它如何不同于典型的自动编码器,以及它的好处。然后,我们将在 Keras 中构建一个可以编码和解码图像的 VAE。
本教程的大纲如下:
* 变分自动编码器简介
* 构建编码器
* 构建解码器
* 建造 VAE
* 训练 VAE
* 看一看代码
* 测试模型
您可以在 [ML Showcase](https://ml-showcase.paperspace.com/projects/build-an-autoencoder-from-scratch-in-keras) 上跟随完整的代码。
## 变分自动编码器简介
自动编码器是一种卷积神经网络(CNN ),它将高维输入转换为低维输入(即潜在向量),然后以可能的最高质量重建原始输入。它由两个相连的 CNN 组成。第一个是编码器网络,它接受原始数据作为输入,并返回一个向量。这个向量然后被馈送到第二 CNN,即重建原始数据的解码器。关于这个架构的更多信息,你可以查看 Keras 中使用自动编码器的教程[图像压缩,也在 Paperspace 博客上。](https://blog.paperspace.com/autoencoder-image-compression-keras)
自动编码器的一个问题是,它们独立于其他样本对数据的每个样本进行编码,即使它们来自同一类。但是如果两个样本属于同一类,则编码版本之间应该存在某种关系。形式化这个讨论,我们可以说这个问题是由于自动编码器没有遵循预定义的分布来编码数据。
换句话说,假设我们有两个来自同一类的样本,`S1`和`S2`。每个样本将被独立编码,这样`S1`被编码成`E1`,而`S2`被编码成`E2`。当`E1`和`E2`被解码时,不能保证重构的样本是相似的,因为每个样本都被独立处理。
这使得事情变得模棱两可。数据编码后,每个样本被编码成一个向量。假设向量长度为 2,那么向量中每个元素的取值范围是多少?是从-1 到 1 吗?还是 0 到 5?还是 100 到 200?答案是未知的,因为没有任何东西迫使向量在某个范围内。例如,您可能会训练编码器网络,发现范围是-10 到 20。另一次可能会变成-15 到 12。您将不得不探索编码的数据来推断向量的值的范围。
下图显示了使用自动编码器压缩的 MNIST 样本的潜在向量(更多细节请看本教程)。范围几乎是从-2.5 到 15.0。当然,当网络再次被训练时,它可能会改变,尤其是当参数改变时。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1d47223c7fe4f5a82f32262815eb0627.png)
*variable*auto encoder 通过创建代表数据的定义分布来解决这个问题。VAEs 试图使分布尽可能接近标准的正态分布,正态分布以 0 为中心。因此,当您从分布中选择一个随机样本进行解码时,您至少知道它的值大约为 0。下图显示了使用 VAE 的编码样本是如何分布的。如你所见,样本以 0 为中心。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/79f33f2e819afbae5fff9723399d6ceb.png)
因为正态分布的特征是基于平均值和方差,所以变分自动编码器计算每个样本的平均值和方差,并确保它们遵循标准的正态分布(因此样本以 0 为中心)。有两层用于计算每个样本的平均值和方差。因此,在较高的层面上,您可以想象该架构如下图所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a777ca768d3cd74ef285a676d99a0205.png)
(关于 VAEs 的更多信息,我推荐这本书:[生成性深度学习:教机器画画、写字、作曲、演奏](https://www.amazon.com/Generative-Deep-Learning-Teaching-Machines/dp/1492041947)大卫·福斯特著。)
现在我们已经有了 VAE 的概述,让我们使用 Keras 来构建编码器。
## **构建编码器**
在本教程中,我们将重点关注如何在 Keras 中构建 VAE,因此我们将坚持使用基本的 MNIST 数据集,以避免从代码中分心。当然,您可以很容易地将它换成您自己感兴趣的数据。
该数据集中的图像是二进制的,宽度和高度为 28 像素。结果,单个图像大小为`28x28x1`。请注意,图像中只有一个通道。下一行创建两个变量来保存图像中通道的大小和数量。
```py
img_size = 28
num_channels = 1
```
根据下一行,我们将使用这些变量来创建编码器的输入层。
```py
x = tensorflow.keras.layers.Input(shape=(img_size, img_size, num_channels), name="encoder_input")
```
输入层之后是以下三层的许多组合:
1. 2D 卷积
2. 批量标准化
3. 李奇注意到了
下面是创建这些层的第一个组合的代码。
```py
encoder_conv_layer1 = tensorflow.keras.layers.Conv2D(filters=1, kernel_size=(3, 3), padding="same", strides=1, name="encoder_conv_1")
encoder_norm_layer1 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_1")(encoder_conv_layer1)
encoder_activ_layer1 = tensorflow.keras.layers.LeakyReLU(name="encoder_leakyrelu_1")(encoder_norm_layer1)
```
下一个代码块创建 code 诺姆-雷卢层的其他组合。
```py
encoder_conv_layer2 = tensorflow.keras.layers.Conv2D(filters=32, kernel_size=(3,3), padding="same", strides=1, name="encoder_conv_2")(encoder_activ_layer1)
encoder_norm_layer2 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_2")(encoder_conv_layer2)
encoder_activ_layer2 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_2")(encoder_norm_layer2)
encoder_conv_layer3 = tensorflow.keras.layers.Conv2D(filters=64, kernel_size=(3,3), padding="same", strides=2, name="encoder_conv_3")(encoder_activ_layer2)
encoder_norm_layer3 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_3")(encoder_conv_layer3)
encoder_activ_layer3 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_3")(encoder_norm_layer3)
encoder_conv_layer4 = tensorflow.keras.layers.Conv2D(filters=64, kernel_size=(3,3), padding="same", strides=2, name="encoder_conv_4")(encoder_activ_layer3)
encoder_norm_layer4 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_4")(encoder_conv_layer4)
encoder_activ_layer4 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_4")(encoder_norm_layer4)
encoder_conv_layer5 = tensorflow.keras.layers.Conv2D(filters=64, kernel_size=(3,3), padding="same", strides=1, name="encoder_conv_5")(encoder_activ_layer4)
encoder_norm_layer5 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_5")(encoder_conv_layer5)
encoder_activ_layer5 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_5")(encoder_norm_layer5)
```
之前架构中最后一层的输出大小为`(7, 7, 64)`。因此,图像大小从`28x28`减少到只有`7x7`,通道数量现在是`64`。由于 VAE 将多维数据转换为矢量,因此必须使用密集图层将输出转换为 1D 矢量(如下所示)。`shape_before_flatten`变量的目的是在结果被展平之前保持其形状,以便成功解码结果。
```py
shape_before_flatten = tensorflow.keras.backend.int_shape(encoder_activ_layer5)[1:]
encoder_flatten = tensorflow.keras.layers.Flatten()(encoder_activ_layer5)
```
在常规自动编码器中,将数据转换为矢量标志着编码器的结束。这不是 VAE 的情况。原因是 VAE 创建了一个标准的正态分布来表示数据。为此,必须考虑分布的参数(平均值和方差)。以下代码为这两个参数创建了两个层。
```py
latent_space_dim = 2
encoder_mu = tensorflow.keras.layers.Dense(units=latent_space_dim, name="encoder_mu")(encoder_flatten)
encoder_log_variance = tensorflow.keras.layers.Dense(units=latent_space_dim, name="encoder_log_variance")(encoder_flatten)
```
`Dense`类构造函数中的`units`参数设置为等于`latent_space_dim`,是设置为`2`的变量,代表潜在向量的长度。如果你想创建一个任意长度的潜在向量,只需为`latent_space_dim`变量指定合适的值。
使用这两层,`encoder_mu`和`encoder_log_variance`,正态分布被随机采样以返回编码器的输出。下一行为此创建了一个自定义的`Lambda`层。有关 Keras 中 Lambda 层的更多信息,请查看教程[在 Keras](https://blog.paperspace.com/working-with-the-lambda-layer-in-keras/) 中使用 Lambda 层。
```py
encoder_output = tensorflow.keras.layers.Lambda(sampling, name="encoder_output")([encoder_mu, encoder_log_variance])
```
Lambda 层调用一个名为`sampling()`的函数,实现如下。
```py
def sampling(mu_log_variance):
mu, log_variance = mu_log_variance
epsilon = tensorflow.keras.backend.random_normal(shape=tensorflow.keras.backend.shape(mu), mean=0.0, stddev=1.0)
random_sample = mu + tensorflow.keras.backend.exp(log_variance/2) * epsilon
return random_sample
```
现在,从输入层到输出层的编码器架构已经完成。剩下的最后一步是根据下一行创建一个模型,将编码器的输入层与输出层关联起来。
```py
encoder = tensorflow.keras.models.Model(x, encoder_output, name="encoder_model")
```
这是构建 VAE 编码器的完整代码。
```py
# Encoder
x = tensorflow.keras.layers.Input(shape=(img_size, img_size, num_channels), name="encoder_input")
encoder_conv_layer1 = tensorflow.keras.layers.Conv2D(filters=1, kernel_size=(3, 3), padding="same", strides=1, name="encoder_conv_1")(x)
encoder_norm_layer1 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_1")(encoder_conv_layer1)
encoder_activ_layer1 = tensorflow.keras.layers.LeakyReLU(name="encoder_leakyrelu_1")(encoder_norm_layer1)
encoder_conv_layer2 = tensorflow.keras.layers.Conv2D(filters=32, kernel_size=(3,3), padding="same", strides=1, name="encoder_conv_2")(encoder_activ_layer1)
encoder_norm_layer2 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_2")(encoder_conv_layer2)
encoder_activ_layer2 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_2")(encoder_norm_layer2)
encoder_conv_layer3 = tensorflow.keras.layers.Conv2D(filters=64, kernel_size=(3,3), padding="same", strides=2, name="encoder_conv_3")(encoder_activ_layer2)
encoder_norm_layer3 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_3")(encoder_conv_layer3)
encoder_activ_layer3 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_3")(encoder_norm_layer3)
encoder_conv_layer4 = tensorflow.keras.layers.Conv2D(filters=64, kernel_size=(3,3), padding="same", strides=2, name="encoder_conv_4")(encoder_activ_layer3)
encoder_norm_layer4 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_4")(encoder_conv_layer4)
encoder_activ_layer4 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_4")(encoder_norm_layer4)
encoder_conv_layer5 = tensorflow.keras.layers.Conv2D(filters=64, kernel_size=(3,3), padding="same", strides=1, name="encoder_conv_5")(encoder_activ_layer4)
encoder_norm_layer5 = tensorflow.keras.layers.BatchNormalization(name="encoder_norm_5")(encoder_conv_layer5)
encoder_activ_layer5 = tensorflow.keras.layers.LeakyReLU(name="encoder_activ_layer_5")(encoder_norm_layer5)
shape_before_flatten = tensorflow.keras.backend.int_shape(encoder_activ_layer5)[1:]
encoder_flatten = tensorflow.keras.layers.Flatten()(encoder_activ_layer5)
encoder_mu = tensorflow.keras.layers.Dense(units=latent_space_dim, name="encoder_mu")(encoder_flatten)
encoder_log_variance = tensorflow.keras.layers.Dense(units=latent_space_dim, name="encoder_log_variance")(encoder_flatten)
encoder_mu_log_variance_model = tensorflow.keras.models.Model(x, (encoder_mu, encoder_log_variance), name="encoder_mu_log_variance_model")
def sampling(mu_log_variance):
mu, log_variance = mu_log_variance
epsilon = tensorflow.keras.backend.random_normal(shape=tensorflow.keras.backend.shape(mu), mean=0.0, stddev=1.0)
random_sample = mu + tensorflow.keras.backend.exp(log_variance/2) * epsilon
return random_sample
encoder_output = tensorflow.keras.layers.Lambda(sampling, name="encoder_output")([encoder_mu, encoder_log_variance])
encoder = tensorflow.keras.models.Model(x, encoder_output, name="encoder_model")
```
为了总结编码器的架构,发出了`encoder.summary()`命令。下面是结果。共有`105,680`个可训练参数。输出的大小是`(None, 2)`,这意味着它为每个输入返回一个长度为 2 的向量。注意,展平图层之前的图层形状是`(7, 7, 64)`,这是保存在`shape_before_flatten`变量中的值。
请注意,您可以自由地更改层中的参数值,并添加或删除层来定制应用程序的模型。
```py
_________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
=========================================================================================
encoder_input (InputLayer) [(None, 28, 28, 1)] 0
_________________________________________________________________________________________
encoder_conv_1 (Conv2D) (None, 28, 28, 1) 10 encoder_input[0][0]
_________________________________________________________________________________________
encoder_norm_1 (BatchNormalizat (None, 28, 28, 1) 4 encoder_conv_1[0][0]
_________________________________________________________________________________________
encoder_leakyrelu_1 (LeakyReLU) (None, 28, 28, 1) 0 encoder_norm_1[0][0]
_________________________________________________________________________________________
encoder_conv_2 (Conv2D) (None, 28, 28, 32) 320 encoder_leakyrelu_1[0][0]
_________________________________________________________________________________________
encoder_norm_2 (BatchNormalizat (None, 28, 28, 32) 128 encoder_conv_2[0][0]
_________________________________________________________________________________________
encoder_activ_layer_2 (LeakyReL (None, 28, 28, 32) 0 encoder_norm_2[0][0]
_________________________________________________________________________________________
encoder_conv_3 (Conv2D) (None, 14, 14, 64) 18496 encoder_activ_layer_2[0][0]
_________________________________________________________________________________________
encoder_norm_3 (BatchNormalizat (None, 14, 14, 64) 256 encoder_conv_3[0][0]
_________________________________________________________________________________________
encoder_activ_layer_3 (LeakyReL (None, 14, 14, 64) 0 encoder_norm_3[0][0]
_________________________________________________________________________________________
encoder_conv_4 (Conv2D) (None, 7, 7, 64) 36928 encoder_activ_layer_3[0][0]
_________________________________________________________________________________________
encoder_norm_4 (BatchNormalizat (None, 7, 7, 64) 256 encoder_conv_4[0][0]
_________________________________________________________________________________________
encoder_activ_layer_4 (LeakyReL (None, 7, 7, 64) 0 encoder_norm_4[0][0]
_________________________________________________________________________________________
encoder_conv_5 (Conv2D) (None, 7, 7, 64) 36928 encoder_activ_layer_4[0][0]
_________________________________________________________________________________________
encoder_norm_5 (BatchNormalizat (None, 7, 7, 64) 256 encoder_conv_5[0][0]
_________________________________________________________________________________________
encoder_activ_layer_5 (LeakyReL (None, 7, 7, 64) 0 encoder_norm_5[0][0]
_________________________________________________________________________________________
flatten (Flatten) (None, 3136) 0 encoder_activ_layer_5[0][0]
_________________________________________________________________________________________
encoder_mu (Dense) (None, 2) 6274 flatten[0][0]
_________________________________________________________________________________________
encoder_log_variance (Dense) (None, 2) 6274 flatten[0][0]
_________________________________________________________________________________________
encoder_output (Lambda) (None, 2) 0 encoder_mu[0][0]
encoder_log_variance[0][0]
=========================================================================================
Total params: 106,130
Trainable params: 105,680
Non-trainable params: 450
_________________________________________________________________________________________
```
现在我们已经建立了编码器网络,让我们建立我们的解码器。
## **构建解码器**
在前面的部分中,编码器接受了一个形状为`(28, 28)`的输入,并返回了一个长度为`2`的向量。在这一部分,解码器应该做相反的事情:接受一个长度为`2`的输入向量,并返回一个形状为`(28, 28)`的结果。
第一步是创建一个保存输入的层,如下所示。请注意,输入的形状被设置为等于`latent_space_dim`,该值之前被赋值为`2`。这意味着解码器的输入将是一个长度为`2`的向量。
```py
decoder_input = tensorflow.keras.layers.Input(shape=(latent_space_dim), name="decoder_input")
```
下一步是创建一个`dense`层,将矢量的长度从`2`扩展到在`shape_before_flatten`变量中指定的值,即`(7, 7, 64)`。`numpy.prod()`用于将三个值相乘并返回一个值。现在密层的形状是`(None, 3136)`。
```py
decoder_dense_layer1 = tensorflow.keras.layers.Dense(units=numpy.prod(shape_before_flatten), name="decoder_dense_1")(decoder_input)
```
下一步是使用`Reshape`层将结果从矢量重塑为矩阵。
```py
decoder_reshape = tensorflow.keras.layers.Reshape(target_shape=shape_before_flatten)(decoder_dense_layer1)
```
之后,我们可以添加一些层来扩展形状,直到达到原始输入的期望形状。
```py
decoder_conv_tran_layer1 = tensorflow.keras.layers.Conv2DTranspose(filters=64, kernel_size=(3, 3), padding="same", strides=1, name="decoder_conv_tran_1")(decoder_reshape)
decoder_norm_layer1 = tensorflow.keras.layers.BatchNormalization(name="decoder_norm_1")(decoder_conv_tran_layer1)
decoder_activ_layer1 = tensorflow.keras.layers.LeakyReLU(name="decoder_leakyrelu_1")(decoder_norm_layer1)
decoder_conv_tran_layer2 = tensorflow.keras.layers.Conv2DTranspose(filters=64, kernel_size=(3, 3), padding="same", strides=2, name="decoder_conv_tran_2")(decoder_activ_layer1)
decoder_norm_layer2 = tensorflow.keras.layers.BatchNormalization(name="decoder_norm_2")(decoder_conv_tran_layer2)
decoder_activ_layer2 = tensorflow.keras.layers.LeakyReLU(name="decoder_leakyrelu_2")(decoder_norm_layer2)
decoder_conv_tran_layer3 = tensorflow.keras.layers.Conv2DTranspose(filters=64, kernel_size=(3, 3), padding="same", strides=2, name="decoder_conv_tran_3")(decoder_activ_layer2)
decoder_norm_layer3 = tensorflow.keras.layers.BatchNormalization(name="decoder_norm_3")(decoder_conv_tran_layer3)
decoder_activ_layer3 = tensorflow.keras.layers.LeakyReLU(name="decoder_leakyrelu_3")(decoder_norm_layer3)
decoder_conv_tran_layer4 = tensorflow.keras.layers.Conv2DTranspose(filters=1, kernel_size=(3, 3), padding="same", strides=1, name="decoder_conv_tran_4")(decoder_activ_layer3)
decoder_output = tensorflow.keras.layers.LeakyReLU(name="decoder_output")(decoder_conv_tran_layer4 )
```
现在,解码器的架构已经完成。剩下的步骤是创建一个连接解码器输入和输出的模型。
```py
decoder = tensorflow.keras.models.Model(decoder_input, decoder_output, name="decoder_model")
```
下面是解码器模型的总结。
```py
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
decoder_input (InputLayer) [(None, 2)] 0
_________________________________________________________________
decoder_dense_1 (Dense) (None, 3136) 9408
_________________________________________________________________
reshape (Reshape) (None, 7, 7, 64) 0
_________________________________________________________________
decoder_conv_tran_1 (Conv2DT (None, 7, 7, 64) 36928
_________________________________________________________________
decoder_norm_1 (BatchNormali (None, 7, 7, 64) 256
_________________________________________________________________
decoder_leakyrelu_1 (LeakyRe (None, 7, 7, 64) 0
_________________________________________________________________
decoder_conv_tran_2 (Conv2DT (None, 14, 14, 64) 36928
_________________________________________________________________
decoder_norm_2 (BatchNormali (None, 14, 14, 64) 256
_________________________________________________________________
decoder_leakyrelu_2 (LeakyRe (None, 14, 14, 64) 0
_________________________________________________________________
decoder_conv_tran_3 (Conv2DT (None, 28, 28, 64) 36928
_________________________________________________________________
decoder_norm_3 (BatchNormali (None, 28, 28, 64) 256
_________________________________________________________________
decoder_leakyrelu_3 (LeakyRe (None, 28, 28, 64) 0
_________________________________________________________________
decoder_conv_tran_4 (Conv2DT (None, 28, 28, 1) 577
_________________________________________________________________
decoder_output (LeakyReLU) (None, 28, 28, 1) 0
=================================================================
Total params: 121,537
Trainable params: 121,153
Non-trainable params: 384
_________________________________________________________________
```
在构建了编码器和解码器的架构之后,剩下的步骤是创建完整的 VAE。
## **建造 VAE**
在前两节中,为编码器和解码器创建了两个独立的模型。在本节中,我们将构建第三个模型,将两者结合起来。
要回答的一个问题是:如果我们已经有了编码器和解码器,为什么还要为 VAE 建立一个模型?原因是我们需要同时训练编码器和解码器,当模型彼此分离时,我们无法这样做。
完成编码器和解码器的架构后,构建 VAE 的架构就非常简单了。我们需要做的就是创建一个输入层来表示 VAE 的输入(与编码器的输入相同)。
```py
vae_input = tensorflow.keras.layers.Input(shape=(img_size, img_size, num_channels), name="VAE_input")
```
然后,VAE 输入层连接到编码器,对输入进行编码并返回潜在向量。
```py
vae_encoder_output = encoder(vae_input)
```
编码器的输出然后连接到解码器以重构输入。
```py
vae_decoder_output = decoder(vae_encoder_output)
```
最后,建立连接编码器和解码器的 VAE 模型。
```py
vae = tensorflow.keras.models.Model(vae_input, vae_decoder_output, name="VAE")
```
这是 VAE 模式的总结。
```py
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
VAE_input (InputLayer) [(None, 28, 28, 1)] 0
_________________________________________________________________
encoder_model (Model) (None, 2) 106130
_________________________________________________________________
decoder_model (Model) (None, 28, 28, 1) 121537
=================================================================
Total params: 227,667
Trainable params: 226,833
Non-trainable params: 834
_________________________________________________________________
```
在开始训练之前,VAE 模型编译如下。
```py
vae.compile(optimizer=tensorflow.keras.optimizers.Adam(lr=0.0005), loss=loss_func(encoder_mu, encoder_log_variance))
```
损失函数的实现如下所示。
```py
def loss_func(encoder_mu, encoder_log_variance):
def vae_reconstruction_loss(y_true, y_predict):
reconstruction_loss_factor = 1000
reconstruction_loss = tensorflow.keras.backend.mean(tensorflow.keras.backend.square(y_true-y_predict), axis=[1, 2, 3])
return reconstruction_loss_factor * reconstruction_loss
def vae_kl_loss(encoder_mu, encoder_log_variance):
kl_loss = -0.5 * tensorflow.keras.backend.sum(1.0 + encoder_log_variance - tensorflow.keras.backend.square(encoder_mu) - tensorflow.keras.backend.exp(encoder_log_variance), axis=1)
return kl_loss
def vae_kl_loss_metric(y_true, y_predict):
kl_loss = -0.5 * tensorflow.keras.backend.sum(1.0 + encoder_log_variance - tensorflow.keras.backend.square(encoder_mu) - tensorflow.keras.backend.exp(encoder_log_variance), axis=1)
return kl_loss
def vae_loss(y_true, y_predict):
reconstruction_loss = vae_reconstruction_loss(y_true, y_predict)
kl_loss = vae_kl_loss(y_true, y_predict)
loss = reconstruction_loss + kl_loss
return loss
return vae_loss
```
现在我们准备训练我们的模型。
## **训练 VAE**
在训练之前,必须加载数据。幸运的是,可以根据这个命令`tensorflow.keras.datasets.mnist.load_data()`使用 Keras 加载 MNIST 数据集。以下是如何加载训练和测试数据。
```py
(x_train, y_train), (x_test, y_test) = tensorflow.keras.datasets.mnist.load_data()
x_train = x_train.astype("float32") / 255.0 x_test = x_test.astype("float32") / 255.0
```
数据集中每张图像的形状只是`(28, 28)`,模型期望的形状是`(28, 28, 1)`。为此,训练和测试数据的形状改变如下:
```py
x_train = numpy.reshape(x_train, newshape=(x_train.shape[0], x_train.shape[1], x_train.shape[2], 1))
x_test = numpy.reshape(x_test, newshape=(x_test.shape[0], x_train.shape[1], x_train.shape[2], 1))
```
终于,VAE 的训练可以开始了。您可以自由更改分配给`epochs`和`batch_size`参数的值。
```py
vae.fit(x_train, x_train, epochs=20, batch_size=32, shuffle=True, validation_data=(x_test, x_test))
```
模型训练完成后,我们可以保存三个模型(编码器、解码器和 VAE)供以后使用。
```py
encoder.save("VAE_encoder.h5")
decoder.save("VAE_decoder.h5")
vae.save("VAE.h5")
```
下图显示了使用 VAE 编码器编码后样本的潜在空间。样本现在以`0`为中心,这意味着 VAE 能够使用正态分布来表示编码样本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cf23b6ef249d4b06ab5f5e3a2b8bc243.png)
## **完整代码**
在 ML Showcase 上免费运行构建和训练 VAE[的完整代码。](https://ml-showcase.paperspace.com/projects/build-an-autoencoder-from-scratch-in-keras)
## **测试 VAE**
为了测试 VAE,我们可以根据下面两行代码加载编码器和解码器模型。
```py
encoder = tensorflow.keras.models.load_model("VAE_encoder.h5")
decoder = tensorflow.keras.models.load_model("VAE_decoder.h5")
```
我们还必须确保数据已经加载。
```py
(x_train, y_train), (x_test, y_test) = tensorflow.keras.datasets.mnist.load_data()
x_test = x_test.astype("float32") / 255.0
x_test = numpy.reshape(x_test, newshape=(x_test.shape[0], x_train.shape[1], x_train.shape[2], 1))
```
现在,我们准备编码和解码测试数据。
```py
encoded_data = encoder.predict(x_test)
decoded_data = decoder.predict(encoded_data)
```
以下是使用 VAE 重建的一些图像的摘要。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5c73e82118f2dd3f44502f46e31f80f6.png)
## **总结**
本教程介绍了 variable auto encoder,这是一种卷积神经网络,用于将数据从高维空间转换到低维空间,然后进行重构。
与普通自动编码器相比,VAE 的优势在于它将数据的分布建模为以 0 为中心的标准正态分布。
使用 Keras,我们实现了一个 VAE 来压缩 MNIST 数据集的图像。编码器、解码器和 VAE 的模型被保存以供以后加载用于测试目的。如有任何意见或问题,请在评论中联系我们。
# 如何开始使用 Agisoft Photoscan
> 原文:<https://blog.paperspace.com/how-to-get-started-with-agisoft-photoscan/>
## 什么是 Photoscan?
Agisoft Photoscan 是一种摄影测量解决方案,广泛应用于建筑行业,用于从现有场地、建筑内部和外部生成 3D 模型。借助 Paperspace 强大的 GPU 和 Photoscan 的 GPU 加速工作流,大型图像数据集的处理可以在几小时内完成,而不是几天。本演练将涵盖安装和许可证转移。
想要更多曝光?
如果您希望您的摄影测量工作流程以 Paperspace 为特色,请给我们发电子邮件至 hello@paperspace.com,或发推特给我们,包括标签#PoweredByPaperspace
## **教程大纲**
* [发动机器](#launch)
1. 机器建议
* [安装照片扫描仪](#install)
1. 下载并安装 Photoscan
* [转让现有许可](#transfer)
* 激活
1. 钝化作用
* [结论](#conclusion)
### **1。**创造一种新的纸张空间机器
登录到 Paperspace 后,创建一台新机器。
* 选择最近的*区域*
* 选择 Windows 或 Linux *模板*
* 选择*每月*或*每小时*,这取决于您希望如何使用 Photoscan。然而,摄影测量工作流可能在每小时的基础上更具成本效益。
* 选择你的*计划*:
* **摄影测量(高性能):**为了利用 Photoscan 的 GPU 加速,需要一台*专用的 GPU* 机器( *GPU+* 、 *P5000* 、 *P6000* )。 *GPU+* 非常适用于较小的对象或图像集,而 *P5000* 和 *P6000* 则专为最大性能而设计,即大型图像数据集。
* 选择您的*存储* —您可以在未来随时增加存储,但我们强烈建议您使用 1TB 或更大容量来管理大型数据集。
* 点击*创建*
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cba185511daf620e96b0dfdcef961853.png)
### **2。**安装 Agisoft Photoscan
![Download](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3f5a35aef24909cfe2245a469aaa4d3e.png)
**步骤 1 -下载安装程序**
[下载链接](http://www.agisoft.com/downloads/installer/)
**步骤 2 -运行安装程序**
**步骤 3 -打开 Photoscan 并激活许可证**
![Activate](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b3d538f445da2e1aa8ec8ac96375e9a7.png)
**第 4 步-照片扫描现在可以处理了!**
Photoscan 提供了示例数据,您可以使用这些数据开始工作。你可以在这里下载。
[样本数据链接](http://www.agisoft.com/downloads/sample-data/)
### **3。**将 Agisoft Photoscan 的本地许可证转移到 Paperspace
如果您已经拥有 Photoscan 的许可证,并希望将其转移到 Paperspace,您的节点锁定许可证(单机版或教育版)可以通过 Photoscan 的停用/激活过程从本地计算机移动到 Paperspace。
要在初始机器上停用 Agisoft PhotoScan,请导航至:
* 帮助菜单->激活产品命令->停用按钮。
停用许可证后,请登录到您的 Paperspace 计算机,并从以下位置运行激活过程:
* 帮助菜单->激活产品命令->输入许可证密钥->按激活产品按钮
也可以从命令行(终端)使用- activate 或- deactivate 参数来激活和停用,例如(在 Windows 上):
`photoscan.exe --activate 11111-22222-33333-44444-55555`
相同的许可证对 Windows 或 Linux 有效。
注意:卸载 Agisoft PhotoScan 不会自动停用许可证。如果您在停用之前卸载了软件,请重新安装 Agisoft PhotoScan 并运行停用程序,如上所述。
### **4。**结论
仅在几分钟内,我们就能够启动并运行 Photoscan。摄影测量是各行各业的重要工作流程,我们很高兴为强大的基础设施提供一个简单的界面,可以为更多人开启这些工作流程。
您可以从他们的[用户手册中了解更多关于 Agisoft Photoscan 和图像捕捉最佳实践的信息。](http://www.agisoft.com/downloads/user-manuals/)
对于 GPU 加速,请参考我们的帖子,其中包括关于 Paperspace 硬件的基准测试。
尽情享受吧!
要用 Photoscan 建立自己的摄影测量工作室,请在此注册。
我们需要你的帮助!
我们正在寻找专注于 IT 工作流和机器学习的内容作家、爱好者和研究人员,以帮助建立我们的社区。给 hello@paperspace.com 发电子邮件,附上写作范例和教学想法
# 如何在 PyTorch 中从头开始实现 YOLO (v3)对象检测器:第 1 部分
> 原文:<https://blog.paperspace.com/how-to-implement-a-yolo-object-detector-in-pytorch/>
图片来源:凯罗尔·马杰克。查看他的 YOLO v3 实时检测视频[这里](https://www.youtube.com/watch?v=8jfscFuP9k)
对象检测是一个从深度学习的最新发展中受益匪浅的领域。近年来,人们开发了许多目标检测算法,其中包括 YOLO,SSD,掩模 RCNN 和 RetinaNet。
对象检测是一个从深度学习的最新发展中受益匪浅的领域。近年来,人们开发了许多目标检测算法,其中包括 YOLO,SSD,掩模 RCNN 和 RetinaNet。
在过去的几个月里,我一直在一个研究实验室致力于改进物体检测。从这次经历中最大的收获之一是认识到学习物体检测的最好方法是自己从头开始实现算法。这正是我们在本教程中要做的。
我们将使用 PyTorch 来实现一个基于 YOLO v3 的对象检测器,这是目前最快的对象检测算法之一。
本教程的代码旨在运行在 Python 3.5 和 PyTorch **0.4** 上。在这个 [Github repo](https://github.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch) 可以找到它的全部内容。
本教程分为 5 个部分:
1. 第一部分(这一部分):理解 YOLO 是如何工作的
2. [第 2 部分:创建网络架构的各层](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-2/)
3. [第三部分:实现网络的前向传递](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-3/)
4. [第 4 部分:客观分数阈值和非最大值抑制](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-4/)
5. [第五部分:设计输入和输出管道](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-5/)
#### 先决条件
* 你应该明白卷积神经网络是如何工作的。这还包括残差块、跳过连接和上采样的知识。
* 什么是物体检测,包围盒回归,IoU 和非最大值抑制。
* PyTorch 的基本用法。您应该能够轻松创建简单的神经网络。
我在文章末尾提供了链接,以防你在任何方面有所欠缺。
#### 什么是 YOLO?
YOLO 代表你只看一眼。这是一个对象检测器,使用深度卷积神经网络学习的特征来检测对象。在我们接触代码之前,我们必须了解 YOLO 是如何工作的。
#### 全卷积神经网络
YOLO 仅利用卷积层,使其成为完全卷积网络(FCN)。它有 75 个卷积层,带有跳跃连接和上采样层。不使用任何形式的池,使用步长为 2 的卷积层对特征图进行下采样。这有助于防止通常归因于池化的低级特性的丢失。
作为 FCN,YOLO 对于输入图像的大小是不变的。然而,在实践中,我们可能希望坚持一个恒定的输入大小,因为各种问题只有在我们实现算法时才会显露出来。
这些问题中的一个大问题是,如果我们想要批量处理我们的图像(批量图像可以由 GPU 并行处理,从而提高速度),我们需要所有图像的高度和宽度都固定。这需要将多个图像连接成一个大批量(将许多 PyTorch 张量连接成一个)
网络通过称为网络的**步距**的因子对图像进行下采样。例如,如果网络的跨距是 32,那么大小为 416×416 的输入图像将产生大小为 13×13 的输出。一般情况下,网络中任一层的 ***步距*等于该层输出小于网络输入图像的因子。**
#### 解释输出
通常,(对于所有对象检测器来说都是如此)卷积层学习的特征被传递到分类器/回归器上,该分类器/回归器进行检测预测(边界框的坐标,类别标签..等等)。
在 YOLO,预测是通过使用 1 x 1 卷积的卷积层来完成的。
现在,首先要注意的是我们的**输出是一个特征图**。因为我们已经使用了 1×1 卷积,所以预测图的大小正好是它之前的特征图的大小。在 YOLO v3(及其后代)中,解释这个预测图的方式是每个单元可以预测固定数量的边界框。
> 虽然在特征图中描述一个单元的技术上正确的术语应该是一个*神经元*,但在我们的上下文中称它为细胞会更直观。
在深度方面,我们在特征图中有(B x (5 + C))个条目。 B 代表每个单元格可以预测的包围盒数量。根据这篇论文,这些 B 包围盒中的每一个都可能专门用于检测某一种对象。每个包围盒具有 *5 + C* 属性,这些属性描述了每个包围盒的中心坐标、尺寸、客观分数和 *C* 类置信度。YOLO v3 预测每个单元有 3 个边界框。
如果对象的中心落在特征图的每个单元的感受野内,你期望该单元通过它的一个边界框来预测对象。(感受野是细胞可见的输入图像区域。请参考卷积神经网络上的链接以获得进一步的说明)。
这与 YOLO 的训练方式有关,只有一个边界框负责检测任何给定的对象。首先,我们必须确定这个边界框属于哪个单元。
为此,我们将**输入**图像划分为一个网格,其尺寸等于最终特征图的尺寸。
让我们考虑下面的例子,其中输入图像是 416×416,网络的跨距是 32。如前所述,特征图的尺寸将是 13×13。然后,我们将输入图像分成 13×13 个单元。
![yolo-5](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8c0057d5335fa9f881d70482170f6afc.png)
然后,选择包含对象的地面真值框的中心的单元(*在输入图像*上)作为负责预测对象的单元。在图像中,是标记为红色的单元格,它包含地面真实框的中心(标记为黄色)。
现在,红色单元格是网格中第 7 行的第 7 个单元格。我们现在将特征图上第 7 行**的第 7 个单元(特征图上对应的单元)指定为负责检测狗的单元。**
现在,这个单元可以预测三个边界框。哪一个会被分配到狗的地面真相标签?为了理解这一点,我们必须先了解锚的概念。
> *注意,我们这里说的单元格是预测特征图上的单元格。我们将**输入图像**分成网格,只是为了确定**预测特征图**的哪个单元负责预测*
#### 锚箱
预测边界框的宽度和高度可能是有意义的,但在实践中,这会导致训练过程中不稳定的梯度。相反,大多数现代对象检测器预测对数空间变换,或者简单地偏移到预定义的默认边界框,称为**锚**。
然后,将这些变换应用于锚盒以获得预测。YOLO v3 具有三个锚,这导致每个单元三个边界框的预测。
回到我们之前的问题,负责检测狗的边界框将是其锚点与地面真实框的 IoU 最高的那个。
#### 做预测
以下公式描述了如何转换网络输出以获得边界框预测。
![YOLO Equations](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e289a333ec5b258865fdfe26bf2f7f99.png)
*bx,by,bw,bh* 是我们预测的 x,y 中心坐标,宽度和高度。 *tx,ty,tw,th* 是网络输出的。 *cx* 和 *cy* 是网格左上角的坐标。 *pw* 和 *ph* 为母扣的锚尺寸。
###### 中心坐标
请注意,我们正在通过 sigmoid 函数运行我们的中心坐标预测。这将强制输出值介于 0 和 1 之间。为什么会这样呢?请原谅我。
通常,YOLO 不会预测边界框中心的绝对坐标。它预测以下偏移量:
* 相对于预测对象的网格单元的左上角。
* 根据特征图中像元的尺寸进行归一化,即 1。
例如,考虑我们的狗图像的情况。如果中心的预测是(0.4,0.7),那么这意味着中心位于 13×13 特征图上的(6.4,6.7)。(因为红色单元格的左上坐标是(6,6))。
但是等等,如果预测的 x,y 坐标大于 1,比如说(1.2,0.7),会发生什么?这意味着中心位于(7.2,6.7)。请注意,中心现在位于我们红色单元格右侧的单元格中,即第 7 行的第 8 个单元格。这打破了 YOLO 背后的理论,因为如果我们假设红盒子负责预测狗,狗的中心一定在红盒子里,而不是在它旁边的那个。
因此,为了解决这个问题,输出通过一个 sigmoid 函数,该函数在 0 到 1 的范围内压缩输出,有效地将中心保持在正在预测的网格中。
###### 边界框的尺寸
通过对输出应用对数空间变换,然后乘以锚点,预测边界框的尺寸。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/03bf22eecaa530eb1eeca50c8e76f07b.png)
*如何对探测器输出进行转换以给出最终预测。图像学分。[http://christopher5106.github.io/](http://christopher5106.github.io/)*
得到的预测值 *bw* 和 *bh* ,通过图像的高度和宽度归一化。(训练标签就是这样选择的)。所以,如果包含狗的盒子的预测 *bx* 和 *by* 是(0.3,0.8),那么 13 x 13 特征图上的实际宽度和高度是(13 x 0.3,13 x 0.8)。
###### 客观性分数
对象分数表示对象包含在边界框内的概率。对于红色和相邻的网格,它应该接近于 1,而对于例如角落处的网格,它应该接近于 0。
客观性分数也通过 s 形线传递,因为它被解释为概率。
###### 班级信心
类别置信度表示检测到的对象属于特定类别(狗、猫、香蕉、汽车等)的概率。在 v3 之前,YOLO 习惯于降低班级分数。
然而,v3 放弃了这种设计选择,作者选择使用 sigmoid 来代替。原因是 Softmaxing 类分数假设类是互斥的。简单地说,如果一个对象属于一个类,那么它肯定不能属于另一个类。这对于我们的探测器所基于的 COCO 数据库来说是真实的。
然而,当我们有像*女人*和*人*这样的职业时,这个假设可能不成立。这就是作者避免使用 Softmax 激活的原因。
#### 不同尺度的预测。
YOLO v3 在 3 个不同的尺度上进行预测。检测层用于在三种不同尺寸的特征图上进行检测,分别具有**步距 32、16、8** 。这意味着,使用 416 x 416 的输入,我们可以在 13 x 13、26 x 26 和 52 x 52 的比例上进行检测。
网络对输入图像进行下采样,直到第一检测层,在第一检测层,使用步长为 32 的层的特征图进行检测。此外,层以因子 2 被上采样,并且与具有相同特征地图大小的先前层的特征地图连接。现在在步长为 16 的层进行另一次检测。重复相同的上采样过程,并且在步长 8 的层进行最终检测。
在每个比例下,每个单元使用 3 个锚点预测 3 个边界框,使得使用的锚点总数为 9。(不同音阶的锚点不同)
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2f2e5b26f9b0b03ff43fd8eebe3fd82f.png)
作者报告说,这有助于 YOLO v3 更好地检测小物体,这是早期版本的 YOLO 经常抱怨的问题。上采样可以帮助网络学习有助于检测小对象的细粒度特征。
#### 输出处理
对于尺寸为 416 x 416 的图像,YOLO 预测((52 x 52)+(26 x 26)+13 x 13))x 3 =**10647 个边界框**。然而,在我们的图像中,只有一个物体,一只狗。我们如何将检测从 10647 减少到 1?
###### 通过对象置信度设定阈值
首先,我们根据它们的客观性分数过滤盒子。通常,分数低于阈值的盒子被忽略。
###### 非最大抑制
NMS 打算解决同一图像多次检测的问题。例如,红色网格单元的所有 3 个边界框可能检测到一个框,或者相邻的单元可能检测到相同的对象。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b98bbebed7b6b3886094716c258cfd09.png)
如果你不知道 NMS,我提供了一个解释这个问题的网站链接。
#### 我们的实施
YOLO 只能检测属于用于训练网络的数据集中存在的类的对象。我们将为我们的探测器使用官方重量文件。这些权重是通过在 COCO 数据集上训练网络而获得的,因此我们可以检测 80 个对象类别。
第一部分到此为止。这篇文章充分解释了 YOLO 算法,使你能够实现检测器。然而,如果你想深入了解 YOLO 是如何工作的,它是如何被训练的,以及它与其他探测器相比表现如何,你可以阅读原始论文,我在下面提供了链接。
这部分到此为止。在下一个[部分](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-2/)中,我们实现了组装探测器所需的各种层。
#### 进一步阅读
1. [YOLO·V1:你只看一次:统一的、实时的物体检测](https://arxiv.org/pdf/1506.02640.pdf)
2. YOLO V2: YOLO9000:更好、更快、更强
3. [YOLO V3:增量改进](https://pjreddie.com/media/files/papers/YOLOv3.pdf)
4. [卷积神经网络](http://cs231n.github.io/convolutional-networks/)
5. [包围盒回归(附录 C)](https://arxiv.org/pdf/1311.2524.pdf)
6. [借据](https://www.youtube.com/watch?v=DNEm4fJ-rto)
7. [非最大抑制](https://www.youtube.com/watch?v=A46HZGR5fMw)
8. [PyTorch 官方教程](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)
Ayoosh Kathuria 目前是印度国防研究与发展组织的实习生,他致力于改进粒状视频中的物体检测。当他不工作的时候,他不是在睡觉就是在用吉他弹奏平克·弗洛伊德。你可以在 [LinkedIn](https://www.linkedin.com/in/ayoosh-kathuria-44a319132/) 上和他联系,或者在[GitHub](https://github.com/ayooshkathuria)T5 上看看他做了些什么
# 如何在 PyTorch 中从头开始实现 YOLO (v3)对象检测器:第 2 部分
> 原文:<https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-2/>
图片来源:凯罗尔·马杰克。查看他的 YOLO v3 实时检测视频[这里](https://www.youtube.com/watch?v=8jfscFuP9k)
这是从头开始实现 YOLO v3 检测器教程的第 2 部分。在最后一部分,我解释了 YOLO 是如何工作的,在这一部分,我们将在 PyTorch 中实现 YOLO 使用的层。换句话说,这是我们创建模型的构建模块的部分。
本教程的代码旨在运行在 Python 3.5 和 PyTorch **0.4** 上。在这个 [Github repo](https://github.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch) 可以找到它的全部内容。
本教程分为 5 个部分:
1. 第一部分:了解 YOLO 是如何运作的
2. 第 2 部分(这一部分):创建网络体系结构的各层
3. [第三部分:实现网络的前向传递](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-3/)
4. [第 4 部分:目标置信度阈值和非最大值抑制](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-4/)
5. [第五部分:设计输入和输出管道](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-5/)
#### 先决条件
* 教程的第一部分/YOLO 如何工作的知识。
* PyTorch 的基本工作知识,包括如何使用`nn.Module`、`nn.Sequential`和`torch.nn.parameter`类创建定制架构。
我想你以前对 PyTorch 有过一些经验。如果你刚刚开始,我建议你在回到这篇文章之前先尝试一下这个框架。
#### 入门指南
首先创建一个目录,用于存放检测器的代码。
然后,创建一个文件`darknet.py`。 **Darknet 是 YOLO** 底层架构的名字。该文件将包含创建 YOLO 网络的代码。我们将用一个名为`util.py`的文件来补充它,该文件将包含各种辅助函数的代码。将这两个文件保存在检测器文件夹中。您可以使用 git 来跟踪更改。
#### 配置文件
官方代码(用 C 编写)使用一个配置文件来构建网络。 *cfg* 文件逐块描述了网络的布局。如果你来自咖啡馆背景,它相当于用来描述网络的`.protxt`文件。
我们将使用作者发布的官方 *cfg* 文件来构建我们的网络。从[这里](https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg)下载它,并把它放在探测器目录下一个名为`cfg`的文件夹中。如果您使用的是 Linux,`cd`进入您的网络目录并键入:
```py
mkdir cfg
cd cfg
wget https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg
```
如果您打开配置文件,您会看到类似这样的内容。
```py
[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
```
我们看到上面有 4 个街区。其中,3 层描述卷积层,随后是一个*快捷方式*层。一个*快捷方式*层是一个跳过连接,就像 ResNet 中使用的那样。YOLO 使用 5 种类型的图层:
**卷积**
```py
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
```
**快捷方式**
```py
[shortcut]
from=-3
activation=linear
```
一个*快捷方式*层是一个跳过连接,类似于 ResNet 中使用的那个。`from`参数为`-3`,表示快捷层的输出是由*快捷层*向后**加上前一层和第三层的**特征图得到的。
**上采样**
```py
[upsample]
stride=2
```
使用双线性上采样以因子`stride`对前一层中的特征地图进行上采样。
**路线**
```py
[route]
layers = -4
[route]
layers = -1, 61
```
*路线*层值得解释一下。它有一个属性`layers`,可以有一个或两个值。
当`layers`属性只有一个值时,它输出由该值索引的图层的要素地图。在我们的示例中,它是-4,因此该图层将从第四层开始从*路线*图层向后输出要素地图。
当`layers`有两个值时,它返回按其值索引的图层的连接要素地图。在我们的示例中,它是-1,61,该图层将输出前一图层(-1)和第 61 个图层的要素地图,沿深度维度连接。
**YOLO**
```py
[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1
```
YOLO 层对应于第 1 部分中描述的检测层。`anchors`描述了 9 个锚,但是仅使用由`mask`标签的属性索引的锚。在这里,`mask`的值是 0,1,2,这意味着使用第一、第二和第三锚。这是有意义的,因为检测层的每个单元预测 3 个盒子。总的来说,我们有 3 个比例的探测层,总共有 9 个锚。
**网**
```py
[net]
# Testing
batch=1
subdivisions=1
# Training
# batch=64
# subdivisions=16
width= 320
height = 320
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
```
cfg 中还有另一种称为`net`的块,但我不会称之为层,因为它只描述关于网络输入和训练参数的信息。在 YOLO 的向前传球中没有使用它。然而,它确实为我们提供了像网络输入大小这样的信息,我们用它来调整前向传递中的锚。
#### 解析配置文件
在我们开始之前,在`darknet.py`文件的顶部添加必要的导入。
```py
from __future__ import division
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
```
我们定义了一个名为`parse_cfg`的函数,它将配置文件的路径作为输入。
```py
def parse_cfg(cfgfile):
"""
Takes a configuration file
Returns a list of blocks. Each blocks describes a block in the neural
network to be built. Block is represented as a dictionary in the list
"""
```
这里的想法是解析 cfg,**将每个块存储为一个字典**。块的属性和它们的值作为键值对存储在字典中。当我们解析 cfg 时,我们不断地将这些字典(在代码中用变量`block`表示)添加到一个列表`blocks`中。我们的函数将返回这个块。
我们首先将 cfg 文件的内容保存在一个字符串列表中。下面的代码对这个列表执行一些预处理。
```py
file = open(cfgfile, 'r')
lines = file.read().split('\n') # store the lines in a list
lines = [x for x in lines if len(x) > 0] # get read of the empty lines
lines = [x for x in lines if x[0] != '#'] # get rid of comments
lines = [x.rstrip().lstrip() for x in lines] # get rid of fringe whitespaces
```
然后,我们对结果列表进行循环以获得块。
```py
block = {}
blocks = []
for line in lines:
if line[0] == "[": # This marks the start of a new block
if len(block) != 0: # If block is not empty, implies it is storing values of previous block.
blocks.append(block) # add it the blocks list
block = {} # re-init the block
block["type"] = line[1:-1].rstrip()
else:
key,value = line.split("=")
block[key.rstrip()] = value.lstrip()
blocks.append(block)
return blocks
```
#### 创建构建模块
现在我们将使用上面的`parse_cfg`返回的列表为配置文件中的块构建 PyTorch 模块。
我们在列表中有 5 种类型的层(如上所述)。PyTorch 为类型`convolutional`和`upsample`提供了预构建的层。我们将不得不通过扩展`nn.Module`类来为其余的层编写我们自己的模块。
`create_modules`函数获取一个由`parse_cfg`函数返回的列表`blocks`。
```py
def create_modules(blocks):
net_info = blocks[0] #Captures the information about the input and pre-processing
module_list = nn.ModuleList()
prev_filters = 3
output_filters = []
```
在我们迭代块列表之前,我们定义一个变量`net_info`来存储关于网络的信息。
###### nn。模块列表
我们的函数将返回一个`nn.ModuleList`。这个类几乎就像一个包含`nn.Module`对象的普通列表。然而,当我们将`nn.ModuleList`添加为`nn.Module`对象的成员时(即当我们将模块添加到我们的网络中时),`nn.ModuleList`内的`nn.Module`对象(模块)的所有`parameter`也被添加为`nn.Module`对象的`parameter`(即我们的网络,我们将`nn.ModuleList`添加为其成员)。
当我们定义一个新的卷积层时,我们必须定义它的核的维数。虽然内核的高度和宽度是由 cfg 文件提供的,但内核的深度恰恰是前一层中存在的过滤器的数量(或特征映射的深度)。这意味着我们需要**跟踪应用卷积层的层中的滤波器数量**。我们使用变量`prev_filter`来做这件事。我们将其初始化为 3,因为图像有 3 个对应于 RGB 通道的滤镜。
路径图层从以前的图层中引入(可能串联)要素地图。如果在路径图层的正前方有一个卷积图层,则内核将应用于之前图层的要素地图,准确地说是路径图层带来的要素地图。因此,我们不仅需要跟踪前一层中的过滤器数量,还需要跟踪前一层中的每个的数量。当我们迭代时,我们将每个块的输出过滤器的数量添加到列表`output_filters`中。
现在,我们的想法是遍历块列表,并为每个块创建一个 PyTorch 模块。
```py
for index, x in enumerate(blocks[1:]):
module = nn.Sequential()
#check the type of block
#create a new module for the block
#append to module_list
```
`nn.Sequential`类用于顺序执行多个`nn.Module`对象。如果您查看 cfg,您会发现一个块可能包含多个层。例如,`convolutional`类型的块除了卷积层之外,还有批量范数层以及泄漏 ReLU 激活层。我们使用`nn.Sequential`将这些层串在一起,这就是`add_module`函数。例如,我们就是这样创建卷积层和上采样层的。
```py
if (x["type"] == "convolutional"):
#Get the info about the layer
activation = x["activation"]
try:
batch_normalize = int(x["batch_normalize"])
bias = False
except:
batch_normalize = 0
bias = True
filters= int(x["filters"])
padding = int(x["pad"])
kernel_size = int(x["size"])
stride = int(x["stride"])
if padding:
pad = (kernel_size - 1) // 2
else:
pad = 0
#Add the convolutional layer
conv = nn.Conv2d(prev_filters, filters, kernel_size, stride, pad, bias = bias)
module.add_module("conv_{0}".format(index), conv)
#Add the Batch Norm Layer
if batch_normalize:
bn = nn.BatchNorm2d(filters)
module.add_module("batch_norm_{0}".format(index), bn)
#Check the activation.
#It is either Linear or a Leaky ReLU for YOLO
if activation == "leaky":
activn = nn.LeakyReLU(0.1, inplace = True)
module.add_module("leaky_{0}".format(index), activn)
#If it's an upsampling layer
#We use Bilinear2dUpsampling
elif (x["type"] == "upsample"):
stride = int(x["stride"])
upsample = nn.Upsample(scale_factor = 2, mode = "bilinear")
module.add_module("upsample_{}".format(index), upsample)
```
###### 路径层/快捷方式层
接下来,我们编写代码来创建*路线*和*快捷方式*层。
```py
#If it is a route layer
elif (x["type"] == "route"):
x["layers"] = x["layers"].split(',')
#Start of a route
start = int(x["layers"][0])
#end, if there exists one.
try:
end = int(x["layers"][1])
except:
end = 0
#Positive anotation
if start > 0:
start = start - index
if end > 0:
end = end - index
route = EmptyLayer()
module.add_module("route_{0}".format(index), route)
if end < 0:
filters = output_filters[index + start] + output_filters[index + end]
else:
filters= output_filters[index + start]
#shortcut corresponds to skip connection
elif x["type"] == "shortcut":
shortcut = EmptyLayer()
module.add_module("shortcut_{}".format(index), shortcut)
```
创建路径层的代码值得好好解释一下。首先,我们提取`layers`属性的值,将其转换为整数并存储在一个列表中。
然后,我们有一个新的层称为`EmptyLayer`,顾名思义,这只是一个空层。
```py
route = EmptyLayer()
```
它被定义为。
```py
class EmptyLayer(nn.Module):
def __init__(self):
super(EmptyLayer, self).__init__()
```
###### 等等,一个空层?
现在,一个空层可能看起来很奇怪,因为它什么也不做。路由层就像任何其他层一样执行操作(向前移动上一层/连接)。在 PyTorch 中,当我们定义一个新的层时,我们子类化`nn.Module`并在`nn.Module`对象的`forward`函数中编写该层执行的操作。
为了设计路由块的层,我们必须构建一个用属性`layers`的值初始化的`nn.Module`对象作为它的成员。然后,我们可以编写代码在`forward`函数中连接/提出特征地图。最后,我们在网络的第`forward`功能中执行这一层。
但是考虑到连接的代码相当短且简单(在特性图上调用`torch.cat`),如上设计一个层将导致不必要的抽象,这只会增加锅炉板代码。相反,我们可以做的是放置一个虚拟层来代替建议的路由层,然后在代表暗网的`nn.Module`对象的`forward`函数中直接执行连接。(如果最后一行对你来说没有太大意义,建议你去看看`nn.Module`类在 PyTorch 中是如何使用的。底部链接)
路径层前面的卷积层将其内核应用于(可能连接)来自前一层的要素地图。以下代码更新了变量`filters`以保存路径图层输出的过滤器数量。
```py
if end < 0:
#If we are concatenating maps
filters = output_filters[index + start] + output_filters[index + end]
else:
filters= output_filters[index + start]
```
快捷层也利用空层,因为它也执行非常简单的操作(加法)。不需要更新变量`filters`,因为它只是将前一层的特征图添加到后一层的特征图中。
###### YOLO 层
最后,我们编写创建 *YOLO* 层的代码。
```py
#Yolo is the detection layer
elif x["type"] == "yolo":
mask = x["mask"].split(",")
mask = [int(x) for x in mask]
anchors = x["anchors"].split(",")
anchors = [int(a) for a in anchors]
anchors = [(anchors[i], anchors[i+1]) for i in range(0, len(anchors),2)]
anchors = [anchors[i] for i in mask]
detection = DetectionLayer(anchors)
module.add_module("Detection_{}".format(index), detection)
```
我们定义了一个新的层`DetectionLayer`,它包含了用来检测边界框的锚点。
检测层定义为
```py
class DetectionLayer(nn.Module):
def __init__(self, anchors):
super(DetectionLayer, self).__init__()
self.anchors = anchors
```
在循环的最后,我们做一些簿记工作。
```py
module_list.append(module)
prev_filters = filters
output_filters.append(filters)
```
这就结束了循环体。在函数`create_modules`的末尾,我们返回一个包含`net_info`和`module_list`的元组。
```py
return (net_info, module_list)
```
#### 测试代码
您可以通过在`darknet.py`的末尾键入以下几行并运行该文件来测试您的代码。
```py
blocks = parse_cfg("cfg/yolov3.cfg")
print(create_modules(blocks))
```
您将看到一个很长的列表,(正好包含 106 个条目),其元素如下所示
```py
.
.
(9): Sequential(
(conv_9): Conv2d (128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(batch_norm_9): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True)
(leaky_9): LeakyReLU(0.1, inplace)
)
(10): Sequential(
(conv_10): Conv2d (64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(batch_norm_10): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True)
(leaky_10): LeakyReLU(0.1, inplace)
)
(11): Sequential(
(shortcut_11): EmptyLayer(
)
)
.
.
.
```
这部分到此为止。在接下来的[部分](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-3/)中,我们将组装已经创建的构建模块,以从图像中产生输出。
#### 进一步阅读
1. [PyTorch 教程](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)
2. [nn。模块,nn。参数类别](http://pytorch.org/tutorials/beginner/blitz/neural_networks_tutorial.html#define-the-network)
3. [nn。ModuleList 和 nn。顺序](https://discuss.pytorch.org/t/when-should-i-use-nn-modulelist-and-when-should-i-use-nn-sequential/5463)
Ayoosh Kathuria 目前在印度国防研究与发展组织实习,致力于改进粒状视频中的物体检测。当他不工作的时候,他不是在睡觉就是在用吉他弹奏平克·弗洛伊德。你可以在 [LinkedIn](https://www.linkedin.com/in/ayoosh-kathuria-44a319132/) 上和他联系,或者在[GitHub](https://github.com/ayooshkathuria)T5 上看看他做了些什么
# 如何在 PyTorch 中从头开始实现 YOLO (v3)对象检测器:第 3 部分
> 原文:<https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-3/>
图片来源:凯罗尔·马杰克。查看他的 YOLO v3 实时检测视频[这里](https://www.youtube.com/watch?v=8jfscFuP9k)
这是从头开始实现 YOLO v3 检测器教程的第 3 部分。在最后一部分中,我们实现了 YOLO 架构中使用的层,在这一部分中,我们将在 PyTorch 中实现 YOLO 的网络架构,这样我们就可以在给定图像的情况下产生一个**输出**。
我们的目标是设计网络的前向通路。
本教程的代码旨在运行在 Python 3.5 和 PyTorch **0.4** 上。在这个 [Github repo](https://github.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch) 可以找到它的全部内容。
本教程分为 5 个部分:
1. 第一部分:了解 YOLO 是如何运作的
2. [第 2 部分:创建网络架构的各层](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-2/)
3. 第 3 部分(这一部分):实现网络的前向传递
4. 第 4 部分:[目标置信度阈值和非最大值抑制](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-4/)
5. [第五部分:设计输入和输出管道](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-5/)
#### 先决条件
* 教程的第 1 部分和第 2 部分。
* PyTorch 的基本工作知识,包括如何使用`nn.Module`、`nn.Sequential`和`torch.nn.parameter`类创建定制架构。
* 在 PyTorch 中处理图像
#### 定义网络
正如我之前指出的,我们使用`nn.Module`类在 PyTorch 中构建定制架构。让我们为检测器定义一个网络。在`darknet.py`文件中,我们添加了下面的类。
```py
class Darknet(nn.Module):
def __init__(self, cfgfile):
super(Darknet, self).__init__()
self.blocks = parse_cfg(cfgfile)
self.net_info, self.module_list = create_modules(self.blocks)
```
在这里,我们对`nn.Module`类进行了子类化,并将我们的类命名为`Darknet`。我们用成员、`blocks`、`net_info`和`module_list`初始化网络。
#### 实现网络的前向传递
网络的前向传递是通过重写`nn.Module`类的`forward`方法来实现的。
`forward`有两个目的。第一,计算输出,第二,以更容易处理的方式变换输出检测特征图(例如变换它们,使得跨多个尺度的检测图可以连接,否则这是不可能的,因为它们具有不同的维度)。
```py
def forward(self, x, CUDA):
modules = self.blocks[1:]
outputs = {} #We cache the outputs for the route layer
```
`forward`接受三个参数,`self`,输入`x`和`CUDA`,如果为真,将使用 GPU 加速向前传递。
在这里,我们迭代`self.blocks[1:]`而不是`self.blocks`,因为`self.blocks`的第一个元素是一个`net`块,它不是向前传递的一部分。
由于*路线*和*快捷方式*层需要前一层的输出地图,我们在 dict `outputs`中缓存每一层的输出特征地图。关键字是层的索引,值是特征图
与使用`create_modules`函数的情况一样,我们现在迭代包含网络模块的`module_list`。这里要注意的是,模块是按照它们在配置文件中出现的顺序添加的。这意味着,我们可以简单地通过每个模块运行我们的输入来获得我们的输出。
```py
write = 0 #This is explained a bit later
for i, module in enumerate(modules):
module_type = (module["type"])
```
###### 卷积和上采样层
如果模块是卷积或上采样模块,这就是正向传递的工作方式。
```py
if module_type == "convolutional" or module_type == "upsample":
x = self.module_list[i](x)
```
###### 路径层/快捷方式层
如果您查看*路由*层的代码,我们必须考虑两种情况(如第 2 部分所述)。对于我们必须连接两个特征图的情况,我们使用第二个参数为 1 的`torch.cat`函数。这是因为我们想要沿着深度连接特征图。(在 PyTorch 中,卷积层的输入和输出具有格式 B X C X H X W。深度对应于信道维度)。
```py
elif module_type == "route":
layers = module["layers"]
layers = [int(a) for a in layers]
if (layers[0]) > 0:
layers[0] = layers[0] - i
if len(layers) == 1:
x = outputs[i + (layers[0])]
else:
if (layers[1]) > 0:
layers[1] = layers[1] - i
map1 = outputs[i + layers[0]]
map2 = outputs[i + layers[1]]
x = torch.cat((map1, map2), 1)
elif module_type == "shortcut":
from_ = int(module["from"])
x = outputs[i-1] + outputs[i+from_]
```
###### YOLO(探测层)
YOLO 的输出是一个卷积要素地图,它包含沿要素地图深度的边界框属性。由像元预测的属性边界框一个接一个地堆叠在一起。因此,如果您必须访问(5,6)处单元格的第二个边界,那么您必须通过`map[5,6, (5+C): 2*(5+C)]`对其进行索引。这种形式对于输出处理来说非常不方便,例如通过对象置信度进行阈值处理、向中心添加网格偏移、应用锚点等。
另一个问题是,由于检测发生在三个尺度上,预测图的维度将是不同的。虽然三个特征图的尺寸不同,但是对它们进行的输出处理操作是相似的。如果必须在单个张量上进行这些操作,而不是在三个独立的张量上,那就太好了。
为了解决这些问题,我们引入了函数`predict_transform`
#### 转换输出
函数`predict_transform`位于文件`util.py`中,当我们在`Darknet`类的`forward`中使用它时,我们将导入该函数。
将导入添加到`util.py`的顶部
```py
from __future__ import division
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
import cv2
```
`predict_transform`接受 5 个参数;*预测*(我们的输出)、 *inp_dim* (输入图像尺寸)、*锚点*、 *num_classes* ,以及一个可选的 *CUDA* 标志
```py
def predict_transform(prediction, inp_dim, anchors, num_classes, CUDA = True):
```
`predict_transform`函数获取一个检测特征图,并将其转换成一个二维张量,其中张量的每一行对应于一个边界框的属性,顺序如下。
![bbox_-2](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bf79827da490ec5da0b4aaf7bc02bc20.png)
下面是完成上述转换的代码。
```py
batch_size = prediction.size(0)
stride = inp_dim // prediction.size(2)
grid_size = inp_dim // stride
bbox_attrs = 5 + num_classes
num_anchors = len(anchors)
prediction = prediction.view(batch_size, bbox_attrs*num_anchors, grid_size*grid_size)
prediction = prediction.transpose(1,2).contiguous()
prediction = prediction.view(batch_size, grid_size*grid_size*num_anchors, bbox_attrs)
```
锚的尺寸与`net`块的`height`和`width`属性一致。这些属性描述了输入图像的尺寸,它比检测图大(大了*步距*倍)。因此,我们必须根据检测特征图的步幅来划分锚点。
```py
anchors = [(a[0]/stride, a[1]/stride) for a in anchors]
```
现在,我们需要根据我们在第 1 部分中讨论的等式来转换我们的输出。
Sigmoid,y 坐标和客观性分数。
```py
#Sigmoid the centre_X, centre_Y. and object confidencce
prediction[:,:,0] = torch.sigmoid(prediction[:,:,0])
prediction[:,:,1] = torch.sigmoid(prediction[:,:,1])
prediction[:,:,4] = torch.sigmoid(prediction[:,:,4])
```
将网格偏移量添加到中心坐标预测中。
```py
#Add the center offsets
grid = np.arange(grid_size)
a,b = np.meshgrid(grid, grid)
x_offset = torch.FloatTensor(a).view(-1,1)
y_offset = torch.FloatTensor(b).view(-1,1)
if CUDA:
x_offset = x_offset.cuda()
y_offset = y_offset.cuda()
x_y_offset = torch.cat((x_offset, y_offset), 1).repeat(1,num_anchors).view(-1,2).unsqueeze(0)
prediction[:,:,:2] += x_y_offset
```
将锚点应用于边界框的尺寸。
```py
#log space transform height and the width
anchors = torch.FloatTensor(anchors)
if CUDA:
anchors = anchors.cuda()
anchors = anchors.repeat(grid_size*grid_size, 1).unsqueeze(0)
prediction[:,:,2:4] = torch.exp(prediction[:,:,2:4])*anchors
```
将 sigmoid 激活应用于课程分数
```py
prediction[:,:,5: 5 + num_classes] = torch.sigmoid((prediction[:,:, 5 : 5 + num_classes]))
```
这里我们要做的最后一件事是将检测图的大小调整为输入图像的大小。这里的边界框属性的大小是根据特征图确定的(比如 13 x 13)。如果输入图像是 416 x 416,我们将属性乘以 32,即变量*的步幅*。
```py
prediction[:,:,:4] *= stride
```
这就结束了循环体。
在函数结束时返回预测值。
```py
return prediction
```
#### 重新访问检测层
既然我们已经转换了输出张量,我们现在可以将三个不同尺度的检测图连接成一个大张量。请注意,这在我们的转换之前是不可能的,因为我们不能连接具有不同空间维度的特征地图。但是从现在开始,我们的输出张量仅仅作为一个表格,用边界框作为它的行,连接是非常可能的。
我们道路上的一个障碍是我们不能初始化一个空张量,然后连接一个非空的(不同形状的)张量。因此,我们延迟收集器(保存检测的张量)的初始化,直到我们获得第一个检测图,然后在我们获得后续检测时连接到它的映射。
注意函数`forward`中循环之前的`write = 0`行。`write`标志用于指示我们是否遇到了第一次检测。如果`write`为 0,表示采集器还没有初始化。如果它是 1,这意味着收集器已经初始化,我们可以将我们的检测图连接到它。
现在,我们已经用`predict_transform`函数武装了自己,我们在`forward`函数中编写处理检测特征图的代码。
在您的`darknet.py`文件的顶部,添加以下导入。
```py
from util import *
```
然后,在`forward`功能中。
```py
elif module_type == 'yolo':
anchors = self.module_list[i][0].anchors
#Get the input dimensions
inp_dim = int (self.net_info["height"])
#Get the number of classes
num_classes = int (module["classes"])
#Transform
x = x.data
x = predict_transform(x, inp_dim, anchors, num_classes, CUDA)
if not write: #if no collector has been intialised.
detections = x
write = 1
else:
detections = torch.cat((detections, x), 1)
outputs[i] = x
```
现在,只需返回检测结果。
```py
return detections
```
#### 测试向前传球
这是一个创建虚拟输入的函数。我们将把这些信息传递给我们的网络。在我们编写这个函数之前,将这个[图像](https://github.com/ayooshkathuria/pytorch-yolo-v3/raw/master/dog-cycle-car.png)保存到您的工作目录中。如果你在 linux 上,那么输入。
```py
wget https://github.com/ayooshkathuria/pytorch-yolo-v3/raw/master/dog-cycle-car.png
```
现在,在`darknet.py`文件的顶部定义函数,如下所示:
```py
def get_test_input():
img = cv2.imread("dog-cycle-car.png")
img = cv2.resize(img, (416,416)) #Resize to the input dimension
img_ = img[:,:,::-1].transpose((2,0,1)) # BGR -> RGB | H X W C -> C X H X W
img_ = img_[np.newaxis,:,:,:]/255.0 #Add a channel at 0 (for batch) | Normalise
img_ = torch.from_numpy(img_).float() #Convert to float
img_ = Variable(img_) # Convert to Variable
return img_
```
然后,我们键入以下代码:
```py
model = Darknet("cfg/yolov3.cfg")
inp = get_test_input()
pred = model(inp, torch.cuda.is_available())
print (pred)
```
您将看到如下输出。
```py
( 0 ,.,.) =
16.0962 17.0541 91.5104 ... 0.4336 0.4692 0.5279
15.1363 15.2568 166.0840 ... 0.5561 0.5414 0.5318
14.4763 18.5405 409.4371 ... 0.5908 0.5353 0.4979
⋱ ...
411.2625 412.0660 9.0127 ... 0.5054 0.4662 0.5043
412.1762 412.4936 16.0449 ... 0.4815 0.4979 0.4582
412.1629 411.4338 34.9027 ... 0.4306 0.5462 0.4138
[torch.FloatTensor of size 1x10647x85]
```
这个张量的形状是`1 x 10647 x 85`。第一个维度是批量大小,简单地说就是 1,因为我们使用了单个图像。对于一批中的每个图像,我们有一个 10647 x 85 的表格。每个表格的行代表一个边界框。(4 个 bbox 属性、1 个对象分数和 80 个类别分数)
此时,我们的网络具有随机的权重,不会产生正确的输出。我们需要在网络中加载一个权重文件。为此,我们将使用官方重量文件。
#### 下载预先训练的重量
将砝码文件下载到您的检测机目录中。从[这里](https://pjreddie.com/media/files/yolov3.weights)抓取权重文件。或者如果你在 linux 上,
```py
wget https://pjreddie.com/media/files/yolov3.weights
```
#### 了解砝码文件
官方重量文件是二进制文件,包含以串行方式存储的重量。
读取重量时必须非常小心。权重只是作为浮点数存储,没有任何东西来指导我们它们属于哪一层。如果你搞砸了,没有什么可以阻止你,比如说,把一个批范数层的权重加载到一个卷积层的权重中。因为您读取的只是浮动,所以无法区分哪个权重属于哪个层。因此,我们必须了解权重是如何存储的。
首先,权重只属于两种类型的层,或者是批范数层,或者是卷积层。
这些层的权重完全按照它们在配置文件中出现的顺序存储。所以,如果一个`convolutional`后面跟着一个`shortcut`块,然后一个`shortcut`块后面跟着另一个`convolutional`块,你会期望文件包含前一个`convolutional`块的权重,然后是后一个块的权重。
当批次标准层出现在`convolutional`块中时,不存在偏差。然而,当没有批规范层时,偏差"权重"必须从文件中读取。
下图总结了权重如何存储权重。
![wts-1](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b864f7597507c177cc8828a02d67a78e.png)
#### 装船重量
让我们写一个函数加载权重。它将是`Darknet`类的成员函数。它需要一个参数,而不是权重文件的路径`self`。
```py
def load_weights(self, weightfile):
```
权重文件的前 160 个字节存储 5 个`int32`值,这些值构成了文件的标题。
```py
#Open the weights file
fp = open(weightfile, "rb")
#The first 5 values are header information
# 1\. Major version number
# 2\. Minor Version Number
# 3\. Subversion number
# 4,5\. Images seen by the network (during training)
header = np.fromfile(fp, dtype = np.int32, count = 5)
self.header = torch.from_numpy(header)
self.seen = self.header[3]
```
其余的位现在按照上述顺序表示权重。权重存储为`float32`或 32 位浮点数。让我们在一个`np.ndarray`中装载其余的重量。
```py
weights = np.fromfile(fp, dtype = np.float32)
```
现在,我们迭代权重文件,并将权重加载到网络的模块中。
```py
ptr = 0
for i in range(len(self.module_list)):
module_type = self.blocks[i + 1]["type"]
#If module_type is convolutional load weights
#Otherwise ignore.
```
进入循环,我们首先检查`convolutional`块的`batch_normalise`是否为真。在此基础上,我们加载重量。
```py
if module_type == "convolutional":
model = self.module_list[i]
try:
batch_normalize = int(self.blocks[i+1]["batch_normalize"])
except:
batch_normalize = 0
conv = model[0]
```
我们保留一个名为`ptr`的变量来跟踪我们在权重数组中的位置。现在,如果`batch_normalize`为真,我们如下加载权重。
```py
if (batch_normalize):
bn = model[1]
#Get the number of weights of Batch Norm Layer
num_bn_biases = bn.bias.numel()
#Load the weights
bn_biases = torch.from_numpy(weights[ptr:ptr + num_bn_biases])
ptr += num_bn_biases
bn_weights = torch.from_numpy(weights[ptr: ptr + num_bn_biases])
ptr += num_bn_biases
bn_running_mean = torch.from_numpy(weights[ptr: ptr + num_bn_biases])
ptr += num_bn_biases
bn_running_var = torch.from_numpy(weights[ptr: ptr + num_bn_biases])
ptr += num_bn_biases
#Cast the loaded weights into dims of model weights.
bn_biases = bn_biases.view_as(bn.bias.data)
bn_weights = bn_weights.view_as(bn.weight.data)
bn_running_mean = bn_running_mean.view_as(bn.running_mean)
bn_running_var = bn_running_var.view_as(bn.running_var)
#Copy the data to model
bn.bias.data.copy_(bn_biases)
bn.weight.data.copy_(bn_weights)
bn.running_mean.copy_(bn_running_mean)
bn.running_var.copy_(bn_running_var)
```
如果 batch_norm 不为真,只需加载卷积层的偏差。
```py
else:
#Number of biases
num_biases = conv.bias.numel()
#Load the weights
conv_biases = torch.from_numpy(weights[ptr: ptr + num_biases])
ptr = ptr + num_biases
#reshape the loaded weights according to the dims of the model weights
conv_biases = conv_biases.view_as(conv.bias.data)
#Finally copy the data
conv.bias.data.copy_(conv_biases)
```
最后,我们加载卷积层的权重。
```py
#Let us load the weights for the Convolutional layers
num_weights = conv.weight.numel()
#Do the same as above for weights
conv_weights = torch.from_numpy(weights[ptr:ptr+num_weights])
ptr = ptr + num_weights
conv_weights = conv_weights.view_as(conv.weight.data)
conv.weight.data.copy_(conv_weights)
```
我们已经完成了这个函数,现在你可以通过调用 darknet 对象上的`load_weights`函数在你的`Darknet`对象中加载权重。
```py
model = Darknet("cfg/yolov3.cfg")
model.load_weights("yolov3.weights")
```
这就是这一部分的全部内容,随着模型的建立和权重的加载,我们终于可以开始检测物体了。在下一个[部分](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-4/)中,我们将介绍使用目标置信度阈值和非最大值抑制来产生我们的最终检测集。
#### 进一步阅读
1. [PyTorch 教程](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)
2. [用 NumPy 读取二进制文件](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.fromfile.html)
3. [nn。模块,nn。参数类别](http://pytorch.org/tutorials/beginner/blitz/neural_networks_tutorial.html#define-the-network)
Ayoosh Kathuria 目前是印度国防研究与发展组织的实习生,他致力于改进粒状视频中的物体检测。当他不工作的时候,他不是在睡觉就是在用吉他弹奏平克·弗洛伊德。你可以在 [LinkedIn](https://www.linkedin.com/in/ayoosh-kathuria-44a319132/) 上和他联系,或者在[GitHub](https://github.com/ayooshkathuria)T5 上看看他做了些什么
# 如何在 PyTorch 中从头开始实现 YOLO (v3)对象检测器:第 4 部分
> 原文:<https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-4/>
图片来源:凯罗尔·马杰克。查看他的 YOLO v3 实时检测视频[这里](https://www.youtube.com/watch?v=8jfscFuP9k)
这是从头开始实现 YOLO v3 检测器教程的第 4 部分。在最后一部分,我们实现了网络的前向传递。在这一部分中,我们通过对象置信度以及随后的非最大抑制来设定检测阈值。
本教程的代码旨在运行在 Python 3.5 和 PyTorch **0.4** 上。在这个 [Github repo](https://github.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch) 可以找到它的全部内容。
本教程分为 5 个部分:
1. 第一部分:了解 YOLO 是如何运作的
2. [第 2 部分:创建网络架构的各层](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-2/)
3. [第三部分:实现网络的前向传递](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-3/)
4. 第 4 部分(这一部分):置信阈值和非最大值抑制
5. [第五部分:设计输入和输出管道](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-5/)
#### 先决条件
1. 教程的第 1-3 部分。
2. PyTorch 的基本工作知识,包括如何使用 nn 创建定制架构。模块,nn。Sequential 和 torch.nn.parameter 类。
3. NumPy 的基础知识
如果你在任何方面有所欠缺,这篇文章下面有一些链接供你参考。
在前面的部分中,我们已经建立了一个模型,该模型在给定输入图像的情况下输出几个对象检测。准确地说,我们的输出是一个形状为`B x 10647 x 85`的张量。b 是一批图像的数量,10647 是每个图像预测的边界框的数量,85 是边界框属性的数量。
然而,如第 1 部分所述,我们必须对我们的输出进行客观分数阈值处理和非最大抑制,以获得我在本文剩余部分称为*真*检测的结果。为此,我们将在文件`util.py`中创建一个名为`write_results`的函数
```py
def write_results(prediction, confidence, num_classes, nms_conf = 0.4):
```
这些函数将`prediction`、`confidence`(客观分数阈值)、`num_classes`(在我们的例子中是 80)和`nms_conf`(NMS IoU 阈值)作为输入。
#### 目标置信度阈值
我们的预测张量包含关于`B x 10647`包围盒的信息。对于每个具有低于阈值的对象性分数的边界框,我们将它的每个属性(代表边界框的整行)的值设置为零。
```py
conf_mask = (prediction[:,:,4] > confidence).float().unsqueeze(2)
prediction = prediction*conf_mask
```
#### 执行非最大抑制
> 注意:我假设你理解什么是 IoU(交集/并集),什么是非最大抑制。如果不是这样,请参考文章末尾的链接)。
我们现在拥有的边界框属性由中心坐标以及边界框的高度和宽度来描述。然而,使用每个盒子的一对对角的坐标,更容易计算两个盒子的 IoU。因此,我们将盒子的**(中心 x,中心 y,高度,宽度)属性转换为(左上角 x,左上角 y,右下角 x,右下角 y)。**
```py
box_corner = prediction.new(prediction.shape)
box_corner[:,:,0] = (prediction[:,:,0] - prediction[:,:,2]/2)
box_corner[:,:,1] = (prediction[:,:,1] - prediction[:,:,3]/2)
box_corner[:,:,2] = (prediction[:,:,0] + prediction[:,:,2]/2)
box_corner[:,:,3] = (prediction[:,:,1] + prediction[:,:,3]/2)
prediction[:,:,:4] = box_corner[:,:,:4]
```
每个图像中的*真*检测的数量可能不同。例如,一批大小为 3 的图像 1、2 和 3 分别具有 5、2、4 个*真*检测。因此,必须一次对一幅图像进行置信阈值处理和 NMS。这意味着,我们不能对所涉及的操作进行矢量化,必须循环遍历第一维度的`prediction`(包含一批图像的索引)。
```py
batch_size = prediction.size(0)
write = False
for ind in range(batch_size):
image_pred = prediction[ind] #image Tensor
#confidence threshholding
#NMS
```
如前所述,`write`标志用于指示我们还没有初始化`output`,我们将使用这个张量来收集整个批次的*真*检测。
一旦进入循环,让我们稍微清理一下。注意每个边界框行有 85 个属性,其中 80 个是类分数。此时,我们只关心具有最大值的类分数。因此,我们从每一行中删除了 80 个类分数,取而代之的是添加具有最大值的类的索引,以及该类的类分数。
```py
max_conf, max_conf_score = torch.max(image_pred[:,5:5+ num_classes], 1)
max_conf = max_conf.float().unsqueeze(1)
max_conf_score = max_conf_score.float().unsqueeze(1)
seq = (image_pred[:,:5], max_conf, max_conf_score)
image_pred = torch.cat(seq, 1)
```
还记得我们已经将对象置信度小于阈值的边界框行设置为零吗?让我们摆脱他们。
```py
non_zero_ind = (torch.nonzero(image_pred[:,4]))
try:
image_pred_ = image_pred[non_zero_ind.squeeze(),:].view(-1,7)
except:
continue
#For PyTorch 0.4 compatibility
#Since the above code with not raise exception for no detection
#as scalars are supported in PyTorch 0.4
if image_pred_.shape[0] == 0:
continue
```
try-except 块用于处理没有检测到的情况。在这种情况下,我们使用`continue`来跳过这个图像的其余循环体。
现在,让我们在图像中检测类别。
```py
#Get the various classes detected in the image
img_classes = unique(image_pred_[:,-1]) # -1 index holds the class index
```
由于同一个类可能有多个*真*检测,我们使用一个名为`unique`的函数来获取任何给定图像中存在的类。
```py
def unique(tensor):
tensor_np = tensor.cpu().numpy()
unique_np = np.unique(tensor_np)
unique_tensor = torch.from_numpy(unique_np)
tensor_res = tensor.new(unique_tensor.shape)
tensor_res.copy_(unique_tensor)
return tensor_res
```
然后,我们按班级表演 NMS。
```py
for cls in img_classes:
#perform NMS
```
一旦我们进入循环,我们做的第一件事就是提取特定类的检测(用变量`cls`表示)。
> 下面的代码在原始代码文件中缩进了三个块,但是我没有在这里缩进,因为这一页的空间有限。
```py
#get the detections with one particular class
cls_mask = image_pred_*(image_pred_[:,-1] == cls).float().unsqueeze(1)
class_mask_ind = torch.nonzero(cls_mask[:,-2]).squeeze()
image_pred_class = image_pred_[class_mask_ind].view(-1,7)
#sort the detections such that the entry with the maximum objectness
s#confidence is at the top
conf_sort_index = torch.sort(image_pred_class[:,4], descending = True )[1]
image_pred_class = image_pred_class[conf_sort_index]
idx = image_pred_class.size(0) #Number of detections
```
现在,我们表演 NMS。
```py
for i in range(idx):
#Get the IOUs of all boxes that come after the one we are looking at
#in the loop
try:
ious = bbox_iou(image_pred_class[i].unsqueeze(0), image_pred_class[i+1:])
except ValueError:
break
except IndexError:
break
#Zero out all the detections that have IoU > treshhold
iou_mask = (ious < nms_conf).float().unsqueeze(1)
image_pred_class[i+1:] *= iou_mask
#Remove the non-zero entries
non_zero_ind = torch.nonzero(image_pred_class[:,4]).squeeze()
image_pred_class = image_pred_class[non_zero_ind].view(-1,7)
```
这里,我们使用一个函数`bbox_iou`。第一个输入是由循环中的变量`i`索引的边界框行。
`bbox_iou`的第二个输入是多行边界框的张量。函数`bbox_iou`的输出是包含由第一输入表示的边界框的 IoUs 的张量,每个边界框出现在第二输入中。
![bbox-3](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6097083229629c8a430660a4c1687f32.png)
如果我们有两个相同类别的边界框,其 IoU 大于阈值,那么具有较低类别置信度的边界框被消除。我们已经挑选出了包围盒,具有较高置信度的包围盒位于顶部。
在循环体中,下面几行给出了框的 IoU,由`i`索引,所有边界框的索引都高于`i`。
```py
ious = bbox_iou(image_pred_class[i].unsqueeze(0), image_pred_class[i+1:])
```
每次迭代,如果索引大于`i`的任何边界框具有大于阈值`nms_thresh`的 IoU(由`i`索引的框),则该特定框被消除。
```py
#Zero out all the detections that have IoU > treshhold
iou_mask = (ious < nms_conf).float().unsqueeze(1)
image_pred_class[i+1:] *= iou_mask
#Remove the non-zero entries
non_zero_ind = torch.nonzero(image_pred_class[:,4]).squeeze()
image_pred_class = image_pred_class[non_zero_ind]
```
还要注意,我们将计算`ious`的代码行放在了一个 try-catch 块中。这是因为循环被设计成运行`idx`次迭代(T2 中的行数)。然而,随着循环的进行,一些边界框可能会从`image_pred_class`中移除。这意味着,即使从`image_pred_class`中移除一个值,我们也不能进行`idx`次迭代。因此,我们可能会尝试索引一个越界的值(`IndexError`),或者切片`image_pred_class[i+1:]`可能会返回一个空张量,指定哪个触发了`ValueError`。在这一点上,我们可以确定 NMS 不能删除更多的边界框,我们打破了循环。
###### 计算欠条
下面是函数`bbox_iou`。
```py
def bbox_iou(box1, box2):
"""
Returns the IoU of two bounding boxes
"""
#Get the coordinates of bounding boxes
b1_x1, b1_y1, b1_x2, b1_y2 = box1[:,0], box1[:,1], box1[:,2], box1[:,3]
b2_x1, b2_y1, b2_x2, b2_y2 = box2[:,0], box2[:,1], box2[:,2], box2[:,3]
#get the corrdinates of the intersection rectangle
inter_rect_x1 = torch.max(b1_x1, b2_x1)
inter_rect_y1 = torch.max(b1_y1, b2_y1)
inter_rect_x2 = torch.min(b1_x2, b2_x2)
inter_rect_y2 = torch.min(b1_y2, b2_y2)
#Intersection area
inter_area = torch.clamp(inter_rect_x2 - inter_rect_x1 + 1, min=0) * torch.clamp(inter_rect_y2 - inter_rect_y1 + 1, min=0)
#Union Area
b1_area = (b1_x2 - b1_x1 + 1)*(b1_y2 - b1_y1 + 1)
b2_area = (b2_x2 - b2_x1 + 1)*(b2_y2 - b2_y1 + 1)
iou = inter_area / (b1_area + b2_area - inter_area)
return iou
```
#### 写预测
函数`write_results`输出形状为 D×8 的张量。这里 D 是所有图像中的*真*检测,每一个由一行表示。每个检测有 8 个属性,即检测所属批次中图像的**索引、 **4 个角坐标、客观分数、最大置信度类的分数以及该类的索引。****
就像以前一样,我们不初始化我们的输出张量,除非我们有一个检测分配给它。一旦它被初始化,我们就将后续的检测连接到它。我们使用`write`标志来表示张量是否已经初始化。**在遍历类**的循环结束时,我们将得到的检测结果添加到张量`output`中。
```py
batch_ind = image_pred_class.new(image_pred_class.size(0), 1).fill_(ind)
#Repeat the batch_id for as many detections of the class cls in the image
seq = batch_ind, image_pred_class
if not write:
output = torch.cat(seq,1)
write = True
else:
out = torch.cat(seq,1)
output = torch.cat((output,out))
```
在函数结束时,我们检查`output`是否已经初始化。如果没有,意味着在该批次的任何图像中没有一个检测到。在这种情况下,我们返回 0。
```py
try:
return output
except:
return 0
```
这就是这篇文章的内容。在这篇文章的最后,我们终于有了一个张量形式的预测,它以行的形式列出了每个预测。现在唯一剩下的就是创建一个输入管道来从磁盘读取图像,计算预测,在图像上绘制边界框,然后显示/写入这些图像。这是我们在下一个[部分](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-5/)要做的事情。
#### 进一步阅读
1. [PyTorch 教程](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)
2. [借据](https://www.youtube.com/watch?v=DNEm4fJ-rto)
3. [非最大抑制](https://www.youtube.com/watch?v=A46HZGR5fMw)
4. [非最大抑制](https://www.google.co.in/search?q=NMS+python&oq=NMS+python+&aqs=chrome..69i57j35i39.2657j0j7&sourceid=chrome&ie=UTF-8)
Ayoosh Kathuria 目前是印度国防研究与发展组织的实习生,他致力于改进粒状视频中的物体检测。当他不工作的时候,他不是在睡觉就是在用吉他弹奏平克·弗洛伊德。你可以在 [LinkedIn](https://www.linkedin.com/in/ayoosh-kathuria-44a319132/) 上和他联系,或者在[GitHub](https://github.com/ayooshkathuria)T5 上看看他做了些什么
# 如何在 PyTorch 中从头开始实现 YOLO (v3)对象检测器:第 5 部分
> 原文:<https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-5/>
图片来源:凯罗尔·马杰克。查看他的 YOLO v3 实时检测视频[这里](https://www.youtube.com/watch?v=8jfscFuP9k)
这是从头开始实现 YOLO v3 检测器教程的第 5 部分。在最后一部分中,我们实现了一个函数来将网络的输出转换成检测预测。有了可用的检测器,剩下的工作就是创建输入和输出管道。
本教程的代码旨在运行在 Python 3.5 和 PyTorch **0.4** 上。在这个 [Github repo](https://github.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch) 可以找到它的全部内容。
本教程分为 5 个部分:
1. 第一部分:了解 YOLO 是如何运作的
2. [第 2 部分:创建网络架构的各层](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-2/)
3. [第三部分:实现网络的前向传递](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-3/)
4. [第 4 部分:置信阈值和非最大值抑制](https://blog.paperspace.com/how-to-implement-a-yolo-v3-object-detector-from-scratch-in-pytorch-part-4/)
5. 第 5 部分(这一部分):设计输入和输出管道
#### 先决条件
1. 教程的第 1-4 部分。
2. PyTorch 的基本工作知识,包括如何使用 nn 创建定制架构。模块,nn。Sequential 和 torch.nn.parameter 类。
3. OpenCV 的基础知识
***编辑**:如果你在 2018 年 3 月 30 日之前访问过这篇文章,我们将任意大小的图像调整到 Darknet 的输入大小的方法就是简单地重新调整尺寸。然而,在最初的实现中,图像被调整大小,保持纵横比不变,并填充被省略的部分。例如,如果我们要将一个 1900 x 1280 的图像调整为 416 x 415,调整后的图像将如下所示。*
![DarknetResizedImage](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c3e76f49b331ba0cddd3e958b039c3af.png)
准备输入的这种差异导致早期实现的性能比最初的稍差。不过,帖子已经更新,加入了最初实现中的调整大小方法
在这一部分中,我们将构建检测器的输入和输出管道。这包括从磁盘上读取图像,进行预测,使用预测在图像上绘制边界框,然后将它们保存到磁盘上。我们还将介绍如何让检测机在摄像头或视频上实时工作。我们将引入一些命令行标志,以允许对网络的各种超参数进行一些实验。让我们开始吧。
> 注意:你需要为这部分安装 OpenCV 3。
在巡回检测器文件中创建一个文件`detector.py`。在它的顶部添加必要的进口商品。
```py
from __future__ import division
import time
import torch
import torch.nn as nn
from torch.autograd import Variable
import numpy as np
import cv2
from util import *
import argparse
import os
import os.path as osp
from darknet import Darknet
import pickle as pkl
import pandas as pd
import random
```
#### 创建命令行参数
因为`detector.py`是我们将执行来运行我们的检测器的文件,所以最好有我们可以传递给它的命令行参数。我已经用 python 的`ArgParse`模块做到了。
```py
def arg_parse():
"""
Parse arguements to the detect module
"""
parser = argparse.ArgumentParser(description='YOLO v3 Detection Module')
parser.add_argument("--images", dest = 'images', help =
"Image / Directory containing images to perform detection upon",
default = "imgs", type = str)
parser.add_argument("--det", dest = 'det', help =
"Image / Directory to store detections to",
default = "det", type = str)
parser.add_argument("--bs", dest = "bs", help = "Batch size", default = 1)
parser.add_argument("--confidence", dest = "confidence", help = "Object Confidence to filter predictions", default = 0.5)
parser.add_argument("--nms_thresh", dest = "nms_thresh", help = "NMS Threshhold", default = 0.4)
parser.add_argument("--cfg", dest = 'cfgfile', help =
"Config file",
default = "cfg/yolov3.cfg", type = str)
parser.add_argument("--weights", dest = 'weightsfile', help =
"weightsfile",
default = "yolov3.weights", type = str)
parser.add_argument("--reso", dest = 'reso', help =
"Input resolution of the network. Increase to increase accuracy. Decrease to increase speed",
default = "416", type = str)
return parser.parse_args()
args = arg_parse()
images = args.images
batch_size = int(args.bs)
confidence = float(args.confidence)
nms_thesh = float(args.nms_thresh)
start = 0
CUDA = torch.cuda.is_available()
```
其中重要的标志有`images`(用于指定输入图像或图像目录)`det`(保存检测的目录)`reso`(输入图像的分辨率,可用于速度-精度的权衡)`cfg`(备选配置文件)`weightfile`。
#### 加载网络
从[这里](https://raw.githubusercontent.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch/master/data/coco.names)下载文件`coco.names`,这个文件包含 COCO 数据集中对象的名称。在探测器目录下创建一个文件夹`data`。同样,如果你在 linux 上,你可以输入。
```py
mkdir data
cd data
wget https://raw.githubusercontent.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch/master/data/coco.names
```
然后,我们在程序中加载类文件。
```py
num_classes = 80 #For COCO
classes = load_classes("data/coco.names")
```
`load_classes`是在`util.py`中定义的一个函数,它返回一个字典,该字典将每个类的索引映射到它的名称字符串。
```py
def load_classes(namesfile):
fp = open(namesfile, "r")
names = fp.read().split("\n")[:-1]
return names
```
初始化网络并加载权重。
```py
#Set up the neural network
print("Loading network.....")
model = Darknet(args.cfgfile)
model.load_weights(args.weightsfile)
print("Network successfully loaded")
model.net_info["height"] = args.reso
inp_dim = int(model.net_info["height"])
assert inp_dim % 32 == 0
assert inp_dim > 32
#If there's a GPU availible, put the model on GPU
if CUDA:
model.cuda()
#Set the model in evaluation mode
model.eval()
```
#### 读取输入图像
从磁盘或目录中读取图像。图像的路径存储在一个名为`imlist`的列表中。
```py
read_dir = time.time()
#Detection phase
try:
imlist = [osp.join(osp.realpath('.'), images, img) for img in os.listdir(images)]
except NotADirectoryError:
imlist = []
imlist.append(osp.join(osp.realpath('.'), images))
except FileNotFoundError:
print ("No file or directory with the name {}".format(images))
exit()
```
`read_dir`是用来测量时间的检查点。(我们会遇到几个这样的例子)
如果由`det`标志定义的保存检测的目录不存在,则创建它。
```py
if not os.path.exists(args.det):
os.makedirs(args.det)
```
我们将使用 OpenCV 来加载图像。
```py
load_batch = time.time()
loaded_ims = [cv2.imread(x) for x in imlist]
```
又是一个检查站。
OpenCV 将图像作为 numpy 数组加载,BGR 作为颜色通道的顺序。PyTorch 的图像输入格式为(批次 x 通道 x 高度 x 宽度),通道顺序为 RGB。因此,我们在`util.py`中编写函数`prep_image`,将 numpy 数组转换成 PyTorch 的输入格式。
在我们写这个函数之前,我们必须写一个函数`letterbox_image`来调整我们的图像,保持长宽比一致,并用颜色(128,128,128)填充剩下的区域
```py
def letterbox_image(img, inp_dim):
'''resize image with unchanged aspect ratio using padding'''
img_w, img_h = img.shape[1], img.shape[0]
w, h = inp_dim
new_w = int(img_w * min(w/img_w, h/img_h))
new_h = int(img_h * min(w/img_w, h/img_h))
resized_image = cv2.resize(img, (new_w,new_h), interpolation = cv2.INTER_CUBIC)
canvas = np.full((inp_dim[1], inp_dim[0], 3), 128)
canvas[(h-new_h)//2:(h-new_h)//2 + new_h,(w-new_w)//2:(w-new_w)//2 + new_w, :] = resized_image
return canvas
```
现在,我们编写一个获取 OpenCV 图像并将其转换为网络输入的函数。
```py
def prep_image(img, inp_dim):
"""
Prepare image for inputting to the neural network.
Returns a Variable
"""
img = cv2.resize(img, (inp_dim, inp_dim))
img = img[:,:,::-1].transpose((2,0,1)).copy()
img = torch.from_numpy(img).float().div(255.0).unsqueeze(0)
return img
```
除了转换后的图像之外,我们还维护一个原始图像列表和一个包含原始图像尺寸的列表`im_dim_list`。
```py
#PyTorch Variables for images
im_batches = list(map(prep_image, loaded_ims, [inp_dim for x in range(len(imlist))]))
#List containing dimensions of original images
im_dim_list = [(x.shape[1], x.shape[0]) for x in loaded_ims]
im_dim_list = torch.FloatTensor(im_dim_list).repeat(1,2)
if CUDA:
im_dim_list = im_dim_list.cuda()
```
#### 创建批处理
```py
leftover = 0
if (len(im_dim_list) % batch_size):
leftover = 1
if batch_size != 1:
num_batches = len(imlist) // batch_size + leftover
im_batches = [torch.cat((im_batches[i*batch_size : min((i + 1)*batch_size,
len(im_batches))])) for i in range(num_batches)]
```
#### 检测回路
我们迭代批次,生成预测,并连接我们必须对其执行检测的所有图像的预测张量(形状为 D×8,是`write_results`函数的输出)。
对于每一批,我们测量检测所需的时间,即从获取输入到产生`write_results`函数的输出所花费的时间。在`write_prediction`返回的输出中,其中一个属性是批量图像的索引。我们以这样一种方式转换这个特定的属性,它现在表示图像在`imlist`中的索引,这个列表包含所有图像的地址。
之后,我们打印每次检测所用的时间以及每幅图像中检测到的对象。
如果批处理的`write_results`函数的输出是一个`int` (0),意味着没有检测,我们使用`continue`来跳过剩余的循环。
```py
write = 0
start_det_loop = time.time()
for i, batch in enumerate(im_batches):
#load the image
start = time.time()
if CUDA:
batch = batch.cuda()
prediction = model(Variable(batch, volatile = True), CUDA)
prediction = write_results(prediction, confidence, num_classes, nms_conf = nms_thesh)
end = time.time()
if type(prediction) == int:
for im_num, image in enumerate(imlist[i*batch_size: min((i + 1)*batch_size, len(imlist))]):
im_id = i*batch_size + im_num
print("{0:20s} predicted in {1:6.3f} seconds".format(image.split("/")[-1], (end - start)/batch_size))
print("{0:20s} {1:s}".format("Objects Detected:", ""))
print("----------------------------------------------------------")
continue
prediction[:,0] += i*batch_size #transform the atribute from index in batch to index in imlist
if not write: #If we have't initialised output
output = prediction
write = 1
else:
output = torch.cat((output,prediction))
for im_num, image in enumerate(imlist[i*batch_size: min((i + 1)*batch_size, len(imlist))]):
im_id = i*batch_size + im_num
objs = [classes[int(x[-1])] for x in output if int(x[0]) == im_id]
print("{0:20s} predicted in {1:6.3f} seconds".format(image.split("/")[-1], (end - start)/batch_size))
print("{0:20s} {1:s}".format("Objects Detected:", " ".join(objs)))
print("----------------------------------------------------------")
if CUDA:
torch.cuda.synchronize()
```
第`torch.cuda.synchronize`行确保 CUDA 内核与 CPU 同步。否则,一旦 GPU 作业排队,CUDA 内核就会在 GPU 作业完成之前将控制权返回给 CPU(异步调用)。如果`end = time.time()`在 GPU 作业实际结束之前被打印,这可能会导致误导时间。
现在,我们有了张量输出中所有图像的检测。让我们在图像上画出边界框。
#### 在图像上绘制边界框
我们使用一个 try-catch 块来检查是否进行了单次检测。如果不是这样,退出程序。
```py
try:
output
except NameError:
print ("No detections were made")
exit()
```
在我们绘制边界框之前,包含在输出张量中的预测符合网络的输入大小,而不是图像的原始大小。因此,在我们绘制边界框之前,让我们将每个边界框的角属性转换为图像的原始尺寸。
在我们绘制边界框之前,输出张量中包含的预测是对填充图像的预测,而不是对原始图像的预测。仅仅将它们重新缩放到输入图像的尺寸在这里是行不通的。我们首先需要根据包含原始图像的填充图像上的区域边界来转换要测量的框的坐标。
```py
im_dim_list = torch.index_select(im_dim_list, 0, output[:,0].long())
scaling_factor = torch.min(inp_dim/im_dim_list,1)[0].view(-1,1)
output[:,[1,3]] -= (inp_dim - scaling_factor*im_dim_list[:,0].view(-1,1))/2
output[:,[2,4]] -= (inp_dim - scaling_factor*im_dim_list[:,1].view(-1,1))/2
```
现在,我们的坐标与填充区域的图像尺寸一致。然而,在函数`letterbox_image`中,我们用一个比例因子调整了图像的两个维度(记住两个维度都用一个公共因子划分以保持纵横比)。我们现在取消这种重新缩放,以获得原始图像上边界框的坐标。
```py
output[:,1:5] /= scaling_factor
```
现在,让我们将任何可能具有图像外部边界的边界框裁剪到图像的边缘。
```py
for i in range(output.shape[0]):
output[i, [1,3]] = torch.clamp(output[i, [1,3]], 0.0, im_dim_list[i,0])
output[i, [2,4]] = torch.clamp(output[i, [2,4]], 0.0, im_dim_list[i,1])
```
如果图像中有太多的边界框,用一种颜色绘制它们可能不是一个好主意。将此[文件](https://github.com/ayooshkathuria/YOLO_v3_tutorial_from_scratch/raw/master/pallete)下载到您的探测器文件夹中。这是一个腌制的文件,包含许多颜色可以随机选择。
```py
class_load = time.time()
colors = pkl.load(open("pallete", "rb"))
```
现在让我们写一个画方框的函数。
```py
draw = time.time()
def write(x, results, color):
c1 = tuple(x[1:3].int())
c2 = tuple(x[3:5].int())
img = results[int(x[0])]
cls = int(x[-1])
label = "{0}".format(classes[cls])
cv2.rectangle(img, c1, c2,color, 1)
t_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_PLAIN, 1 , 1)[0]
c2 = c1[0] + t_size[0] + 3, c1[1] + t_size[1] + 4
cv2.rectangle(img, c1, c2,color, -1)
cv2.putText(img, label, (c1[0], c1[1] + t_size[1] + 4), cv2.FONT_HERSHEY_PLAIN, 1, [225,255,255], 1);
return img
```
上面的函数用从`colors`中随机选择的颜色绘制了一个矩形。它还在边界框的左上角创建一个填充矩形,并在填充矩形上写入检测到的对象的类。`cv2.rectangle`函数的`-1`参数用于创建填充矩形。
我们在本地定义了`write`函数,这样它就可以访问`colors`列表。我们也可以把`colors`作为一个参数,但是那会让我们每张图片只使用一种颜色,这违背了我们想要做的目的。
一旦我们定义了这个函数,现在让我们在图像上绘制边界框。
```py
list(map(lambda x: write(x, loaded_ims), output))
```
上面的代码片段在中修改了`loaded_ims` **中的图像。**
通过在图像名称前加上前缀“det_”来保存每个图像。我们创建一个地址列表,用于保存我们的检测图像。
```py
det_names = pd.Series(imlist).apply(lambda x: "{}/det_{}".format(args.det,x.split("/")[-1]))
```
最后,将检测到的图像写入`det_names`中的地址。
```py
list(map(cv2.imwrite, det_names, loaded_ims))
end = time.time()
```
#### 打印时间摘要
在检测器的最后,我们将打印一份摘要,其中包含代码的哪一部分执行了多长时间。当我们必须比较不同的超参数如何影响检测机的速度时,这是很有用的。当在命令行上执行脚本`detection.py`时,可以设置超参数,如批量大小、对象置信度和 NMS 阈值(分别用`bs`、`confidence`、`nms_thresh`标志传递)。
```py
print("SUMMARY")
print("----------------------------------------------------------")
print("{:25s}: {}".format("Task", "Time Taken (in seconds)"))
print()
print("{:25s}: {:2.3f}".format("Reading addresses", load_batch - read_dir))
print("{:25s}: {:2.3f}".format("Loading batch", start_det_loop - load_batch))
print("{:25s}: {:2.3f}".format("Detection (" + str(len(imlist)) + " images)", output_recast - start_det_loop))
print("{:25s}: {:2.3f}".format("Output Processing", class_load - output_recast))
print("{:25s}: {:2.3f}".format("Drawing Boxes", end - draw))
print("{:25s}: {:2.3f}".format("Average time_per_img", (end - load_batch)/len(imlist)))
print("----------------------------------------------------------")
torch.cuda.empty_cache()
```
#### 测试对象检测器
例如,在终端上运行,
```py
python detect.py --images dog-cycle-car.png --det det
```
产生输出
> 下面的代码在 CPU 上运行。预计 GPU 上的检测时间要快得多。在特斯拉 K80 上大约是 0.1 秒/图像。
```py
Loading network.....
Network successfully loaded
dog-cycle-car.png predicted in 2.456 seconds
Objects Detected: bicycle truck dog
----------------------------------------------------------
SUMMARY
----------------------------------------------------------
Task : Time Taken (in seconds)
Reading addresses : 0.002
Loading batch : 0.120
Detection (1 images) : 2.457
Output Processing : 0.002
Drawing Boxes : 0.076
Average time_per_img : 2.657
----------------------------------------------------------
```
名为`det_dog-cycle-car.png`的图像保存在`det`目录中。
![det_dog-cycle-car](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cbabf48b1bbdabeff2f3c2c809576492.png)
#### 在视频/网络摄像头上运行检测机
为了在视频或网络摄像头上运行检测器,代码几乎保持不变,只是我们不需要迭代批次,而是视频的帧。
在视频上运行检测器的代码可以在 github 存储库中的文件`video.py`中找到。代码与`detect.py`非常相似,除了一些变化。
首先,我们在 OpenCV 中打开视频/摄像机提要。
```py
videofile = "video.avi" #or path to the video file.
cap = cv2.VideoCapture(videofile)
#cap = cv2.VideoCapture(0) for webcam
assert cap.isOpened(), 'Cannot capture source'
frames = 0
```
我们以类似于迭代图像的方式迭代帧。
许多代码在许多地方都得到了简化,因为我们不再需要处理批处理,而是一次只处理一个图像。这是因为一次只能出现一帧。这包括用一个元组代替张量用于`im_dim_list`和在`write`函数中的微小变化。
每次迭代,我们都在一个名为`frames`的变量中记录捕获的帧数。然后,我们将这个数字除以从第一帧开始打印视频的 FPS 所经过的时间。
代替使用`cv2.imwrite`将检测图像写入磁盘,我们使用`cv2.imshow`显示其上绘制有边界框的帧。如果用户按下`Q`按钮,就会导致代码中断循环,视频结束。
```py
frames = 0
start = time.time()
while cap.isOpened():
ret, frame = cap.read()
if ret:
img = prep_image(frame, inp_dim)
# cv2.imshow("a", frame)
im_dim = frame.shape[1], frame.shape[0]
im_dim = torch.FloatTensor(im_dim).repeat(1,2)
if CUDA:
im_dim = im_dim.cuda()
img = img.cuda()
output = model(Variable(img, volatile = True), CUDA)
output = write_results(output, confidence, num_classes, nms_conf = nms_thesh)
if type(output) == int:
frames += 1
print("FPS of the video is {:5.4f}".format( frames / (time.time() - start)))
cv2.imshow("frame", frame)
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break
continue
output[:,1:5] = torch.clamp(output[:,1:5], 0.0, float(inp_dim))
im_dim = im_dim.repeat(output.size(0), 1)/inp_dim
output[:,1:5] *= im_dim
classes = load_classes('data/coco.names')
colors = pkl.load(open("pallete", "rb"))
list(map(lambda x: write(x, frame), output))
cv2.imshow("frame", frame)
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break
frames += 1
print(time.time() - start)
print("FPS of the video is {:5.2f}".format( frames / (time.time() - start)))
else:
break
```
#### 结论
在这一系列教程中,我们已经从头实现了一个对象检测器,并为达到这一步而欢呼。我仍然认为,能够生成高效代码是深度学习实践者可能拥有的最被低估的技能之一。无论你的想法多么具有革命性,除非你能检验它,否则毫无用处。为此,你需要有很强的编码技能。
我还了解到,了解深度学习中任何主题的最佳方式是实现代码。它迫使你浏览一个你在阅读论文时可能会错过的主题的细微但基本的微妙之处。我希望这一系列教程能够锻炼你作为深度学习实践者的技能。
#### 进一步阅读
1. [PyTorch 教程](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)
2. [OpenCV 基础知识](https://pythonprogramming.net/loading-images-python-opencv-tutorial/)
3. [Python ArgParse](www.pythonforbeginners.com/argparse/argparse-tutorial)
Ayoosh Kathuria 目前是印度国防研究与发展组织的实习生,他致力于改进粒状视频中的物体检测。当他不工作的时候,他不是在睡觉就是在用吉他弹奏平克·弗洛伊德。你可以在 [LinkedIn](https://www.linkedin.com/in/ayoosh-kathuria-44a319132/) 上和他联系,或者在[GitHub](https://github.com/ayooshkathuria)T5 上看看他做了些什么
# 如何通过找到合适的批处理大小来最大化 GPU 利用率
> 原文:<https://blog.paperspace.com/how-to-maximize-gpu-utilization-by-finding-the-right-batch-size/>
在建立机器学习模型时,我们总是在为我们的模型和训练配置寻找最有效的参数。这些设置可能包括优化器、批量大小、层堆栈设置,甚至数据形状。在本文中,我们将讨论在训练神经网络架构时可能遇到的批量大小约束。我们还将看到这个元素如何受到 GPU 的能力及其可用内存的影响。然后,我们将看看如何为我们的 GPU/模型组合找到最佳批量。
## 理解术语
### 样品
一个样本代表一个数据元素。它包括用于训练算法的输入以及用于通过与预测进行比较来计算误差的输出。也可以叫做:**观测**,一个**输入向量**,或者一个**特征向量**。
### 批量
批量指的是在更新其可训练模型变量或权重和偏差之前,用于训练模型的样本的**数量。也就是说,一批样本在每个训练步骤通过模型,然后反向传递以确定每个样本的梯度。为了确定可训练模型变量的更新,然后根据所使用的优化器的类型,对所有样本的梯度进行平均或相加。参数更新后,将对后续批次的样本重复此过程。**
### 世
历元数,这是一个超参数,控制学习算法将在整个训练数据集中运行**多少次**。
一个时期表示内部模型参数有机会为训练数据集中的每个样本更新一次。根据配置,在一个时期内可以有一个或多个批次。
### 循环
一次迭代就是完成一个时期所需的**批数。例如,如果一个数据集包含被分成 *20* 批的 *10000* 个样本。然后,我们需要多次 *500 次*迭代(10 000 = 500 × 20)来遍历一个历元。**
## 确定批量大小对培训的影响
### GPU 使用和内存
首先,让我们来评估不同的批处理大小对 GPU 使用和 GPU 内存的影响。机器学习[俄亥俄大学的研究人员](https://etd.ohiolink.edu/apexprod/rws_etd/send_file/send?accession=osu1587693436870594)实际评估了增加批量大小对 GPU 利用率的影响。他们使用了 3 个最常用的机器学习框架(TensorFlow、PyTorch 和 MXnet ),然后记录了结果:
**TensorFlow** :
| 批量 | four | eight | Sixteen | Thirty-two | Sixty-four | One hundred and twenty-eight | Two hundred and fifty-six | Five hundred and twelve | One thousand and twenty-four |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| GPU 使用率(%) | 5.65% | 5.65% | 29.01% | 30.46% | 36.17% | 30.67% | 24.88% | 22.45% | 22.14% |
| GPU 内存使用量(MB) | Six hundred and ninety-four point two seven four | Six hundred and sixty-five point eight zero four | Six hundred and forty-nine point one three seven | Six hundred and twenty point nine zero nine | Six hundred and ninety-four point five six three | Six hundred and forty-one | Five hundred and forty-one point three three three | Five hundred and thirty-five point zero six three | Five hundred and thirty-five point zero six three |
**指针** :
| 批量 | four | eight | Sixteen | Thirty-two | Sixty-four | One hundred and twenty-eight | Two hundred and fifty-six | Five hundred and twelve | One thousand and twenty-four |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| GPU 使用率(%) | 9.59% | 9.98% | 11.46% | 11.00% | 13.29% | 13.00% | 9.91% | 11.00% | 9.67% |
| GPU 内存使用量(MB) | Five hundred and twenty point two two two | Five hundred and sixteen point seven eight four | Five hundred and nine point two nine six | Five hundred and thirty | Four hundred and ninety-five point two five | Five hundred and two point six four two | Five hundred and three point four seven eight | Five hundred and twenty-five point eight one eight | Five hundred and fifty-three point five two four |
**MXNet** :
| 批量 | four | eight | Sixteen | Thirty-two | Sixty-four | One hundred and twenty-eight | Two hundred and fifty-six | Five hundred and twelve | One thousand and twenty-four |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| GPU 使用率(%) | 22.13% | 24.65% | 30.22% | 33.14% | 34.91% | 34.83% | 38.33% | 46.09% | 46.09% |
| GPU 内存使用量(MB) | Five hundred and two point four five two | Five hundred and five point four seven nine | Five hundred and thirty-six point six seven two | Six hundred and six point nine five three | Seven hundred and twenty point eight one one | Two thousand four hundred and ninety-five point one five three | Two thousand four hundred and eighty-five point six three nine | Two thousand four hundred and ninety-one point nine two | Two thousand five hundred and eleven point four seven two |
然后将这些结果绘制成图表进行比较
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7465532d8426a689f6361ea4ae27eae3.png)
GPU Usage under 3 frameworks ([Source](https://etd.ohiolink.edu/apexprod/rws_etd/send_file/send?accession=osu1587693436870594))
我们看到,当批量相对较小时,TensorFlow 的 GPU 使用率最低,MXnet 的 GPU 使用率最高。而当批量相对较大时,TensorFlow 的 GPU 利用率最高,PyTorch 最低。此外,我们还可以观察到,随着批处理大小的增长,GPU 消耗也将大幅增长。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6ca50556fb177b9d54cc150a3a07ec40.png)
GPU Memory Usage under 3 frameworks ([Source](https://etd.ohiolink.edu/apexprod/rws_etd/send_file/send?accession=osu1587693436870594))
当批处理大小增加时,我们可以看到 MXnet 比其他两个占用了更多的内存,尽管它们几乎没有变化。大多数时候,MXnet 占用的内存最多。MXnet 将所有批量数据保存在 GPU RAM 中,然后在整个应用程序运行完毕后释放它们,这是一个潜在的解释。另一方面,批量数据处理是 TensorFlow 和 PyTorch 的操作方式。在处理单个时期后,与单个批次相关的数据将从存储器中移除。
增加批处理大小是提高 GPU 使用率的一种直接方法,尽管它并不总是成功的。批量大小的梯度通常在 GPU 上并行计算。因此,只要有足够的内存来容纳一切,大批量就可以提高 GPU 的使用率和训练性能。在 PyTorch 的情况下,GPU 和内存利用率基本保持不变。这是因为 PyTorch 采用了一种分配方法,当批量过大时,这种方法会降低 GPU 利用率、CPU 内存和训练速度,反之亦然。
一般来说,内存消耗随着批处理大小值的增加而增加。
使用的框架,模型的参数和模型本身以及每一批数据都会影响内存的使用。然而,这种**效果**很大程度上取决于**使用的型号**!
### 精确度和算法性能
同一篇学术论文还包括一些关于批量大小及其对训练算法准确性的影响的有趣发现。对于 TensorFlow 框架,研究人员评估了不同批量值的 10 个时期后的准确性:
| 批量 | four | eight | Sixteen | Thirty-two | Sixty-four | One hundred and twenty-eight | Two hundred and fifty-six | Five hundred and twelve | One thousand and twenty-four |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 10 个时期后的准确度 | 32.55% | 45.83% | 63.06% | 68.59% | 68.97% | 69.77% | 64.82% | 57.28% | 51.02% |
然后我们可以在下图中绘制这种变化:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/05b196d214b922914babc689de8bd11b.png)
Accuracy after 10 epoches vs Batch size for Tensorflow framework ([Source](https://etd.ohiolink.edu/apexprod/rws_etd/send_file/send?accession=osu1587693436870594))
评估准确度时,采用不同的批次大小值可能会产生重要影响,在选择批次大小时应予以考虑。使用小批量或大批量可能有两个主要的负面影响。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9392b6ca4e9551ffa5404f7bcf1a5d12.png)
Illustration of Underfitting, Overfitting and Ideal scenarios of model training
**过拟合**
批量大可能导致泛化能力差,模型甚至会陷入局部极小值。由于泛化,神经网络将在训练集之外的样本上表现得相当好。然而,也可能发生相反的情况,神经网络在训练集之外发现的样本上表现不佳,这实质上是过度拟合。从图表中可以看出这种影响;从批量大小 256 开始,该模型偏离绝对最小值,并且在新数据上表现出较低的准确性。一项关于深度学习的大批量训练的研究通过分析检验了同样的现象,并证实了这一发现。
**欠拟合(缓慢收敛)**
小批量可能是学习算法收敛缓慢的原因。在每个阶段执行并使用一批样本计算的变量更新将决定未来一批样本的起始点。因为每一步都涉及从训练集中随机选择训练样本,所以产生的梯度是基于不完整数据的噪声估计。我们在单个批次中使用的数据越少,梯度估计就越嘈杂,越不精确。换句话说,当批量很小时,单个样本对应用的变量更新有更大的影响。换句话说,较小的批量可能会导致学习过程更加嘈杂和不规则,从而延迟学习过程。在上面的例子中,我们可以看到,在 10 个时期后,批量大小为 4 只能使我们达到 32%的准确度,这不如批量大小为 128 所获得的准确度好。
## 如何最大化批量
如前一节所述,批量大小是一个重要的超参数,对模型的拟合或缺失有重大影响。它也可能对 GPU 的使用产生影响。我们可以预计,提高批量大小将提高训练速度(即 GPU 利用率),但这种增益将受到前述收敛问题(过拟合)的限制。
因此,在本节中,我们将讨论一些用于确定最佳批处理大小的策略,这些策略利用了 GPU 所提供的一切,同时避免了过度拟合的极端情况。
### 数据扩充
可以考虑的技术之一是数据扩充。该方法将提供增加批量大小的可能性,同时保持每个时期良好且可接受的精度增加。虽然这项技术的应用因地区而异,但通常需要通过对训练数据进行仔细控制的更改来增加数据集。例如,在图像识别的情况下,可以通过平移、旋转、剪切、调整大小、裁剪甚至翻转训练数据中的图像来放大训练集。
### 保守训练
在大批量训练的情况下,一些优化器的收敛速度比其他的更好。[关于随机优化技术效果的研究](https://dl.acm.org/doi/10.1145/2623330.2623612)表明,标准随机梯度下降(SGD)优化器的收敛速度可以提高,特别是对于大批量。在前面提到的论文中介绍了一种基于每个小批量内保守正则化目标函数的近似优化的方法。此外,研究表明收敛速度不受小批量大小的影响。他们还表明,通过替代优化器的正确实现,我们可以胜过传统的 SGD,例如**亚当**。ADAM 优化方法是一种随机梯度下降扩展,最近在深度学习应用中,尤其是在计算机视觉和自然语言处理中,越来越受欢迎。
### 强健的训练
稳健训练是另一种自然的技术,可以防止在增加批量时出现不良泛化的后果。这些策略试图通过最大化最坏情况(稳健)成本而不是目标函数的名义成本来减少增加批量的影响。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ec48e0f2521c2f2c1e3437fd425b8303.png)
Example of two minima with the different nominal and robust cost ([Source](https://arxiv.org/pdf/1609.04836.pdf))
在前面的例子中,最大稳健最坏情况的位置不同于目标函数的标称最小位置。
### 案例研究:猫狗图像分类
在本节中,我们将使用 TensorFlow/Keras 为猫和狗的图像构建一个分类器。然后,使用标准 SGD 优化器和默认数据格式,针对各种批量值评估模型的性能。然后,使用前面讨论的策略,我们将尝试增加批量大小,同时保持甚至提高精度。
让我们首先准备[数据集](https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_5340.zip)
**下载&解压**
```py
curl https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_5340.zip --output dataset.zip
7z x dataset.zip
```
**分割训练/验证/测试子集**
首先,我们创建文件夹来存储调整后的图像
```py
mkdir -p train/Cat
mkdir -p train/Dog
mkdir -p validation/Cat
mkdir -p validation/Dog
mkdir -p test/Cat
mkdir -p test/Dog
```
然后,使用 Keras 图像预处理库,我们加载图像,将它们调整为 200 像素/200 像素的文件,然后将结果保存在文件夹中。我们每个班总共取 1000 幅图像:600 幅用于训练,200 幅用于验证,200 幅用于测试。
```py
# importing libraries
from keras.preprocessing.image import load_img,save_img,ImageDataGenerator
from os import listdir
from tensorflow import keras
# load dogs vs cats dataset, reshape into 200px x 200px image files
classes = ['Cat','Dog']
photos, labels = list(), list()
files_per_class = 1000
for classe in classes:
i = 0
# enumerate files in the directory
for file in listdir('PetImages/'+classe):
if file.endswith(".jpg"):
# determine class
output = 0.0
if classe == 'Dog':
output = 1.0
# load image
photo = load_img('PetImages/'+classe +'/' + file, target_size=(200, 200))
if i < 600:
save_img('train/'+classe+'/'+file, photo)
elif i < 800:
save_img('validation/'+classe+'/'+file, photo)
else:
save_img('test/'+classe+'/'+file, photo)
i = i + 1
if i == files_per_class:
break
```
**培训&评估模型**
对于模型,我们将使用 Tensorflow/Keras 序列模型。训练和评估过程嵌入在一个函数中,该函数将批量大小和优化器作为输入。这个函数稍后将用于评估优化器对批量大小的影响
```py
def get_accuracy_for_batch_size(opt, batch_size):
print("Evaluating batch size " + str(batch_size))
# prepare iterators
datagen = ImageDataGenerator(rescale=1.0/255.0)
train_it = datagen.flow_from_directory(directory='train/',
class_mode='binary', batch_size=batch_size, target_size=(200, 200))
val_it = datagen.flow_from_directory(directory='validation/',
class_mode='binary', batch_size=batch_size, target_size=(200, 200))
test_it = datagen.flow_from_directory('test/',
class_mode='binary', batch_size=batch_size, target_size=(200, 200))
# create model
model = keras.Sequential()
model.add(keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(200, 200, 3)))
model.add(keras.layers.MaxPooling2D((2, 2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(128, activation='relu', kernel_initializer='he_uniform'))
model.add(keras.layers.Dense(1, activation='sigmoid'))
# compile model
model.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])
# train the model
model.fit(train_it,
validation_data = train_it,
steps_per_epoch = train_it.n//train_it.batch_size,
validation_steps = val_it.n//val_it.batch_size,
epochs=5, verbose=0)
# evaluate model
_, acc = model.evaluate(test_it, steps=len(test_it), verbose=0)
return acc
```
**比较优化器:SGD vs Adam**
对于不同的批量大小值(16、32、64 和 128),我们将在 5 个时期后评估 Adam 和 SGD 优化器的模型准确性。
```py
batch_size_array = [16,32,64,128]
accuracies_sgd = []
optimizer = "sgd"
for bs in batch_size_array:
accuracies_sgd.append(get_accuracy_for_batch_size(optimizer, bs))
accuracies_adam = []
optimizer = "adam"
for bs in batch_size_array:
accuracies_adam.append(get_accuracy_for_batch_size(optimizer, bs))
```
然后,我们将使用 ***Matplotlib*** 绘制结果
```py
import matplotlib.pyplot as plt
plt.plot(batch_size_array, accuracies_sgd, color='green',label='SGD')
plt.plot(batch_size_array, accuracies_adam, color='red',label='Adam')
plt.show()
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c821c059c76be165800bf87f0caed9ac.png)
Accuracy vs batch size for Adam & SGD optimizers
我们可以清楚地看到,对于批量 16, **SGD** 的准确度约为 62%,随着批量的增加,准确度开始迅速下降。这与**亚当**、**T5 的情况不同,准确度下降不太明显,在批量 64 左右,准确度仍在 67%附近,这表示增加了四倍,同时仍保持可接受的准确度。**
但是,我们也可以注意到,对于这两个优化器,从批量大小 128 开始,准确性急剧下降。因此,我们可以推导出,在这个模型中,Adam 的最佳批量是 64,SGD 的最佳批量是 16。
**数据扩充的效果**
让我们首先为扩充的训练和验证数据创建新的目录。
```py
!mkdir -p train_augmented/Cat
!mkdir -p train_augmented/Dog
!mkdir -p validation_augmented/Cat
!mkdir -p validation_augmented/Dog
```
现在,让我们用原始数据和每个图像的新的调整大小和裁剪版本填充这些文件夹。这将导致最终数据集的大小是原始数据集的两倍。所述操作也通过图像预处理 Keras 库来完成:
```py
from tensorflow import image
# load dogs vs cats dataset, reshape into 200x200 files
classes = ['Cat','Dog']
photos, labels = list(), list()
files_per_class = 1000
for classe in classes:
i = 0
# enumerate files in the directory
for file in listdir('PetImages/'+classe):
if file.endswith(".jpg"):
# determine class
output = 0.0
if classe == 'Dog':
output = 1.0
# load image
photo = load_img('PetImages/'+classe +'/' + file, target_size=(200, 200))
photo_resized = photo.resize((250,250))
photo_cropped = photo_resized.crop((0,0, 200, 200))
if i < 600:
save_img('train_augmented/'+classe+'/'+file, photo)
save_img('train_augmented/'+classe+'/augmented_'+file, photo_cropped)
elif i < 800:
save_img('validation_augmented/'+classe+'/'+file, photo)
save_img('validation_augmented/'+classe+'/augmented_'+file, photo_cropped)
else:
save_img('test/'+classe+'/'+file, photo)
i = i + 1
if i == files_per_class:
break
```
现在我们引入一个新的训练和评估函数,它将训练/验证文件夹以及批量大小作为输入。该函数将对所有数据集使用 Adam 优化器。
```py
def get_accuracy_for_batch_size_augmented_data(train_folder,validation_folder, batch_size):
print("Evaluating batch size " + str(batch_size))
# prepare iterators
datagen = ImageDataGenerator(rescale=1.0/255.0)
train_it = datagen.flow_from_directory(directory=train_folder,
class_mode='binary', batch_size=batch_size, target_size=(200, 200))
val_it = datagen.flow_from_directory(directory=validation_folder,
class_mode='binary', batch_size=batch_size, target_size=(200, 200))
test_it = datagen.flow_from_directory('test/',
class_mode='binary', batch_size=batch_size, target_size=(200, 200))
# create model
model = keras.Sequential()
model.add(keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(200, 200, 3)))
model.add(keras.layers.MaxPooling2D((2, 2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(128, activation='relu', kernel_initializer='he_uniform'))
model.add(keras.layers.Dense(1, activation='sigmoid'))
# compile model
model.compile(optimizer="adam", loss='binary_crossentropy', metrics=['accuracy'])
# train the model
model.fit(train_it,
validation_data = train_it,
steps_per_epoch = train_it.n//train_it.batch_size,
validation_steps = val_it.n//val_it.batch_size,
epochs=5, verbose=0)
# evaluate model
_, acc = model.evaluate(test_it, steps=len(test_it), verbose=0)
return acc
```
然后,我们将评估不同批量的准确性。在原始数据和扩充数据集的两种情况下。
```py
batch_size_array = [16,32,64,128,256]
accuracies_standard_data = []
for bs in batch_size_array:
accuracies_standard_data.append(get_accuracy_for_batch_size_augmented_data("train/","validation/", bs))
accuracies_augmented_data = []
for bs in batch_size_array:
accuracies_augmented_data.append(get_accuracy_for_batch_size_augmented_data("train_augmented/","validation_augmented/", bs))
```
最后,我们绘制两个数据集的结果精度值。您可能会在一个免费的 GPU 上耗尽内存,因此[考虑升级到我们的 Pro 或 Growth plan,以访问 Paperspace 上更强大的 GPU。](https://www.paperspace.com/pricing)
```py
import matplotlib.pyplot as plt
plt.plot(batch_size_array, accuracies_standard_data, color='green',label='Standard data')
plt.plot(batch_size_array, accuracies_augmented_data, color='red',label='Augmented data')
plt.legend(bbox_to_anchor =(1.25, 0.8))
plt.show()
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b96667ab73485862a8fb937d470abebb.png)
Accuracy vs batch size for Standard & Augmented data
使用增加的数据,我们可以增加批量大小,同时降低对准确性的影响。事实上,仅用 5 个历元进行训练,我们就可以以 **58%** 的准确度读取批量 **128** ,以 **57.5%** 的准确度读取 **256** 。
由于结合了这两种策略(Adam optimizer 和数据扩充),我们能够采用 128 而不是 16 的批量大小,这是 8 倍的收益,同时保持了可接受的准确率。
## 结论
在本文中,我们讨论了在训练神经网络体系结构时可能出现的批量大小限制。我们还看到了 GPU 的能力和内存容量如何影响这一因素。然后,我们评估了批量增加对模型精度的影响。最后,我们研究了如何确定我们的 GPU/模型组合的理想批量大小。
通常存在一个阈值,超过该阈值,模型的质量会随着用于模型训练的批量的增加而降低。然而,通过一些正确的方法,如案例研究中所示,这种降级可能会传递到更大的批量,从而允许我们增加该参数并更好地利用现有的 GPU 功能。
## 资源
[https://etd.ohiolink.edu/apexprod/rws_etd/send_file/send?登录=osu1587693436870594](https://etd.ohiolink.edu/apexprod/rws_etd/send_file/send?accession=osu1587693436870594)
[https://arxiv.org/pdf/1711.00489.pdf](https://arxiv.org/pdf/1711.00489.pdf)
[https://arxiv.org/pdf/1609.04836.pdf](https://arxiv.org/pdf/1609.04836.pdf)
[https://dl.acm.org/doi/10.1145/2623330.2623612](https://dl.acm.org/doi/10.1145/2623330.2623612)
# 如何在 Chromebook 上运行 Tableau
> 原文:<https://blog.paperspace.com/how-to-run-tableau-on-chromebook/>
## 什么是 Tableau 桌面?
Tableau Desktop 是一个商业分析解决方案,可以可视化数据并提供来自几乎任何数据源的见解。它是为协作而构建的,可以处理大量数据。Chromebooks 和其他瘦客户端为团队提供了廉价的硬件来支持协作,但不能满足 Tableau 桌面的要求。借助 Paperspace,数据分析师团队可以在 Chromebooks(和任何其他设备)上的安全云环境中轻松地进行远程协作。
想要更多曝光?
如果您希望您的数据科学工作流以 Paperspace 为特色,请给我们发送电子邮件至 hello@paperspace.com,或发推特给我们,包括标签#PoweredByPaperspace
## **教程大纲**
* [发动机器](#launch)
1. 机器建议
* [Tableau 桌面需求](#requirements)
1. 推荐的纸张空间机器
* [找到您的产品密钥](#locate)
1. 客户门户
* [下载并安装 Tableau 桌面](#install)
1. 注册
* [GPU 加速用 Tableau 桌面](#gpu)
1. 运动能力
* [结论](#conclusion)
### **1。**创造一种新的纸张空间机器
登录到 Paperspace 后,创建一台新机器。
* 选择最近的*区域*
* 选择一个 Windows *模板*
* 根据您的需要选择*每月*或*每小时*。
* 选择你的*计划*:
* **数据分析:**由于 Tableau Desktop 不像其他平台那样利用 GPU 加速,我们建议至少采用*标准计划*。也可以参考下面的 [GPU 加速数据库](#gpu)。
* 选择您的*存储* —您可以在未来的任何时候增加您的存储,但我们强烈建议您使用 500GB 或更大的容量来管理大型数据集。
* 点击*创建*
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cba185511daf620e96b0dfdcef961853.png)
### **2。** Tableau 桌面需求
要安装 Tableau Desktop,您需要:
* [**安装程序**](https://www.tableau.com/products/desktop)
* **您的产品密钥。**
该字符串将您标识为 Tableau 客户,并且是激活产品所必需的。下面我们将讨论如何找到您的产品密钥。
注意:如果您是学生或教师,您可以访问他们的学术计划页面申请许可证,并获得有关如何下载您自己的 Tableau Desktop 副本的说明。你也可以尝试 14 天的免费试用。
### **3。**找到您的产品密钥
在您购买 Tableau Desktop 后,Tableau 会向您发送一封欢迎电子邮件,其中包含您登录其客户门户所需的信息。您的产品密钥位于客户门户网站中。安装过程完成后,您需要产品密钥来激活 Tableau Desktop。如果您在登录客户入口网站页面时遇到问题,或者如果您在检索产品密钥时需要帮助,请联系客户服务并提供您的姓名、公司、电话号码和电子邮件地址。
按照以下步骤找到您的产品密钥,以便您在进入激活步骤时做好准备。
**查找您的产品密钥**
* 使用您的电子邮件地址和 Tableau 提供的密码登录 Tableau 网站上的客户门户页面。
* 点击**我的钥匙。**
* 从表中的“密钥名称”列复制您的产品密钥。把这把钥匙准备好并保管好。
* 安装并激活 Tableau Desktop 后,您还可以从工具栏菜单的*帮助>管理产品密钥下查看电脑上使用的产品密钥。*
### **4。**下载并安装 Tableau 桌面
要安装 Tableau Desktop:
1. 将安装程序下载到您的 Paperspace 计算机中。
有关 Tableau Desktop 的最新版本,请访问客户门户网站页面。在“产品下载”部分,单击下载用于 Windows 64 位的 Tableau 桌面安装程序。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0e4b7c26bc886cf87ce42ed1762ffc20.png)
2. 运行安装程序。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/63a19a2af4cbda8a38dbb440859bc887.png)
3. 允许更改设备。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/016770f2adcf0e6ba2f8145616583504.png)
4. Tableau 桌面安装过程完成后,打开 Tableau。这将启动 Tableau 注册表单,您可以在其中注册并激活您的产品。
5. 填写注册表上的字段,然后单击激活 Tableau。
6. 删除激活面板中的任何现有文本,复制您的产品密钥(从您在查找产品密钥过程中保存它的位置)并将其粘贴到文本框中,然后单击激活。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fb0309ddb3a3889ae16a49d94e12fbe1.png)
6. 将出现第二个屏幕来完成激活过程。单击继续完成该过程。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4a1b2aeacda23b829f498f8cca5e375c.png)
### **5。【Tableau 桌面的 GPU 加速**
虽然 Tableau Desktop 本身没有利用 GPU,但有 GPU 加速的数据库为 Tableau 提供集成和支持。我们在下面列出了几个例子。
* [**Kinetica**](https://www.kinetica.com/)
* [**MapD**](https://www.mapd.com/)
### **6。**结论
Tableau 提供强大的数据分析工具,可以利用 Paperspace 的同类最佳云桌面实现轻松协作。我们希望你能在[hello@paperspace.com](mailto:hello@paperspace.com)与我们分享你自己的经历。
尽情享受吧!
要使用 Tableau 和 Paperspace 构建您自己的数据科学实验室,[请在此注册。](https://www.paperspace.com/account/signup?utm-campaign=tableaublog)
我们需要你的帮助!
我们正在寻找专注于 IT 工作流和机器学习的内容作家、爱好者和研究人员,以帮助建立我们的社区。给 hello@paperspace.com 发电子邮件,附上写作范例和教学想法
# 如何在 Paperspace 上运行 StyleGAN2-ADA-PyTorch
> 原文:<https://blog.paperspace.com/how-to-set-up-stylegan2-ada-pytorch-on-paperspace/>
阅读本文后,您将能够设置、训练、测试和使用 PyTorch 的最新 StyleGAN2 实现。
这篇文章的结构如下。如果您只想开始运行 StyleGAN2-ADA,请直接跳到第 4 部分。
1. StyleGAN2 是什么?
2. 训练 StyleGAN2 有什么要求?
3. 最新的 StyleGAN2 (ADA-PyTorch)与以前的实现
4. 在图纸空间
1 上设置。创建新笔记本
2。配置笔记本
3。等待笔记本启动
4。创建笔记本文件
5。安装依赖包
6。生成种子
7。开始训练
5. 进一步阅读
# 1.StyleGAN2 是什么?
NVIDIA 的 StyleGAN2 基于一个生成式对抗网络(GAN)。gan 是由 [Ian Goodfellow 和他的同事在 2014 年](https://arxiv.org/abs/1406.2661)设计并推出的。
在 [vanilla GAN](https://docs.paperspace.com/machine-learning/wiki/generative-adversarial-network-gan) 中,一个神经网络(生成器)生成数据,另一个神经网络(鉴别器)试图将生成的数据与原始数据(训练数据)区分开来。
换句话说——一个网络试图愚弄另一个网络,而另一个网络试图发现自己是否被愚弄了。通过这种机制,GANs 能够产生最先进的结果。
有很多 GAN 应用,从数据增强到文本到图像的翻译。GANs 的优势之一是图像生成。在撰写本文时,StyleGAN2-ADA 是用于图像生成的最先进的 GAN 实现( [FID](https://en.wikipedia.org/wiki/Fr%C3%A9chet_inception_distance) 得分为 [2.42](https://arxiv.org/abs/2006.06676) )。
# 2.训练 StyleGAN2 有什么要求?
训练用于图像生成的 GAN 需要大量的计算能力。
建议至少使用一个 16GB 的 NVIDIA V100(更多 RAM 和/或更多 GPU 更好)。
更差的 GPU 可能会导致相同的结果,但会花费很多时间,即使是低分辨率。根据经验,16GB 内存的 NVIDIA Tesla P100 比 V100 多花了将近 4 倍的时间!
| 解决 | 绘图处理器 | 1000 kimg | 25000 kimg | 秒/千克 | GPU 内存 | CPU 内存 |
| --- | --- | --- | --- | --- | --- | --- |
| 128x128 | one | 4h 05m | 4d 06h | 12.8–13.7 | 7.2 GB | 3.9 GB |
| 128x128 | Two | 2h 06m | 2d 04h | 6.5–6.8 | 7.4 GB | 7.9 GB |
| 128x128 | four | 1h 20m | 1d 09h | 4.1–4.6 | 4.2 GB | 16.3 GB |
| 128x128 | eight | 1 小时 13 分钟 | 1d 06h | 3.9–4.9 | 2.6 GB | 31.9 GB |
| 256x256 | one | 6h 36m | 6d 21h | 21.6–24.2 | 5.0 GB | 4.5 GB |
| 256x256 | Two | 3h 27m | 3d 14h | 11.2–11.8 | 5.2 GB | 9.0 GB |
| 256x256 | four | 1h 45m | 1 天 20 小时 | 5.6–5.9 | 5.2 GB | 17.8 GB |
| 256x256 | eight | 1h 24m | 1d 11h | 4.4–5.5 | 3.2 GB | 34.7 GB |
| 512x512 | one | 21 小时 03 分 | 21d 22h | 72.5–74.9 | 7.6 GB | 5.0 GB |
| 512x512 | Two | 10 小时 59 分钟 | 11d 10h | 37.7–40.0 | 7.8 GB | 9.8 GB |
| 512x512 | four | 5h 29m | 5d 17h | 18.7–19.1 | 7.9 GB | 17.7 GB |
| 512x512 | eight | 2h 48m | 2d 22h | 9.5–9.7 | 7.8 GB | 38.2 GB |
| 1024x1024 | one | 1 天 20 小时 | 46d 03h | 154.3–161.6 | 8.1 GB | 5.3 GB |
| 1024x1024 | Two | 23h 09m | 24d 02h | 80.6–86.2 | 8.6 GB | 11.9 GB |
| 1024x1024 | four | 11h 36m | 12d 02h | 40.1–40.8 | 8.4 GB | 21.9 GB |
| 1024x1024 | eight | 5h 54m | 6d 03h | 20.2–20.6 | 8.3 GB | 44.7 GB |
*NVIDIA Tesla V100 上的预期培训时间。*
*来源:*[https://github . com/NV labs/style gan 2-ada-py torch/blob/main/readme . MD](https://github.com/NVlabs/stylegan2-ada-pytorch/blob/main/README.md)
通常,为了达到收敛(模型的最佳状态),需要 25000 个 kimg(其中 1 个 kimg = 1,000 个图像)显示给鉴别器。
在 5000 kimg 左右可以得到合理的结果。
请注意,在接下来的教程中,`train.py`文件[指定了](https://github.com/NVlabs/stylegan2-ada-pytorch/blob/d4b2afe9c27e3c305b721bc886d2cb5229458eba/train.py#L154)相当长的基本配置!
```py
cfg_specs = {
'auto': dict(ref_gpus=-1, kimg=25000, mb=-1, mbstd=-1, fmaps=-1, lrate=-1, gamma=-1, ema=-1, ramp=0.05, map=2),
'stylegan2': dict(ref_gpus=8, kimg=25000, mb=32, mbstd=4, fmaps=1, lrate=0.002, gamma=10, ema=10, ramp=None, map=8),
'paper256': dict(ref_gpus=8, kimg=25000, mb=64, mbstd=8, fmaps=0.5, lrate=0.0025, gamma=1, ema=20, ramp=None, map=8),
'paper512': dict(ref_gpus=8, kimg=25000, mb=64, mbstd=8, fmaps=1, lrate=0.0025, gamma=0.5, ema=20, ramp=None, map=8),
'paper1024': dict(ref_gpus=8, kimg=25000, mb=32, mbstd=4, fmaps=1, lrate=0.002, gamma=2, ema=10, ramp=None, map=8),
'cifar': dict(ref_gpus=2, kimg=100000, mb=64, mbstd=32, fmaps=1, lrate=0.0025, gamma=0.01, ema=500, ramp=0.05, map=2),
}
```
如果计算能力有限,将您的模型训练到 5000 kimg 是一个很好的策略,评估它,调整它,然后再次训练它。一旦你对你的结果感到满意,你可以试着把它训练到 25000 kimg。
此外,要使用您自己的数据集训练模型,您将需要图像。
虽然官方论文指出,一个人只需要几千张图像就可以获得良好的结果,但你将从更多的图像中获得更多样、更准确的结果。
在本教程中,我们将使用 [Met Faces](https://github.com/NVlabs/metfaces-dataset) 图像数据集。请注意[通过 NVIDIA 已经有了 metfaces](https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/) 的预训练模型,所以我们从 metfaces repo 进行训练只是为了提供一个演示!
# 3.最新的 StyleGAN2 (ADA-PyTorch)与以前的实现
我们将在本教程中使用的 StyleGAN2-ADA Pytorch 实现[代码](https://github.com/NVlabs/stylegan2-ada-pytorch)是该算法的最新实现。
第一个实现是在 2017 年推出的渐进式 GAN。2018 年,StyleGAN 作为新版本紧随其后。StyleGAN2 于 2019 年发布,StyleGAN2-ADA 于 2020 年发布。
StyleGAN2-ADA 最初是使用 TensorFlow 实现的。在 2021 年,这个版本被我们将在这里使用的版本所取代 PyTorch 实现。
你可以在这里找到所有版本的完整列表。PyTorch 版本相对于 TensorFlow 版本的一个最重要的优势就是训练速度快了 5%-30%。
# 4.在图纸空间上设置
操作 StyleGAN2 代码最简单的方法就是使用笔记本。因此,我们将使用[纸张空间渐变](https://gradient.paperspace.com/)笔记本环境。
如果您不熟悉,Paperspace 的渐变笔记本只是托管在 Paperspace CPU 和 GPU 实例上的 Jupyter 笔记本。
今天,我们将使用 Paperspace [V100 实例](https://gradient.paperspace.com/instances),但也可以使用任何 GPU 实例,包括 Paperspace 的[免费 GPU 实例](https://gradient.paperspace.com/free-gpu)——只是要注意,StyleGAN2 是非常计算密集型的,可能需要非常长的时间来训练较少的 GPU。
我们开始吧!
## 1.创建新笔记本
首先,你需要在 Gradient 中创建新的笔记本。
这里我们使用的是我们的[团队工作空间](https://docs.paperspace.com/gradient/more/overview/teams/creating-a-team),而不是我们的个人工作空间。我们将通过导航到`Notebooks`选项卡并选择`Create`来创建一个笔记本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0890e8f44213e6e62a7e9bd4ccf64577.png)
Select the team you'd like to use, navigate to the Notebooks tab, and then create a new notebook
## 2.配置笔记本
接下来,我们将为笔记本命名并选择 PyTorch 1.8 运行时,它将预装许多 PyTorch 助手。我们还将指定我们想要手动使用的 PyTorch 版本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a643bdc59bd05ecc9411b075e85b69ba.png)
Give your notebook a name and select the PyTorch runtime
然后我们应该选择 [V100 GPU](https://gradient.paperspace.com/instances) 并配置一个至少 12 小时的[自动关闭](https://docs.paperspace.com/gradient/get-started/tutorials-list/getting-started-with-gradient-notebooks-old#launching-a-notebook-instance)周期。如果你想从头开始训练你的网络,考虑至少 12-24 小时。
请注意,一旦达到自动关机时间间隔,无论您是否仍在笔记本中工作,它都会保存并停止您的笔记本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dc128c583741f50dcf8392456845d0bd.png)
Select the V100 instance and at least 12 hours for auto-shutdown interval
接下来我们将打开高级选项,这样我们就可以插入一个自定义的`Workspace URL`。这将告诉 Paperspace 将 GitHub repo 的所有内容提取到新笔记本中。
在这里,我们将输入:
```py
https://github.com/NVlabs/stylegan2-ada-pytorch.git
```
梯度笔记本现在将自动下载整个回购到我们的新笔记本!
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7ce730e34fa6f41334b7c84101d4b1b6.png)
Add the StyleGAN2-ADA PyTorch GitHub repository to the custom workspace field
## 3.等待笔记本启动
接下来,Paperspace 需要几分钟时间来复制 GitHub 存储库并启动 PyTorch 1.8 容器。耐心点!
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a1f79cdc5340a284b14078fbe1c4f793.png)
It will take a few minutes to copy over the repository and build the notebook from the PyTorch container
现在容器正在运行,我们可以切换到实例选择器,查看 CPU 和 RAM 的使用情况。此信息也可以在底部状态栏上找到。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1336595ff5b18624eb598411b944cdad.png)
System metrics are available in the instance selector panel
## 4.创建笔记本文件
接下来,我们将创建一个新的。ipynb 文件来执行我们的工作。确保创建一个以`.ipynb`结尾的文件。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/372df84ae07a4896226045031b64e230.png)
Select the `New File` button in the file manager in the sidebar and give your new file a name
## 5.安装依赖包
在撰写本文时,存储库的[最新提交](https://github.com/NVlabs/stylegan2-ada-pytorch/commit/25063950fc72acaa9740ba936379b36df1c5e456)使用以下依赖关系:
* 64 位 Python 3.7 和 PyTorch 1.7.1
* 包:点击请求 tqdm pyspng 忍者 imageio-ffmpeg==0.4.3
Paperspace 笔记本附带 Python 3.8 和 Anaconda。让我们确认这个事实:
```py
!python --version
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/58c7bcab89f2625b6f4360bdc916cedd.png)
First we check to see what version of Python is installed in our notebook
好极了。看起来我们在这个实例上有 Python 3.8.5,所以我们应该降级到 Python 3.7 以避免任何冲突。
```py
!conda install -y python=3.7
```
这里我们包含了`-y`标签,以确保我们提前确认了安装选项。我们这样做是因为我们从笔记本而不是从终端运行这个命令。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c38d1b2bed30ef3365892481ba1d820f.png)
We downgrade to Python 3.7
现在,我们再次检查以确保 Python 安装正确
```py
!python --version
```
这应该会返回一个 Python 3.7.x 版本。如果没有,请再次尝试上述命令或重新创建笔记本。
我们还可以检查我们的 GPU,看看我们的 GPU 设置是否成功。为此,请执行以下命令:
```py
!nvidia-smi
```
您的输出应该如下所示:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/582fd63d44839d7a383095f48c39bc1d.png)
Check to make sure the notebook has GPU attached
你应该会看到 GPU 名称“Tesla V100-PCIE”。如果您看不到此 GPU(或您选择的 GPU),请重新创建您的笔记本并确保您选择了 GPU。
接下来,我们将创建一个名为`requirements.txt`的文件,在一个地方管理我们的依赖关系。
再次使用“新建文件”按钮,并将文件命名为:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8837022fc96d686d89d4e6ed6d03e493.png)
Create a new file in the root called "requirements.txt"
接下来,我们切换到刚刚创建的文件,并添加我们的依赖项:
```py
install
click
requests
tqdm
pyspng
ninja
imageio-ffmpeg==0.4.3
torch==1.7.1+cu110
torchvision==0.8.2+cu110
torchaudio===0.7.2
```
确保使用 CMD + S 保存文件!
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/82b9f70c415b2a2bcc5e51f60c665cae.png)
Save the .txt file with CMD + S
一旦我们保存了`requirements.txt`,我们运行命令:
```py
!pip install -r ./requirements.txt -f https://download.pytorch.org/whl/torch_stable.html
```
这告诉笔记本安装依赖项列表。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fee9fa6b52971119f5f35e36c90f0b45.png)
Install dependencies from requirements.txt file
太好了!
## 6.生成种子
为了检查一切是否正常,让我们使用预训练的 metfaces 模型并创建几个样本:
```py
!python generate.py --outdir=out --trunc=1 --seeds=85,265,297 --network=https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/metfaces.pkl
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/faab6eb9d531ab1c393b911ce7dd2de5.png)
Generate seed images using the pre-trained metfaces model
在新创建的文件夹`out/`中,您可以找到您的样本!太棒了。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a103339d9b8bb71ad5f9b4560b6bf5bd.png)
Sample image created via the pretrained metfaces model that comes with this repository.
## 7.开始训练
要开始我们自己的训练,我们应该首先上传您的数据集。
如前所述,出于本教程的目的,我们将使用 [Metfaces 数据集](https://github.com/NVlabs/metfaces-dataset),但请记住已经有一个使用 metfaces 的预训练模型,因此在该数据集上的训练不一定会有成效。
为了方便起见,我们在这里上传了 metfaces 数据[的 zip 文件,我们将使用这个端点将图像集上传到我们的笔记本中。](https://s3.amazonaws.com/ps.public.resources/ml-showcase/stylegan2-ada-pytorch/n02106166-Border_collie.zip)
首先,我们从链接上传压缩数据集:
```py
!wget https://s3.amazonaws.com/ps.public.resources/ml-showcase/stylegan2-ada-pytorch/n02106166-Border_collie.zip
```
上传 zip 文件后,我们将使用三个变量:
```py
raw_dataset_path = /notebooks/images.zip # change name of images.zip
dataset_path = /notebooks/dataset_ready
log_path = /notebooks/log_folder
```
然后,我们可以将我们的图像转换成正确的格式。
官方存储库提供了一个工具,因为宽度和高度应该是 x 值。如果你最初的图像不是方形的,这会导致奇怪的图像。
不同的图像尺寸有不同的解决方案(请阅读[官方文档](https://github.com/NVlabs/stylegan2-ada-pytorch))。
现在,我们将创建宽度和高度为 64px 的图像:
```py
!python dataset_tool.py --source {raw_dataset_path} --dest {dataset_path} --width=64 --height=64
```
对于更高的分辨率,这可能需要相当长的时间。
做完了,现在是时候从头开始训练我们的网络了!
当我们在 V100 上运行本教程时,需要几个 ***小时*** 才能达到 kimg = 1000。
所以请注意,这个模型可能需要一些时间来训练-特别是如果你使用的东西没有 V100 强大。
也就是说,我们现在可以通过运行以下命令从头开始训练网络:
```py
!python train.py --outdir={log_path} --data={dataset_path} --gpus=1
```
在“log_folder”文件夹中,我们可以找到不同的文件。最有趣的是 fakesxxxxxx.png 档案。这个文件向我们展示了由我们新训练的模型创建的不同样本。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f35c101d1c97e8ac3af5a6ed25d24afb.png)
Images within log_folder get better and better each 200 kimg sample
随着你越来越多地训练模型,你会看到那些图像变得越来越好。
默认情况下,每 200 kimg 创建一个样本和检查点。这也意味着你不必担心失去你的进步。您可以随时使用`--resume={insert_path_to_pkl}`标志继续训练(点击了解更多信息[)。](https://github.com/NVlabs/stylegan2-ada-pytorch/blob/main/docs/train-help.txt)
为了充分利用您的模型,您应该阅读 StyleGAN 资源库的官方文档[。他们非常详细地解释了不同的功能以及如何应用它们。](https://github.com/NVlabs/stylegan2-ada-pytorch)
# 5.进一步阅读
感谢您的阅读!
除了官方资料库,我强烈推荐你查看 YouTube 频道[人工图像](https://www.youtube.com/user/bustbright/videos)。他有很多很好的教程和讲解。
更技术性的理解,我推荐阅读[官方研究论文](https://arxiv.org/abs/2006.06676)。
# 如何在 YouTube 上流式播放&用 OBS 抽动
> 原文:<https://blog.paperspace.com/how-to-stream-on-youtube-twitch-with-obs/>
开放广播软件(OBS)是最流行的视频录制和直播工具之一。它是免费和开源的,易于设置,并内置了对 Twitch 和 YouTube 等最常见的直播平台的支持。在本指南中,我们将从头到尾介绍如何使用 OBS Studio 开始直播。
[Open Broadcaster 软件](https://obsproject.com/)(即 OBS)是最流行的视频录制和直播工具之一。OBS 是免费和开源的,易于设置,并内置了对 Twitch 和 YouTube 等最常见的直播平台的支持。在本指南中,我们将从头到尾介绍如何使用 OBS Studio 开始流媒体播放。
想要更多曝光?
如果你想让你的信息流被 Paperspace 收录,请给我们发邮件到 hello@paperspace.com,或者发推特给我们,附上#PoweredByPaperspace 的标签
## **教程大纲**
* [推出一款机子](#launch)
* 高性能:游戏流媒体
1. 基本:音频和简单应用流
* [安装 OBS](#install)
* 下载安装 OBS
1. 下载并安装 Visual Studio 2013 运行时
* [配置 OBS](#configuring)
* 比特率
* 编码
* 设置您的场景
1. 解决
* [连接到 Twitch](#twitch)
* 配置 OBS 连接到 Twitch
1. 找到您的流密钥
* [连接 YouTube](#youtube)
* 配置 OBS 连接 YouTube
1. 找到您的流密钥
* [结论](#conclusion)
* * *
### **1。**创造一种新的纸张空间机器
登录到 Paperspace 后,创建一台新机器。
* 选择最近的*区域*
* 选择一个 Windows *模板*
* 选择*每月*或*每小时*取决于你的流媒体播放频率
* 选择你的*计划*:
* **音频和非图形密集型应用(基本):**如果您正在传输非密集型应用,如代码编辑器、在线扑克游戏或音频混音等。,这些*标准 GPU* 机器( *Air* 、*标准*、*高级*和 *Pro* )工作起来棒极了。在这种情况下,您将在 OBS 中使用软件编码。您无法在这一层上运行任何高端游戏,也无法获得与 GPU 编码一样高的比特率。如果这听起来像你想要的设置,那么*高级*计划是最受欢迎的。
* **游戏流(高性能):**如果您正在运行图形密集型游戏,如《巫师》、《守望先锋》等。,并希望向您的观众交付高比特率流,需要一台*专用 GPU* 机器( *GPU+* 、 *P5000* 和 *P6000* )。 *GPU+* 是中端游戏的理想选择,而 *P5000* 和 *P6000* 旨在实现最高性能。
* 选择您的*存储空间* —您可以在未来随时增加存储空间
* 点击*创建*
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cba185511daf620e96b0dfdcef961853.png)
### **2。**安装 OBS
**步骤 1 -先决条件**
请下载并运行所需的 [Vistual Studio 运行时](https://obsproject.com/visual-studio-2013-runtimes)的**两个**,以便继续设置:
[Visual Studio 2013 运行时[64 位] - vcredist_x64.exe](https://obsproject.com/downloads/vcredist_x64.exe)
[Visual Studio 2013 运行时[32 位] - vcredist_x86.exe](https://obsproject.com/downloads/vcredist_x86.exe)
注意:*即使您有 64 位版本的 Windows,也应该安装 32 位和 64 位版本。*
现在您已经准备好安装 OBS Studio 了:
**步骤 2 -安装 OBS Studio**
[下载链接](https://obsproject.com/download)
使用默认设置运行安装程序。在最后一步,取消选中“运行 OBS studio”
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b8c54c3faf8a212e2b909474c8b4ba24.png)
为什么?默认情况下,OBS 以 32 位模式启动,没有 64 位选项快。单击开始菜单并启动 64 位版本:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b7809e16a369e29e9c852a0ed17b3bf7.png)
当 OBS 启动时,您可以忽略自动配置向导,因为我们将手动配置 OBS。如果您想在以后启动该向导,可以在工具下拉列表中找到它:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/210f34b2ec8b867a4d68483cc7214bb6.png)
### **3。**配置 OBS
要启动一个基本流,您只需要在 OBS 中配置一些东西。对于 OBS 中更高级的定制,查看他们的指南或其他在线教程。
我们将从选择流的分辨率开始。单击右下方的设置图标:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9c600594f3996506a93b15ed0c270684.png)
在视频选项卡上,选择所需的基本分辨率和输出分辨率:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3e37edae058bf4fb68e6f0b210b80a2c.png)
接下来,我们将跳转到*输出*选项卡,选择*视频比特率*和*编码器*。
默认比特率 2,500 足以满足大多数情况,但可以根据您的喜好增加/减少。
编码器取决于运行的[机器的类型。](#launch)
* 如果您使用的是*标准 GPU* 机器( *Air* 、*标准*、*高级*和 *Pro* ),您需要选择 ***软件(x264)*** 选项,该选项将使用 CPU 对流进行编码(较慢)。
* 如果您使用的是*专用 GPU* 机器( *GPU+* 、 *P5000* 和 *P6000* ),您应该选择 ***硬件(NVENC)*** 选项,该选项将使用 GPU 来编码流(更快)。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/734d2b19411a33a28c4d740c471201bd.png)
关闭设置菜单,我们将创建一个*信号源*。这基本上是我们将要捕获的屏幕的一部分。单击“+”号开始:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/847b9c54c67265872c0bc2d250a1e382.png)
对于各种类型的流,这里有几个选项:
* *显示捕获*将捕获整个显示,但不是全屏游戏
* *游戏捕捉*用于全屏捕捉游戏
* *窗口捕获*用于捕获您正在运行的特定窗口(应用程序)
其他模式不太常见。更多信息请参考 OBS 工作室的[文档](https://obsproject.com/forum/)。在这个演示中,我们将使用*显示捕捉*模式。单击“确定”添加源:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f91f56bc81d46b5a0d8f3e37187b8914.png)
再次单击“确定”接受属性:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/705a3a9b5378a18a1e73e0cf23b597df.png)
通过抓取红色轮廓并将其拉进以适应黑盒(场景)来完成流的合成:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e2ca9314d3bf32b8785d8bbc1fd16407.png)
就是这样!您现在可以开始流式播放了。
### **4。**连着抽动
首先导航到抽动[设置页面](https://www.twitch.tv/papertest1/dashboard/settings)。在这里,您将看到一个显示您的密钥的选项,我们会将该密钥插入 OBS:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f986293d69c056c2fd6d72f923320387.png)
**请注意:** *确保不要与任何人共享您的流密钥,否则他们将能够流至您的 Twitch 频道。*
复制密钥并导航回 OBS 中的*流*选项卡。在*服务*下,选择*抽动*。然后选择最近的服务器(在本例中,*美国东部:纽约,NY* ,然后将您的流密钥粘贴到*流密钥*字段:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8f7d5b0d1adb31bb8f0940b764361875.png)
你都准备好了!只需点击“开始播放”即可开始播放:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1141d174fdd9483fdb03ff02ac86d3fc.png)
### **5。**连接到 YouTube
首先导航到 [YouTube 直播仪表盘](https://www.youtube.com/live_dashboard)。在页面底部的*编码器设置*部分,你会找到你的*流键*。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2d4aec4d9a22b1c2f0b28c6fadff22b4.png)
**请注意:** *确保不要与任何人共享您的流密钥,否则他们将能够传输到您的 YouTube 频道。*
复制密钥并导航回 OBS 中的*流*选项卡。在*服务*下,选择 *YouTube / YouTube 游戏*。在*服务器*下,选择*主 YouTube 摄取服务器*,然后将您的流密钥粘贴到*流密钥*字段:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bbdd624d63d84c92192dc61d290e7e2c.png)
你都准备好了!只需点击“开始播放”即可开始播放:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1141d174fdd9483fdb03ff02ac86d3fc.png)
### **6。**结论
在短短几分钟内,我们就能够使用 OBS Studio 和 Twitch/YouTube 启动并运行实时流。如您所见,OBS 消除了屏幕捕获组件的麻烦。尽管 OBS 使用起来非常简单,但它仍然具有值得探索的高级功能。尽情享受吧!
要开始自己的直播,请在此注册。
# 如何训练问答机学习模型(BERT)
> 原文:<https://blog.paperspace.com/how-to-train-question-answering-machine-learning-models/>
问答模型是机器或深度学习模型,可以在给定一些上下文的情况下回答问题,有时没有任何上下文(例如开放域 QA)。他们可以从段落中提取答案短语,概括地解释答案,或者从给定选项列表中选择一个选项,等等。这完全取决于它被训练的数据集(例如[小队](https://rajpurkar.github.io/SQuAD-explorer/)、 [CoQA](https://stanfordnlp.github.io/coqa/) 等)。)或者它被训练的问题,或者在某种程度上神经网络架构。因此,举例来说,如果你将这一段(上下文)输入到你的模型中,该模型被训练来从上下文中提取答案短语,并问一个类似“什么是问答模型?”,它应该输出这一段的第一行。
这种模型需要理解语言的结构,对上下文和问题有语义理解,有能力定位回答短语的位置,等等。所以毫无疑问,训练执行这些任务的模型是很困难的。幸运的是,神经网络中的注意力概念已经成为这种困难任务的救命稻草。自从[为序列建模任务引入](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html#a-family-of-attention-mechanisms)以来,出现了许多具有复杂注意机制的 RNN 网络,如 [R-NET](https://www.microsoft.com/en-us/research/wp-content/uploads/2017/05/r-net.pdf) 、 [FusionNet](https://arxiv.org/pdf/1711.07341.pdf) 等。在 QA 任务中表现出很大的改进。然而,一种基于注意力,特别是自我注意力的全新神经网络架构,称为 [Transformer](https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html) ,已经成为 NLP 中真正的游戏规则改变者。在这里,我将讨论一个名为 BERT 的 Transformer 架构的变体,简要概述其架构,如何执行问答任务,然后编写代码来训练这样一个模型,以回答研究论文中与新冠肺炎相关的问题。
你可以从 [ML Showcase](https://ml-showcase.paperspace.com/projects/answer-covid-19-questions-using-bert) 跟随代码,并在渐变社区笔记本中免费运行它。让我们开始吧。
## 语言模型和转换器
在跳到 BERT 之前,让我们了解一下什么是语言模型,变形金刚是如何进入画面的。
语言模型是一种概率模型,它基于在训练期间看到的文本示例来学习句子或标记序列出现的概率。例如:
> P( *没有杀死我们的使我们更强大*)= P(*That*)P(*which | That*)P(*does | That,which* )P( *not|That,which,does* ...
> P( *没有杀死我们的东西让我们更强大* ) = 0.65
如果这些语言模型足够大,并在足够大的数据集上进行训练,它们可以很好地理解任何语言及其复杂性。传统上,由于语言的顺序结构,rnn 被用于训练这种模型,但是它们训练缓慢(由于每个标记的顺序处理),并且有时难以收敛(由于消失/爆炸梯度)。
然而,变形金刚的不同变体,凭借其并行处理令牌的能力以及由于自我注意机制和不同的预训练目标而产生的令人印象深刻的性能,已经使得训练大型模型(有时[非常非常大型的模型](https://arxiv.org/abs/2005.14165))成为可能,这些模型理解自然语言非常好。不同的基于 Transformer 的语言模型,在架构和预训练目标上有微小的变化,在不同类型的任务上表现不同。 [BERT](https://ai.googleblog.com/2018/11/open-sourcing-bert-state-of-art-pre.html) (来自变压器的双向编码器表示)就是这样一个模型。BERT 已经使用 Transformer 编码器架构进行了训练,具有掩蔽语言建模(MLM)和下一句预测(NSP)预训练目标。
## 伯特及其变体
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/88f0ad107a1e699b313a45fa91f42c31.png)
BERT Architecture ([source](https://arxiv.org/pdf/1810.04805.pdf))
现在我们已经知道了 BERT 是什么,让我们简单了解一下它的体系结构和培训前目标。BERT 使用来自[原始变压器纸](https://arxiv.org/abs/1706.03762)的变压器编码器。一个编码器有一堆编码器模块(其中一个模块的输出作为下一个模块的输入),每个编码器模块由两个神经网络层组成。首先是自我关注层(这是使变形金刚如此强大的神奇操作),然后是简单的前馈层。每一层之后都有一个残差连接和一个图层归一化操作,如下图所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/42b6675840ea404d349ce2cff8b375e6.png)
One Encoder Block ([source](http://jalammar.github.io/illustrated-transformer/)). Here X1, X2 are input vectors. One vector for each token.
因此,对于每个编码器层,输入向量和输出向量的数量(最大限制为 512)总是相同的。并且在第一编码器层之前,通过添加记号嵌入、位置嵌入和片段嵌入来获得每个记号的输入向量。使用矩阵乘法在每个编码器层内并行处理这些向量,并将获得的输出向量馈送到下一个编码器块。通过 *N* 这样的块顺序处理后,得到的输出向量开始很好的理解自然语言。
这是对 BERT 架构的一个非常压缩的概述,只关注我们理解博客其余部分所需的想法。关于各层如何发生不同操作、多头自我关注以及理解并行令牌处理的更详细讨论,请查看 [Jay Alammar 的博客](http://jalammar.github.io/illustrated-transformer/)。
### 培训前目标
预训练目标是一项任务,在为最终任务进行微调之前,对模型进行训练。GPT 模型在生成性预训练任务(因此命名为 GPT)上被训练,即在被微调到例如 SST-2(句子分类数据)上以分类句子之前,在给定先前记号的情况下生成下一个记号。
同样,BERT 将 MLM 和 NSP 作为其预培训目标。它使用一些特殊的令牌,如 CLS、SEP 和 MASK 来完成这些目标。在我们完成培训前目标时,我们将看到这些令牌的使用。但是在继续之前,我们应该知道,馈送到 BERT 的每个标记化样本在开始时附加有 CLS 标记,并且来自 BERT 的 CLS 的输出向量用于不同的分类任务。现在让我们从 MLM 开始。
在 MLM 目标中,一定百分比的标记被屏蔽,即被特殊的标记屏蔽代替,并且模型被要求预测正确的标记来代替屏蔽。为了实现这一点,在最终编码器块上增加了屏蔽语言模型头,它只为屏蔽标记的输出向量(从最终编码器块输出)计算词汇上的概率分布。在《NSP》中,两个被标记化的句子和附加在它们末尾的 SEP 标记被连接起来,并被送给 BERT。然后,使用 CLS 令牌的输出向量来计算句子对中的第二个句子是否是原始文档中的后续句子的概率。对于这两个目标,使用 AdamW 优化器的标准交叉熵损失来训练权重。
与其他预训练目标(例如,生成性预训练目标)相比,上述预训练目标在捕捉自然语言的语义方面非常强大。因此,许多具有类似或稍微调整的预训练目标的模型,具有或多或少与 BERT 相同的架构,已经被训练以在许多 NLP 任务上实现 SOTA 结果。[罗伯塔](https://arxiv.org/abs/1907.11692)、[斯潘伯特](https://www.cs.princeton.edu/~danqic/papers/tacl2020.pdf)、[迪翁伯特](https://arxiv.org/abs/1910.01108)、[艾伯特](https://arxiv.org/abs/1909.11942)等。是其中的几个。
在对这些预训练目标进行训练后,这些模型在特殊任务上进行微调,如问题回答、命名实体识别等。在这里,我们将看到如何训练伯特回答问题的目标。
## 问题回答目标
### 数据集
如前所述,QA 任务可以用不同的方式构建。在这里,我将重点关注基于上下文的问题回答,即从给定的段落中提出问题。SQuAD 是这项任务的一个流行数据集,它包含许多文本段落、与段落相关的不同问题、它们的答案以及段落中答案的起始索引。SQuAD 有两个版本,SQuAD1.1 和 SQuAD2.0,主要区别在于 SQuAD2.0 包含超过 50,000 个无法回答的问题,这些问题看起来与可回答的问题相似。因此,要在 SQuAD2.0 上做得好,系统不仅必须尽可能地回答问题,还必须确定段落何时不支持任何答案,并放弃回答。这两个数据集都是公开的,可以从[这里](https://rajpurkar.github.io/SQuAD-explorer/)下载。在这里,我将使用 SQuAD2.0。
### 伯特班建筑
为了执行问答任务,我们在 BERT 上添加了一个新的问题回答头,就像我们添加了一个掩蔽语言模型头来执行 MLM 任务一样。该问答头的目的是找到给定段落的答案的开始标记和结束标记,例如:
> BERT-large 真的很大...它有 24 层,嵌入大小为 1024,总共有 340M 个参数!总共有 1.34GB,所以预计下载需要几分钟。
> **问题**:BERT-large 有几个参数?
> **回答** : 340M 参数
> **开始令牌** : 340
> **结束令牌**:参数
介于两者之间的所有内容,包括开始和结束标记,都被视为答案。
在问答头部中有两组权重,一组用于开始标记,另一组用于结束标记,它们与输出嵌入具有相同的维数。所有标记的输出嵌入被馈送到这个头部,并且分别计算它们与开始和结束标记的权重集之间的点积。换句话说,取开始标记权重和输出嵌入之间的点积,也取结束标记权重和输出嵌入之间的点积。然后,应用 softmax 激活,以在开始和结束记号集(每个集合也是单独的)的所有记号上产生概率分布。具有最大概率的记号被分别选为开始和结束记号。在此过程中,可能会出现结束标记出现在开始标记之前的情况。在这种情况下,输出一个空字符串作为预测答案。下面的数字应该使操作更加清晰。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/997bac71c3ecdb36a1308fb1832e6af7.png)![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/117d839bc6b56e2698928e62867642ee.png)
Question Answering Head Separately for Start and End Token ([source](https://mccormickml.com/2020/03/10/question-answering-with-a-fine-tuned-BERT/#part-1-how-bert-is-applied-to-question-answering))
在流行的实现中,这个头被实现为前馈层,它接受与 BERT 输出嵌入相同维度的输入,并返回一个二维向量,该向量然后被馈送到 softmax 层。使用开始和结束令牌的交叉熵损失来微调完整的 BERT 小队模型。
## 训练问答模型
我们将使用拥抱脸的[变形金刚](https://github.com/huggingface/transformers/)库来训练我们的 QA 模型。我们还将使用 BioBERT,这是一种基于 BERT 的语言模型,唯一的区别是它已经根据 MLM 和 NSP 的目标对一般&生物医学领域语料库的不同组合进行了微调。不同的领域有特定的术语,这些术语在标准英语中很少出现,如果它们出现,可能意味着不同的事情,或者意味着不同的上下文。因此,像 BioBERT,LegalBERT 等模型。已经被训练来学习特定领域文本的这种细微差别,使得特定领域的 NLP 任务可以更准确地执行。
在这里,我们旨在使用质量保证模型从新冠肺炎研究文献中提取相关信息。因此,我们将使用拥抱脸的变形金刚库对 SQuADv2 数据进行微调。
在变形金刚库的示例部分,Hugging Face 已经提供了一个脚本`run_squad.py`,用于在小队数据上训练 QA 模型。使用下面的命令可以很容易地运行这个脚本。
您也可以从 [ML Showcase](https://ml-showcase.paperspace.com/projects/answer-covid-19-questions-using-bert) 的渐变社区笔记本中免费运行代码。
```py
python run_squad.py \
--model_type bert \
--model_name_or_path monologg/biobert_v1.1_pubmed \
--do_train \
--do_eval \
--train_file train-v2.0.json \
--predict_file dev-v2.0.json \
--per_gpu_train_batch_size 24 \
--per_gpu_eval_batch_size 24 \
--learning_rate 3e-5 \
--num_train_epochs 4 \
--max_seq_length 384 \
--doc_stride 128 \
--save_steps 1500 \
--output_dir ./biobert \
--overwrite_output_dir \
--thread 32 \
```
人们可以从它们的名字中理解大多数参数。关于参数的更多细节和可以调整的参数的详尽列表,可以参考`run_squad.py` [脚本](https://github.com/huggingface/transformers/blob/master/examples/question-answering/run_squad.py)。
使用这个脚本,可以很容易地对模型进行微调,以执行 QA 任务。然而,运行这个脚本占用大量内存,因为`squad_convert_examples_to_features`试图一次处理完整的队伍数据,需要超过 12GB 的内存。所以,我修改了`load_and_cache_examples`,增加了一个名为`read_saved_data`的新功能,可以批量处理阵容数据。你可以看看下面的这些方法。
`load_and_cache_examples`:
```py
def load_and_cache_examples(args, tokenizer, evaluate=False, output_examples=False):
if args.local_rank not in [-1, 0] and not evaluate:
# Make sure only the first process in distributed training process the dataset, and the others will use the cache
torch.distributed.barrier()
# Load data features from cache or dataset file
input_dir = args.data_dir if args.data_dir else "."
cached_features_file = os.path.join(
input_dir,
"cached_{}_{}_{}".format(
"dev" if evaluate else "train",
list(filter(None, args.model_name_or_path.split("/"))).pop(),
str(args.max_seq_length),
),
)
root_dir = os.path.join(cached_features_file+"_dir")
features_file = os.path.join(root_dir,'features')
datasets_file = os.path.join(root_dir,'datasets')
examples_file = os.path.join(root_dir,'examples')
# Init features and dataset from cache if it exists
if os.path.exists(cached_features_file) and not args.overwrite_cache:
logger.info("Loading features from cached file %s", cached_features_file)
features_and_dataset = torch.load(cached_features_file)
features, dataset, examples = (
features_and_dataset["features"],
features_and_dataset["dataset"],
features_and_dataset["examples"],
)
if output_examples:
return features, dataset, examples
else:
return dataset
elif os.path.exists(datasets_file) and not output_examples and not args.overwrite_cache:
pass
elif os.path.exists(datasets_file) and os.path.exists(features_file) and os.path.exists(examples_file) and output_examples and not args.overwrite_cache:
pass
else:
logger.info("Creating features from dataset file at %s", input_dir)
if not args.data_dir and ((evaluate and not args.predict_file) or (not evaluate and not args.train_file)):
try:
import tensorflow_datasets as tfds
except ImportError:
raise ImportError("If not data_dir is specified, tensorflow_datasets needs to be installed.")
if args.version_2_with_negative:
logger.warn("tensorflow_datasets does not handle version 2 of SQuAD.")
tfds_examples = tfds.load("squad")
examples = SquadV1Processor().get_examples_from_dataset(tfds_examples, evaluate=evaluate)
else:
processor = SquadV2Processor() if args.version_2_with_negative else SquadV1Processor()
if evaluate:
examples = processor.get_dev_examples(args.data_dir, filename=args.predict_file)
else:
examples = processor.get_train_examples(args.data_dir, filename=args.train_file)
for i,j in enumerate(range(0,len(examples),args.data_process_batch)):
sub_examples = examples[j:j+args.data_process_batch]
features, dataset = squad_convert_examples_to_features(
examples=sub_examples,
tokenizer=tokenizer,
max_seq_length=args.max_seq_length,
doc_stride=args.doc_stride,
max_query_length=args.max_query_length,
is_training=not evaluate,
return_dataset="pt",
threads=args.threads,
)
if args.local_rank in [-1, 0]:
if not os.path.exists(os.path.join(features_file)):
os.makedirs(os.path.join(features_file))
if not os.path.exists(os.path.join(datasets_file)):
os.makedirs(os.path.join(datasets_file))
if not os.path.exists(os.path.join(examples_file)):
os.makedirs(os.path.join(examples_file))
logger.info("Saving features into cached files %s, %s, %s", os.path.join(features_file,'features_'+str(i)),os.path.join(datasets_file,'datasets_'+str(i)),os.path.join(examples_file,'examples_'+str(i)))
torch.save({"features": features}, os.path.join(features_file,'features_'+str(i)))
torch.save({"datasets": dataset}, os.path.join(datasets_file,'datasets_'+str(i)))
torch.save({"examples": sub_examples}, os.path.join(examples_file,'examples_'+str(i)))
if args.local_rank == 0 and not evaluate:
# Make sure only the first process in distributed training process the dataset, and the others will use the cache
torch.distributed.barrier()
return read_saved_data(root_dir,evaluate=evaluate,output_examples=output_examples)
```
`read_saved_data`:
```py
def read_saved_data(input_dir,evaluate=False,output_examples=False):
from torch.utils.data import TensorDataset
if output_examples:
feat="features;datasets;examples"
else:
feat="datasets"
all_features = {"features":[],"examples":[],"datasets":[]}
all_input_ids = torch.tensor([], dtype=torch.long)
all_attention_masks = torch.tensor([], dtype=torch.long)
all_token_type_ids = torch.tensor([], dtype=torch.long)
all_cls_index = torch.tensor([], dtype=torch.long)
all_p_mask = torch.tensor([], dtype=torch.float)
all_is_impossible = torch.tensor([], dtype=torch.float)
all_start_positions = torch.tensor([], dtype=torch.long)
all_end_positions = torch.tensor([], dtype=torch.long)
for i in feat.split(";"):
for file_name in os.listdir(os.path.join(input_dir,i)):
data = torch.load(os.path.join(input_dir,i,file_name))[i]
if isinstance(data,TensorDataset):
if evaluate:
all_input_ids = torch.cat([all_input_ids,data.tensors[0]],dim=0)
all_attention_masks = torch.cat([all_attention_masks,data.tensors[1]],dim=0)
all_token_type_ids = torch.cat([all_token_type_ids,data.tensors[2]],dim=0)
all_cls_index = torch.cat([all_cls_index,data.tensors[4]],dim=0)
all_p_mask = torch.cat([all_p_mask,data.tensors[5]],dim=0)
else:
all_input_ids = torch.cat([all_input_ids,data.tensors[0]],dim=0)
all_attention_masks = torch.cat([all_attention_masks,data.tensors[1]],dim=0)
all_token_type_ids = torch.cat([all_token_type_ids,data.tensors[2]],dim=0)
all_start_positions = torch.cat([all_start_positions,data.tensors[3]],dim=0)
all_end_positions = torch.cat([all_end_positions,data.tensors[4]],dim=0)
all_cls_index = torch.cat([all_cls_index,data.tensors[5]],dim=0)
all_p_mask = torch.cat([all_p_mask,data.tensors[6]],dim=0)
all_is_impossible = torch.cat([all_is_impossible,data.tensors[7]],dim=0)
elif isinstance(data,list):
all_features[i] += data
if evaluate and "datasets" in feat.split(";"):
all_example_index = torch.arange(all_input_ids.size(0), dtype=torch.long)
all_features["datasets"] = TensorDataset(all_input_ids, all_attention_masks, all_token_type_ids, all_example_index, all_cls_index, all_p_mask)
elif not evaluate and "datasets" in feat.split(";"):
all_features["datasets"] = TensorDataset(all_input_ids,all_attention_masks,all_token_type_ids,all_start_positions,all_end_positions,all_cls_index,all_p_mask,all_is_impossible,)
if output_examples:
return all_features['datasets'], all_features['examples'], all_features['features']
else:
return all_features['datasets']
```
基本上,添加的修改在小批量数据上运行相同的方法`squad_convert_examples_to_features`,并将创建的特征保存在一个文件夹中。可以通过在`run_squad.py`的第 660 行添加下面一行,并在我上面提到的命令中提供一个参数`data_process_batch`,来定义迷你批处理的大小。
```py
parser.add_argument(
"--data_process_batch",
default=50000,
type=int,
help="Number of batches in which SQuAD data will be processed.",
)
```
修改后的`modified_run_squad.py`可以从[这里](https://gist.github.com/SKRohit/04ac2b4805382f72e7591630ee971bda)下载。
## 模型验证
使用`run_squad.py` [脚本](https://github.com/huggingface/transformers/blob/master/examples/question-answering/run_squad.py)中提到的默认超参数,我们训练的模型能够在 4 个时期后在 SQuADv2 数据上获得 70 的 F1 分数和 67.8 的精确匹配。现在,让我们看看这个经过训练的模型在来自[新冠肺炎开放研究数据集挑战(CORD-19)](https://www.kaggle.com/allen-institute-for-ai/CORD-19-research-challenge) 的一些研究文章上的表现。以下是从研究文章中获得的一些示例文本、对示例文本提出的问题以及预测的答案。
> **背景:** 结论:本研究首先展示了重庆市新冠肺炎的地区差异,并进一步深入比较了重症患者与非重症患者的差异。重庆市 3 所定点医院新冠肺炎患者的 28 天死亡率为 1。5 %,低于湖北省和包括湖北省在内的 mainland China。然而,重症患者的 28 -死亡率相对较高,当出现并发症时死亡率更高。值得注意的是,并发严重 ARDS 的危重患者的死亡率高达 44%。4 %.因此,对重症新冠肺炎病例,尤其是合并急性呼吸窘迫综合征的病例,早期诊断和加强监护对降低死亡率至关重要。
> **问题***:*ARDS 的死亡率是多少?
> **预测答案** : 44.4 %
> **背景:** 这是 2020 年 1 月 31 日至 2 月 6 日武汉市 COVID - 2019 定点医院武汉大学人民医院收治的 3 例 2019 - nCoV 感染患者的回顾性研究。所有患者均根据中国国家卫生委员会发布的《新型冠状病毒肺炎的诊断和治疗》(第 6 版)进行诊断和分类。我们详细记录了流行病学史、人口统计学特征、临床特征、症状和体征、治疗和临床结果。此外,我们发现益生菌的比例明显减少,如双歧杆菌、乳酸杆菌和真细菌,条件致病菌的比例明显增加,如放线菌的棒状杆菌和厚壁菌门的黑鲁氏菌。值得注意的是,所有患者都死亡了。
> ***问题** :* 病毒感染引起的 ARDS 死亡率是多少?
> **预测答案:** 患者全部死亡。
> ***背景:*** 同时,新冠肺炎感染者中有一定数量的患者存在慢性共病,主要为高血压、糖尿病和心血管疾病,与 MERS - COV 人群相似。这些结果表明,患有慢性基础疾病的老年男性可能更容易感染新冠肺炎或 COV 中东呼吸综合征..CC - BY - NC - ND 4。在实验室检测方面,新冠肺炎和 MERS - COV 患者的淋巴细胞减少,CRP 增加。这一结果表明,新冠肺炎可能与细胞免疫反应有关,主要作用于淋巴细胞,就像 MERS - COV 那样$[ 48 ]$。被病毒感染的细胞在体内诱导大量促炎细胞因子的释放和炎症风暴。此外,细胞因子增加可能会对相关器官造成损害,如肝脏$[ 49 ]$。我们的研究结果表明,在 MERS - COV 人群中发现了 AST 异常值,而在新冠肺炎人群中没有发现。可能的原因是新冠肺炎人群的随访时间太短,而肝脏。
> **问题:** 什么样的细胞因子在宿主反应中起主要作用?
> **预测答案:** 促炎
## 尾注
在本文中,我们简要介绍了 BERT 的架构,了解了 BERT 在问答任务中的表现,使用`modified_run_squad.py`(这减少了 RAM 的使用)在 SQuADv2 数据上训练了一个版本的 BERT 模型(Bio-BERT),并了解了训练后的模型在 COVID 相关研究文章的文本上的性能。这种模型的性能在很大程度上取决于提供给模型的上下文和相关问题。在这里,上下文是从文章中手动提取出来并输入到模型中的。在后面的文章中,我们将看到一种基于深度学习的方法,在给定特定问题的情况下,从研究文章中找到最合适的段落。
# 如何训练缩放的 YOLOv4 对象检测模型
> 原文:<https://blog.paperspace.com/how-to-train-scaled-yolov4-object-detection/>
随着 [Scaled-YOLOv4](https://arxiv.org/abs/2011.08036) 的发布,物体检测技术向前迈进了一步——这是一种用于[物体检测](https://blog.roboflow.com/object-detection/)的最新机器学习模型。
在这篇博文中,我们将看看在创建缩放的 YOLOv4 模型中涉及的突破,然后我们将通过一个示例来研究如何在自定义数据集上概括和训练该模型,以检测自定义对象。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f182dbd4c4c7f3e91de664246b753323.png)
Detecting lifts and jet skis from above via drone using Scaled-YOLOv4\. Training data: [public Aerial Maritime dataset](https://public.roboflow.com/object-detection/aerial-maritime).
我们在本教程中包含了以下资源:
* [缩放后的 YOLOv4 分解](https://blog.roboflow.com/scaled-yolov4-tops-efficientdet/)
* [缩放后的 YOLOv4 回购](https://github.com/WongKinYiu/ScaledYOLOv4)
* [公共航空海事数据集](https://public.roboflow.com/object-detection/aerial-maritime)
* 缩放的 YOLOv4 自定义培训代码(如下)
在本教程中,我们将利用 [**Roboflow**](https://roboflow.com/) 进行计算机视觉数据管理,利用 [**Paperspace**](https://www.paperspace.com/) 进行 GPU 计算资源。
## 为什么选择 Scaled-YOLOv4?
基于微软 COCO 基准测试的[,缩放后的 YOLOv4 现在](https://blog.roboflow.com/coco-dataset/)[是对象检测](https://blog.roboflow.com/scaled-yolov4-tops-efficientdet/)的最佳模型。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bf369e38bff769173caaf5602fc4ee37.png)
Scaled-YOLOv4 achieves record breaking performance on the [COCO benchmark](https://blog.roboflow.com/coco-dataset/). Not pictured [YOLOv4-tiny](https://blog.roboflow.com/train-yolov4-tiny-on-custom-data-lighting-fast-detection/) running at 1774 FPS on the RTX 2080ti ([source](https://arxiv.org/pdf/2011.08036.pdf))
在 Roboflow,我们发现,在推理速度和网络准确性的权衡连续体中,缩放后的 YOLOv4 系列模型在效率和所有其他现有的对象检测网络(由 T2 平均精度衡量)中名列前茅。
如果你想了解为什么 Scaled-YOLOv4 这么好,请查看我们在 Roboflow 博客上写的 [Scaled-YOLOv4 分解](https://blog.roboflow.com/scaled-yolov4-tops-efficientdet/)。我们还建议查看一下[缩放的 YOLOv4 论文](https://arxiv.org/abs/2011.08036),以探索原作者提出的基准。
一旦您阅读了支持技术,让我们开始培训吧!
## 组装自定义对象检测数据
为了监督我们定制的缩放 YOLOv4 对象检测器,我们需要收集对象检测训练数据。如果您想直接学习本教程,您可以使用数据集页面右上角的`Fork`按钮来分叉[公共航空海事数据集](https://public.roboflow.com/object-detection/aerial-maritime):
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/943972e28271f3bb17d64cb20f049a84.png)
To fork the public aerial maritime dataset, use the **Fork Dataset** feature in Roboflow
### 收集您自己的图像
或者,如果您想使用自己的图像,我们建议您收集能够代表您的模型在部署时将面临的条件的图像。你可以从一小批图像开始,以评估可行性,然后扩大规模——但一般来说,图像越多样化,最终结果越好。
### 标注您的数据
在本教程中,我们将使用公共航空海事数据集中的影像。一旦你有了你想用来训练你的模型的图像,就该给它们贴标签了。
您现在可以直接在 [Roboflow](https://roboflow.com/) 中标记您的数据,如下所示:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fb7ce443b28febbd3a8f777fdfed922b.png)
Uploading data Roboflow after creating a new dataset
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0872798a610080330d9006066aa44987.png)
Labeling data in Roboflow
要给图像加标签,需要在要检测的对象周围绘制边界框。这里有一些关于为这种计算机视觉应用标记图像的提示:
1. 标记每幅图像中的每个感兴趣的对象
2. 标记一个物体的整体
3. 标记遮挡的对象
4. 创建紧密的边界框
5. 创建特定的标签名称
6. 保持清晰的标签说明
7. 使用标签工具,如 [CVAT](https://blog.roboflow.com/getting-started-with-cvat/) 、[标签](https://blog.roboflow.com/labelimg/)、矩形标签和[机器人流程](https://docs.roboflow.com/annotate)
要在 Roboflow 中手动标记数据,首先需要[上传原始图像并创建一个新的数据集](https://docs.roboflow.com/adding-data)。在新数据集页面中,开始标注所需的全部操作就是单击图像并绘制边界框。关于注释的更多细节,请查看[标签文档](https://docs.roboflow.com/annotate)。
### 将数据导出到图纸空间
一旦您对标注的数据集感到满意,您就可以继续在 Roboflow 中生成数据集版本。为此,从 Roboflow 的数据集视图中选择`Download`。您还可以选择任何您喜欢的预处理和增强选项。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b0c4b6176891a651228d032a5bd22de0.png)
Select **Download** to generate a dataset
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bec2f304289b2ad249fa89407f5a77c4.png)
You should end up with a curl link to the dataset
选择`Download`后,选择`TXT > Scaled-YOLOv4`作为输出格式,然后选择`Get Link`获得一个`curl`链接到您的数据。
请保留此链接,因为您将使用它在一分钟内将您的数据集导入到您的 Paperspace 笔记本中。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f88c9b1db5e01880fdda89c10cebfe06.png)
Choose the Scaled-YOLOv4 dataset format
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9063c33dc55b0be6ceeb907feaef687a.png)
Visualizing our training data within Scaled YOLOv4
## 在 Paperspace 上设置我们的培训环境
为了训练我们的缩放 YOLOv4 模型,我们首先需要提供 GPU 资源来运行我们的训练作业。
由于在家庭中使用更大的网络时,Scaled-YOLOv4 培训需求会大幅增加,因此 Paperspace 是一个自然的起点,因为有各种各样的[按需 GPU 支持的实例](https://gradient.paperspace.com/instances)可用。
当然,您可以使用任何可用的 GPU 资源,并且仍然遵循本教程。
### 创建托管集群
首先,您需要创建一个 Paperspace 帐户。
登录到 Paperspace 后,导航到`Gradient`和`Clusters`,然后选择`Create a Managed Cluster`。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dce4cda384f4b23e9d1ff3f0a7e09e18.png)
Navigate to the Clusters tab in Paperspace Gradient and select **Create a Managed Cluster**
您将看到新的私有集群处于`Provisioning`状态,集群将需要几分钟的时间进行配置,因此请耐心等待。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7eec75980a11ec9f7e27aeae994356d8.png)
It will take a few minutes for the new cluster to provision
### 创建笔记本
配置好集群后,您可以在`Notebooks`选项卡中启动笔记本进行培训。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/baa195690d422ee122a831ecd31e70fa.png)
Select Create Notebook to initialize a new notebook in Paperspace
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d760de0513c22a047b01a5a88f8a50ca.png)
Name your notebook and select an auto-shutdown interval
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/252f8414dba0cd50392c8465218ce94c.png)
Select an instance from your new private cluster
在图纸空间的笔记本设置提示中:
1. 在步骤 1 中,给你的笔记本起一个名字,例如 **YOLOv4 教程**。您可能还想将`auto-shutdown`设置为 1 小时,以防万一您稍后忘记关机时过度充电。
2. 在步骤 2 中,我们可以将容器选择留空。我们将在步骤 4 中指定一个自定义容器。
3. 在第 3 步中,选择`My Private Clusters`,然后选择带有您想要训练的 GPU 的私有集群。更大的 GPU 将更快地训练你的模型——我们在本教程中使用了 P4000。
4. 在步骤 4 中,打开`Advanced Options`并将下面的 [NVIDIA PyTorch 容器](https://ngc.nvidia.com/catalog/containers/nvidia:pytorch)复制到`Container Name`字段中:
```py
nvcr.io/nvidia/pytorch:20.06-py3
```
Paste this image location in `Container Name` field under Advanced Options
高级选项应该是这样的:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a8f78ed0c68c27704d9a6dc3fe906b4a.png)
Enter the image location into the **Container Name** field
点击`Create Notebook`,Paperspace 将在您的新私有集群上启动您的培训环境。这个过程也可能需要几分钟。
## 安装缩放的 YOLOv4 依赖项
现在是时候打开朱庇特笔记本了。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c43b99d710106e35f236cb6973d7d8c5.png)
Installing a few dependencies in the new notebook
一旦我们进入笔记本电脑,我们需要在准备培训之前进行一些安装。
### 安装依赖项
我们克隆了[缩放后的 YOLOv4 回购](https://github.com/WongKinYiu/ScaledYOLOv4),并切换到`yolov4-large`分支。
```py
#clone Scaled_YOLOv4
!git clone https://github.com/WongKinYiu/ScaledYOLOv4.git # clone repo
%cd ./ScaledYOLOv4/
#checkout the yolov4-large branch
!git checkout yolov4-large
%cd ..
```
接下来,我们将为我们的 GPU 安装`mish-cuda`,这样我们就可以在笔记本的 GPU 上快速运行 mish 激活功能。
```py
!git clone https://github.com/JunnYu/mish-cuda
%cd mish-cuda
!python setup.py build install
%cd ..
```
### 下载数据
最后,从 Roboflow 导入您的`curl`链接,以正确的格式导入您的数据。这应该从`ScaledYOLOv4` repo 文件夹向上一个目录执行。
```py
!curl -L "https://app.roboflow.com/ds/h91wwIw5An?key=[YOUR KEY HERE]" > roboflow.zip; unzip roboflow.zip; rm roboflow.zip
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/640c987a41feee17d0b4889d12900162.png)
Downloading data into the notebook
## 开始规模化 YOLOv4 培训
现在我们已经设置好了一切,我们只需要调用一个命令来开始对我们的定制数据进行训练。
```py
cd ./ScaledYOLOv4/
!python train.py --img 416 --batch 16 --epochs 50 --data '../data.yaml' --cfg ./models/yolov4-csp.yaml --weights '' --name yolov4-csp-results --cache
```
以下选项是可能的:
```py
- img: define input image size
- batch: determine batch size
- epochs: define the number of training epochs. (Note: often, 3000+ are common here!)
- data: set the path to our yaml file
- cfg: specify our model configuration
- weights: specify a custom path to weights.
- name: result names
- nosave: only save the final checkpoint
- cache: cache images for faster training
```
一旦训练开始,你要观察 [图(平均精度)](https://blog.roboflow.com/mean-average-precision/)度量上升,如果它稳定下来你就可以停止脚本。
训练结束后,你可以看看你的 Tensorboard 指标,再次关注地图:
```py
# Start tensorboard
# Launch after you have started training
# logs save in the folder "runs"
%load_ext tensorboard
%tensorboard --logdir runs
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6001b3a6aa90c5d34a0b89afd18dbf09.png)
Scaled YOLOv4 Tensorboard
### 按比例放大
如果你想使用更大版本的网络,在训练中切换 cfg 参数。在`models`文件夹中,你会看到各种型号配置选项,包括`yolov4-p5`、`yolov4-p6`和著名的`yolov4-p7`。为了训练这些较大的模型,单个 GPU 可能不适合您,您可能需要启动一个多 GPU 服务器,并通过分布式启动在多 GPU 上进行训练:
```py
python -m torch.distributed.launch --nproc_per_node 4 train.py --batch-size 64 --img 896 896 --data coco.yaml --cfg yolov4-p5.yaml --weights '' --sync-bn --device 0,1,2,3 --name yolov4-p5
```
## 使用缩放的 YOLOv4 模型进行推理
现在您已经训练了缩放的 YOLOv4 模型,您可以利用您的模型对新图像进行推断。为此,我们将模型指向数据集的测试集,并将检测脚本指向我们的自定义权重(您也可以在此处指定视频):
```py
!python detect.py --weights ./runs/exp0_yolov4-csp-results/weights/best.pt --img 416 --conf 0.4 --source ../test/images
```
并且推理发生得很快(特别是在 GPU 上)
```py
/content/ScaledYOLOv4
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.4, device='', img_size=416, iou_thres=0.5, output='inference/output', save_txt=False, source='../test/images', update=False, view_img=False, weights=['./runs/exp1_yolov4-csp-results/weights/best.pt'])
Using CUDA device0 _CudaDeviceProperties(name='Tesla V100-SXM2-16GB', total_memory=16130MB)
Fusing layers... Model Summary: 235 layers, 5.24921e+07 parameters, 5.04494e+07 gradients
image 1/32 /content/teimg/DJI_0262_JPG.rf.3878c367b5f00b7ce0f5c9bdcb4d8486.jpg: 416x416 Done. (0.020s)
image 2/32 /content/teimg/DJI_0262_JPG.rf.47ce7cf6d8e3e310ab9b2b5c15ebba72.jpg: 416x416 Done. (0.020s)
image 3/32 /content/teimg/DJI_0262_JPG.rf.560b36a2e292c1b3dee7eae7e1f3fbf0.jpg: 416x416 1 docks, Done. (0.021s)
image 4/32 /content/teimg/DJI_0262_JPG.rf.5f24b2ccccf544d3bb0c3cb740be0f4b.jpg: 416x416 1 lifts, Done. (0.021s)
image 5/32 /content/teimg/DJI_0262_JPG.rf.66b031d30a28587d2c06f38af05cb4ec.jpg: 416x416 1 docks, Done. (0.021s)
image 6/32 /content/teimg/DJI_0262_JPG.rf.8c378a23b8822f63a44ad24c8787fab3.jpg: 416x416 1 lifts, Done. (0.025s)
```
Inference speed of YOLOv4-CSP on Colab V100, single batch, 50FPS
然后,我们可以可视化我们的网络测试推理。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a2886ffcf600eda11be9fc1a4856184a.png)
Test inference on an image the model has never seen.
## 导出重量和部署
最后,在笔记本的最后,我们可以下载我们定制的模型重量。这些目前在 PyTorch 框架中,您可以使用我们用于培训的相同软件来调用它们。你也可以把这些权重转换成其他框架,比如`Tensor RT`、`ONNX`、`TorchScript`、`TensorFlow Saved Graph`、`TFLite`。
这些其他格式的实现将是具有新依赖性的新软件。旅程开始了!
## 后续步骤
一旦您对在笔记本中训练您的网络有了感觉,您可以考虑在存储库中正式化您的训练和推理流程,并利用 Paperspace 的自动缩放功能来[部署您的推理网络](https://docs.paperspace.com/gradient/tutorials/dealing-with-gradient-deployments)。
## 结论
恭喜你!您已经学习了如何使用 Scaled-YOLOv4 在自定义对象上训练最先进的技术。
在 Paperspace 和 Roboflow,我们总是对您下一步可能要做的事情感到兴奋。
一如既往,快乐训练!
# 快速行动,深入思考:研究是如何进行的@ Paperspace
> 原文:<https://blog.paperspace.com/how-we-do-research-at-paperspace/>
[2021 年 12 月 2 日更新:本文包含关于梯度实验的信息。实验现已被弃用。有关当前梯度资源的更多信息,请参见[梯度文档](https://docs.paperspace.com/gradient/explore-train-deploy/workflows)
先进技术组是 Paperspace 的一个以研发为中心的团队,由 ML 的工程师和研究人员组成。作为一个团队,我们有兴趣探索深度学习、数据工程、计算机系统和 UI/UX 方面的高级主题,并致力于构建智能应用程序。如果你对我们的工作感兴趣,考虑申请我们的[研究奖学金](https://jobs.lever.co/paperspace/7b9db8b1-36da-435c-9357-f737cb73e0ed)!
在本帖中,我们将以高级研究工作流程的形式,对高级技术小组(ATG)用于探索各种研究的工具和实践进行概述。我们的许多研究课题都位于深度学习和计算机系统等领域的交叉点。我们倾向于快速行动,处理雄心勃勃的计算密集型实验,由于我们通过 Paperspace 的梯度平台提供了许多非常有用的工具和强大的计算能力,我们可以追求涉及学术界更传统的研究小组有时会回避的主题的研究问题。
在这里,我们概述了研究工作流程的一般进展,我们发现这在我们处理的项目类型中非常有用。我们将讨论我们通常如何从最初的探索阶段前进,在这个阶段我们确定问题的范围并得到一些初步的结果。然后,我们将介绍如何在纸张空间云上扩展我们的实验。最后,我们将介绍我们如何将实验版本化,并跟踪研究议程的内部进展。
## 跟上 ML 消防水管
在机器学习领域,分享的想法和发表的论文数量之多几乎令人难以理解。要跟上每天冒出来的每一个新想法是非常困难的,其中许多当然是在基础突破上的渐进改进。在 ATG,我们的研究人员带着他们打算追求的特定想法加入团队,通常还有一些如何实现的想法。过去的想法和项目包括 GPU 内核编程、对抗性学习方案和神经架构搜索。
我们一直致力于引入与 ATG 的深度跨区域协作文化,并且经常会有想法转变,以纳入团队中其他感兴趣的成员或 Paperspace 中任何其他人的专业知识。我们也对许多 ML 理论家偏离的主题开放,包括可解释性、设计和人在回路系统。通过午餐和学习讲座、阅读小组会议以及允许任何人与其他人就一个有趣的项目展开对话的普遍开放的文化,可以分享新的想法。我们已经有软件工程师、项目经理和深度学习研究人员兴奋地讨论深度神经网络中模块化和修剪的含义。这是一次很棒的经历。聪明的人、天生的好奇心和高度协作的文化导致了许多不可思议的想法和项目在 Paperspace 形成。
## 探索一个想法:研究的面包和黄油
对于那些不熟悉研究的人来说,深入研究似乎是一项非常模糊和令人畏惧的任务,尤其是如果你的唯一经历是阅读论文和看到最终结果。实验的现实,尤其是在 ATG,是我们从一些实验结果的小延伸或问题开始的。我们可能会试图复制一篇论文的结果,或者在一个新的领域测试一个想法。自然地,当我们更好地理解作品的含义和基础时,有趣的想法和扩展就会出现。
当一个新的想法作为这个过程的结果开始形成时,我们把它缩小到经验上或理论上可测试的东西。尽可能缩小范围并保持简单是至关重要的,这样就可以清楚、直接地看到所产生的机制或期望的结果。作为一个例子,考虑我们可能想要测试一个新的修剪机制。我们将首先训练一个简单的完全连接的前馈架构,并在那里测试修剪机制,而不是在 ResNet 这样的复杂架构上测试新的修剪方案。然后,我们可以将 CNN 添加到探索性代码中,并在新架构上测试剪枝机制。
无论是重新实现另一篇论文的结果还是尝试一个你自己的新想法,目标都是在过程中有一个高水平的粒度和控制。在我们的团队中,我们发现梯度笔记本在这个过程中是一个非常有价值的工具。渐变笔记本允许我们使用预装了库和软件的容器,并向我们展示了一个 Jupyter 笔记本界面,该界面可以访问共享工作区,从而实现快速迭代和探索。
由于快速行动和测试许多小范围的可能性以获得概念性和经验性的理解是关键,我们非常频繁地使用这个特性。我们最近还在探索在笔记本电脑中使用 [Gradient SDK](https://docs.paperspace.com/gradient/gradient-python-sdk/gradient-python-sdk) ,使我们能够快速启动实验和更大的工作负载。如果我们生成了一个有用的结果,我们可以将它存储到共享的工作空间存储中,如果我们愿意,可以在更严肃的后续实验中使用它。此外,如果研究的某些内容是计算密集型的,即使我们将其范围缩小到概念验证实验,Gradient 也允许我们指定我们希望为我们的笔记本提供什么样的 GPU,这是我们在 Google Colab 等其他服务或本地 Jupyter 笔记本安装上无法做到的。
## 巨大的初始成果带来了大规模的后续实验。
哇哦。对我们新想法的初步探索产生了一些非常有趣的结果。我们的假设可能是正确的,那么现在呢?嗯,在很多领域,包括深度学习,你的方法或结果真的应该在一些更大的基准任务上进行测试。有些可能很小,但有些可能计算量很大。
较大的实验倾向于更加结构化,并且涉及相当程度的软件工程。它们需要更长的时间来设置,也要进行更严格的测试,以确保培训确实在进行。这通常是当我们团队的成员开始转向更有组织的代码库而不是单一的文件时。我们将开始使用设计原则,并开始真正记录一些工程决策。作为一名研究人员,当涉及到这些更大规模的实验时,笔记本界面开始变得有点缺乏,因为我不再花大部分时间通过小调整重新运行细胞并快速重新设计代码库。
在 ATG,我们可以访问 Gradient 的实验接口,这允许我们基本上将特定代码库的计算密集型运行视为作业。这些实验将运行我们指定的代码,并访问我们之前指定的共享工作区。结果是能够并行进行多个实验,并快速获得结果。我们还利用多节点特性和适当的分布式培训。Gradient 还自动解析关于我们的模型流程的统计数据,因此我们可以获得一些关于性能和其他重要指标的有用分析。
**关于工具**的快速说明。我们倾向于使用 Tensorflow,因为它有广阔的生态系统和对大型系统级实验的支持。我们也用过 Pytorch,发现它非常有用。
## 使用渐变 CI 体验版本控制
ML 研究中的一个正在进行的问题,也许是 CS 研究中的一个普遍问题,是决定如何将你的研究模型和实验版本化。作为研究人员,我们有时会发现调整代码库中的小值,如超参数值,可能会对我们的结果产生巨大的影响。但是,将学习率从 0.001 改变到 0.005 是否构成了我们正在跟踪的一个全新的实验?在 ATG,我们从我们的软件工程根源中获得灵感,并决定任何有用的提交变更都应该构成一个实验版本。毕竟,一个失败实验的成本肯定比跟踪许多增量实验的成本要高。Paperspace 的 [GradientCI 工具](https://docs.paperspace.com/gradient/projects/gradientci)可以跟踪变化,并在我们需要时自动运行这些变化作为实验。它还会以类似于 Gradient 客户端的方式,自动生成我们想要的各种指标的有用报告。
## 做研究没有正确的方法!
真的没有。研究过程应该是对你正在做的工作有意义的东西和让你的研究小组感到舒适和兴奋的东西的结合。在 ATG,我们结合了工程和研究背景,并发现我们上面提到的方法对测试 DL 和系统领域的大量有趣的想法非常有用。
从笔记本等灵活的工具转移到实验等更强大的界面,似乎遵循了我们正在进行的研究工作的自然流程,并允许我们利用软件工程最佳实践来提高工作效率。随着我们团队的成长,我们与全球其他世界级研究人员合作并建立更紧密的联系,我们希望进一步改善我们开放、合作和好奇的文化。
有兴趣加入 Paperspace 吗?你可以点击这里查看我们的招聘信息!
# 通过 Paperspace 向您的客户分发您的产品
> 原文:<https://blog.paperspace.com/how_to_setup_sales_trials_on_paperspace/>
Paperspace 是一种强大的分发工具,以简单易管理的方式展示您产品的功能。您的潜在客户将能够在几分钟内用任何样本或真实世界的数据来测试您的软件。只需要一封电子邮件邀请。
## 它是如何工作的?
拥有需要基于桌面安装的产品的公司面临着特殊的挑战。
* 演示产品通常是必要的
* 安装过程中的摩擦搅动了销售漏斗中的潜在客户。这可能是由于许多不可控的因素造成的,包括不良的硬件要求、不良的操作系统要求等。
* 预装的云桌面,准备了产品和演示材料,提高了从潜在客户到合格销售线索的转化率。
* 带着昂贵(通常很重)的设备去贸易展会可能会很麻烦。借助 Paperspace 的云桌面,可以在轻量级 Chromebooks 上进行演示。
下面是通过 Paperspace 的云桌面向潜在客户部署您自己的软件的入门指南。
想要更多曝光?
如果您希望您的销售支持工作流以 Paperspace 为特色,请给我们发送电子邮件至 hello@paperspace.com,或发推特给我们,包括标签#PoweredByPaperspace
## **指南大纲**
当计划使用 Paperspace 向潜在客户试用您的软件时,本实用指南将确保说明以下内容:
* [账户管理](#account)
1. 机器建议
* [机器管理](#machine)
* 创建机器
* 设置您的试用机器
* 测试您的产品
* 创建模板
* 创建新机器
1. 考虑
* [用户管理](#user)
* 添加新用户
* 分配用户到机器
* 设置明确试用日期
1. 考虑
* [部署和计费](#payments)
1. 部署注意事项
* [伙伴关系](#partnerships)
1. 联系我们
* [结论](#conclusion)
### **1。**账户管理
```py
1\. Sign Up
2\. Provide Billing Info
3\. Request Team Access from Support@paperspace.com
```
### **2。**机器管理
Paperspace 提供了一个简单的用户界面来创建、删除和管理机器。在我们的在向潜在客户分发产品试用版时,您应该考虑以下问题。
**考虑:**
*你需要多少台机器?*
*Windows/Linux?*
由于试用中利用率的不确定性,我们建议按月计费。
**创建一台机器**
* 选择最近的*区域*
* 选择 Windows 或 Linux *模板*
* 根据您的需要选择*每月*或*每小时*。
* 选择你的*计划*:
* **高性能**如果您的产品利用了我们的 GPU,请务必选择 *GPU+* 、 *P5000* 或 *P6000* 。
* 选择您的*存储空间* —您可以在未来随时增加存储空间,但我们强烈建议您的产品演示至少要有 100GB 或以上的存储空间。
* 点击*创建*
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cba185511daf620e96b0dfdcef961853.png)
**设置您的试用机器**
安装您的软件及其所有依赖项。如果您的软件尚未包含样本数据或文件,我们建议您包含样本数据或文件。
保存在桌面上的简单文本文件(即常见问题解答、自述文件等)。)为您的潜在客户的试用体验提供指导,也会证明是有用的。
**测试您的产品**
在创建模板以方便分发之前,请确保您的产品按预期工作。
**创建模板**
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/acbbc56fa484b173077d1ebe1d4ddbd9.png)
在控制台中,从该机器创建一个模板
注意:
模板当前仅限于最初创建它们的数据中心。例如,从 NY1 中的机器生成的模板将不能用于在西海岸或欧洲创建机器。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b0f23c1bda94749bcbcd9a6ecb54bd54.png)
**创建新机器**
根据需要从模板创建新机器
### **3。**用户管理
试用期需要围绕访问进行仔细规划。在开始分发试用产品之前,您应该能够回答以下问题。很重要的一点是,要想清楚产品试用将如何成为有凝聚力的销售流程和剧本的一部分。虽然您可以让多个团队成员成为团队管理员,但请考虑确保最佳销售体验所需的工作流程。
**考虑:**
这些用户可以访问多长时间?几天?几个月?
我们将如何管理这些试验的部署?
**添加新用户**
通过“用户”选项卡,您可以查看和管理所有现有用户,并创建新用户。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/8fc9d8fa46d1328ffc7a3726a6b7451c.png)
添加用户后,他们会收到一封电子邮件,通知他们可以访问。创建新用户时,您可以选择将他们标记为团队管理员。
将用户分配到特定的机器。
我们不允许多人同时使用同一台机器。因此,请确保将额外的机器作为试验计算的一部分。如上所述,只有团队管理员可以将用户分配给机器。虽然您可以有几个团队管理员,但请仔细考虑这可能会对您的销售行动手册的设计产生什么影响。
设定明确的审判日期。
设置这些日期将有助于确保您可以在试用期到期后从用户的计算机上停用用户。我们目前正在构建一个允许编程控制的 API。在那之前,大部分都是手动的。
### **4。**部署和计费
**部署**
对于您的潜在客户来说,登录您的销售演示机器将会非常简单。他们只需要自己的电子邮件和密码就可以通过浏览器访问。不需要安装。对于键盘快捷键,我们建议您的潜在客户将我们的专用应用下载到他们基于 Windows 或 MacOS 的笔记本电脑/台式机上。
**计费**
每月的纸张空间账单。我们建议您将 Paperspace 的成本纳入您的销售支持预算,以了解是免费试用还是付费试用最适合您的产品。
### **5。**伙伴关系
我们一直有兴趣与那些希望通过 Paperspace 发布软件的公司建立合作伙伴关系。我们已经与 Fast.ai、H2O.ai 和 Parsec 等公司和组织合作,为用户提供定制模板,预配置所有必要的软件和依赖项,让他们的客户获得出色的体验。如果这是你有兴趣讨论的事情,请在[hello@paperspace.com](mailto:hello@paperspace.com)告诉我们!
### **6。**结论
Paperspace 强大的云桌面正在推动营销和销售组织内部协作的未来。我们轻松的用户体验使销售团队能够通过向潜在客户轻松分发台式机产品的销售演示来提高销售速度,从而消除传统试用期的瓶颈。
对 Paperspace 如何为您的销售团队提供动力有什么想法?我们希望你能在[hello@paperspace.com](mailto:hello@paperspace.com)与我们分享你自己的经历。
尽情享受吧!
要让您的销售组织加入 Paperspace,[请在此注册。](https://www.paperspace.com/account/signup?utm-campaign=distributeblog)
我们需要你的帮助!
我们正在寻找专注于销售和营销工作流的内容作家,以帮助建立我们的社区。给 hello@paperspace.com 发电子邮件,附上写作范例和教学想法
# 关于基于 ARM 的深度学习架构,超级计算的历史可以教给我们什么?
> 原文:<https://blog.paperspace.com/hpc-supercomputing-deep-learning-history-arm/>
NVIDIA [最近发布了](https://www.anandtech.com/show/16610/nvidia-unveils-grace-a-highperformance-arm-server-cpu-for-use-in-ai-systems)Grace——一款新的基于 ARM 的 CPU 芯片,针对神经网络工作负载进行了优化。
这让我们不禁想知道 Grace 是否标志着 64 位 x86 体系结构在高性能计算(HPC)领域长达 15 年以上的主导地位开始终结?对于我们这些构建深度学习应用的人来说,这可能意味着什么?我们会很快将代码移植到 ARM 上吗?
我们希望通过了解 HPC 的一些历史来解决 NVIDIA 的声明引发的这些和其他问题。希望我们可以先了解一点我们是如何走到这一步的,然后这对深度学习的未来可能意味着什么。
## 超级计算早期
超级计算有着悠久而迷人的历史——从 20 世纪 60 年代最初的 IBM、CDC 和 Cray 机器,它们以每秒 10^6 浮点运算数来衡量性能,到今天的机器,它们计划在 2021 年底突破每秒 10^18 浮点运算数的障碍。
超级计算也是一种观赏性运动。每年有两次[500 强](https://top500.org/lists/top500/list/2020/11/)榜单在[两次](https://www.isc-hpc.com/)和[最大的](https://sc21.supercomputing.org/)超级计算会议期间发布——争夺榜单榜首的竞争非常激烈。自 1993 年以来,500 强榜单一直在持续发布,这对所有参与者来说都是一件大事——从托管这些集群的站点到组成这些集群的组件制造商,再到使用这些集群的科学界(和国家)。
同样值得注意的是,Top 500 仅限于发布 *public* 的集群。虽然不是这篇博客的主题,但超级计算也有很长的保密历史——从艾伦·图灵[在 20 世纪 40 年代破解德国海军之谜](https://en.wikipedia.org/wiki/Cryptanalysis_of_the_Enigma)到[高级模拟和计算程序](https://en.wikipedia.org/wiki/Advanced_Simulation_and_Computing_Program),该程序至今仍负责维护美国的核储备。
但那是以后的事了。我们今天想要了解的是,我们在超级计算架构方面已经取得了哪些进展,我们将走向何方——特别是当它与日益并行化的机器学习和深度学习应用相关时。
## 向量处理器的时代
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ad32e7511815eced394fa5b67bbe6f83.png)
The 80 MHz Cray-1 was so highly anticipated by the time it was released in 1975 that a bidding war broke out between two US national labs to secure Unit #001\. ([Source](https://en.wikipedia.org/wiki/Cray-1))
在 [1966](https://en.wikipedia.org/wiki/Vector_processor#Supercomputers) ,[控制数据公司](https://en.wikipedia.org/wiki/Control_Data_Corporation) (CDC)和[德州仪器](https://en.wikipedia.org/wiki/Texas_Instruments) (TI)都向市场推出了利用向量处理的机器。从此,早期的超级计算被向量处理器或阵列处理器机器所统治。在 20 世纪 70 年代中期之前,大型机制造商 CDC 和国际商用机器公司(IBM)一直占据主导地位,直到克雷(CDC 的一个分支)接过了接力棒。
国家研究委员会的一份报告解释了向量架构的早期优势:
> Cray-1 支持向量体系结构,在这种体系结构中,浮点数的向量可以从内存加载到向量寄存器中,并在算术单元中以流水线方式进行处理,其速度远远高于标量操作数。向量处理成为超级计算的基石。
1976 年,当著名的 Cray-1 在洛斯阿拉莫斯国家实验室发射时,Cray 已经在制造效率惊人的向量处理机了。CDC、Cray 和其他人开创的体系结构在 20 世纪 90 年代一直保持主导地位。
今天这个事实特别值得注意,因为在这些早期 Cray 机器的技术架构和现代基于 GPU 的机器的并行计算架构之间有一些现代的相似之处。
## 个人电脑改变游戏
当早期的超级计算先驱们忙于建造单处理器机器时,英特尔在 1971 年生产了 T2 第一个商用微处理器。到 20 世纪 80 年代初,这项技术已经足够成熟——也就是说可以大规模生产了——个人电脑革命开始成形。
大约在 1984 年,当麦金塔电脑第一次引起 T2 全国的关注时,个人电脑进入了主流。事后看来,我们可以看到苹果和其他早期的个人电脑组装商正乘着微处理器成本大幅下降的东风。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7df4d91020f3f4a4e166ee26d97da1eb.png)
Microprocessors have been getting cheaper for decades. ([Source](http://www.singularity.com/charts/page62.html))
到 20 世纪 90 年代,Cray 和其他继续生产单处理器机器的传统超级计算制造商将无法跟上商用 CPU 架构提供的性价比。
要理解其中的原因,看看微处理器市场增长的速度有多快是有帮助的:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1391fe5b9f80acd6eff1407c09a53325.png)
Semiconductor sales with few exceptions have been increasing for decades. ([Source](https://brandongaille.com/25-microprocessor-industry-statistics-and-trends/))
在此期间,微处理器的成本直线下降。到 1995 年,克雷破产了。约翰·马科夫在当时为《纽约时报》写道:
> 但在 20 世纪 90 年代,廉价而强大的微处理器芯片的出现给计算世界带来了变革,极大地削弱了价值数百万美元的“大铁”系统,而这正是克雷的标志。此外,冷战的结束意味着政府采购机器的预算下降,这些机器曾经是美国武器实验室的支柱,也是“星球大战”战略防御计划的核心。
旧的“大铁”系统被商品风格的微处理器架构取代——这一事实自 Cray(第一个)消亡以来塑造了过去 25 年的计算。
## 力量
下图显示了自 20 世纪 90 年代初该榜单发布以来,Top500 超级计算机所使用的处理器/架构。如果我们一直向左看,我们可以看到克雷最近几年的统治地位。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c8907845969f0a9093ac52afc424d46d.png)
Top500 supercomputers have had a large number of dominant architectures through the years. ([Source](https://en.wikipedia.org/wiki/TOP500))
不久之后,我们看到太阳微系统公司的 [SPARC](https://en.wikipedia.org/wiki/SPARC) 和 DEC 公司的[阿尔法](https://en.wikipedia.org/wiki/DEC_Alpha)的出现。就像 IBM 的 POWER 一样,这些 RISC 架构在 20 世纪 80 年代早期受到了 [Berkeley RISC](https://en.wikipedia.org/wiki/Berkeley_RISC) (由 ARPA 资助)工作的严重影响。
RISC 项目(以及同时期斯坦福大学的 MIPS 项目)着手了解如何创造一个更简单的 CPU。其概念是以这样一种方式限制处理器的指令集,即它仍然能够执行大多数频繁执行的计算,同时降低复杂性。
20 世纪 90 年代初,我们也开始看到大规模并行系统。到 1993 年,[英特尔 Paragon](https://en.wikipedia.org/wiki/Intel_Paragon) 可以支持 1000 多个英特尔处理器。富士通的[数字风洞](https://en.wikipedia.org/wiki/Numerical_Wind_Tunnel)于 1994 年与 QTY 166 矢量处理器一起发货。到 1995 年,Cray 也开始用其 [T3E](https://en.wikipedia.org/wiki/Cray_T3E) 系统运送大规模并行系统。
大约在同一时间,我们看到 IBM 在 1990 年左右推向市场的 POWER(后来演变成 PowerPC)的出现。
POWER 最终会将 HPC 的市场权力让给 x86,但这要等到 x86 64 位真正起飞之后。在那之前,权力是一种过渡手段。在有限的时间内,性能优于 16 位和 32 位 x86,但在 2004-2005 年左右,64 位 x86(首先是 AMD,然后是 Intel)开始占据前 500 位。
随着并行性的出现(以及很快 64 位架构的出现),新架构将很快取代旧架构。
## x86 取而代之,行业永不回头
尚不清楚行业*决定*转向 x86 架构的确切时间,但几年前发生的一个重要事件似乎已经推动了这一进程 IBM 在 20 世纪 80 年代初为其个人电脑选择了[英特尔 8088](https://en.wikipedia.org/wiki/Intel_8088) 。
英特尔的 x86 架构并不一定比我们刚刚了解到的基于 RISC 的 PowerPC 架构等替代产品更好,但也许英特尔只是在满足蓬勃发展的市场需求方面做得更好。
英特尔接着在 1985 年发布了第一款 32 位 x86 芯片,到 21 世纪初,英特尔和 AMD 都成功地批量生产了低成本的 64 位 x86 处理器。
到 2005 年中期,80%以上的 500 强超级计算机都采用 x86-64。
然后,在没有太多警告的情况下,到 2005 年[免费午餐结束了](http://www.gotw.ca/publications/concurrency-ddj.htm)——CPU 时钟速度不再像几十年前那样增长。我们实际上可以看到这种现象,在 2005 年之前的几年中,峰值速度一直处于平稳状态。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4413661fe5f0c46051a12039600c3d9d.png)
Top supercomputer speeds occasionally plateau for periods of time over 60 years. ([Source](https://en.wikipedia.org/wiki/Supercomputer))
现在,我们突然看到多核矢量指令集芯片需要回到菜单上。旨在渲染图形的大规模并行芯片的设计者——比如英伟达的人——肯定会注意到这一点。
## 加速器的兴起
早在 2003 年,与 CPU 上的顺序处理相比,甚至与多核 CPU 相比,GPU 上的多核并行处理就开始显示出巨大的潜力。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b18c3912eb9ec7785f863f59dfb3bdb9.png)
With CPUs plateauing leading into the mid-2000s, NVIDIA GPU processors began to show promise. ([Source](https://www.researchgate.net/figure/GPU-vs-CPU-Performance_fig2_270222593))
NVIDIA 认识到了 CPU 的瓶颈,并希望为 GPU 带来除渲染图形之外的另一个用例,[在 2007 年发布了 CUDA](https://dl.acm.org/doi/10.1145/1296907.1296909) ,以帮助开发人员在 GPU 上运行通用计算任务。
值得注意的是,加速器的兴起是超级计算的一种回归。就架构而言,大规模并行 GPU 加速系统的设计更接近 Cray,而不是 x86。
GPU 加速的回报比任何人合理预测的都要高。近二十年来,高性能计算领域一直由 x86 CPU 架构主导,现在它的重心已经大规模转移到 GPU 加速器上。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/deae6b5b9277866896f44a2f89e88832.png)
Machines in the Top500 using accelerators since 2006\. ([Source](https://www.nextplatform.com/2020/11/16/the-many-facets-of-hybrid-supercomputing-as-exascale-dawns/))
截至 2020 年,500 强中有 146 家正在使用英伟达 GPU 加速卡——包括最近前 10 名的[个系统。](https://top500.org/lists/top500/list/2020/11/)
NVIDIA 在加速卡领域并非没有挑战。看到 CPU 到矢量加速器过渡的机会,英特尔在 2011 年发布了[骑士渡口/骑士角](https://en.wikipedia.org/wiki/Xeon_Phi)架构(后来被称为至强融核),这是一款基于 x86 的众核处理器,但具有不同的线程模型和额外的 SIMD 通道(AVX 系列指令)。
尽管承诺简单(无需重写代码即可在 x86 上运行),但不幸的是,英特尔似乎没有准备好与 NVIDIA 的 CUDA 部署和爆炸式社区竞争,或者在竞争中失败。
## 武器经济学
从今天的 500 强来看,并不明显的是,尽管 x86 几乎和 Linux 一样在列表中占据主导地位(这是一个完美的 500/500),但 x86 体系结构本身对于现代 HPC 来说并不完全是必要的。
在 GPU 加速器时代,CPU 在 HPC 环境中完成的大部分工作只是简单地向 GPU 馈送数据。没有真正的高度架构依赖性。今天,CPU 方面的最大限制是内存和 PCI-e 带宽。(这就是为什么像 [NVLink](https://en.wikipedia.org/wiki/NVLink) 这样的解决方案似乎有助于 NVIDIA 大幅提高 CPU 性能。)
正如 POWER/PowerPC 曾经是 HPC 的默认选择,早期的多核 AMD 处理器曾经是新 HPC 系统的首选一样,今天看来 ARM 有可能取代基于英特尔的 x86,成为世界上最快的集群的主导架构。
去年被 NVIDIA 以 400 亿美元收购的 ARM 最初是受 20 世纪 80 年代 RISC 的启发。该公司成立于 1990 年,使用的架构[与英特尔的 x86 架构](https://www.androidauthority.com/arm-vs-x86-key-differences-explained-568718/#:~:text=Arm%20is%20RISC%20(Reduced%20Instruction,(Complex%20Instruction%20Set%20Computing).&text=This%20is%20a%20key%20difference,%2C%20instruction%20set%2C%20and%20hardware.)大不相同。他们的工作导致了[Acorn Achimedes](https://en.wikipedia.org/wiki/Acorn_Archimedes)——第一台基于 RISC 的个人电脑。
ARM 芯片因其极低的功耗和高效率而备受青睐。它们也正处于生产热潮之中——很像上世纪 90 年代个人电脑革命之初的微处理器。正如一位作者所说:
> 基于 Arm 的产品显然无处不在。任何“智能”设备都可能包含 Arm 技术。作为最受欢迎的微处理器架构,Arm 报告称,其硅合作伙伴在 2020 年第四季度出货了创纪录的 67 亿片基于 Arm 的芯片,超过了其他流行的 CPU 指令集架构的总和...虽然 Arm 一直是几乎所有智能手机的首选 CPU,但 Nvidia-Arm 的收购将对更广泛的计算行业产生惊人的影响。
ARM 的成本下降如此之快也就不足为奇了——当个人电脑首次压低 x86 价格时,我们看到了同样的推动作用。ARM 支持的不仅仅是智能手机。也是像物联网传感器,MacBooks,数据中心服务器之类的东西!
因此,得知 500 强中排名第一的计算机是位于日本神户的富士通公司生产的基于 ARM 的 Fugaku 就不足为奇了。
Fugaku 拥有 158,976 个 A64FX 处理器(顺便说一下,这些处理器取代了旧的 SPARC 处理器),并将继续成为地球上最快的公共计算机,直到 2021 年底。
## 这对深度学习意味着什么
由于架构、成本和效率的原因,我们可能会看到高性能计算从 x86 到 ARM 的全行业过渡,这种过渡可能会渗透到深度学习计算中。
尽管作为计算的一个子集,HPC 历史悠久,但它是深度学习的优秀领导者。从张量核也有利于传统 HPC 应用的意义上来说,这些应用非常相似。
训练深度学习模型的计算需求每年都在以天文数字的速度增长:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c39af047dda9352486197ebdffb8a80b.png)
Deep learning compute time over the years ([Source](https://ark-invest.com/articles/analyst-research/ai-training/))
同时,计算成本也在降低。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e769d4cb91c9fe71bc13ea4fcff15da6.png)
How much does it cost to train ResNet-50? ([Source](https://ark-invest.com/articles/analyst-research/ai-training/))
这是自 CDC 和 IBM 大型机时代以来计算的相同自然循环:计算需求增加,而单位计算成本降低。
深度学习成本正在快速降低。一位分析师写道:
> 根据其成本下降的速度,人工智能处于非常早期的阶段。在摩尔定律的第一个十年,晶体管数量每年翻一番——或者说是其后几十年变化速度的两倍。我们在人工智能训练和人工智能推理方面看到的 10-100 倍的成本下降表明,人工智能在其发展中处于萌芽状态,或许未来几十年将会缓慢但持续地增长。
ARM 代表了一条令人兴奋的道路,可以满足深度学习对计算的贪得无厌的胃口。产量已经到位——每季度大约 70 亿个 ARM 单元。许可模式也已到位,允许供应商扩展,以提供与加速卡的深度集成。世界顶级超级计算机已经展示了基于 ARM 架构的可能性。
那么,期待很快看到深度学习库移植到 ARM 上合理吗?
可能性看起来很大。如果有一件事是确定的——如果它真的发生了,它会比我们预期的要早得多。
# Paperspace 赞助了一个以 GANs 为主题的拥抱脸社区冲刺
> 原文:<https://blog.paperspace.com/hugging-face-community-sprint-gan/>
我们最喜欢的话题之一是 GANs 或生成性对抗网络。近年来,GAN 的实现已经激增,其结果是用简单的指令集来生成像素艺术、印象派绘画以及介于两者之间的一切。最近几个月,我们发表了关于 [JoJoGAN](https://blog.paperspace.com/one-shot-face-stylization-with-jojogan/) 、 [GFP-GAN](https://blog.paperspace.com/restoring-old-photos-using-gfp-gan/) 、 [VQGAN-CLIP](https://blog.paperspace.com/how-i-made-this-articles-cover-photo-with-vqgan-clip/) 、 [Wasserstein GANs](https://blog.paperspace.com/wgans/) 、[超分辨率 GANs](https://blog.paperspace.com/super-resolution-generative-adversarial-networks/) 和 [DCGANs](https://blog.paperspace.com/face-generation-with-dcgans/) 的文章,仅举几例。
这就是为什么我们如此兴奋地宣布,我们将与我们的朋友合作,在本月以 GAN 为重点的社区冲刺拥抱脸提供免费的计算资源。
请继续阅读,了解如何加入这一有趣的活动。
## 介绍
Paperspace 和 Hugging Face 合作为即将到来的 [HugGAN 社区冲刺](https://discuss.huggingface.co/t/open-to-the-community-huggan-sprint/16302)的参与者提供免费计算资源。
参与者将有两周的时间使用 Paperspace compute 和拥抱面部工具来训练和展示 GANs。
## 日期
**开球**:2022 年 4 月 4 日
**投稿**:2022 年 4 月 4 日-4 月 15 日
**奖品公布**:2022 年 4 月 22 日
## 如何参与
* 点击查看活动信息页面
* 填写[这张](https://docs.google.com/forms/d/e/1FAIpQLSd_mpK4dYu1V-ejeTzoiIsTiMSVlZ0kYQCEoBmoa0vH-bNuag/viewform)表格
* 创建[拥抱面部中枢](https://huggingface.co/join)账户并加入[拥抱](https://huggingface.co/organizations/huggan/share/bekBYwkjyeJOAlxpcYRKgjLaRcrnIOeuge)组织
* 加入[不和谐频道](https://discord.gg/H3bUrDPTfS)
## 建议
需要一些 GANs 合作的想法吗?我们掩护你!这里有五个尚未在拥抱脸空间上出现的 GAN 库:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/763b3fa5be8a5eae7b706c99f6094907.png)
Pixray - [Source](https://github.com/pixray/pixray)
* [Pixray](https://github.com/pixray/pixray) :一种图像生成方法,用于创建高度精细的像素和绘画图像和艺术品。Pixray 使用 CLIP 将文本提示转化为独特的生成作品。
* [Transeditor](https://github.com/billyxyb/transeditor) :这款基于变形金刚的双空间 GAN 允许高度可控的面部调制,如变换姿势或改变发型。
* :这款场景生成 GAN 可以生成无限数量的复杂而独特的场景,如风景或城市景观。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/cc691dc71c3da6c0e208b86073f5a76e.png)
Anycost-GAN - [Source](https://github.com/mit-han-lab/anycost-gan)
* [Anycost-GAN](https://github.com/mit-han-lab/anycost-gan) :一种用于面部图像编辑的 GAN 方法,可以通过修改通道和再分辨率配置设置在各种计算成本上运行。
* [StyleMapGAN:](https://github.com/naver-ai/StyleMapGAN) 这种 GAN 利用潜在的空间维度对面部图像进行精细的细节编辑。
# 使用 Keras 调谐器自动优化超参数
> 原文:<https://blog.paperspace.com/hyperparameter-optimization-with-keras-tuner/>
**超参数**是决定机器学习模型结构并控制其学习过程的配置。它们不应该与模型参数(如偏差)混淆,后者的最佳值是在训练期间确定的。
超参数是可调整的配置,可手动设置和调整以优化模型性能。它们是顶级参数,其值有助于确定模型参数的权重。两种主要类型的超参数是确定模型结构的模型超参数(如层数和层单元)和影响和控制学习过程的算法超参数(如优化算法和学习速率)。
用于训练神经网络的一些标准超参数包括:
1.隐藏层数
2.隐藏层的单位数
3.退出率——通过在训练期间随机退出节点,可以使用单一模型来模拟大量不同的网络架构
4.激活函数(Relu,Sigmoid,Tanh) -在给定一个或一组输入的情况下,定义该节点的输出
5.优化算法(随机梯度下降、Adam Optimizer、RMSprop、e.t.c) -用于更新模型参数和最小化损失函数值的工具,根据训练集进行评估。
6.损失函数——衡量你的模型在预测预期结果方面有多好
7.学习率——控制每次更新模型权重时,响应估计误差而改变模型的程度
8.训练迭代次数(epochs) -学习算法在整个训练数据集中工作的次数。
9.批量大小——这个梯度下降的超参数控制着在模型的内部参数更新之前训练样本的数量。
在建立机器学习模型时,会设置超参数来指导训练过程。根据初始训练后模型的性能,这些值被反复调整以改进模型,直到选择出产生最佳结果的值的组合。调整超参数以获得优化机器学习模型性能的正确值集的过程被称为超参数调整。
在深度学习中,调整超参数可能具有挑战性。这主要是由于需要正确设置的不同配置、重新调整这些值以提高性能的几次尝试以及为超参数设置次优值所产生的不良结果。在实践中,这些值通常是基于某些推理来设置和微调的,例如特定问题的一般原则(例如,使用 softmax 激活函数进行多类分类)、构建模型的先前经验(例如,将隐藏层的单元逐渐减少到原来的 2 倍)、领域知识和输入数据的大小(为较小的数据集构建更简单的网络)。
即使有了这种认识,仍然很难为这些超参数得出完美的值。从业者通常使用试错法来确定最佳超参数。这是通过基于他们对问题的理解来初始化值,然后在为模型选择具有最佳性能的最终值之前,根据模型的性能在几次训练试验中本能地调整值来完成的。
以这种方式手动微调超参数对于管理计算资源来说通常是费力、耗时、次优和低效的。另一种方法是利用可扩展的超参数搜索算法,如贝叶斯优化、随机搜索和超波段。Keras Tuner 是一个可扩展的 Keras 框架,它提供了这些内置的算法,用于深度学习模型的超参数优化。它还提供了一种优化 Scikit-Learn 模型的算法。
在本文中,我们将学习如何使用 Keras Tuner 的各种功能来自动搜索最佳超参数。任务是使用 Keras 调谐器获得最佳超参数,以构建一个模型,对 CIFAR-10 数据集的图像进行准确分类。
## 1.设置。
使用 Keras Tuner 需要安装 Tensorflow 和 Keras Tuner 包,并导入构建模型所需的库。
KerasTuner 需要 Python 3.6+和 TensorFlow 2.0+。这些预装在梯度机器上。
```py
# install required packages
pip install tensorflow
pip install keras_tuner
```
```py
# import required packages
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Input, Model
from tensorflow.keras.layers import Dense, Flatten, Convolution2D, BatchNormalization
from tensorflow.keras.layers import ReLU, MaxPool2D, AvgPool2D, GlobalAvgPool2D
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.utils import plot_model
import keras_tuner as kt
from sklearn.model_selection import train_test_split
```
## 2.加载并准备数据集。
我们将加载包含 10 个对象类的 50,000 个训练和 10,000 个测试图像的 CIFAR-10 数据集。你可以在这里阅读更多关于数据集[的内容。我们还归一化图像像素值以具有相似的数据分布并简化训练。](https://www.cs.toronto.edu/~kriz/cifar.html)
预处理数据集版本被预加载到 Keras 数据集模块中,以便于访问和使用。
### 2.1 加载数据集并归一化图像像素值。
```py
# load the CIFAR-10 dataset from keras
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
# Normalize the image pixel values
img_train = x_train.astype('float32') / 255.0
img_test = x_test.astype('float32') / 255.0
# split the train data into train and validation sets
x_train, y_train, x_val, y_val = train_test_split(x_train, y_train, test_size=0.25)
```
## 3.打造超模。
现在我们已经完成了设置并准备好了输入数据,我们可以为超调构建模型了。这是通过使用 Keras Tuner 定义一个搜索模型(称为超级模型)来完成的,然后将该模型传递给一个调谐器进行超调。
超模型要么通过创建一个定制的模型构建器函数,利用内置模型来定义,要么为高级用例子类化 Tuner 类。
我们将使用前两种方法来创建搜索模型,以自动调整我们的超参数。
### 3.a .使用定制模型。
为了使用定制模型,我们将通过定义我们需要的层来定义模型构建功能,定制用于找到最佳参数的搜索空间,并在我们不调整超参数时为它们定义默认值。
#### 3.a.1 定义建模功能。
该函数采用一个参数(hp ),该参数实例化 Keras Tuner 的*超参数*对象,并用于定义超参数值的搜索空间。我们还将编译并返回超模型以供使用。我们将使用 Keras 功能模型模式来构建我们的模型。
```py
# function to build an hypermodel
# takes an argument from which to sample hyperparameters
def build_model(hp):
inputs = Input(shape = (32, 32, 3)) #input layer
x = inputs
# iterate a number of conv blocks from min_value to max_value
# tune the number of filters
# choose an optimal value from min_value to max_value
for i in range(hp.Int('conv_blocks',min_value = 3, max_value = 5, default=3)): # Int specifies the dtype of the values
filters = hp.Int('filters_' + str(i),min_value = 32,max_value = 256, step=32)
for _ in range(2):
# define the conv, BatchNorm and activation layers for each block
x = Convolution2D(filters, kernel_size=(3, 3), padding= 'same')(x)
x = BatchNormalization()(x)
x = ReLU()(x)
# choose an optimal pooling type
if hp.Choice('pooling_' + str(i), ['avg', 'max']) == 'max': # hp.Choice chooses from a list of values
x = MaxPool2D()(x)
else:
x = AvgPool2D()(x)
x = GlobalAvgPool2D()(x) # apply GlobalAvG Pooling
# Tune the number of units in the Dense layer
# Choose an optimal value between min_value to max_value
x = Dense(hp.Int('Dense units',min_value = 30, max_value = 100, step=10, default=50), activation='relu')(x)
outputs = Dense(10, activation= 'softmax')(x) # output layer
# define the model
model = Model(inputs, outputs)
# Tune the learning rate for the optimizer
# Choose an optimal value frommin_value to max_value
model.compile(optimizer= Adam(hp.Float('learning_rate', min_value = 1e-4, max_value =1e-2, sampling='log')),
loss= 'sparse_categorical_crossentropy', metrics = ['accuracy'])
return model
```
###### **理解代码。**
**第 3 行:**我们定义了一个模型构建函数( **build_model** )并传递了一个参数 *(hp* ),该参数实例化了 Keras Tuner 包的超参数对象,用于定义超参数值的搜索空间。
第 5-6 行:我们定义我们的输入层,并将其传递给一个变量(x)
**第 11 行:**我们为我们的模型定义卷积块数量的搜索空间。我们使用 hp.Int 函数*来创建一个整数超参数搜索空间。这创建了从*最小值* + 1 到*最大值*的搜索空间。这将在 4 和 5 个卷积块的空间中搜索使精度最大化的最佳值。*
**第 12 行:**我们为块中每个卷积层的滤波器数量定义一个搜索空间。32 的*步骤*将连续卷积层的滤波器单元增加 32。
第 14-24 行:我们为每个块定义一组三层。每个子层对输入应用卷积、批量归一化和 ReLU 激活。*惠普。池层的 Choice* 函数随机选择一个提供的池应用于输入。然后,我们将预定义的滤波器搜索空间传递给卷积层。
**第 26 行:**我们应用全局平均池和密集层,搜索空间从*最小值*到*最大值*,步长*为 10。我们还通过激活 *softmax* 来定义输出层。*
第 34-40 行:最后,我们使用输入和输出层定义模型,编译模型并返回构建好的超模型。
为了编译模型,我们用 *hp 定义了一个学习率搜索空间。Float* 函数创建一个从 0.0001 到 0.002 的搜索空间,用于选择最佳学习率。
#### 3.a.2 初始化搜索算法(调谐器)。
在构建了*超模型*之后,我们现在可以初始化我们的搜索算法了。我们将不得不从内置的搜索算法中进行选择,如*贝叶斯优化、超波段和随机搜索、*经典机器学习模型。
在我们的例子中,我们将使用*超波段*搜索算法。tuner 函数接受参数,如*超级模型*、用于评估模型的*目标*、用于训练的 *max_epochs* 、每个模型的 *hyperband_iterations* 的数量、用于保存训练日志(可以使用 Tensorboard 可视化)的*目录*和 *project_name* 。
```py
# initialize tuner to run the model.
# using the Hyperband search algorithm
tuner = kt.Hyperband(
hypermodel = build_model,
objective='val_accuracy',
max_epochs=30,
hyperband_iterations=2,
directory="Keras_tuner_dir",
project_name="Keras_tuner_Demo")
```
### 3.b .使用内置模型。
Keras Tuner 目前提供了两个可调的内置模型,HyperResnet 和 HyperXception 模型,它们分别搜索 Resnet 和 Xception 架构的不同组合。使用内置模型定义调谐器类似于使用模型构建功能。
```py
# Initialize a random search tuner
# using the Resnet architecture
# and the Random Search algorithm
tuner = kt.tuners.RandomSearch(
kt.applications.HyperResNet(input_shape=(32, 32, 3), classes=10),
objective='val_accuracy',
max_trials=30)
```
### 4.运行最佳超参数搜索。
然后,我们可以使用我们的调谐器在定义的搜索空间内搜索模型的最佳超参数。该方法类似于使用 Keras 拟合模型。
```py
# Run the search
tuner.search(x_train, y_train,
validation_data= (x_test,y_test),
epochs=30,
callbacks=[tf.keras.callbacks.EarlyStopping(patience=2)])
```
### 5.获得并显示最佳超参数和模型。
可以使用调谐器实例的 *get_best_hyperparameters* 方法获得定义的搜索空间内的模型的最佳超参数,并使用 *get_best_models* 方法获得最佳模型。
```py
# Get the optimal hyperparameters
best_hps= tuner.get_best_hyperparameters(1)[0]
# get the best model
best_model = tuner.get_best_models(1)[0]
```
我们还可以查看最佳超参数。在我们的例子中,我们可以这样实现:
```py
nblocks = best_hps.get('conv_blocks')
print(f'Number of conv blocks: {nblocks}')
for hyparam in [f'filters_{i}' for i in range(nblocks)] + [f'pooling_{i}' for i in range(nblocks)] + ['Dense units'] + ['learning_rate']:
print(f'{hyparam}: {best_hps.get(hyparam)}')
```
这将显示卷积块的数量、卷积和密集层的过滤器和单位的最佳值、池层的选择以及学习率。
我们还可以使用适当的 Keras 函数查看优化模型的概要和结构。
```py
# display model structure
plot_model(best_model, 'best_model.png', show_shapes=True)
# show model summary
best_model.summary()
```
## 6.训练模型。
最后,在调用 *fit* 函数来训练模型之前,我们将使用最佳超参数建立模型。
```py
# Build the model with the optimal hyperparameters
# train the model.
model = tuner.hypermodel.build(best_hps)
model.fit(x_train, y_train,
validation_data= (x_val,y_val),
epochs= 25,
callbacks=[tf.keras.callbacks.EarlyStopping(patience=5)])
```
在这里,我对模型进行了 50 个时期的训练,并添加了一个 *EarlyStopping* 回调,以便在模型不再改进时停止训练。
## 6.评估模型。
我们可以在测试集上评估模型。我们将使用模型的*损失*和准确度*得分*来评估模型。您可以尝试其他适用的指标。
```py
# evaluate the result
eval_result = model.evaluate(x_test, y_test)
print(f"test loss: {eval_result[0]}, test accuracy: {eval_result[1]}")
```
## 总结。
超参数是机器学习模型性能的关键决定因素,用试错法来调整它们是低效的。Keras Tuner 应用搜索算法在定义的搜索空间中自动找到最佳超参数。
在本文中,我们利用 Keras 调谐器来确定多类分类任务的最佳超参数。我们能够使用自定义模型和内置模型在超模中定义搜索空间,然后利用提供的搜索算法自动搜索几个值和组合,为我们的模型找到超参数的最佳组合。
您可以查看 [Keras Tuner guide](https://keras.io/guides/keras_tuner/) ,了解如何在 Tensorboard 上可视化调优过程、分发超调过程、定制搜索空间以及为高级用例子类化 Tuner 类。
# 在 CIFAR10 数据集上应用模型可解释性算法
> 原文:<https://blog.paperspace.com/illustration-of-integrated-gradients-on-cifar10-dataset/>
### 介绍
像 Torchvision 和 Torchtext 这样的 PyTorch 库支持专门的数据,如计算机视觉和自然语言数据。[torch vision . datasets](https://pytorch.org/vision/0.8/datasets.html)模块演示了如何使用内置类加载数据。使用 [torchvision.datasets](https://pytorch.org/vision/0.8/datasets.html) 模块时,您可以通过各种子类从众所周知的数据集中加载图像数据。本教程演示了如何将 Captum 库中的模型可解释性算法应用于一个简单的模型和来自 CIFAR 数据集的测试样本。
在本教程中,我们将建立一个基本模型,类似于这里的[所示的](https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#sphx-glr-beginner-blitz-cifar10-tutorial-py)。然后,我们将使用 IntegratedGradients、显著性、DeepLift 和 NoiseTunnel 等归因算法将图像的标签归因于输入像素,并将其可视化。在学习本教程之前,需要安装 torchvision 和 captum。
### 导入库:
```py
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
import torch
import torchvision
import torchvision.transforms as transforms
import torchvision.transforms.functional as TF
import torch.optim as optim
import torch.nn as nn
import torch.nn.functional as F
from torchvision import models
from captum.attr import IntegratedGradients
from captum.attr import Saliency
from captum.attr import DeepLift
from captum.attr import NoiseTunnel
from captum.attr import visualization as v
```
### 使用数据加载器为培训准备数据
我们通过下面的代码加载测试和训练数据集,定义图像转换器,并支持分类标签类。在使用数据对 NN 模型进行训练和测试之前,可能需要对数据进行一些调整。数据的值可以被标准化以促进训练过程,被补充以产生更广泛的数据集,或者从一种类型的对象改变为张量。
通过使用**[transforms . compose](https://pytorch.org/vision/stable/generated/torchvision.transforms.Compose.html)**,我们定义了一个转换集合。这个类将接受一个转换列表,然后按照给定的顺序应用它们。在这一步中,我们首先将图像转换为张量,然后根据预设的平均值和标准偏差对张量的值进行归一化。**torch . utils . data . data loader**类使批处理变得简单。
一种常见的做法是以“小批量”的方式通过模型输入样本,在每次迭代中重新排列数据以最小化过度拟合,并利用 Python 的多重处理来加速数据检索。一个名为`DataLoader`的 iterable 用一个简单的 API 为我们包装了这种复杂性。我们的模型使用两个卷积层、两个最大池层和三个全连接或线性层。
```py
class Net(nn.Module): ## create layers as class attributes
def __init__(self): ## Parameters initialization with __init__() function
super(Net, self).__init__() ## call the parent constuctor
self.conv1 = nn.Conv2d(3, 6, 5) ## Appy our first set of conv layers
self.pool1 = nn.MaxPool2d(2, 2) ## Apply our first set of max pooling layers
self.pool2 = nn.MaxPool2d(2, 2) ## Apply our second set of maxpooling layers
self.conv2 = nn.Conv2d(6, 16, 5) ## second set of conv layers
self.fc1 = nn.Linear(16 * 5 * 5, 120) ##first set of fully conneted layers
self.fc2 = nn.Linear(120, 84) ## second set of fullly conneted layers
self.fc3 = nn.Linear(84, 10) ## third set of fully connected layer
self.relu1 = nn.ReLU() ## Apply RELU activation function
self.relu2 = nn.ReLU()
self.relu3 = nn.ReLU()
self.relu4 = nn.ReLU()
def forward(self, x): ## specify how the model handles the data.
x = self.pool1(self.relu1(self.conv1(x)))
x = self.pool2(self.relu2(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = self.relu3(self.fc1(x))
x = self.relu4(self.fc2(x))
x = self.fc3(x)
return x
## Model initialization
net = Net()
```
### 定义损失函数和优化器
接下来,我们需要指定损失函数(也称为标准),以及优化它的技术。损失函数决定了我们的模型表现如何,它用于计算实际结果和预测之间的损失。在训练过程中,我们将调整模型参数,以尽量减少损失。确保在代码中包含您的型号的`model.parameters()` 。本教程将 [CrossEntropyLoss()](https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html) 函数与随机梯度下降(SGD)优化器结合使用。以下代码显示了如何使用 [torch.optim](https://pytorch.org/docs/stable/optim.html) 和 [torch.nn](https://pytorch.org/docs/stable/nn.html) 包来创建损失函数和优化器。
```py
# Initialize criterion and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
```
### 训练模型
我们在“模型”文件夹中保存了一个预训练模型的版本,以便我们可以加载它,而不必每次都从头开始训练过程。您可以从这里下载:
[https://github . com/py torch/captum/blob/master/tutorials/models/cifar _ torch vision . pt](https://github.com/pytorch/captum/blob/master/tutorials/models/cifar_torchvision.pt)
```py
USE_PRETRAINED_MODEL = True
## If using the pretrained model, load it through the function load_state_dict
if USE_PRETRAINED_MODEL:
print("Using existing trained model")
net.load_state_dict(torch.load('models/cifar_torchvision.pt'))
else:
for epoch in range(5): # loop over the dataset multiple times
running_loss = 0.0 ## Resetting running_loss to zero
for i, data in enumerate(trainloader, 0): ## restarts the trainloader iterator on each epoch.
# get the inputs
inputs, labels = data
# If you don't reset the gradients to zero before each ##backpropagation run, you'll end up with an accumulation of them.
optimizer.zero_grad()
outputs = net(inputs) ## Carry out the forward pass.
loss = criterion(outputs, labels)## loss computation
loss.backward() ## Carry out backpropagation, and estimate ##gradients.
optimizer.step() ## Make adjustments to the parameters according ##to the gradients.
# print statistics
running_loss += loss.item() ## Build up the batch loss so that we ##can get an average across the epoch.
if i % 2000 == 1999: # print every 2000 mini-batches
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
torch.save(net.state_dict(), 'models/cifar_torchvision.pt')
```
* 如果使用预先训练好的模型,通过函数 [load_state_dict()](https://pytorch.org/docs/stable/generated/torch.nn.Module.html?highlight=load_state_dict#torch.nn.Module.load_state_dict) 加载。如果没有,就跟着程序的其余部分走。
* 对于生成的每一批,输入都被传递到模型中。成功完成正向传递后,它返回计算的输出。
* 我们的下一步是使用`criterion()`函数,通过将模型结果(输出)与来自训练数据集的实际值进行比较来计算误差或损失。
* 接下来,我们通过调整模型参数来最小化损失。为此,我们将首先使用`loss.backward()` 对梯度计算进行反向传播,然后使用`backward().step()`执行优化程序,根据已经计算的梯度更新参数。
### 制作一个图像网格
下面的代码加载测试数据集中的大量图像样本,然后进行一些预测。我们使用函数[torch vision . utils . make _ grid()](https://pytorch.org/vision/stable/generated/torchvision.utils.make_grid.html)来制作图像网格,并显示 groundtruth 和预测标签。一步一步来就行了。
```py
## Define imwshow function
def imshow(img, transpose = True):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy() ## convert image to numpy
plt.imshow(np.transpose(npimg, (1, 2, 0))) ## The supplied matrix, npimg, ##has to be transposed into numpy with the values of x,y, and z positioned at ##the indexes 1,2,0 respectively.
plt.show()
## iterate through the dataset. Each iteration returns a batch of images and ##labels
dataiter = iter(testloader)
images, labels = dataiter.next()
# print images
imshow(torchvision.utils.make_grid(images)) ## Display images with ##torchvision.utils.make_grid() function
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4))) ## Display labels for ground truth
outputs = net(images) ## outcome prediction for each batch
_, predicted = torch.max(outputs, 1) ## Find the class index that has the ##highest probability and pick that one.
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] ## Display labels for predicted classes
for j in range(4)))
```
输出:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d4904fa7c6a0ea379da353333a14202a.png)
让我们选择一个索引为`ind`的测试图像,并对其运行一些属性算法。
```py
ind = 3
input = images[ind].unsqueeze(0) ## adds an additional dimension to the tensor.
input.requires_grad = True
```
**注**:最常用的 [requires grad_()](https://pytorch.org/docs/stable/generated/torch.Tensor.requires_grad_.html) 功能是指示亲笔签名开始对张量**张量**进行记录操作。如果一个**张量**具有 **requires_grad=False** 属性(因为它是通过数据加载器或所需的预处理或初始化获得的),调用`tensor.requires_grad_()` 将导致亲笔签名的开始对**张量**进行记录操作。
现在,出于解释目的,我们将模型设置为 eval 模式。重要的是要记住,在开始推理之前,您需要执行`model.eval()`方法。如果不采取这一步骤,推理结果将是不一致的。
```py
## Set the model in evaluation mode
net.eval()
```
输出:
```py
Net(
(conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
(pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=400, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
(relu1): ReLU()
(relu2): ReLU()
(relu3): ReLU()
(relu4): ReLU()
)
```
让我们定义特征属性的函数。
```py
def attribute_image_f(algorithm, input, **kwargs):
net.zero_grad()
tensor_attributions = algorithm.attribute(input,
target=labels[ind],
**kwargs
)
return tensor_attributions
```
### 什么是显著图
[显著性](https://captum.ai/api/saliency.html)图是神经网络决策过程的可视化表示。它们也有助于确定卷积层的具体重点,让我们更好地了解如何做出决策。
卷积神经网络使用显著图向我们展示它们对预测结果最感兴趣的地方。
这是计算输入属性的基本方法。它返回相对于输入的梯度。显著图的目的是强调输入图像中对输出分类贡献最大的像素。现在,考虑输出类得分相对于输入图像像素值的梯度。具有显著(正或负)梯度的像素是那些需要最轻微的变化来最大程度地影响类分数的像素。物体在图像中的位置可以从这些像素中推断出来。这是显著图背后的基本概念。在这里,我们计算关于类“ind”的梯度,并为了可视化的目的对它们进行转置。
```py
saliency = Saliency(net)
grads = saliency.attribute(input, target=labels[ind].item())
grads = np.transpose(grads.squeeze().cpu().detach().numpy(), (1, 2, 0))
```
### 集成渐变
Mukund Sundararajan、Ankur Taly 和 Qiqi Yan 在他们题为“[深层网络的公理化属性(ICML 2017)](https://arxiv.org/pdf/1703.01365.pdf) 的论文中研究了使用集成梯度的概念。通过他们对当时流行的归因方案的分析,作者关注于他们认为所有特征归因方案都应该遵守的两个公理:
* **敏感度:**如果每个输入和基线在一个特征上不同,但具有不同的预测,则不同的特征应被赋予非零属性。可以证明 [LRP](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0130140) 和 DeepLiFT 由于总相关性守恒而坚持敏感性。然而,基于梯度的方法不能保证灵敏度公理。当分数函数在某些输入特征上局部“平坦”时,饱和发生在 ReLU 或 MaxPool 阶段。特征归因研究中的一个常见主题是需要通过饱和激活来恰当地传递相关性或归因。
* **实现不变性:**尽管具有非常不同的实现,但是如果所有输入的输出相同,则两个网络被认为是功能相似的。[香草渐变](https://christophm.github.io/interpretable-ml-book/pixel-attribution.html)理论上确保实现不变性。LRP 和迪普利特可以通过对梯度的粗略近似来突破这一假设。作者提供了 LRP 和深度提升破坏实现不变性的例子。
作者建议对特征属性采用综合梯度,其定义如下:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/27dd8ed0334df7a561220e280ac0cc41.png)
作者证明了上述定义符合两个期望的假设:
* **灵敏度:**积分梯度,像 LRP 和 DeepLiFT,根据微积分的[基本定理,对特征分数的差异求和。](https://openstax.org/books/calculus-volume-1/pages/5-3-the-fundamental-theorem-of-calculus) LRP 和 DeepLiFT 同样敏感。
* **实现不变性:**由于其基于梯度的定义,所以尊重实现不变性的原则。
我们将对测试图像应用[综合梯度](https://captum.ai/api/integrated_gradients.html)属性技术。积分梯度执行必要的计算,以确定类别索引`ind`相对于输入图像像素的输出预测的梯度积分。
```py
ig = IntegratedGradients(net)
attrig, delta = attribute_image_f(ig, input, baselines=input * 0, return_convergence_delta=True)
attrig = np.transpose(attrig.squeeze().cpu().detach().numpy(), (1, 2, 0))
print('Approximation delta: ', abs(delta))
```
使用测试图像,使用综合梯度和带有 smoothgrad square 选项的噪声隧道所需的步骤如下所示。[带 smoothgrad square 选项的噪声隧道](https://captum.ai/api/noise_tunnel.html)将标准偏差为 **stdevs=0.2** 的高斯噪声应用于输入图像 **nt_samples** 次,计算 **nt_samples** 图像的属性,然后返回 **nt_samples** 图像的平方属性的平均值。
```py
ig = IntegratedGradients(net)
nt = NoiseTunnel(ig)
attrig_nt = attribute_image_f(nt, input, baselines=input * 0, nt_type='smoothgrad_sq',
nt_samples=100, stdevs=0.2)
attrig_nt = np.transpose(attrig_nt.squeeze(0).cpu().detach().numpy(), (1, 2, 0))
```
### 深层提升
在他们的工作[通过传播激活差异学习重要特征](https://arxiv.org/pdf/1704.02685.pdf) (ICML 2017)中,两代情·施里库马尔、佩顿·格林赛德和安舒尔·昆达杰介绍了深度提升方法。这些研究人员受到塞巴斯蒂安·巴赫及其同事对[LRP/泰勒分解](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0130140)的研究的启发。
与输入图像一起,被称为 DeepLiFT(深度学习重要特征)的深度学习算法使用参考图像来解释输入像素。
尽管 LRP 坚持守恒原则,但仍不清楚净相关性应如何分布在不同的像素上。DeepLiFT 通过强制使用额外的公理来向下传播相关性,为这个问题提供了一个解决方案。以下是 DeepLiFT 坚持的两条公理:
* **总相关性守恒:**它规定所有输入的相关性之和必须等于每个神经元的输入图像分数和基线图像分数之间的分数差。这个公理是相同的一个可以在 LRP 发现。
* **链式法则/反向传播:**每个输入的相关性像梯度一样遵循链式法则。使用这些信息,我们可以使用每个输入的类似梯度的相关性来反向传播它。这个公理使 DeepLiFT 的梯度反向传播更接近于普通梯度。
在下面的代码中,我们对测试图像执行 DeepLift 操作。 [DeepLIFT](https://captum.ai/api/deep_lift.html) 根据输入与某些“参考”输入的差异来解释输出与某些“参考”输出的差异。
```py
dl = DeepLift(net)
attrdl = attribute_image_f(dl, input, baselines=input * 0)
attrdl = np.transpose(attrdl.squeeze(0).cpu().detach().numpy(), (1, 2, 0))
```
### 属性可视化
在接下来的程序中,我们将看到如何使用 SmoothGrad 可视化显著图、深度提升、集成梯度和集成梯度的属性。我们使用函数 [visualize_image_attr](https://captum.ai/api/utilities.html) ,它负责可视化给定图像的属性。这是通过首先标准化所需符号的属性值(正、负、绝对值或全部)来实现的,然后使用所选模式在 matplotlib 图形中显示它们。
```py
print('Original Image')
print('Predicted:', classes[predicted[ind]],
' Probability:', torch.max(F.softmax(outputs, 1)).item())
original_image = np.transpose((images[ind].cpu().detach().numpy() / 2) + 0.5, (1, 2, 0))
_ = v.visualize_image_attr(None, original_image,
method="original_image", title="Original Image")
_ = v.visualize_image_attr(grads, original_image, method="blended_heat_map", sign="absolute_value",
show_colorbar=True, title="Overlayed Gradient Magnitudes")
_ = v.visualize_image_attr(attrig, original_image, method="blended_heat_map",sign="all",
show_colorbar=True, title="Overlayed Integrated Gradients")
_ = v.visualize_image_attr(attrig_nt, original_image, method="blended_heat_map", sign="absolute_value",
outlier_perc=10, show_colorbar=True,
title="Overlayed Integrated Gradients \n with SmoothGrad Squared")
_ = v.visualize_image_attr(attrdl, original_image, method="blended_heat_map",sign="all",show_colorbar=True,
title="Overlayed DeepLift")
```
**注意**:读者可以运行代码并可视化输出。
### 结论
在本教程中,我们展示了如何将 Captum 库中的模型可解释性算法应用于一个简单的模型和来自 CIFAR 数据集的测试样本。我们已经建立了一个基本模型,并使用属性算法,如综合梯度,显著性,深度提升和噪声隧道,将图像的标签归因于输入像素并将其可视化。
### 参考
[https://arxiv.org/pdf/1703.01365.pdf](https://arxiv.org/pdf/1703.01365.pdf)
[https://debugger cafe . com/凸图-卷积-神经网络/](https://debuggercafe.com/saliency-maps-in-convolutional-neural-networks/)
[https://towardsdatascience . com/explable-神经网络-近期-进展-part-4-73 cacc 910 fef](https://towardsdatascience.com/explainable-neural-networks-recent-advancements-part-4-73cacc910fef)
[https://captum.ai/tutorials/CIFAR_TorchVision_Interpret](https://captum.ai/tutorials/CIFAR_TorchVision_Interpret)
[https://www . oreilly . com/library/view/py torch-pocket-reference/97814920](https://www.oreilly.com/library/view/pytorch-pocket-reference/9781492089995/)
# 人工智能图像字幕
> 原文:<https://blog.paperspace.com/image-captioning-with-ai/>
在人工智能领域,一个真正引起许多人注意的应用是图像字幕。如果你仔细想想,似乎没有办法告诉一堆数字来为一幅图像提供一个准确描述它的标题。现在,借助深度学习的力量,我们可以比我们想象的更准确地实现这一点。
为图像编写标题的问题有两个方面:你需要通过提取相关特征来理解图像的含义,还需要将这些特征转换成人类可读的格式。在本教程中,我们将分别研究这两个阶段,然后将各个部分连接在一起。我们将从特征提取阶段开始。
本教程的灵感来自于关于图像字幕的 TensorFlow 教程。对于本教程,我们将使用 [COCO](http://cocodataset.org/#home) 数据集(CommonOobjects inContext),它由超过 200k 个带标签的图像组成,每个图像配有五个标题。
您可以使用免费的 GPU 和 Jupyter 笔记本在 [ML Showcase](https://ml-showcase.paperspace.com/projects/image-captioning-with-keras) 上运行本教程的代码。
## 步骤 1:图像特征提取
对于给定的图像,总是有可能存在几乎不能以任何方式描述图像的冗余元素。例如,赛马图像上的水印实际上并不能告诉我们图像本身的任何信息。我们需要一种算法,能够提取有用的特征,并省去多余的特征,比如本例中的水印。几年前,我可能不会写这篇教程,因为用于特征提取的方法需要大量的数学和特定领域的专业知识。随着深度学习方法的出现,现在可以用最少的努力和时间来执行特征提取,同时仅用在许多图像上训练过的单个神经网络来实现更强的鲁棒性。
但是,等等,我们如何获得如此大量的图像来制作一个令人难以置信的基于神经网络的特征提取器呢?感谢[迁移学习](https://machinelearningmastery.com/transfer-learning-for-deep-learning/),或者使用预先训练的模型对新的和不同的问题进行推理的能力,我们不需要一张图片就可以开始。有许多规范的卷积网络模型已经在数百万张图像上进行了训练,比如 [ImageNet](http://www.image-net.org/) 。我们所要做的就是切掉这些网络中与任务相关的部分,这样我们就有了一个非常强大的特征提取器。
当我们实际上更深入地挖掘这些网络的层时,我们观察到每一层在训练期间以某种方式被分派任务来提取特定的特征。因此,我们在一个网络中有一堆特征提取器。在本教程中,我们将使用由 Google 开发的强大模型 Inception v3 作为我们的特征提取器。我们只需要三行 Keras 代码就可以获得这个模型。
```py
image_model = tf.keras.applications.InceptionV3(include_top=False,
weights='imagenet')
new_input = image_model.input
hidden_layer = image_model.layers[-1].output
```
由于无论历元或迭代如何,每个影像都将具有唯一的要素表示,因此建议将所有影像通过要素提取器运行一次,并将提取的要素缓存在磁盘上。这节省了大量时间,因为我们不需要在每个时期通过特征提取器执行前向传播。特征提取过程的概括工作流程如下:
* 获得特征提取模型(在这种情况下,我们使用 Inception V3)
* 使用`tf.data`加载图像数据集
* 缓存所有图像通过特征提取器一次获得的所有特征
在代码中,这看起来像:
```py
def load_image(image_path):
img = tf.io.read_file(image_path)
img = tf.image.decode_jpeg(img, channels=3)
img = tf.image.resize(img, (299, 299))
img = tf.keras.applications.inception_v3.preprocess_input(img)
return img, image_path
encode_train = sorted(set(img_name_vector))
# use the tf.data api to load image dataset from directory into batches
image_dataset = tf.data.Dataset.from_tensor_slices(encode_train)
image_dataset = image_dataset.map(
load_image, num_parallel_calls=tf.data.experimental.AUTOTUNE).batch(16)
# iterate through batches of image dataset and extract features using our feature extractor(image_features_extract_model) by doing a forward propagation.
# this is going to take some time
for img, path in image_dataset:
batch_features = image_features_extract_model(img)
batch_features = tf.reshape(batch_features,
(batch_features.shape[0], -1, batch_features.shape[3]))
# iterate through batches of features obtained from feature extractor and store in a serialized format.
for bf, p in zip(batch_features, path):
path_of_feature = p.numpy().decode("utf-8")
np.save(path_of_feature, bf.numpy())
```
现在我们已经完成了字幕任务的第一阶段,我们进入第二阶段,处理文本。这个阶段还使用神经网络,特别是用一些机制改进的递归神经网络(RNN ),以增加将特征翻译成语言的鲁棒性。
## 第二步:解码阶段
### 自然语言处理
我必须承认,这是最乏味的部分,但我们将保持简单明了。这里我们要做的第一件事是用四个简单的步骤处理我们的文本数据集:
* 修剪和简化数据集
* 将文本数据标记化
* 填充单词序列
* 使用`tf.data` API 对数据集进行批处理,并将其分成训练集和验证集
让我们看看实现这一点的代码。
**注意:阅读代码中的注释,了解每一行的解释。**
```py
top_k = 5000
# Create a tokenizer object from keras
tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=top_k,
oov_token="<unk>",
filters='!"#$%&()*+.,-/:;=?@[\]^_`{|}~ ')
# Creates a vocabulary with words from the caption dataset
tokenizer.fit_on_texts(train_captions)
# Convert each word into a unique integer
train_seqs = tokenizer.texts_to_sequences(train_captions)
# Assign token for pad
tokenizer.word_index['<pad>'] = 0
tokenizer.index_word[0] = '<pad>'
# Pads sentences. How? It calcuates the length of the longest sentence and fills the deficit of the other shorter sentences with zeros. This is to make batching possible.
cap_vector = tf.keras.preprocessing.sequence.pad_sequences(train_seqs, padding='post')
# Split each dataset into training and validation sets. Note that we're splitting both the image dataset and caption dataset.
# An image in image_name_train will correspond to its caption in cap_train.
img_name_train, img_name_val, cap_train, cap_val = train_test_split(img_name_vector,
cap_vector,
test_size=0.2,
random_state=0)
dataset = tf.data.Dataset.from_tensor_slices((img_name_train, cap_train))
# Load the serialized numpy files that we stored on disk earlier
def map_func(img_name, cap):
img_tensor = np.load(img_name.decode('utf-8')+'.npy')
return img_tensor, cap
# Use map to load the numpy files in parallel
dataset = dataset.map(lambda item1, item2: tf.numpy_function(
map_func, [item1, item2], [tf.float32, tf.int32]),
num_parallel_calls=tf.data.experimental.AUTOTUNE)
BATCH_SIZE = 64
BUFFER_SIZE = 1000
# Shuffle and batch
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
# Prefetching ensures that resources don't go idle during training process. To put this another way, while the model is executing time step t, the input pipeline is loading data for time step t+1.
dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
```
既然我们已经建立了所有的数据集,让我们继续建立我们的第二个网络,一个语言模型。之前我提到过,我们的模型将通过一个注意力机制得到增强。这就是我们现在要做的。
### 注意机制
注意力机制的整个概念非常直观。注意力机制变得非常流行的一个热门领域是神经机器翻译,机器翻译中注意力机制背后的想法与图像字幕中的想法非常相似。在机器翻译中,我们试图将一个句子从一种语言翻译成另一种语言,当解码单个单词(在输出句子中)时,我们希望“注意”输入句子中与我们正在解码的单个单词语义相关的一些特定单词。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dd331c54a4c58381313fd4e67f556f66.png)
Source: [Neural Morphological Analysis: Encoding-Decoding Canonical Segments](https://www.researchgate.net/publication/312417239_Neural_Morphological_Analysis_Encoding-Decoding_Canonical_Segments)
如果你想更多地了解注意力机制是如何工作的,或者如果你在理解下面的代码方面有任何困难,请查看我之前关于[神经机器翻译](https://blog.paperspace.com/neural-machine-translation-with-tensorflow/)的帖子。
```py
# Bahdanau is one variant of the attention mechanism.
# The other variant is the Luong attention.
class BahdanauAttention(tf.keras.Model):
def __init__(self, units):
super(BahdanauAttention, self).__init__()
self.W1 = tf.keras.layers.Dense(units)
self.W2 = tf.keras.layers.Dense(units)
self.V = tf.keras.layers.Dense(1)
def call(self, features, hidden):
# features(CNN_encoder output) shape == (batch_size, 64, embedding_dim)
# hidden shape == (batch_size, hidden_size)
# hidden_with_time_axis shape == (batch_size, 1, hidden_size)
hidden_with_time_axis = tf.expand_dims(hidden, 1)
# score shape == (batch_size, 64, hidden_size)
score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))
(batch_size,64,hidden_size) + (batch_size,1,hidden_size)
# attention_weights shape == (batch_size, 64, 1)
# You get 1 at the last axis because you are applying score to self.V
attention_weights = tf.nn.softmax(self.V(score), axis=1)
# context_vector shape after sum == (batch_size, hidden_size)
context_vector = attention_weights * features
context_vector = tf.reduce_sum(context_vector, axis=1)
return context_vector, attention_weights
```
回想一下,我们的特征提取器的输出是形状 *batch_size*8*8*2048* (这是紧接在分类层之前的初始网络的最后一层的输出形状)。我们要把这个折叠成一个*【batch _ size * 64 * 2048】(8 * 8 = 64)*特征张量,通过一个具有 ReLU 激活函数的单层线性网络传递。这应该输出一个形状为*batch _ size * 64 * embedding _ size*的张量,其中嵌入大小是我们设置的任意整数(但一定要保持较低)。线性层的输出是我们输入到用注意力机制改造的循环网络中的内容。
```py
class CNN_Encoder(tf.keras.Model):
def __init__(self, embedding_dim):
super(CNN_Encoder, self).__init__()
# shape after fc == (batch_size, 64, embedding_dim)
self.fc = tf.keras.layers.Dense(embedding_dim)
def call(self, x):
x = self.fc(x)
x = tf.nn.relu(x)
return x
```
最后让我们把注意力网络和循环网络结合起来。
```py
class RNN_Decoder(tf.keras.Model):
def __init__(self, embedding_dim, units, vocab_size):
super(RNN_Decoder, self).__init__()
self.units = units
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
self.gru = tf.keras.layers.GRU(self.units,
return_sequences=True,
return_state=True,
recurrent_initializer='glorot_uniform')
self.fc1 = tf.keras.layers.Dense(self.units)
self.fc2 = tf.keras.layers.Dense(vocab_size)
self.attention = BahdanauAttention(self.units)
def call(self, x, features, hidden):
# defining attention as a separate model
context_vector, attention_weights = self.attention(features, hidden)
# x shape after passing through embedding == (batch_size, 1, embedding_dim)
x = self.embedding(x)
# x shape after concatenation == (batch_size, 1, embedding_dim + hidden_size)
x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
# passing the concatenated vector to the GRU
output, state = self.gru(x)
# shape == (batch_size, max_length, hidden_size)
x = self.fc1(output)
# x shape == (batch_size * max_length, hidden_size)
x = tf.reshape(x, (-1, x.shape[2]))
# output shape == (batch_size * max_length, vocab)
x = self.fc2(x)
return x, state, attention_weights
def reset_state(self, batch_size):
return tf.zeros((batch_size, self.units))
encoder = CNN_Encoder(embedding_dim)
decoder = RNN_Decoder(embedding_dim, units, vocab_size)
```
现在我们有了一个完整的模型,剩下的步骤就是通常的机器学习训练和验证步骤。我假设你熟悉模型训练和梯度下降(如果不熟悉,查看[这篇文章](https://blog.paperspace.com/intro-to-optimization-in-deep-learning-gradient-descent/))所以从这里我将进入验证阶段。
### 确认
与其他机器学习验证管道不同,在这种情况下,我们不会根据某个指标来验证我们的模型。相反,我们将根据它是否生成了正确的标题来验证我们的模型,最重要的是,它在生成这些标题时是否注意到了正确的特征。我们可以通过将为特定图像生成标题时生成的注意力矩阵权重叠加在图像本身上来实现这一点。这会产生一个带有一些斑点的图像,这些斑点表明网络在生成字幕时关注的是什么。让我们看一些例子。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/178cbfa2882074a2d3837f7158837ed6.png)
密切注意上面有“男性”的图像。你可以清楚地看到,大部分的白盒都以不同的强度聚集在冲浪板上的那个人身上。这些白色方框表示网络正在关注图像中的这些区域;强度越大,网络越关注该区域。解码字幕使用贪婪搜索算法,如下面的代码所示。其工作原理是将特定时间步的解码字作为输入输入到下一个时间步。通过使用比贪婪搜索算法更健壮的波束搜索解码器,可以引入解码字幕的进一步改进,但是实现起来有点复杂。下面还包含了显示注意力权重重叠的图像的代码。
```py
def evaluate(image):
attention_plot = np.zeros((max_length, attention_features_shape))
hidden = decoder.reset_state(batch_size=1)
temp_input = tf.expand_dims(load_image(image)[0], 0)
img_tensor_val = image_features_extract_model(temp_input)
img_tensor_val = tf.reshape(img_tensor_val, (img_tensor_val.shape[0], -1, img_tensor_val.shape[3]))
features = encoder(img_tensor_val)
dec_input = tf.expand_dims([tokenizer.word_index['<start>']], 0)
result = []
for i in range(max_length):
predictions, hidden, attention_weights = decoder(dec_input, features, hidden)
attention_plot[i] = tf.reshape(attention_weights, (-1, )).numpy()
predicted_id = tf.random.categorical(predictions, 1)[0][0].numpy()
result.append(tokenizer.index_word[predicted_id])
if tokenizer.index_word[predicted_id] == '<end>':
return result, attention_plot
dec_input = tf.expand_dims([predicted_id], 0)
attention_plot = attention_plot[:len(result), :]
return result, attention_plot
def plot_attention(image, result, attention_plot):
temp_image = np.array(Image.open(image))
fig = plt.figure(figsize=(10, 10))
len_result = len(result)
for l in range(len_result):
temp_att = np.resize(attention_plot[l], (8, 8))
ax = fig.add_subplot(len_result//2, len_result//2, l+1)
ax.set_title(result[l])
img = ax.imshow(temp_image)
ax.imshow(temp_att, cmap='gray', alpha=0.6, extent=img.get_extent())
plt.tight_layout()
plt.show()
```
这是本教程的总结。
## 后续步骤
1. 在不同的数据集上尝试你的模型,比如 [Flickr](https://www.kaggle.com/hsankesara/flickr-image-dataset) 数据集。
2. 将你的知识扩展到不同的领域,比如神经机器翻译。你可以在这里查看我的教程。
3. 尝试不同的架构,比如 transformer network,它也使用注意力(在这种情况下是自我注意力)。
# 使用 TensorFlow 和 Keras 的图像字幕
> 原文:<https://blog.paperspace.com/image-captioning-with-tensorflow/>
当我们看一幅图像时,我们对该特定图像的视觉感知可以解释许多不同的事情。例如,在上面的图像中,我们大脑中的视觉感知的一个解释可能是“一艘载有乘客的船在河中航行”,或者我们可以完全忽略船,而专注于其他元素,例如“一个美丽的风景描绘了一些有桥的建筑。”虽然所提供的任何图像的这些观点都是正确的,但有时必须指出图像模式中的主要实体,并对其进行注释。在观察任何特定的视觉模式时,人类大脑访问以下任何信息应该没有问题。然而,神经网络真的有可能发现如此复杂的问题,并像我们拥有完美视觉受体的人脑那样解释解决方案吗?
这个问题挺耐人寻味的。这个问题的解决方案看起来非常乐观,因为有了正确的想法,我们可以成功地执行前面提到的任务。图像字幕的问题可以通过结合使用我们之前的两篇文章来解决,即“[具有序列到序列模型和点注意机制的机器翻译](https://blog.paperspace.com/nlp-machine-translation-with-keras/)和[“迁移学习的完整直观指南”](https://blog.paperspace.com/transfer-learning-explained/)我强烈建议尚未阅读以下两篇文章的读者,如果他们对这两个要素没有完整的理论知识,请立即阅读。用于分析图像的迁移学习和注意解释适当文本数据的序列到序列模型的组合知识对于解决图像字幕问题是有用的。
通过简短的介绍和一些其他更小的复杂的细节,本指南将直接进入 TensorFlow 和 Keras 的图像字幕项目的收缩。我强烈建议任何深度学习的初学者在不跳过任何基本概念的情况下检查大多数基本主题。但是,如果您对图像字幕项目感到满意,并且只想关注某些细节或要点,下面提供了目录列表。本指南应该能够引导您完成本文中讨论的各种重要主题。
## 简介:
在我们直接进入项目的构造之前,让我们了解一下图像字幕的概念。那么,到底什么是图像字幕呢? ***图像字幕*** 是一种为任何提供的视觉表示(如图像或视频)生成文本描述的方法。虽然为特定图像思考适当的说明或标题的过程对于任何人来说都不是复杂的问题,但这种情况对于深度学习模型或一般机器来说并不相同。然而,有了适量的信息和数据集、完善的建筑结构和足够的有用资源,就可以实现相当高精度的图像字幕任务。
在现代社会中,图像字幕的应用极其广泛。为了提及图像字幕的一些显著应用,我们可以将它们的用例包括在编辑应用程序的推荐中、在虚拟助理中的使用中、在图像索引中的使用中、在视障人士中的使用中、在社交媒体中的使用中,以及在许多其他自然语言处理应用程序中的使用中。在 TensorFlow 和 Keras 的帮助下从头开始构建也是一个有趣的项目,但请确保您的环境满足运行这样一个项目的要求。
深度学习框架 TensorFlow 和 Keras 的知识对于理解这个项目中实现的所有理论和实践概念极其重要。在深入研究任何进一步的代码块之前,强烈建议您查看我以前关于这些主题的文章,这些文章详细介绍了 [TensorFlow](https://blog.paperspace.com/absolute-guide-to-tensorflow/) 和 [Keras](https://blog.paperspace.com/the-absolute-guide-to-keras/) 。此外,如前所述,我们将利用迁移学习的方法进行模型的视觉推理。我们还可以利用[序列的概念,对注意力](https://blog.paperspace.com/nlp-machine-translation-with-keras/)进行序列建模,以成功生成以下每个图像的文本描述或标题。那些至少对这些主题有基本了解的人将从这篇文章中受益匪浅。
* * *
## 方法和途径:
对于这个关于 TensorFlow 和 Keras 图像字幕的项目,我们的首要目标是收集所有有用的信息和数据。用于这项任务的流行数据集之一是 Flickr 数据集。一旦我们为我们的训练过程和模型架构的构建收集了足够的信息,我们将理解预处理的基本方法。我们将相应地预处理和准备图像及其各自的标签/说明。
对于标题的准备,我们将删除任何不必要的元素,并将重点放在主要要求上。首先,我们必须执行自然语言处理(NLP)风格的清理和数据集准备。我们会将所有的字母更新为小写格式,并从句子中删除任何单个字母或标点符号。为了处理每个图像,我们将利用迁移学习模型 inception V3。这种迁移学习模型的目的是将我们的图像转换成适当的矢量表示。对于这一步,我们将排除最后的 SoftMax 功能层。我们将使用 2048 特性来执行精确的矢量编码。
一旦我们将所有的图像向量和单词转换成它们各自的索引号,我们将定义一个预先设想的最大长度,并创建适当的嵌入。下一个重要步骤是构建我们的数据加载器/数据生成器,用于相应地提供相应的输入和输出。最后,我们将为图像字幕任务的计算构建我们的 LSTM 模型架构。训练过程完成后,我们将保存模型及其重量。这些保存的模型可用于重新训练以提高准确性。您还可以将此保存的模型用于部署目的,以便对其他影像和数据集进行预测。有了这个主题的基本要点和理解,让我们从主要目标开始。
* * *
### 数据集集合:
任何深度学习项目的第一个基本步骤是收集可用于完成特定任务的有用数据。对于图像字幕问题,我们将考虑的两个主要数据收集来源如下:
1. 1gb 飞行数据集-[https://www.kaggle.com/adityajn105/flickr8k/activity](https://www.kaggle.com/adityajn105/flickr8k/activity)
2. 完整的飞行数据集-[https://www.kaggle.com/hsankesara/flickr-image-dataset](https://www.kaggle.com/hsankesara/flickr-image-dataset)
Kaggle 数据集存储库中的 zip 文件包含 8000 多张图像。以下每张图片都来自六个不同的 Flickr 群组。这些图片被配上了五种不同的说明文字。所有这些标题都清晰准确地描述了可能的特征、突出的实体和其他事件。第二个 URL 链接为用户提供了一个使用更大的数据集来构建一个整体复杂且高效的模型的机会。数据集包含更多的信息和数据,可用于构建有效的模型架构。
然而,对于本文,我们将坚持使用更小的 1gb 数据集,因为这样更方便。它也适用于更广泛的受众,这些受众由于缺乏资源而无法准备和训练更复杂的数据集。无论如何,如果你对处理大型数据集感兴趣,如果你想用有限的 PC 资源开发更高端的模型,我强烈建议你看看 Paperspace 上的[渐变平台](https://gradient.run)。Gradient 使个人和团队能够快速开发、跟踪和协作任何规模和复杂性的机器学习模型。
* * *
## **数据集准备:**
我们项目中最重要的第一步是预处理我们使用的所有数据。这个过程以这样一种方式完成,即,当使用深度学习模型(即,LSTM 层)来获得自然语言处理任务的上下文的知识时,所获得的所有结果都是以兼容的形式来获得最佳结果。在这一步的第一部分,我们将导入解决图像字幕任务所需的所有基本库。我们将需要 TensorFlow 和 Keras 深度学习框架以及一些其他基本库,如 numpy、glob、cv2 等。,以顺利完成本项目。如果你想了解这两个深度学习框架的更多信息,你可以从以下链接中了解它们。下面的[链接](https://blog.paperspace.com/absolute-guide-to-tensorflow/)用于 TensorFlow,这个[链接](https://blog.paperspace.com/the-absolute-guide-to-keras/)用于 Keras。
### 导入基本库:
```py
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
from tensorflow.keras.applications import InceptionV3
from tensorflow.keras.models import Model
from keras.preprocessing.sequence import pad_sequences
from keras.utils import to_categorical
import numpy as np
from numpy import array
import pandas as pd
import cv2
from glob import glob
import PIL
import time
from tqdm import tqdm
import os
```
一旦我们导入了完成任务所需的所有必要的库,我们就可以继续添加各自的路径,并在存储图像的目录中找到图像的总数。为了执行这个操作,我们将利用 glob 库并计算。jpg 格式在我们的图像目录。如果路径不同于您正在使用的特定系统,请随意更改。
```py
image_path = "Images/"
images = glob(image_path + "*.jpg")
len(images)
```
### 输出:
```py
8091
```
### 可视化数据:
在下面的块中,我们将可视化一个小的数据样本(大约五个图像),以了解我们在该数据集中处理的图像类型。matplotlib 库用于绘制这些图像,而计算机视觉 OpenCV 库用于读取图像,然后将它们从默认的 BGR 格式转换为 RGB 格式。
```py
for i in range(5):
plt.figure()
image = cv2.imread(images[i])
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/92e7e42b4c61b7a5a3a7886fb76189bf.png)
2 sample images from the Flickr datasets, converted to RGB format.
这是通过运行以下代码可以看到的前两个图像。运行代码块后,您可以看到另外三个图像。[整个 Jupyter 笔记本可以在这里找到](https://github.com/gradient-ai/Image_Captioning_TF_Keras/blob/main/Image%20Captioning.ipynb)。请随意查看提供的笔记本中的完整代码和可视化效果,看看其他三个图像是什么样子的。
### 数据预处理:
现在,我们已经对我们的可视化效果和我们正在处理的图像类型有了一个简单的概念,是时候预处理标题了。让我们创建一个函数来加载适当的文件,然后相应地查看它们。
```py
# Defining the function to load the captions
def load(filename):
file = open(filename, 'r')
text = file.read()
file.close()
return text
file = "captions.txt"
info = load(file)
print(info[:484])
```
### 输出:
```py
image,caption
1000268201_693b08cb0e.jpg,A child in a pink dress is climbing up a set of stairs in an entry way .
```
您将收到另外五个输出,但它们与我们当前的目标无关。我们可以看到,每个图像都用逗号与它的特定语句隔开。我们的目标是分离这两个实体。但首先,让我们删除第一行,这是不必要的,也要确保在文本文档的结尾没有空行。将此文件重命名并保存为 captions1.txt。这样做是为了确保万一我们犯了错误,我们可以用初始的 captions.txt 重新执行这些步骤。保存预处理数据的副本通常也是一种好的做法。现在让我们想象一下您的数据应该是什么样子。
```py
# Delete the first line as well as the last empty line and then rename the file as captions1.txt
file = "captions1.txt"
info = load(file)
print(info[:470])
```
### 输出:
```py
1000268201_693b08cb0e.jpg,A child in a pink dress is climbing up a set of stairs in an entry way .
```
在下一步中,我们将创建一个字典来存储图像文件及其相应的标题。我们知道每张图片都有五种不同的标题可供选择。因此,我们将定义。jpg 图像作为键,它们各自的五个标题代表这些值。我们将适当地拆分这些值,并将它们存储在一个字典中。下面的函数可以这样写。请注意,我们将所有这些信息存储在数据变量中。
```py
# Creating a function to create a dictionary for the images and their respective captions
def load_captions(info):
dict_1 = dict()
count = 0
for line in info.split('\n'):
splitter = line.split('.jpg,')
# print(splitter)
# The image_code and image_captions are the list of the images with their respective captions
image_code, image_caption = splitter[0], splitter[1]
# Create dictionary
if image_code not in dict_1:
dict_1[image_code] = list()
dict_1[image_code].append(image_caption)
return dict_1
data = load_captions(info)
print(len(data))
```
您可以用下面几行来查看字典的键和值。
```py
list(data.keys())[:5]
```
```py
['1000268201_693b08cb0e',
'1001773457_577c3a7d70',
'1002674143_1b742ab4b8',
'1003163366_44323f5815',
'1007129816_e794419615']
```
```py
data['1000268201_693b08cb0e']
```
```py
['A child in a pink dress is climbing up a set of stairs in an entry way .',
'A girl going into a wooden building .',
'A little girl climbing into a wooden playhouse .',
'A little girl climbing the stairs to her playhouse .',
'A little girl in a pink dress going into a wooden cabin .']
```
我们的下一步是继续进一步预处理数据集,并通过进行一些必要的更改来准备字幕数据。我们将确保每个句子中的所有单词都被转换为小写,因为我们不希望在计算问题时将同一个单词存储为两个独立的向量。我们还将删除长度小于 2 的单词,以确保我们删除了不相关的字符,如单个字母和标点符号。完成此任务的函数和代码编写如下:
```py
# Cleanse and pre-process the data
def cleanse_data(data):
dict_2 = dict()
for key, value in data.items():
for i in range(len(value)):
lines = ""
line1 = value[i]
for j in line1.split():
if len(j) < 2:
continue
j = j.lower()
lines += j + " "
if key not in dict_2:
dict_2[key] = list()
dict_2[key].append(lines)
return dict_2
data2 = cleanse_data(data)
print(len(data2))
```
请注意,我们将所有这些重要的清理信息存储在 data2 变量中,需要时将在编程部分的其余部分使用该变量。
### 更新词汇和数据:
一旦我们完成了文本文件中图像标题的清理和预处理,我们就可以继续创建单词的词汇表,并计算存在的唯一实体的总数。在我们完成构建模型架构之后,我们将更新用于计算和进行预测的唯一单词。让我们看看执行以下任务的代码块。
```py
# convert the following into a vocabulary of words and calculate the total words
def vocabulary(data2):
all_desc = set()
for key in data2.keys():
[all_desc.update(d.split()) for d in data2[key]]
return all_desc
# summarize vocabulary
vocabulary_data = vocabulary(data2)
print(len(vocabulary_data))
```
最后,在完成所有这些基本步骤之后,我们现在可以用下面的代码更新 captions1.txt 文件了。
```py
# save descriptions to file, one per line
def save_dict(data2, filename):
lines = list()
for key, value in data2.items():
for desc in value:
lines.append(key + ' ' + desc)
data = '\n'.join(lines)
file = open(filename, 'w')
file.write(data)
file.close()
save_dict(data2, 'captions1.txt')
```
您的文本文件和存储的数据应该类似于下面的几行。
```py
1000268201_693b08cb0e child in pink dress is climbing up set of stairs in an entry way
1000268201_693b08cb0e girl going into wooden building
```
随着文本数据的初始预处理的完成,让我们继续下一步并构建我们的模型架构,用于预测相应图像的适当标题。
* * *
## 图像预处理:
现在我们已经完成了文本描述的预处理,让我们继续相应地预处理图像。我们将利用预处理功能并加载每个图像的图像路径来适当地执行这个动作。让我们先看看代码,然后理解我们的下一步。另外,请注意,在本文的其余部分,我们将利用以下两个参考来简化完整的操作图像字幕任务。我们将使用官方 TensorFlow [文档](https://www.tensorflow.org/tutorials/text/image_captioning)中关于具有视觉注意力的图像标题和下面的 GitHub [参考](https://github.com/hlamba28/Automatic-Image-Captioning/blob/master/Automatic%20Image%20Captioning.ipynb)链接来成功计算下面的任务。现在让我们来看看相应地预处理图像的代码块。
```py
images = 'Images/'
img = glob(images + '*.jpg')
print(len(img))
def preprocess(image_path):
# Convert all the images to size 299x299 as expected by the inception v3 model
img = keras.preprocessing.image.load_img(image_path, target_size=(299, 299))
# Convert PIL image to numpy array of 3-dimensions
x = keras.preprocessing.image.img_to_array(img)
# Add one more dimension
x = np.expand_dims(x, axis=0)
# pre-process the images using preprocess_input() from inception module
x = keras.applications.inception_v3.preprocess_input(x)
return x
```
这一步是最关键的步骤之一,因为我们将利用 Inception V3 迁移学习模型来将每个相应的图像转换成它们的固定向量大小。该自动特征提取过程包括排除最终的 SoftMax 激活函数,以达到 2096 个矢量特征的瓶颈。该模型利用图像网络上预先训练的权重,相对容易地实现后续任务的计算。众所周知,Inception V3 模型在包含图像的数据集上表现更好。然而,我也建议尝试其他迁移学习模式,看看它们在相同问题上的表现如何。
```py
# Load the inception v3 model
input1 = InceptionV3(weights='imagenet')
# Create a new model, by removing the last layer (output layer) from the inception v3
model = Model(input1.input, input1.layers[-2].output)
model.summary()
```
一旦我们完成图像预处理的计算,我们将把所有这些值保存在一个 pickle 文件中。将这些文件保存为单独的元素将有助于我们在预测过程中单独使用这些模型。这个过程可以用下面的代码块来完成。
```py
# Function to encode a given image into a vector of size (2048, )
def encode(image):
image = preprocess(image) # preprocess the image
fea_vec = model.predict(image) # Get the encoding vector for the image
fea_vec = np.reshape(fea_vec, fea_vec.shape[1]) # reshape from (1, 2048) to (2048, )
return fea_vec
encoding = {}
for i in tqdm(img):
encoding[i[len(images):]] = encode(i)
import pickle
# Save the features in the images1 pickle file
with open("images1.pkl", "wb") as encoded_pickle:
pickle.dump(encoding, encoded_pickle)
```
* * *
## 创建我们的数据加载器:
在我们创建数据加载器和数据生成器之前,让我们保存包含要索引的唯一单词的信息的所有基本文件,以及链接到它们各自单词的所有索引。执行此操作对于模型的生成器和预测阶段都至关重要。我们还将为此操作定义一个固定的最大长度,以便它不会超过我们设置的限制。包括将这些必要的文档保存在 pickle 文件中的整个过程可以如下进行:
```py
# Create a list of all the training captions
all_train_captions = []
for key, val in data2.items():
for cap in val:
all_train_captions.append(cap)
len(all_train_captions)
# Consider only words which occur at least 10 times in the corpus
word_count_threshold = 10
word_counts = {}
nsents = 0
for sent in all_train_captions:
nsents += 1
for w in sent.split(' '):
word_counts[w] = word_counts.get(w, 0) + 1
vocab = [w for w in word_counts if word_counts[w] >= word_count_threshold]
print('preprocessed words %d -> %d' % (len(word_counts), len(vocab)))
# Converting the words to indices and vice versa.
ixtoword = {}
wordtoix = {}
ix = 1
for w in vocab:
wordtoix[w] = ix
ixtoword[ix] = w
ix += 1
vocab_size = len(ixtoword) + 1 # one for appended 0's
vocab_size
# Save the words to index and index to word as pickle files
with open("words.pkl", "wb") as encoded_pickle:
pickle.dump(wordtoix, encoded_pickle)
with open("words1.pkl", "wb") as encoded_pickle:
pickle.dump(ixtoword, encoded_pickle)
# convert a dictionary of clean descriptions to a list of descriptions
def to_lines(descriptions):
all_desc = list()
for key in descriptions.keys():
[all_desc.append(d) for d in descriptions[key]]
return all_desc
# calculate the length of the description with the most words
def max_length(descriptions):
lines = to_lines(descriptions)
return max(len(d.split()) for d in lines)
# determine the maximum sequence length
max_length = max_length(data2)
print('Description Length: %d' % max_length)
```
### 构建发电机:
我们的最后一步是创建数据生成器,它将加载图像向量的全部信息、模型的相应描述、最大长度、索引值的固定字、每个时期运行的步数以及所有其他基本操作。每个图像被转换成其各自的特征长度,并且所有的描述被相应地填充,使得当通过 LSTM 类型的结构时,我们可以预测未来的单词。使用带有注意力模型的序列到序列的 TensorFlow 文档方法更好地获得了更高的准确性,并且使用 tf.data()方法进行了更好的训练。然而,我们将使用一个稍微简单的方法来解决这个问题,以获得更快的结果。
```py
# data generator, intended to be used in a call to model.fit_generator()
def data_generator(descriptions, photos, wordtoix, max_length, num_photos_per_batch):
X1, X2, y = list(), list(), list()
n=0
# loop for ever over images
while 1:
for key, desc_list in descriptions.items():
n+=1
# retrieve the photo feature
photo = photos[key+'.jpg']
for desc in desc_list:
# encode the sequence
seq = [wordtoix[word] for word in desc.split(' ') if word in wordtoix]
# split one sequence into multiple X, y pairs
for i in range(1, len(seq)):
# split into input and output pair
in_seq, out_seq = seq[:i], seq[i]
# pad input sequence
in_seq = pad_sequences([in_seq], maxlen=max_length)[0]
# encode output sequence
out_seq = to_categorical([out_seq], num_classes=vocab_size)[0]
# store
X1.append(photo)
X2.append(in_seq)
y.append(out_seq)
# yield the batch data
if n==num_photos_per_batch:
yield ([array(X1), array(X2)], array(y))
X1, X2, y = list(), list(), list()
n=0
```
通过构建训练生成器来适当地加载我们的值,我们可以进入构建模型架构的最后一步,以完成图像字幕的任务。
* * *
## 构建最终的模型架构:
现在,我们已经完成了预处理标题标签、文本描述和图像数据的大部分步骤,我们终于可以开始构建模型了。然而,在我们构建模型之前,我们需要为每个固定长度的唯一单词创建单词嵌入向量。单词嵌入方法用于将单词的每个索引映射到 200 长度的向量,以使其大小均匀分布。请从这个[链接](https://github.com/stanfordnlp/GloVe)下载下面的手套矢量。预训练的 glove.6B.200d.txt 用于该模型,以为所有唯一的单词创建均匀分布的 200 长度的嵌入矩阵。下面的代码块执行所需的操作。
```py
# Download, install, and then Load the Glove vectors
# https://github.com/stanfordnlp/GloVe
embeddings_index = {} # empty dictionary
f = open('glove.6B.200d.txt', encoding="utf-8")
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close()
print('Found %s word vectors.' % len(embeddings_index))
embedding_dim = 200
# Get 200-dim dense vector for each of the 10000 words in out vocabulary
embedding_matrix = np.zeros((vocab_size, embedding_dim))
for word, i in wordtoix.items():
#if i < max_words:
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# Words not found in the embedding index will be all zeros,
embedding_matrix[i] = embedding_vector
embedding_matrix.shape
```
### 输出:
```py
Found 400001 word vectors.
(1976, 200)
```
最后,让我们定义 LSTM 和嵌入层来创建我们的模型架构。对于这个模型,我们将利用两个输入,即图像向量和字幕的单词嵌入,来进行预测。嵌入向量通过 LSTM 架构,该架构将学习如何进行适当的字预测。图像向量和 LSTM 预测被组合在一起作为一个单元,并通过一些密集层和 SoftMax 激活函数,这相当于预定义词汇的大小。最后,我们将构建这个模型,并从我们的培训流程开始。
```py
from tensorflow.keras.layers import Dense, Input, Conv2D, MaxPool2D, LSTM, add
from tensorflow.keras.layers import Activation, Dropout, Flatten, Embedding
from tensorflow.keras.models import Model
inputs1 = Input(shape=(2048,))
fe1 = Dropout(0.5)(inputs1)
fe2 = Dense(256, activation='relu')(fe1)
inputs2 = Input(shape=(max_length,))
se1 = Embedding(vocab_size, embedding_dim, mask_zero=True)(inputs2)
se2 = Dropout(0.5)(se1)
se3 = LSTM(256)(se2)
decoder1 = add([fe2, se3])
decoder2 = Dense(256, activation='relu')(decoder1)
outputs = Dense(vocab_size, activation='softmax')(decoder2)
model = Model(inputs=[inputs1, inputs2], outputs=outputs)
model.summary()
```
```py
Model: "model_1"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_3 (InputLayer) [(None, 33)] 0
__________________________________________________________________________________________________
input_2 (InputLayer) [(None, 2048)] 0
__________________________________________________________________________________________________
embedding (Embedding) (None, 33, 200) 395200 input_3[0][0]
__________________________________________________________________________________________________
dropout (Dropout) (None, 2048) 0 input_2[0][0]
__________________________________________________________________________________________________
dropout_1 (Dropout) (None, 33, 200) 0 embedding[0][0]
__________________________________________________________________________________________________
dense (Dense) (None, 256) 524544 dropout[0][0]
__________________________________________________________________________________________________
lstm (LSTM) (None, 256) 467968 dropout_1[0][0]
__________________________________________________________________________________________________
add (Add) (None, 256) 0 dense[0][0]
lstm[0][0]
__________________________________________________________________________________________________
dense_1 (Dense) (None, 256) 65792 add[0][0]
__________________________________________________________________________________________________
dense_2 (Dense) (None, 1976) 507832 dense_1[0][0]
==================================================================================================
Total params: 1,961,336
Trainable params: 1,961,336
Non-trainable params: 0
__________________________________________________________________________________________________
```
现在,我们已经完成了整体模型架构的构建,让我们继续使用分类交叉熵损失函数和 Adam 优化器来编译模型。由于时间限制,我将对模型进行大约十个纪元的训练。然而,你可以选择在你方便的时候探索和训练更多的纪元。
```py
model.layers[2].set_weights([embedding_matrix])
model.layers[2].trainable = False
model.compile(loss='categorical_crossentropy', optimizer='adam')
epochs = 10
number_pics_per_bath = 3
steps = len(data2)//number_pics_per_bath
features = pickle.load(open("images1.pkl", "rb"))
# https://stackoverflow.com/questions/58352326/running-the-tensorflow-2-0-code-gives-valueerror-tf-function-decorated-functio
tf.config.run_functions_eagerly(True)
for i in range(epochs):
generator = data_generator(data2, features, wordtoix, max_length, number_pics_per_bath)
model.fit(generator, epochs=1, steps_per_epoch=steps, verbose=1)
model.save('model_' + str(i) + '.h5')
```
### 输出:
```py
2697/2697 [==============================] - 986s 365ms/step - loss: 4.5665
2697/2697 [==============================] - 983s 365ms/step - loss: 3.4063
2697/2697 [==============================] - 987s 366ms/step - loss: 3.1929
2697/2697 [==============================] - 987s 366ms/step - loss: 3.0570
2697/2697 [==============================] - 989s 366ms/step - loss: 2.9662
2697/2697 [==============================] - 995s 369ms/step - loss: 2.9011
2697/2697 [==============================] - 989s 367ms/step - loss: 2.8475
2697/2697 [==============================] - 986s 366ms/step - loss: 2.8062
2697/2697 [==============================] - 986s 366ms/step - loss: 2.7689
2697/2697 [==============================] - 987s 365ms/step - loss: 2.7409
```
我们可以注意到,通过仅仅十个时期的训练,我们的损失已经从初始值显著减少。我只训练了十个纪元的模型,因为你可以注意到每个纪元在我的 GPU 上运行大约需要 1000 秒。如果您有一个更快的计算 GPU 或更多的时间,您可以为更多的历元训练模型,以实现整体更好的结果。
* * *
## 做出预测:
在进行适当的预测时,我们将使用一个[单独的笔记本。](https://github.com/gradient-ai/Image_Captioning_TF_Keras/blob/main/Prediction.ipynb)这样做是为了证明所有保存的文件和模型都可以独立用于对给定的图像数据进行最精确的预测。让我们首先导入所有必要的库并开始特定的任务。
### 导入所需的库
这些是定义预测函数时必须导入的一些基本库。
```py
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.sequence import pad_sequences
import numpy as np
import matplotlib.pyplot as plt
import pickle
```
### 加载各自的功能:
让我们将所有保存的文件加载到它们各自的变量中。该过程如下进行。
```py
features = pickle.load(open("images1.pkl", "rb"))
model = load_model('model_9.h5')
images = "Images/"
max_length = 33
words_to_index = pickle.load(open("words.pkl", "rb"))
index_to_words = pickle.load(open("words1.pkl", "rb"))
```
### 做出预测:
最后,让我们定义相应的函数,该函数将接收图像,加载它们的向量,创建单词嵌入,并利用保存的模型进行适当的预测。让我们随机测试两幅图像,并想象它们各自的结果。
```py
def Image_Caption(picture):
in_text = 'startseq'
for i in range(max_length):
sequence = [words_to_index[w] for w in in_text.split() if w in words_to_index]
sequence = pad_sequences([sequence], maxlen=max_length)
yhat = model.predict([picture,sequence], verbose=0)
yhat = np.argmax(yhat)
word = index_to_words[yhat]
in_text += ' ' + word
if word == 'endseq':
break
final = in_text.split()
final = final[1:-1]
final = ' '.join(final)
return final
```
我将随机选择一张图片,我们将尝试对两张图片的模型产生的输出进行可视化。
```py
z = 20
pic = list(features.keys())[z]
image = features[pic].reshape((1,2048))
x = plt.imread(images+pic)
plt.imshow(x)
plt.show()
print("Caption:", Image_Caption(image))
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6768ee38e385351a4c8e803e636f74c6.png)
```py
z = 100
pic = list(features.keys())[z]
image = features[pic].reshape((1,2048))
x = plt.imread(images+pic)
plt.imshow(x)
plt.show()
print("Caption:", Image_Caption(image))
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c528440a0f3c86f5d873e4b96eb7db16.png)
我们可以注意到,我们的图像字幕模型的性能对于仅仅十个时期的训练和小数据集来说是相当好的。虽然图像字幕任务工作得相当不错,但值得注意的是,可以进一步降低损耗,以实现更高的准确度和精度。可以进行的两个主要更改和改进是增加数据集的大小,以及在当前模型上运行更多时期的以下计算。具有注意力的序列对序列模型也可以用于实现更好的结果。也可以对不同类型的迁移学习模型进行其他实验。
* * *
## 结论:
![Ads around an intersection](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e9af20a4a97c78310c018ced611878cf.png)
Photo by [Daryan Shamkhali](https://unsplash.com/@daryan?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)
在这个使用 Keras 和 TensorFlow 生成图像标题的演示中,我们展示了如何使用 Flickr 数据集中先前识别的文本标题数据来生成新颖的标题。该过程包括加载相关库、预处理图像和文本数据、创建数据加载器和生成器,以及最终构建 LSTM 架构以生成预测字幕的步骤。用户可以进一步了解细节,并通过使用 Gradient-AI 的 GitHub 上的公共笔记本[来尝试重现这一作品。](https://github.com/gradient-ai/Image_Captioning_TF_Keras/)
# 注意力图像分类
> 原文:<https://blog.paperspace.com/image-classification-with-attention/>
图像分类可能是计算机视觉中最流行的子领域之一。图像分类的过程包括理解图像中的上下文信息以将它们分类到一组预定义的标签中。作为一个领域,图像分类在第一次 ImageNet 挑战赛后变得很有名,因为庞大的 ImageNet 数据集的新颖可用性,深度神经网络的成功以及它在数据集上获得的相应的惊人性能。第一个网络叫做 Alexnet。
然后,谷歌的 Inception networks 引入了一种新的概念,即采用不同大小的过滤器来接收相同的输入,并在 Imagenet 挑战中成功超越了 Alexnet。然后是 Resnet、VGG-16 和许多其他算法,它们在 Imagenet 和 Alexnet 的基础上继续改进。
与此同时,NLP 领域也在迅速发展,随着开创性论文[的发布,你所需要的就是关注!](https://arxiv.org/abs/1706.03762),从来没有一样。虽然注意力从根本上改变了 NLP,但它仍然没有广泛应用于计算机视觉问题。
在我们继续之前,让我们试着更好地理解注意力。
神经网络中的注意机制倾向于模仿人类所拥有的认知注意。该功能的主要目的是强调信息的重要部分,并尽量不强调不相关的部分。由于人类和机器的工作记忆都是有限的,所以这个过程是避免系统记忆负担过重的关键。在深度学习中,注意力可以被解释为重要性权重的向量。当我们预测一个元素时,它可能是图像中的一个像素或句子中的一个单词,我们使用注意力向量来推断它与其他元素的相关程度。
让我们来看看这篇优秀博客中提到的一个例子, *[关注?立正!](https://lilianweng.github.io/posts/2018-06-24-attention/)* 。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/608497e73c057320bdda962b2d8d0fb7.png)
The name of the dog is Shiba Inu
如果我们把注意力集中在红色盒子里的狗的特征上,比如它的鼻子、尖尖的右耳和神秘的眼睛,我们就能猜出黄色盒子里应该是什么。然而,仅通过查看灰色框中的像素,您将无法预测黄色框中应该出现什么。注意机制对正确方框中的像素比对黄色方框中的像素的权重更大。而灰色框中的像素权重较低。
现在,我们对注意力的作用有了一个很好的了解。在这个博客中,我们将注意力用于一个崇高的追求。我们将黑色素瘤图像分为恶性和良性,然后尝试解释这些预测。
但是我们可以在没有注意的情况下对图像进行分类,对吗?
如果你仍然不相信为什么我们应该把注意力放在图像上,这里有更多的观点来证明这一点:
* 在训练图像模型时,我们希望模型能够关注图像的重要部分。实现这一点的方法之一是通过 ****可训练的注意力**** 机制(但你已经知道这一点了,对吗?继续读..)
* 在我们的例子中,我们正在处理病变图像,并且能够 ****解释**** 模型变得更加必要。理解图像的哪一部分对癌症被分类为良性/恶性更有贡献是很重要的。
* 像 ****Grad-CAM**** 这样的事后分析并不等同于关注。它们并不打算改变模型学习的方式,或者改变模型学习的内容。它们被应用于已经训练好的具有固定权重的模型,并且仅用于提供对模型决策的洞察。
现在,你已经对我们为什么使用注意力进行图像分类有了全面的了解,让我们深入了解一下。我们将使用 Pytorch。它更冗长,似乎有很多代码,但由于它广泛使用了类,所以更 pythonic 化,并且与 TensorFlow 相比,给了用户更多的控制权。
我正在使用来自 Kaggle 的这个数据集。所有图像都是皮肤损伤,形状为 512 x 512 x 3。要设置 Kaggle API 并将数据集下载到渐变笔记本中以下载该数据,请按照以下步骤操作:
* 首先,创建并登录 Kaggle 帐户
* 其次,通过进入您的帐户设置创建一个 API 令牌,并将 kaggle.json 保存到您的本地机器上
* 三、上传 kaggle.json 到渐变记事本第四、把文件移到~/。kaggle/使用终端命令`cp kaggle.json ~/.kaggle/`
* 四、安装 kaggle: `pip install kaggle`
* 第五,使用 API 通过终端下载数据集:`kaggle datasets download shonenkov/melanoma-merged-external-data-512x512-jpeg`
* 六、使用终端解压数据集:`unzip melanoma-merged-external-data-512x512-jpeg.zip`
您还需要再做几个步骤来设置以使其正确运行。在终端中,`pip install opencv-python kaggle`然后运行`apt-get install libgl1`。
然后通过运行包含以下内容的单元来导入以下库:
```py
import pandas as pd
from sklearn.model_selection import train_test_split
from torchvision import transforms
import torch.nn as nn
import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt
import torchvision.models as models
from torch.utils.data import DataLoader
```
## 预处理数据
我只使用了图像中的一个样本(因为数据集很大),并做了以下预处理:
* 调整大小、标准化、居中和裁剪训练和测试图像。
* 仅在训练图像上的数据扩充(随机旋转/水平翻转)。
```py
# read the data
data_dir='melanoma-merged-external-data-512x512-jpeg/512x512-dataset-melanoma/512x512-dataset-melanoma/'
data=pd.read_csv('melanoma-merged-external-data-512x512-jpeg/marking.csv')
# balance the data a bit
df_0=data[data['target']==0].sample(6000,random_state=42)
df_1=data[data['target']==1]
data=pd.concat([df_0,df_1]).reset_index()
#prepare the data
labels=[]
images=[]
for i in range(data.shape[0]):
images.append(data_dir + data['image_id'].iloc[i]+'.jpg')
labels.append(data['target'].iloc[i])
df=pd.DataFrame(images)
df.columns=['images']
df['target']=labels
# Split train into train and val
X_train, X_val, y_train, y_val = train_test_split(df['images'],df['target'], test_size=0.2, random_state=1234)
```
现在让我们使用 PyTorch 的转换模块来转换图像。
```py
train_transform = transforms.Compose([
transforms.RandomRotation(10), # rotate +/- 10 degrees
transforms.RandomHorizontalFlip(), # reverse 50% of images
transforms.Resize(224), # resize shortest side to 224 pixels
transforms.CenterCrop(224), # crop longest side to 224 pixels at center
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
])
val_transform = transforms.Compose([
transforms.Resize(224),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
])
```
接下来,让我们编写一个继承 PyTorch 的 Dataset 类的类。我们将使用这个类来读取、转换和放大图像。
```py
class ImageDataset(Dataset):
def __init__(self,data_paths,labels,transform=None,mode='train'):
self.data=data_paths
self.labels=labels
self.transform=transform
self.mode=mode
def __len__(self):
return len(self.data)
def __getitem__(self,idx):
img_name = self.data[idx]
img = cv2.imread(img_name)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img=Image.fromarray(img)
if self.transform is not None:
img = self.transform(img)
img=img.cuda()
labels = torch.tensor(self.labels[idx]).cuda()
return img, labels
train_dataset=ImageDataset(data_paths=X_train.values,labels=y_train.values,transform=train_transform)
val_dataset=ImageDataset(data_paths=X_val.values,labels=y_val.values,transform=val_transform)
train_loader=DataLoader(train_dataset,batch_size=100,shuffle=True)
val_loader=DataLoader(val_dataset,batch_size=50,shuffle=False)
```
### 型号- VGG16 注意
在实际的分类任务中,我们将使用带有注意力层的 VGG16。该架构最初是在这篇论文中提出的,你可以在我们的博客[这里](https://blog.paperspace.com/vgg-from-scratch-pytorch/)找到从头开始编写该算法的指南(较少关注注意力机制)。给你一个 VGG16 的入门,它有 16 层深,设计在 2014 年赢得了 ImageNet 竞赛。他们使用 3×3 滤波器大小的卷积层,步长为 1,ReLu 作为其激活函数。Maxpooling 层具有跨距为 2 的 2x2 过滤器。最后有 2 个密集层,后面是一个 softmax 层。
VGG16 将是主干,不会有任何密集层。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/5684046f13ca6068ae187285b309a200.png)
[[Source](https://www2.cs.sfu.ca/~hamarneh/ecopy/ipmi2019.pdf)]
* 应用了两个注意模块(灰色块)。中间特征图(池 3 和池 4)的输出用于推断注意力图。pool-5 的输出充当一种形式的全局引导,因为最后一级特征包含整个图像的最抽象和最压缩的信息。
* 三个特征向量(绿色块)通过全局平均池计算,并连接在一起以形成最终的特征向量,其用作分类层的输入(此处未示出)。
如果您对此不太清楚,请不要担心,我将在下一步对其进行详细说明。
### 实现关注层
下面是一个定义注意力层的类。
```py
class AttentionBlock(nn.Module):
def __init__(self, in_features_l, in_features_g, attn_features, up_factor, normalize_attn=True):
super(AttentionBlock, self).__init__()
self.up_factor = up_factor
self.normalize_attn = normalize_attn
self.W_l = nn.Conv2d(in_channels=in_features_l, out_channels=attn_features, kernel_size=1, padding=0, bias=False)
self.W_g = nn.Conv2d(in_channels=in_features_g, out_channels=attn_features, kernel_size=1, padding=0, bias=False)
self.phi = nn.Conv2d(in_channels=attn_features, out_channels=1, kernel_size=1, padding=0, bias=True)
def forward(self, l, g):
N, C, W, H = l.size()
l_ = self.W_l(l)
g_ = self.W_g(g)
if self.up_factor > 1:
g_ = F.interpolate(g_, scale_factor=self.up_factor, mode='bilinear', align_corners=False)
c = self.phi(F.relu(l_ + g_)) # batch_sizex1xWxH
# compute attn map
if self.normalize_attn:
a = F.softmax(c.view(N,1,-1), dim=2).view(N,1,W,H)
else:
a = torch.sigmoid(c)
# re-weight the local feature
f = torch.mul(a.expand_as(l), l) # batch_sizexCxWxH
if self.normalize_attn:
output = f.view(N,C,-1).sum(dim=2) # weighted sum
else:
output = F.adaptive_avg_pool2d(f, (1,1)).view(N,C) # global average pooling
return a, output
```
这个图可以解释注意力层内部发生了什么。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/25ec2d62f82543ac5fe7e454b4009e3f.png)
[Source]
* 中间特征向量(F)是池-3 或池-4 的输出,而全局特征向量(池-5 的输出)作为输入被馈送到注意层。
* 两个特征向量都通过卷积层。当全局特征和中间特征的空间大小不同时,通过双线性插值进行特征上采样。 *up_factor* 确定卷积的全局特征向量必须被放大的因子。
* 之后,进行逐元素求和,然后进行卷积运算,将 256 个通道减少到 1 个。
* 这然后被输入到一个 Softmax 层,这给了我们一个标准化的注意力地图(A)。A 中的每个标量元素代表 f 中对应的空间特征向量的受关注程度。
* 然后通过*逐像素*乘法计算新的特征向量𝐹̂。也就是说,每个特征向量 f 乘以关注元素 a
* 因此,注意力图 a 和新的特征向量𝐹̂是注意力层的输出。
```py
class AttnVGG(nn.Module):
def __init__(self, num_classes, normalize_attn=False, dropout=None):
super(AttnVGG, self).__init__()
net = models.vgg16_bn(pretrained=True)
self.conv_block1 = nn.Sequential(*list(net.features.children())[0:6])
self.conv_block2 = nn.Sequential(*list(net.features.children())[7:13])
self.conv_block3 = nn.Sequential(*list(net.features.children())[14:23])
self.conv_block4 = nn.Sequential(*list(net.features.children())[24:33])
self.conv_block5 = nn.Sequential(*list(net.features.children())[34:43])
self.pool = nn.AvgPool2d(7, stride=1)
self.dpt = None
if dropout is not None:
self.dpt = nn.Dropout(dropout)
self.cls = nn.Linear(in_features=512+512+256, out_features=num_classes, bias=True)
# initialize the attention blocks defined above
self.attn1 = AttentionBlock(256, 512, 256, 4, normalize_attn=normalize_attn)
self.attn2 = AttentionBlock(512, 512, 256, 2, normalize_attn=normalize_attn)
self.reset_parameters(self.cls)
self.reset_parameters(self.attn1)
self.reset_parameters(self.attn2)
def reset_parameters(self, module):
for m in module.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='relu')
if m.bias is not None:
nn.init.constant_(m.bias, 0.)
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1.)
nn.init.constant_(m.bias, 0.)
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight, 0., 0.01)
nn.init.constant_(m.bias, 0.)
def forward(self, x):
block1 = self.conv_block1(x) # /1
pool1 = F.max_pool2d(block1, 2, 2) # /2
block2 = self.conv_block2(pool1) # /2
pool2 = F.max_pool2d(block2, 2, 2) # /4
block3 = self.conv_block3(pool2) # /4
pool3 = F.max_pool2d(block3, 2, 2) # /8
block4 = self.conv_block4(pool3) # /8
pool4 = F.max_pool2d(block4, 2, 2) # /16
block5 = self.conv_block5(pool4) # /16
pool5 = F.max_pool2d(block5, 2, 2) # /32
N, __, __, __ = pool5.size()
g = self.pool(pool5).view(N,512)
a1, g1 = self.attn1(pool3, pool5)
a2, g2 = self.attn2(pool4, pool5)
g_hat = torch.cat((g,g1,g2), dim=1) # batch_size x C
if self.dpt is not None:
g_hat = self.dpt(g_hat)
out = self.cls(g_hat)
return [out, a1, a2]
```
* VGG16 的架构基本保持不变,只是移除了密集层。
* 我们将池 3 和池 4 通过注意力层,得到𝐹̂ 3 和𝐹̂ 4。
* 𝐹̂ 3、𝐹̂ 4 和 G(pool-5)被连接起来并被输入到最终的分类层。
* 整个网络被端到端地训练。
```py
model = AttnVGG(num_classes=1, normalize_attn=True)
model=model.cuda()
```
我使用焦点损失而不是常规的二进制交叉熵损失,因为我们的数据是不平衡的(像大多数医学数据集一样),并且焦点损失可以自动降低训练集中样本的权重。
```py
class FocalLoss(nn.Module):
def __init__(self, alpha=0.25, gamma=2.0, logits=False, reduce=True):
super(FocalLoss, self).__init__()
self.alpha = alpha
self.gamma = gamma
self.logits = logits
self.reduce = reduce
def forward(self, inputs, targets):
if self.logits:
BCE_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduce=False)
else:
BCE_loss = F.binary_cross_entropy(inputs, targets, reduce=False)
pt = torch.exp(-BCE_loss)
F_loss = self.alpha * (1-pt)**self.gamma * BCE_loss
if self.reduce:
return torch.mean(F_loss)
else:
return F_loss
criterion = FocalLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
```
现在该训练模型了。我们将对 2 个时期执行此操作,您可以通过更改分配给下述单元格中“时期”变量的值来查看增加时期是否会提高性能。
```py
import time
start_time = time.time()
epochs = 2
train_losses = []
train_auc=[]
val_auc=[]
for i in range(epochs):
train_preds=[]
train_targets=[]
auc_train=[]
loss_epoch_train=[]
# Run the training batches
for b, (X_train, y_train) in tqdm(enumerate(train_loader),total=len(train_loader)):
b+=1
y_pred,_,_=model(X_train)
loss = criterion(torch.sigmoid(y_pred.type(torch.FloatTensor)), y_train.type(torch.FloatTensor))
loss_epoch_train.append(loss.item())
# For plotting purpose
if (i==1):
if (b==19):
I_train = utils.make_grid(X_train[0:8,:,:,:], nrow=8, normalize=True, scale_each=True)
__, a1, a2 = model(X_train[0:8,:,:,:])
optimizer.zero_grad()
loss.backward()
optimizer.step()
try:
auc_train=roc_auc_score(y_train.detach().to(device).numpy(),torch.sigmoid(y_pred).detach().to(device).numpy())
except:
auc_train=0
train_losses.append(np.mean(loss_epoch_train))
train_auc.append(auc_train)
print(f'epoch: {i:2} loss: {np.mean(loss_epoch_train):10.8f} AUC : {auc_train:10.8f} ')
# Run the testing batches
with torch.no_grad():
for b, (X_test, y_test) in enumerate(val_loader):
y_val,_,_ = model(X_test)
loss = criterion(torch.sigmoid(y_val.type(torch.FloatTensor)), y_test.type(torch.FloatTensor))
loss_epoch_test.append(loss.item())
val_auc.append(auc_val)
print(f'Epoch: {i} Val Loss: {np.mean(loss_epoch_test):10.8f} AUC: {auc_val:10.8f} ')
print(f'\nDuration: {time.time() - start_time:.0f} seconds') # print the time elapsed
```
我们的输出将如下所示:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e1eb200972fd7c86e8a90848e71fc918.png)
验证 AUC 看起来不错,现在让我们解释模型。
## 视觉化注意力
我们将可视化由 pool-3 和 pool-4 创建的注意力地图,以了解图像的哪一部分负责分类。
```py
def visualize_attention(I_train,a,up_factor,no_attention=False):
img = I_train.permute((1,2,0)).cpu().numpy()
# compute the heatmap
if up_factor > 1:
a = F.interpolate(a, scale_factor=up_factor, mode='bilinear', align_corners=False)
attn = utils.make_grid(a, nrow=8, normalize=True, scale_each=True)
attn = attn.permute((1,2,0)).mul(255).byte().cpu().numpy()
attn = cv2.applyColorMap(attn, cv2.COLORMAP_JET)
attn = cv2.cvtColor(attn, cv2.COLOR_BGR2RGB)
attn = np.float32(attn) / 255
# add the heatmap to the image
img=cv2.resize(img,(466,60))
if no_attention:
return torch.from_numpy(img)
else:
vis = 0.6 * img + 0.4 * attn
return torch.from_numpy(vis)
```
```py
orig=visualize_attention(I_train,a1,up_factor=2,no_attention=True)
first=visualize_attention(I_train,a1,up_factor=2,no_attention=False)
second=visualize_attention(I_train,a2,up_factor=4,no_attention=False)
fig, (ax1, ax2,ax3) = plt.subplots(3, 1,figsize=(10, 10))
ax1.imshow(orig)
ax2.imshow(first)
ax3.imshow(second)
ax1.title.set_text('Input Images')
ax2.title.set_text('pool-3 attention')
ax3.title.set_text('pool-4 attention')
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e57f7870f907ae7e8405c222736bf0c2.png)
* ****恶性图像的工作方式**** :-较浅的层(pool-3)倾向于聚焦于更一般和扩散的区域,而较深的层(pool-4)则更集中,聚焦于病灶,避开无关像素。
* 但由于我们的情况下大多数图像都是良性的,pool-3 试图学习一些区域,但 pool-4 最终会最小化激活的区域,因为图像是良性的。
### 结论
这篇博文基本展示了如何将注意力机制与预先训练好的图像模型结合使用,并揭示了使用它的好处。值得注意的是,原论文还声称,由于消除了密集层,参数数量大大减少,网络训练更轻。如果你打算在今后的工作中加入注意力机制,请记住这一点。如果你想进一步提高图像分类技术,还有一些事情你可以自己尝试,如调整超参数,改变主干结构或使用不同的损失函数。
### 参考
* [https://www2.cs.sfu.ca/~hamarneh/ecopy/ipmi2019.pdf](https://www2.cs.sfu.ca/~hamarneh/ecopy/ipmi2019.pdf)
* [https://towards data science . com/learn-to-pay-attention-training-visual-attention-in-CNNs-87e 2869 f89 f1](https://towardsdatascience.com/learn-to-pay-attention-trainable-visual-attention-in-cnns-87e2869f89f1)
* [https://github . com/SaoYan/IPMI 2019-Attn Mel/tree/99 E4 a9 b 71717 FB 51 f 24d 7994948 b 6 a 0 e 76 bb 8d 58](https://github.com/SaoYan/IPMI2019-AttnMel/tree/99e4a9b71717fb51f24d7994948b6a0e76bb8d58)
# 图像去雾的深度学习——什么、为什么和如何
> 原文:<https://blog.paperspace.com/image-dehazing-the-what-why-and-how/>
雾霾是一种常见的大气现象,会损害日常生活和机器视觉系统。雾霾的存在降低了场景的能见度,影响了人们对物体的判断,浓厚的雾霾甚至会影响日常活动,如交通安全。对于计算机视觉来说,在大多数情况下,雾霾通过部分或完全遮挡物体来降低捕获图像的质量。它会影响模型在高级视觉任务中的可靠性,进一步误导机器系统,如自动驾驶。所有这些使得图像去雾成为一项有意义的低级视觉任务。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/37c8713b6fd8579b8548858a74e5b503.png)
Examples of images with haze and their corresponding dehazed images. Source: [O-HAZE Dataset](https://data.vision.ee.ethz.ch/cvl/ntire18//o-haze/)
因此,许多研究者试图从模糊图像中恢复出高质量的清晰场景。在深度学习被广泛用于计算机视觉任务之前,图像去雾算法主要依赖于各种先验假设和大气散射模型。这些基于统计规则的方法的处理流程具有良好的可解释性,但是当面对复杂的真实世界场景时,它们可能会表现出缺点。因此,最近在图像去雾方面的努力采用了深度学习模型。
在这篇文章中,我们将涵盖图像去雾的数学基础,并讨论不同类别的去雾。我们将看看深度学习文献中提出的最流行的图像去雾架构,并详细讨论图像去雾的应用。
# 图像去雾的数学建模
大气散射模型是模糊图像生成过程的经典描述,其数学定义如下:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/42b7e6af3878310c5cedb48cee0b978a.png)
这里, *I(x)* 是观察到的模糊图像, *J(x)* 是要恢复的场景亮度(“干净图像”)。有两个关键参数: *A* 表示全局大气光, *t(x)* 是传输矩阵,定义如下,散射系数“ *β* ”,物体与相机的距离 *d(x)* :
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/19888a44dd29ef9255f371fc3100c234.png)
这个数学模型背后的想法是,光线在到达相机镜头之前会被空气中的悬浮颗粒(薄雾)散射。实际捕获的光量取决于存在多少薄雾,反映在 *β* 中,也取决于物体离相机有多远,反映在 *d(x)* 中。
深度学习模型试图学习传输矩阵。大气光单独计算,基于大气散射模型恢复干净图像。
# 图像去雾的评价指标
视觉线索不足以评估和比较不同去雾方法的性能,因为它们本质上是非常主观的。需要一种通用的去雾性能定量方法来公平地比较模型。
峰值信噪比(PSNR)和结构相似性(SSIM)指数是用于评估去雾性能的两个最常用的评估度量。一般来说,与现有技术相比,PSNR 和 SSIM 度量都用于去雾方法的公平评估
接下来让我们讨论这些指标。
## PSNR
峰值信噪比或 PSNR 是一种客观度量,其测量通过去雾算法获得的无雾图像和地面真实图像之间的信号失真程度。在数学上,它定义如下,其中“DH”表示从模型获得的去雾图像,而“GT”表示地面真实干净图像:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/22c43aba63dbb88afdb0bdf4977c921c.png)
这里,“MSE”代表图像之间的像素级均方误差,“M”是图像中一个像素的最大可能值(对于 8 位 RGB 图像,我们习惯 M=255)。PSNR 值(以分贝/dB 为单位)越高,重建质量越好。在`Python3`中,PSNR 可以编码如下:
```py
import numpy as np
from math import log10
def PSNR(img1, img2):
mse = np.mean((img1 - img2) ** 2)
if(mse == 0): # MSE is zero means no noise is present in the signal .
# Therefore PSNR have no importance.
return 100
max_pixel = 255.0
psnr = 10 * log10(max_pixel**2 / mse)
return psnr
```
## SSIM
由于 PSNR 在人类视觉判断方面无效,因此,许多研究人员利用了结构相似性指数度量(SSIM),这是一种主观测量,根据地面真实图像和去雾图像之间的对比度、亮度和结构一致性来评估去雾性能。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/128f720ccf7c29bda5399b19baae0043.png)![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/e65609ad156eb3ab1586e67984ff4020.png)
SSIM 的范围在 0 和 1 之间,其中较高的值表示较大的结构一致性,因此去雾效果较好。在`Python3`中,`skimage`包中包含了 SSIM 函数,可以很方便地使用:
```py
from skimage.metrics import structural_similarity as ssim
similarity = ssim(img1, img2)
```
# 单幅与多幅图像去雾
根据可用的输入信息量,存在两种类型的图像去雾方法。在单幅图像去雾中,只有一幅模糊图像可用,它需要映射到它的去雾对应物。然而,在多图像去雾中,同一场景或物体的多个模糊图像是可用的,它们都被用于映射到单个去雾图像。
在单幅图像去雾方法中,由于可用的输入信息量较少,因此在重构的去雾图像中可能出现伪图案,该伪图案与原始图像的上下文没有可辨别的联系。这可能会产生歧义,进而导致最终(人类)决策者的误导。
在完全监督的多图像去雾方法中,典型地,对干净图像(地面实况)使用不同的退化函数,以获得相同场景的略微不同类型的模糊图像。由于可用信息的多样性,这有助于模型做出更好的概括。
直观上,我们可以理解多图像去雾性能更好,因为它有更多的输入信息要处理。然而,在这种情况下,计算成本也增加了几倍,使得它在许多具有大量资源约束的应用场景中不可行。此外,获得同一物体的多个模糊图像通常是乏味且不实际的。因此,在诸如监视、水下探测等应用中,单幅图像去雾与真实世界更密切相关。,虽然是更有挑战性的问题陈述。
然而,在一些应用领域,如遥感领域,同一场景的多幅模糊图像通常很容易获得,因为多颗卫星和多台相机(通常在不同的曝光设置下)同时拍摄同一地点的图像。
一种使用多图像去雾的方法是基于 CNN 的 [RSDehazeNet](https://ieeexplore.ieee.org/abstract/document/9134800) 模型,以从多光谱遥感数据中去除薄雾。RSDehazeNet 由三种类型的模块组成:
1. 通道细化块或 CRB,用于对通道特征之间的相互依赖性进行建模,因为多光谱图像的通道高度相关
2. 残差信道细化块或 RCRB,因为具有残差块的 CNN 模型能够捕捉弱信息,从而驱动网络以更快的速度收敛并具有更高的性能。
3. 特征融合块(FFB ),用于特征地图的全局融合和多光谱遥感图像的去雾。
RSDehazeNet 模型的架构如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/141dae3f9c53af8039a9937f145e33fa.png)
Source: [Paper](https://ieeexplore.ieee.org/abstract/document/9134800)
通过 RSDehazeNet 模型获得的一些结果如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/7af125e8b16f224dfd6ba4e94547c2ce.png)
Source: [Paper](https://ieeexplore.ieee.org/abstract/document/9134800)
# 去雾方法的分类
基于模糊输入图像的地面真实图像的可用性,图像去雾方法可以分为以下几类:
1. 监督方法
2. 半监督方法
3. 无监督方法
接下来让我们讨论这些方法。
## 监督方法
在完全监督的方法中,对应于模糊图像的去雾图像都可以用于训练网络。有监督去雾模型通常需要不同类型的监督信息来指导训练过程,如透射图、大气光、无霾图像标签等。
快速准确的多尺度去雾网络就是这样一种解决单幅图像去雾问题的监督方法,其目的是提高网络的推理速度。fame ed-Net 由一个完全卷积的端到端多尺度网络(带有三个不同尺度的编码器)组成,可以有效地处理任意大小的模糊图像。著名的网络模型的架构如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/d74f7aec5d9df4a1db62989f9832ed24.png)
Source: [Paper](https://doi.org/10.1109/TIP.2019.2922837)
## 半监督方法
半监督学习是一种机器学习范式,其中大数据集的小子集(比如说 5-10%的数据)包含基础事实标签。因此,一个模型会受到大量未标记数据以及少量用于网络训练的标记样本的影响。与全监督方法相比,半监督方法在图像去雾方面受到的关注较少。
PSD 或 Principled Synthetic-to-Real 去雾是一种半监督去雾模型,包括两个阶段:监督预训练和非监督微调。对于预训练,作者使用合成数据并使用真实模糊的未标记数据进行无监督微调,采用无监督的[域适应](https://www.v7labs.com/blog/domain-adaptation-guide)进行合成到真实图像的转换。模型框架如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ea380609d4150350eb91fc0f46721bfd.png)
Source: [Paper](https://openaccess.thecvf.com/content/CVPR2021/papers/Chen_PSD_Principled_Synthetic-to-Real_Dehazing_Guided_by_Physical_Priors_CVPR_2021_paper.pdf)
与现有技术相比,通过 PSD 方法获得的一些视觉结果如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/85340aa6e7a2ae69dccb8ced619aeaaf.png)
Source: [Paper](https://openaccess.thecvf.com/content/CVPR2021/papers/Chen_PSD_Principled_Synthetic-to-Real_Dehazing_Guided_by_Physical_Priors_CVPR_2021_paper.pdf)
## 监督方法
在无监督学习中,数据标签是完全缺失的。深度网络将需要完全使用非结构化数据进行训练。这使得图像去雾问题更加具有挑战性。
例如, [SkyGAN](https://openaccess.thecvf.com/content/WACV2021/papers/Mehta_Domain-Aware_Unsupervised_Hyperspectral_Reconstruction_for_Aerial_Image_Dehazing_WACV_2021_paper.pdf) 是一种无监督去雾模型,其利用生成式对抗网络(GAN)架构来对高光谱图像去雾。SkyGAN 使用具有循环一致性的条件 GAN (cGAN)框架,即,添加正则化参数,假设去模糊图像在降级时应该再次返回模糊输入图像。SkyGAN 模型的架构如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/ed96b39d2384b189124ba62d59d331c0.png)
Source: [Paper](http://paper)
SkyGAN 模型获得的结果令人印象深刻,因为它们甚至超过了完全监督的方法。获得的一些视觉结果如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c37dcc678ec75e93cd6a17a7a6992a12.png)
Source: [Paper](http://paper)
受监督的单一去雾问题的代码中最重要的部分是管理自定义数据集,以获得模糊和干净的图像。同样的 PyTorch 代码如下所示:
```py
import torch
import torch.utils.data as data
import torchvision.transforms as transforms
import numpy as np
from PIL import Image
import os
class DehazingDataset(data.Dataset):
def __init__(self, hazy_images_path, clean_images_path, transform=None):
#Get the images
self.hazy_images = [hazy_images_path + f for f in os.listdir(hazy_images_path) if f.endswith('.jpg') or f.endswith('.png') or f.endswith('.jpeg')]
self.clean_images = [clean_images_path + f for f in os.listdir(clean_images_path) if f.endswith('.jpg') or f.endswith('.png') or f.endswith('.jpeg')]
#Filter the images to ensure they are counterparts of the same scene
self.filter_files()
self.size = len(self.hazy_images)
self.transform=transform
def filter_files(self):
assert len(self.hazy_images) == len(self.clean_images)
hazy_ims = []
clean_ims = []
for hazy_img_path, clean_img_path in zip(self.hazy_images, self.clean_images):
hazy = Image.open(hazy_img_path)
clean = Image.open(clean_img_path)
if hazy.size == clean.size:
hazy_ims.append(hazy_img_path)
clean_ims.append(clean_img_path)
self.hazy_images = hazy_ims
self.clean_images = clean_ims
def __getitem__(self, index):
hazy_img = self.rgb_loader(self.hazy_images[index])
clean_img = self.rgb_loader(self.clean_images[index])
hazy_img = self.transform(hazy_img)
clean_img = self.transform(clean_img)
return hazy_img, clean_img
def rgb_loader(self, path):
with open(path, 'rb') as f:
img = Image.open(f)
return img.convert('RGB')
def __len__(self):
return self.size
#Main code for dataloader
hazy_path = "/path/to/train/haimg/"
clean_path = "/path/to/train/cleimg/"
transforms = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])
])
dataset = DehazingDataset(hazy_path, clean_path, transform = transforms)
data_loader = data.DataLoader(dataset=dataset,
batch_size=32,
shuffle=True,
num_workers=2)
```
一旦数据集准备就绪,任何图像到图像的转换模型代码都可以应用于它。
# 流行的模型
多年来,已经提出了几种不同的基于深度学习的模型架构来解决图像去雾问题。让我们讨论一些主要的模型,它们已经成为未来研究方向的垫脚石。这些模型中的大多数在各自的 GitHub 存储库中都有 Python 实现。
如果你想在渐变中使用下面的代码,只需打开下面的链接,将库克隆到笔记本中。这个笔记本预先设置了上面使用的代码。
## MSCNN
MSCNN ,或称多尺度卷积神经网络,是针对单幅图像去雾问题的早期尝试之一。顾名思义,MSCNN 本质上是多尺度的,有助于从朦胧图像中学习有效特征,用于场景透射图的估计。场景透射图首先由粗尺度网络估计,然后由细尺度网络细化。
从粗尺度网络获得每个图像的场景透射图的粗略结构,然后由细尺度网络进行细化。粗尺度和细尺度网络都应用于原始输入模糊图像。此外,粗网络的输出作为附加信息传递给细网络。因此,精细尺度网络可以用细节来细化粗略预测。MSCNN 模型的架构如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/6ca8931ea4d17cef9461aa23cb90e30c.png)
Source: [Paper](https://link.springer.com/chapter/10.1007/978-3-319-46475-6_10)
与当时的一些基线方法相比,MSCNN 结果获得的结果的一些例子如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/bad5ae87b3823e4d175a08b7098e1023.png)
Source: [Paper](https://link.springer.com/chapter/10.1007/978-3-319-46475-6_10)
## 去雾网
[去雾网](https://doi.org/10.1109/TIP.2016.2598681)是另一种解决单幅图像去雾问题的早期方法。去雾网的代码可以在[这里](https://github.com/caibolun/DehazeNet)找到。DehazeNet 是一个系统,它学习和估计输入图像中的模糊斑块与其介质传输之间的映射。简单的 CNN 模型用于特征提取,多尺度映射用于实现尺度不变性。
DehazeNet 是一种完全监督的方法,其中干净的自然场景图像被手动降级,以通过使用深度元数据来模拟模糊的输入图像。DehazeNet 的模型架构如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3fa55f5af16743a8c418ca5870c00471.png)
Source: [Paper](https://doi.org/10.1109/TIP.2016.2598681)
下面显示了与当时最先进的方法相比,由去雾网模型获得的一些结果。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0c15c9d1e8f39d999b415f14f3a99556.png)
Source: [Paper](https://doi.org/10.1109/TIP.2016.2598681)
## AOD-Net
[AOD 网](https://openaccess.thecvf.com/content_ICCV_2017/papers/Li_AOD-Net_All-In-One_Dehazing_ICCV_2017_paper.pdf)或一体化去雾网络是一种流行的端到端(全监督)基于 CNN 的图像去雾模型。这个代码的实现可以在[这里找到。](https://github.com/walsvid/AOD-Net-PyTorch)AOD 网络的主要新颖之处在于,它是第一个优化从模糊图像到清晰图像的端到端管道的模型,而不是一个中间参数估计步骤。AOD 网络是基于一个重新制定的大气散射模型设计的。使用以下等式从大气散射模型获得清晰图像:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/2879b3462cfe8f24e74d0f146c11412f.png)
与使用从模糊图像到传输矩阵的端到端学习的去雾网络不同,AOD 网络使用深度端到端模型进行图像去雾操作。AOD 网络模型的架构图如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/aea0c7cd926bfd9d5a4c05842ed52f77.png)
Source: [Paper](https://doi.org/10.1109/TIP.2016.2598681)
通过 AOD 网络模型获得的一些结果如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/21baee4b04dc87a4dfaac4f4ef4bafe7.png)
Source: [Paper](https://doi.org/10.1109/TIP.2016.2598681)
## DCPDN
[DCPDN](https://openaccess.thecvf.com/content_cvpr_2018/papers/Zhang_Densely_Connected_Pyramid_CVPR_2018_paper.pdf) 或密集连接的金字塔去雾网络是一种图像去雾模型,其利用了用于去雾的[生成对抗网络](https://blog.paperspace.com/complete-guide-to-gans/)架构。这个模型的代码可以在[这里找到。](https://github.com/hezhangsprinter/DCPDN)DCP dn 模型从图像中学习结构关系,并由编码器-解码器结构组成,该结构可被联合优化以同时估计透射图、大气光线以及图像去雾。与此同时,大气模型也包含在架构中,以便更好地优化整个学习过程。
然而,训练如此复杂的网络(具有三个不同的任务)在计算上是非常昂贵的。因此,为了简化训练过程并加速网络收敛,DCPDN 利用了分阶段学习技术。首先对网络的每个部分进行渐进优化,然后对整个网络进行联合优化。GAN 架构的主要目的是利用透射图和去雾图像之间的结构关系。DCPDN 模型的架构如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b62ce2d4a724031a5cfd65e77b3a43bf.png)
Source: [Paper](https://openaccess.thecvf.com/content_cvpr_2018/papers/Zhang_Densely_Connected_Pyramid_CVPR_2018_paper.pdf)
上图中的联合鉴别器区分一对估计的透射图和去雾图像是真的还是假的。此外,为了保证大气光也能在整个结构中得到优化,采用了 U-net 来估计均匀的大气光图。此外,采用多级金字塔池块来确保来自不同尺度的特征被有效地嵌入到最终结果中。
与最先进的模型相比,DCPDN 模型获得的一些结果如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/794a22073dd6e34f76e4c1124b4111a9.png)
Source: [Paper](https://openaccess.thecvf.com/content_cvpr_2018/papers/Zhang_Densely_Connected_Pyramid_CVPR_2018_paper.pdf)
## FFA-Net
[FFA-Net](https://ojs.aaai.org/index.php/AAAI/article/download/6865/6719) 或特征融合注意力网络是一种相对较新的单幅图像去雾网络,它使用端到端的架构来直接恢复无雾图像。这个模型的代码可以在[这里找到。](https://github.com/zhilin007/FFA-Net)大多数早期模型同等对待通道特征和像素特征。然而,雾度在图像上分布不均匀;非常薄的霾的权重应该明显不同于厚霾区域像素的权重。
FFA-Net 的新颖之处在于它区别对待薄霾和厚霾区域,从而通过避免对不太重要的信息进行不必要的计算来节省资源。这样,网络能够覆盖所有像素和通道,并且网络的表现不受阻碍。
FFA-Net 试图保留浅层的特性,并将它们传递到架构的深层。在将所有特征馈送到融合模块之前,该模型还给予不同级别的特征不同的权重。通过特征注意模块(由通道注意和像素注意模块的级联组成)的自适应学习来获得权重。FFA-Net 模型的架构图如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/395304ebec1d14250f44d6239d9b6321.png)
Source: [Paper](https://ojs.aaai.org/index.php/AAAI/article/download/6865/6719)
下面显示了由 FFA-Net 模型获得的一些去雾结果。FFA-Net 模型的 python 实现可以在[这里](https://github.com/zhilin007/FFA-Net)找到。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/1a6e11907fd2681d3666a7563571f4e4.png)
Source: [Paper](https://ojs.aaai.org/index.php/AAAI/article/download/6865/6719)
## **脱模工**
[去雾器](https://arxiv.org/pdf/2204.03883.pdf)是文献中提出的最新单幅图像去雾深度模型之一。实现这个的代码可以在[这里](https://github.com/IDKiro/DehazeFormer)找到。它在其内核中使用了[视觉变压器](https://openreview.net/pdf?id=YicbFdNTTy) (ViT),具体来说就是 [Swin 变压器](https://openaccess.thecvf.com/content/ICCV2021/papers/Liu_Swin_Transformer_Hierarchical_Vision_Transformer_Using_Shifted_Windows_ICCV_2021_paper.pdf)。CNN 多年来一直主导着大多数计算机视觉任务,而最近,ViT 架构显示出取代 CNN 的能力。ViT 开创了 Transformer 架构的直接应用,通过逐块线性嵌入将图像投影到令牌序列中。
去混叠器使用 U-Net 型编码器-解码器架构,但是卷积块被去混叠器块代替。去雾器使用 Swin Transformer 模型,并对其进行了一些改进,例如用同一篇论文中提出的新“RescaleNorm”层替换了 [LayerNorm](https://arxiv.org/pdf/1607.06450.pdf) 归一化层。RescaleNorm 对整个特征图执行归一化,并重新引入归一化后丢失的特征图的均值和方差。
此外,作者提出了优于全局残差学习的基于先验的软重建模块和基于 [SKNet](https://openaccess.thecvf.com/content_CVPR_2019/papers/Li_Selective_Kernel_Networks_CVPR_2019_paper.pdf) 的多尺度特征图融合模块(如在 MSCNN 和 FFA-Net 架构中使用的)来代替级联融合。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dac82596c305a2b412e82c0ed2037a96.png)
Source: [Paper](https://arxiv.org/pdf/2204.03883.pdf)
去雾器模型大大优于当代方法,当代方法大多使用具有较低开销的卷积网络,这从下面所示的定量结果中显而易见。通过去雾模型获得的一些定性结果也如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/674a35a2bdc81124878c49e8c566dbe7.png)
Source: [Paper](https://arxiv.org/pdf/2204.03883.pdf)
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/a5ae7e7eb01d173215f5afcd1aba2379.png)
Source: [Paper](https://arxiv.org/pdf/2204.03883.pdf)
## 天气变化
[TransWeather](https://openaccess.thecvf.com/content/CVPR2022/papers/Valanarasu_TransWeather_Transformer-Based_Restoration_of_Images_Degraded_by_Adverse_Weather_Conditions_CVPR_2022_paper.pdf) 是图像去雾文献中最新的深度学习架构。你可以在这里访问这个[模型的代码。它是一种通用模型,使用单编码器-单解码器变压器网络来解决所有不利天气消除问题(雨、雾、雪等)。)立刻。作者没有使用多个编码器,而是在 transformer 解码器中引入了天气类型查询来学习任务。](https://github.com/jeya-maria-jose/TransWeather)
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/34133ffa026781ad357cf87b9603741f.png)
Source: [Paper](https://openaccess.thecvf.com/content/CVPR2022/papers/Valanarasu_TransWeather_Transformer-Based_Restoration_of_Images_Degraded_by_Adverse_Weather_Conditions_CVPR_2022_paper.pdf)
TransWeather 模型由一个新颖的变压器编码器和片内变压器(Intra-PT)模块组成。Intra-PT 处理从原始补片创建的子补片,并挖掘更小补片的特征和细节。因此,Intra-PT 将注意力集中在主补丁内部,以有效地消除天气退化。作者还使用有效的自我注意机制来计算子补丁之间的注意,以保持较低的计算复杂度。TransWeather 模型的架构如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/0d1e35448d943f470c10206d1987663e.png)
Source: [Paper](https://openaccess.thecvf.com/content/CVPR2022/papers/Valanarasu_TransWeather_Transformer-Based_Restoration_of_Images_Degraded_by_Adverse_Weather_Conditions_CVPR_2022_paper.pdf)
通过 TransWeather 模型获得的一些定性结果如下所示。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/9ba6285f9f88282c305a7970421207db.png)
Source: [Paper](https://openaccess.thecvf.com/content/CVPR2022/papers/Valanarasu_TransWeather_Transformer-Based_Restoration_of_Images_Degraded_by_Adverse_Weather_Conditions_CVPR_2022_paper.pdf)
## 图像去雾的应用
图像去雾是计算机视觉中的一项重要任务,因为它在现实世界中有着广泛的应用。接下来让我们看看图像去雾的一些最重要的应用。
1. ***监控:*** 使用图像或视频(本质上是一系列图像)的监控对于安全至关重要。视觉监控系统的有效性和准确性取决于视觉输入的质量。不利的天气条件会使监视系统变得无用,从而使图像去雾成为该区域的一项重要操作。
2. *******智能交通系统:******* 大雾天气条件会影响驾驶员的能力,并因能见度有限而显著增加事故风险和出行时间。一般来说,飞机的起飞和降落在雾蒙蒙的环境中也是一项非常具有挑战性的任务。随着自动驾驶技术的出现,图像去雾是自动驾驶汽车或飞机正常运行不可或缺的操作。
3. *******水下图像增强:******* 水下成像经常会出现能见度差和颜色失真的情况。能见度低是由雾霾效应造成的,雾霾效应是由水粒子多次散射光线造成的。颜色失真是由于光的衰减,使图像带蓝色。因此,在海洋生物学研究中流行的水下视觉系统需要图像去雾算法作为预处理,以便人类可以看到水下物体。比如这篇[论文](https://openaccess.thecvf.com/content_cvpr_2017_workshops/w27/papers/Skinner_Underwater_Image_Dehazing_CVPR_2017_paper.pdf)专门处理水下图像去雾的问题,结果如下图所示。
4. *******遥感:*********在遥感中,通过拍摄图像来获取物体或区域的信息。这些图像通常来自卫星或飞机。由于相机和场景之间的距离差异很大,所以在捕获的场景中会引入薄雾效果。因此,这种应用还需要图像去雾作为预处理工具,以在分析之前提高图像的视觉质量。**
**![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/dd52dd4942157c72e714e8af95c2c8b3.png)
Underwater Image Dehazing. Source: [Paper](https://openaccess.thecvf.com/content_cvpr_2017_workshops/w27/papers/Skinner_Underwater_Image_Dehazing_CVPR_2017_paper.pdf)** **![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/f0e28178e5ba6274448088c5f957be83.png)
Image Dehazing in Remote Sensing Images. Source: [Paper](https://www.mdpi.com/2072-4292/13/21/4443)**
# **结论**
**图像去雾是计算机视觉中的一项重要任务,因为它在安全和自动驾驶等领域有着广泛的应用,在这些领域,清晰的去雾图像对于进一步的信息处理是必要的。多年来,已经提出了几种深度学习模型来解决这个问题,其中大多数都使用了完全监督学习。基于 CNN 的架构是首选方法,直到最近变压器模型获得了更好的性能。**
**该领域的最新研究集中在减少图像去雾中对监督(标记数据)的要求。尽管使用数学函数来模拟模糊图像会降低自然图像的质量,但它仍然不是准确的表示。因此,为了可靠地从图像中去除模糊,需要原始模糊图像以及它们的去模糊对应物用于监督学习。像自我监督学习和零触发学习这样的方法完全缓解了这种需求,因此已经成为最近研究的主题。**
# 图象分割法
> 原文:<https://blog.paperspace.com/image-segmentation-using-segmentation_models_pytorch/>
计算机视觉是一个热门领域,它的快速发展和扩大是众所周知的。在过去的几年里,新的最先进的库不断发布,它是许多伟大的数据科学家进入深度学习的门户。最流行的计算机视觉类型之一是图像分割。
在本文中,我们将定义图像分割和 Segmentation_models_PyTorch,发现在这些任务中使用的正确指标,并演示一个端到端管道,该管道可用作处理图像分割问题的模板。我们将通过必要的步骤,从数据准备到使用 Segmentation_models_pytorch 包的模型设置,这将使我们的任务更容易,到结果的可视化。最后,我们将讨论图像分割的一些有用的应用。除了又酷又有见识。
* * *
# 图像分割:
我们可以将**图像分割**视为像素级的分类任务,我们对图像中的每个像素进行分类,将其分配到相应的类别,因此,如果我们有一个 256*192 的图像,我们实际上必须进行 48768 像素的分类。根据任务的不同,我们可以有一个**语义分割**,我们必须对照片中的每个像素进行分类,或者有一个**实例分割**,我们只需要对代表某种感兴趣的对象的像素进行分类。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/fd1dfe8932e81fc015165894a16f72c1.png)
[Image source](https://arxiv.org/pdf/1405.0312.pdf)
* * *
# 图像分割的度量
在图像分割任务中最容易使用的度量是**像素精度**,正如其名称所示,它帮助我们找出像素分类的精度。不幸的是,我们不能完全依赖它,如果相关像素不占图片的大部分,那么像素精度非常高,因此它没有分割任何东西,所以在这种情况下它是无用的。
因此,在这种情况下,可以使用其他指标;例如并集上的交集和 dice 系数,大多数情况下我们都可以把它们联系起来。
### 并集上的交集
并集(IoU) 上的**交集也被称为**雅克卡指数**。使用 IoU 意味着我们有两个图像进行比较:一个是我们的预测,另一个是基本事实,如果获得的值接近数字 1,这意味着预测是相似的,接近我们的基本事实。反之亦然,IoU 越低,我们的结果越差。**
在下图中,我们可以看到计算中涉及的区域的可视化表示。如果我们预测了一个比它应该的更大的区域(分母会更大)或者一个更小的区域(分子会更小),我们可以计算出度量是如何被有效惩罚的。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b0b299a1160cb1d6431552a7a2e6d5c6.png)
Visual representation of the IoU calculation
### 骰子系数
另一个有用的度量是 **Dice 系数**,它是预测和实际面积的重叠面积的两倍,然后除以预测和实际面积的总和:
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/b5080e36579bd0e0bf1d8cd8b36c270e.png)
Visual representation of the Dice calculation
它与上一个指标具有相同的功能,但在这个指标中,我们不考虑分母中的联合,两者非常相似,并且它们产生几乎相同的结果,1 表示预测值和真实值之间的完美匹配,值越接近 0,预测值就越接近所需的基本事实。
* * *
# 细分 _ 模型 _pytorch
**Segmentation _ models _ pytorch**是一个构建在 py torch 框架上的令人敬畏的库,用于创建 PyTorch nn。用于图像分割任务的模块(只有**两行代码**,它包含用于二进制和多类分割的 **5 个模型架构**(包括传说中的 [Unet](https://blog.paperspace.com/unet-architecture-image-segmentation/) )、用于每个架构的 **46 个编码器**,并且所有编码器都有**预训练的权重**用于更快和更好的收敛。
* * *
# 从头开始使用 segmentation_models_pytorch 进行图像分割
在本节中,我们将演示一个端到端的管道,它可以用作处理图像分割问题的模板,我们将使用来自 Kaggle 的[监督过滤的分割人物数据集](https://www.kaggle.com/datasets/tapakah68/supervisely-filtered-segmentation-person-dataset) ,它包含 2667 个人物图像及其分割。
#### 要在渐变笔记本上安装 Kaggle 和使用 Kaggle 数据集,请遵循以下说明:
1.获得一个 Kaggle 帐户
2.转到您的帐户设置创建一个 API 令牌,并将 kaggle.json 保存到您的本地计算机。注意:如果您已经创建了一个 api 令牌,您可能需要创建一个新的 API 令牌。
3.将 kaggle.json 上传到您的渐变笔记本
4.运行下面的单元格或在终端中运行以下命令(这可能需要一段时间)
终端说明:
mkdir ~/。kaggle/
mv kaggle.json ~/。kaggle/
pip 安装卡格尔
kaggle 数据集下载 tapakah 68/超级过滤-细分-人-数据集
解压缩 supervisely-filtered-segmentation-person-dataset . zip
单元格中的线条魔术(针对免费 GPU 笔记本用户):
!mkdir ~/。kaggle/
!mv kaggle.json ~/。kaggle/
!pip 安装卡格尔
!kaggle 数据集下载 tapakah 68/超级过滤-细分-人-数据集
!解压缩 supervisely-filtered-segmentation-person-dataset . zip
让我们从加载必要的库开始。
### 加载我们需要的所有依赖项
我们首先将为线性代数引入 numpy,为与操作系统的交互引入 os。然后,我们想要使用 PyTorch,所以我们导入 Torch,并从那里导入 nn。这将帮助我们创建和训练网络,并让我们导入 optim,这是一个实现各种优化算法(如 sgd、adam、..).我们从 torch.utils.data 导入数据集以准备数据集,并导入数据加载器以创建小批量。
我们还将导入 torchvision,因为我们正在处理图像,segmentation_models_pytorch 使我们的任务更容易,albumentations 用于数据扩充,tqdm 用于显示进度条,最后 matplotlib.pyplot 用于显示结果并将它们与地面实况进行比较。
```py
import os
import numpy as np
from PIL import Image
import torch
from torch import nn, optim
from torch.utils.data import Dataset, DataLoader
import torchvision
import segmentation_models_pytorch as smp
import albumentations as A
from albumentations.pytorch import ToTensorV2
from tqdm import tqdm
import matplotlib.pyplot as plt
```
### 播种一切
让我们播种一切,使结果具有一定的可重复性
```py
def seed_everything(seed):
os.environ["PYTHONHASHSEED"] = str(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = True
seed_everything(42)
```
### 资料组
为了设置数据加载部分,我们将创建一个名为 **SegmentationDataset,**的类,并从**数据集**继承它。这个类将有三个方法:
* **init** :将自身、包含人物图像的输入目录、包含人物分割图像的输出目录、默认为 None 的数据转换的转换、is_train 作为参数,当训练阶段有 80%的数据时为真,当验证阶段有 20%的数据时为假。
* **len** :返回数据集的长度。
* getitem :它将 self 和 index 作为输入,我们将获得图像路径,这将是我们存储图像的图像目录的路径,并将它与特定图像的文件连接,然后我们做同样的事情来获得遮罩路径。现在我们有了蒙版路径和图像路径,在将蒙版转换为 L 以将其转换为灰度(1 个通道)并将图像转换为 RGB (3 个通道)之后,我们加载这两个路径,然后将它们转换为 NumPy 数组,因为我们使用的是 albumentations 库,将它们除以 255 以获得 0 到 1 之间的像素,如果不是 None,则对它们应用 transform,最后返回图像和蒙版。
```py
class SegmentationDataset(Dataset):
def __init__(self, input_dir, output_dir, is_train, transform=None):
self.input_dir = input_dir
self.output_dir = output_dir
self.transform = transform
if is_train == True:
x = round(len(os.listdir(input_dir)) * .8)
self.images = os.listdir(input_dir)[:x]
else:
x = round(len(os.listdir(input_dir)) * .8)
self.images = os.listdir(input_dir)[x:]
def __len__(self):
return len(self.images)
def __getitem__(self, index):
img_path = os.path.join(self.input_dir, self.images[index])
mask_path = os.path.join(self.output_dir, self.images[index])
img = np.array(Image.open(img_path).convert("RGB"), dtype=np.float32) / 255
mask = np.array(Image.open(mask_path).convert("L"), dtype=np.float32) / 255
if self.transform is not None:
augmentations = self.transform(image=img, mask=mask)
img = augmentations["image"]
mask = augmentations["mask"]
return img, mask
```
### 超参数和初始化
让我们用图像的路径初始化 train_inp_dir,用掩码的路径初始化 train_out_dir,如果设备可用,用 Cuda 初始化 device,否则用 CPU 初始化。设置一些超参数(学习率、批量大小、时期数...).最后,初始化包含调整图像大小和一些增强(水平翻转和颜色抖动)的训练变换,并将它们转换为张量。除了扩充之外,验证过程是相同的。
```py
TRAIN_INP_DIR = '../input/supervisely-filtered-segmentation-person-dataset/supervisely_person_clean_2667_iimg/'
TRAIN_OUT_DIR = '../input/supervisely-filtered-segmentation-person-dataset/supervisely_person_clean_2667_img/masks/'
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
LEARNING_RATE = 3e-4
BATCH_SIZE = 64
NUM_EPOCHS = 10
IMAGE_HEIGHT = 256
IMAGE_WIDTH = 192
train_transform = A.Compose(
[
A.Resize(height=IMAGE_HEIGHT, width=IMAGE_WIDTH),
A.ColorJitter(p=0.2),
A.HorizontalFlip(p=0.5),
ToTensorV2(),
],
)
val_transform = A.Compose(
[
A.Resize(height=IMAGE_HEIGHT, width=IMAGE_WIDTH),
ToTensorV2(),
],
)
```
现在让我们创建一个函数 **get_loaders** ,其中使用 SegmentationDataset 类准备数据,并使用 DataLoader 创建小批量大小,以便返回 **train_loader** 和 **val_loader**
```py
def get_loaders( inp_dir, mask_dir,batch_size,
train_transform, val_tranform ):
train_ds = SegmentationDataset( input_dir=inp_dir, output_dir=mask_dir,
is_train=True, transform=train_transform)
train_loader = DataLoader( train_ds, batch_size=batch_size, shuffle=True )
val_ds = SegmentationDataset( input_dir=inp_dir, output_dir=mask_dir,
is_train=False, transform=val_transform)
val_loader = DataLoader( val_ds, batch_size=batch_size, shuffle=True )
return train_loader, val_loader
```
### 检查数据加载器
让我们检查一下是否一切正常,看看数据是什么样的。
```py
train_loader, val_loader = get_loaders( TRAIN_INP_DIR, TRAIN_OUT_DIR,
BATCH_SIZE, train_transform, val_transform)
inputs, masks = next(iter(train_loader))
_, ax = plt.subplots(1,2)
ax[0].imshow(inputs[0].permute(1,2,0))
ax[1].imshow(masks[0])
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/90e01f6faeabf138c77f32cc9420dabd.png)
### 检查准确性:
让我们构建 check_accuracy 函数,使用像素精度和骰子点数来检查模型的验证精度,我们向该函数发送加载程序、模型和设备。
我们将 num_correct、num_pixels 和 dice_score 设置为 0,以计算像素精度和 dice 分数字母。
我们将模型切换到评估模式,并使用 torch.no_grad 包装所有内容,之后我们通过 loader,将图像及其蒙版移动到设备中,并使用 sigmoid 运行模型以获得一些预测,使像素介于 0 和 1 之间,然后将所有高于 0.5 的像素转换为 1,将所有低于 0 的像素转换为 0。 因为对于分割,我们输出每个单独像素的预测(1 表示人,0 表示其他人),然后我们计算正确预测的数量除以像素的数量,以计算**像素精度**,接下来我们计算骰子得分。 最后,我们将模型切换到训练模式。
```py
def check_accuracy(loader, model, device="cuda"):
num_correct = 0
num_pixels = 0
dice_score = 0
model.eval()
with torch.no_grad():
for img, mask in tqdm(loader):
img = img.to(device)
mask = mask.to(device).unsqueeze(1)
preds = torch.sigmoid(model(img))
preds = (preds > 0.5).float()
num_correct += (preds == mask).sum()
num_pixels += torch.numel(preds)
dice_score += (2 * (preds * mask).sum()) / (
(preds + mask).sum() + 1e-7
)
print(
f"Got {num_correct}/{num_pixels} with pixel accuracy {num_correct/num_pixels*100:.2f}"
)
print(f"Dice score: {dice_score/len(loader)*100:.2f}")
model.train()
```
### 模型、损失函数和优化器
在这个模型中,我们将使用 UNet,这是一种最初为医学成像分割提出的语义分割技术。到目前为止,它已经超过了先前的最佳分割方法,也用于许多高级的 GANs,如 pix2pix。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/4bd7f050c25012fa991ccf92e07edf77.png)
Unet architecture - [Image source](https://datascientest.com/u-net)
* 我们将使用 Segmentation_models_pytorch 只用一行代码构建这样一个强大的模型,我们选择了具有迁移学习的传奇 UNet 架构“efficientnet-b3”,输入数为 3 (RGB),类别数为 1,不使用任何激活函数。但是在使用预测之前,我们使用 sigmoid 使像素在 1 和 0 之间。最后,我们将模型移动到设备上。
* 我们有一个二进制类分割,所以基本上,它只是一个像素级的二进制分类任务,所以在损失函数中,我们会使用 **BCEWithLogitsLoss。**
* 对于优化器,我们将使用 Adam。
```py
model = smp.Unet(encoder_name='efficientnet-b3', in_channels=3, classes=1, activation=None).to(DEVICE)
loss_fn = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
```
### 培养
现在有趣的部分,魔术将会发生,在这部分我们将训练我们的模型。
* 我们循环使用数据加载器创建的所有小批量,将图像及其蒙版移动到设备中。
* 对于正向传递,我们使用模型来预测掩码,并计算预测值和地面真实值之间的损失。
* 对于后向传递,我们将梯度设置为零,因为否则梯度会累积(PyTorch 中的默认行为),然后我们使用损失后向传递方法,并更新权重。
* 最后但同样重要的是,我们更新 tqdm 循环。
```py
def train_fn(loader, model, optimizer, loss_fn):
loop = tqdm(loader)
for batch_idx, (image, mask) in enumerate(loop):
image = image.to(device=DEVICE)
mask = mask.float().unsqueeze(1).to(device=DEVICE)
# forward
predictions = model(image)
loss = loss_fn(predictions, mask)
# backward
model.zero_grad()
loss.backward()
optimizer.step()
# update tqdm loop
loop.set_postfix(loss=loss.item())
```
为了好玩,让我们在任何训练之前检查准确性。
```py
check_accuracy(val_loader, model, device=DEVICE)
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/68f997229c7676916043c6b449298728.png)
现在让我们开始训练模型,并在每个时期后检查准确性。
```py
for epoch in range(NUM_EPOCHS):
print('########################## epoch: '+str(epoch))
train_fn(train_loader, model, optimizer, loss_fn)
# check accuracy
check_accuracy(val_loader, model, device=DEVICE)
```
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/3fbb4d0444d2ad4c822d3de4cdb89ed0.png)
我们可以看到,我们获得了 97.21%的像素精度和 95.68%的骰子得分。
### 可视化结果
现在是真相大白的时刻,让我们将结果可视化,并与地面真相进行比较。
```py
inputs, masks = next(iter(val_loader))
output = ((torch.sigmoid(model(inputs.to('cuda')))) >0.5).float()
_, ax = plt.subplots(2,3, figsize=(15,10))
for k in range(2):
ax[k][0].imshow(inputs[k].permute(1,2,0))
ax[k][1].imshow(output[k][0].cpu())
ax[k][2].imshow(masks[k])
```
下面的图像代表这个代码的输出,在第一列,我们找到图像本身,在第二列我们有预测,在最后一列是基本事实。
> 你可能没有相同的图像,因为我们正在洗牌
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/c98387fbbfce34f5caab74d51efae2ce.png)
Plot the results and compare them with the ground truth
* * *
# 图像分割的应用
图像分割有许多有用的应用,例如:
* **医学图像**
图像分割是最有用的东西,它在认识和诊断不同的疾病以及模式识别研究中非常重要。例如,在成像领域用于定位肿瘤、研究解剖结构等。
* **物体检测**
图像分割的主要作用和功能是通过检测对象、理解交互等来识别图像并对其进行分析。这比从像素中寻找意义要容易得多。例如,如果我们拍摄一张报纸的照片,可以使用分割过程来分离报纸中的图像和文本。从你的照片中,眼睛、鼻子或嘴巴可以从图像的其余部分中分离出来。
图像分割还有许多其他有用的应用,如图像编辑,计数图像中的事物(人群中的人,天空中的星星...),并在卫星图像中定位一些对象(道路、森林...).
# 图像超分辨率:综述
> 原文:<https://blog.paperspace.com/image-super-resolution/>
图像超分辨率是指将图像的分辨率从低分辨率(LR)提高到高分辨率(HR)的任务。它广泛用于以下应用:
1. **监视:**对从安全摄像头获得的低分辨率图像进行检测、识别和执行面部识别。
2. **医学:**在扫描时间、空间覆盖范围和信噪比(SNR)方面,捕捉高分辨率 MRI 图像可能很棘手。超分辨率通过从低分辨率 MRI 图像生成高分辨率 MRI 来帮助解决这一问题。
3. **媒体:**超分辨率可以用来降低服务器成本,因为媒体可以以较低的分辨率发送,并可以动态升级。
深度学习技术在解决图像和视频超分辨率问题方面已经相当成功。在本文中,我们将讨论所涉及的理论、所使用的各种技术、损失函数、度量和相关数据集。您还可以在 [ML Showcase](https://ml-showcase.paperspace.com/projects/image-super-resolution) 上免费运行我们将介绍的模型之一 ESPCN 的代码。
![](https://github.com/OpenDocCN/geekdoc-dl-zh/raw/master/paperspace/img/47388d08f8c2187513a488677716effe.png)
([source](https://newatlas.com/super-resolution-weizmann-institute/23486/))
# 图像超分辨率
低分辨率图像可以使用下面的公式从高分辨率图像建模,其中 *D* 是退化函数,*I[y]是高分辨率图像,*I[x]是低分辨率图像,并且$\sigma$是噪声。**
$ $ I _ { x } = D(I _ y;\sigma) \]
退化参数 D 和\(\sigma\)未知;仅提供高分辨率图像和相应的低分辨率图像。神经网络的任务是仅使用 HR 和 LR 图像数据找到退化的反函数。
超分辨率方法和技术
有许多方法可以解决这个问题。我们将涵盖以下内容:
- 预上采样超分辨率
- 后上采样超分辨率
- 剩余网络
- 多级残差网络
- 递归网络
- 渐进重建网络
- 多分支网络
- 基于注意力的网络
- 生成模型
我们将看一下每种算法的几个例子。
预上采样超分辨率
这种方法使用传统技术,如双三次插值和深度学习,来改进上采样图像。
最流行的方法 SRCNN 也是第一个使用深度学习的,并取得了令人印象深刻的效果。
SRCNN
Source
SRCNN 是一个简单的 CNN 架构,由三层组成:一层用于面片提取、非线性映射和重建。面片提取层用于从输入中提取密集面片,并使用卷积滤波器来表示它们。非线性映射层由 1×1 卷积滤波器组成,用于改变通道数量和增加非线性。正如你可能已经猜到的,最终重建层重建高分辨率图像。
MSE 损失函数用于训练网络,PSNR(下面在度量部分讨论)用于评估结果。稍后我们将更详细地讨论这两个问题。
VDSR
Source
甚深超分辨率(VDSR)是对 SRCNN 的改进,增加了以下功能:
- 顾名思义,使用带有小型 3×3 卷积滤波器的深度网络,而不是带有大型卷积滤波器的小型网络。这是基于 VGG 架构的。
- 网络试图学习输出图像和插值输入的残差,而不是学习直接映射(像 SRCNN),如上图所示。这简化了任务。将初始低分辨率图像添加到网络输出中,以获得最终的 HR 输出。
- 梯度裁剪用于以更高的学习速率训练深度网络。
后上采样超分辨率
由于预上采样 SR 中的特征提取过程发生在高分辨率空间中,因此所需的计算能力也在高端。后上采样 SR 试图通过在较低分辨率空间中进行特征提取来解决这一问题,然后仅在最后进行上采样,因此显著减少了计算。此外,不是使用简单的双三次插值来进行上采样,而是使用反卷积/子像素卷积形式的学习上采样,从而使得网络是端到端可训练的。
让我们按照这个结构讨论一些流行的技术。
FSRCNN(密西西比州)
Source
从上图可以看出,SRCNN 和 FSRCNN 之间的主要变化是:
- 开始时没有预处理或上采样。特征提取发生在低分辨率空间。
- 在最初的 5×5 卷积后使用 1×1 卷积,以减少通道数量,从而减少计算和内存,类似于初始网络的开发方式。
- 使用多个 3×3 卷积,而不是大型卷积滤波器,类似于 VGG 网络通过简化架构来减少参数数量的工作方式。
- 通过使用学习的去卷积滤波器来完成上采样,从而改进模型。
FSRCNN 最终获得了比 SRCNN 更好的结果,同时也更快。
espn
ESPCN 引入了子像素卷积的概念,以取代用于上采样的去卷积层。这解决了与之相关的两个问题:
- 去卷积发生在高分辨率空间,因此计算量更大。
- 它解决了反卷积中的棋盘问题,该问题是由于卷积的重叠操作而出现的(如下所示)。
Source
亚像素卷积通过将深度转换为空间来工作,如下图所示。来自低分辨率图像中多个通道的像素被重新排列成高分辨率图像中的单个通道。举例来说,大小为 5×5×4 的输入图像可以将最后四个通道中的像素重新排列为一个通道,从而产生 10×10 小时的图像。
Source
现在让我们讨论几个基于下图技术的架构。
Source
剩余网络
EDSR
Source
EDSR 架构基于 SRResNet 架构,由多个残差块组成。EDSR 的剩余区块如上图所示。与 SRResNet 的主要区别在于移除了批处理规范化层。作者陈述了 BN 将输入归一化,从而限制了网络的范围;BN 的去除导致精度的提高。BN 层也会消耗内存,删除它们可以减少多达 40%的内存,从而提高网络训练的效率。
Source
MDSR
MDSR 是 EDSR 的扩展,具有多个输入和输出模块,可提供 2 倍、3 倍和 4 倍的相应分辨率输出。首先,针对特定尺度输入的预处理模块由两个具有 5×5 核的残差块组成。大的核用于预处理层,以保持网络浅,同时仍然实现高感受野。特定于尺度的预处理模块的末尾是共享残差块,它是所有分辨率的数据的公共块。最后,在共享残差块之后是特定尺度的上采样模块。虽然 MDSR 的总深度是单尺度 EDSR 的 5 倍,但由于参数共享,参数数量只有 2.5 倍,而不是 5 倍。MDSR 取得了与特定尺度的 EDSR 相当的结果,尽管该网络的参数比特定尺度的 EDSR 模型的总和还要少。
堆石标记
在论文快速、精确、轻量级的级联残差网络超分辨率中,作者在传统残差网络的基础上提出了以下改进:
- 在本地和全球级别的级联机制,以结合来自多个层的特征,并给予网络接收更多信息的能力。
- 除了 CARN 之外,在递归网络架构的帮助下,提出了更小的 CARN-M 以具有更轻的架构,而结果没有太大的恶化。
Source
CARN 中的全球连接如上图所示。具有 1×1 卷积的每个级联块的顶点接收来自所有先前级联块的输入和初始输入,从而导致信息的有效传输。
Source
级联块中的每个残差块都以 1x1 卷积结束,该卷积具有来自所有先前残差块以及主输入的连接,类似于全局级联的工作方式。
ResNet 中的 residual 块被一种新设计的 Residual-E 块所取代,这种 Residual-E 块是受 MobileNet 中深度方向卷积的启发而设计的。使用组卷积而不是深度卷积,结果显示所使用的计算量减少了 1.8-14 倍,这取决于组的大小。
为了进一步减少参数的数量,使用了共享的残差块(递归块),从而使参数的数量减少到原始数量的三倍。从上面的 (d) 中可以看出,递归共享块有助于减少参数的总数。
多级残差网络
为了分别处理低分辨率空间和高分辨率空间中的特征提取任务,在一些架构中考虑了多级设计以提高它们的性能。第一阶段预测粗糙特征,而后期对其进行改进。让我们讨论一个包含这些多级网络之一的架构。
BTSRN
Source
从上图可以看出,BTSRN 由两个阶段组成:低分辨率(LR)阶段和高分辨率(HR)阶段。LR 级由 6 个残余块组成,而 HR 级包含 4 个残余块。HR 阶段的卷积比 LR 阶段需要更多的计算,因为输入尺寸更大。两个阶段中的块的数量以这样一种方式确定,即实现精度和性能之间的折衷。
LR 级的输出在被发送到 HR 级之前被上采样。这是通过添加去卷积层和最近邻 USP 采样的输出来完成的。
作者提出了一种新的残差块,命名为 PConv ,如上图 (d) 所示。基于该结果,所提出的块实现了精度和性能之间的良好平衡。
与 EDSR 类似,避免批量标准化以防止重新居中和重新缩放,因为发现这是有害的。这是因为超分辨率是一项回归任务,因此目标输出与输入的一阶统计量高度相关。
递归网络
递归网络在卷积层中使用共享网络参数来减少内存占用,如上文 CARN-M 所示。让我们再讨论几个涉及递归单元的架构。
Source
DRCN
深度递归卷积网络(DRCN)涉及多次应用相同的卷积层。从上图可以看出,残差块中的卷积层是共享的。
Source
所有中间共享卷积块的输出与输入一起被发送到重建层,该层使用所有输入生成高分辨率图像。因为有多个输入用于产生输出,所以这种架构可以被认为是网络的集合。
DRRN
深度递归残差网络(DRRN)是对 DRCN 的改进,通过在简单卷积层上的网络中具有残差块。每个残差块中的参数都与其他残差块共享,如上图所示。
Source
如图所示,在参数数量相当的情况下,DRRN 的性能优于 SRCNN、ESPCN、VDSR 和 DRCN。
渐进重建网络
CNN 通常在单个镜头中给出输出,但是对于神经网络来说,获得具有大比例因子(比如 8 倍)的高分辨率图像是一项艰巨的任务。为了解决这个问题,一些网络架构逐步提高图像的分辨率。现在让我们讨论几个遵循这种风格的网络。
拉普森
Source
LAPSRN 或 MS-LAPSRN 由拉普拉斯金字塔结构组成,它可以使用逐步方法将图像放大到 2x、4x 和 8x。
从上图可以看出,LAPSRN 由多个阶段组成。该网络包括两个分支:特征提取分支和图像重建分支。每个迭代阶段由特征嵌入块和特征上采样块组成,如下图所示。输入图像通过特征嵌入层以提取低分辨率空间中的特征,然后使用转置卷积对其进行上采样。学习的输出是残差图像,其被添加到插值输入以获得高分辨率图像。特征上采样块的输出也被传递到下一级,该下一级用于细化该级的高分辨率输出并将其缩放到下一级。由于较低分辨率的输出用于细化进一步的阶段,因此存在共享学习,这有助于网络更好地执行。
Source
为了减少网络的内存占用,特征嵌入、特征上采样等中的参数。被递归地跨阶段共享。
Source
在特征嵌入块中,单独的残差块由共享的卷积参数组成(如上图所示),以进一步减少参数的数量。
作者认为,由于每个 LR 输入可以有多个 HR 表示,L2 损失函数在所有表示上产生平滑的输出,从而使图像看起来不清晰。为了处理这个问题,使用了 Charbonnier 损失函数,它可以更好地处理异常值。
多分支网络
到目前为止,我们已经看到了一个趋势:更深的网络会带来更好的结果。但是由于信息流的问题,训练更深层次的网络是困难的。残余网络通过使用快捷连接在一定程度上解决了这个问题。多分支网络通过具有多个信息可以通过的分支来改善信息流,从而导致来自多个感受野的信息的融合,并因此得到更好的训练。让我们讨论几个采用这种技术的网络。
CMSC
与其他超分辨率框架一样,级联多尺度交叉网络(CMSC)有一个特征提取层、级联子网和一个重建层,如下所示。
Source
级联子网络由两个分支组成,如 (b) 所示。每个分支有不同大小的过滤器,因此导致不同的感受野。来自模块中不同感受野的信息融合导致更好的信息流。MSC 的多个块被一个接一个地堆叠,以迭代地逐渐减小输出和 HR 图像之间的差异。所有模块的输出一起传递到重构模块,以获得最终的 HR 输出。
IDN
信息蒸馏网络(IDN)被提出用于实现超分辨率任务的快速和准确的结果。像其他多分支网络一样,IDN 利用多个分支的能力来改善深层网络中的信息流。
IDN 架构由执行特征提取的 FBlock、多个数据块和执行转置卷积以实现学习放大的 RBlock 组成。本文的贡献在于数据块,它由两个单元组成:增强单元和压缩单元。
Source
增强单元的结构如上图所示。输入通过三个大小为 3×3 的卷积滤波器,然后进行限幅。切片的一部分与初始输入连接,通过快捷连接传递到最终层。剩余的切片通过另一组大小为 3×3 的卷积滤波器。通过将输入和最终层相加产生最终输出。具有这种结构有助于同时捕获短程信息和远程信息。
压缩单元获取增强单元的输出,并将其通过 1×1 卷积滤波器,以压缩(或减少)通道数量。
基于注意力的网络
到目前为止讨论的网络给予所有空间位置和渠道同等的重要性。一般来说,有选择地关注图像中的不同区域可以得到更好的结果。我们现在将讨论几个有助于实现这一目标的体系结构。
挑选
Source
SelNet 提出了一种在卷积块末端的新型选择单元,它有助于决定有选择地传递哪些信息。选择模块由 ReLu 激活、1×1 卷积和 sigmoid 门控组成。选择单元是选择模块和身份连接的乘积。
子像素层(类似于 ESPCN)被保持在网络的末端,以实现学习的放大。网络学习残差 HR 图像,然后将其添加到插值输入中,以获得最终的 HR 图像。
RCAN
通过这篇文章,我们观察到拥有更深的网络可以提高性能。为了训练更深的网络,剩余渠道注意力网络(RCAN)建议使用渠道注意力的 RIR 模块。让我们更详细地讨论这些。
Source
RCAN 中的输入通过单个卷积滤波器进行特征提取,然后通过长跳跃连接绕过最终层。添加长跳跃连接以承载来自 LR 图像的低频信号,而主网络(即 RIR)专注于捕捉高频信息。
RIR 由多个 RG 嵌段组成,每个嵌段都具有上图所示的结构。每个 RG 模块都有多个 RCAB 模块以及一个跳接连接,称为短跳接连接,以帮助传输低频信号。
Source
RCAB 有一个由 GAP 模块组成的结构(如上图)来实现通道注意力,类似于 SqueezeNet 中的 Squeeze 和 Excite 块。信道方式的注意力与来自卷积块的 sigmoid 门控功能的输出相乘。然后将该输出添加到快捷输入连接中,以获得 RCAB 模块的最终输出值。
生成模型
到目前为止讨论的网络优化了预测和输出 HR 图像之间的像素差异。尽管这种度量很好,但并不理想;人类不是通过像素差异来区分图像,而是通过感知质量。生成模型(或 gan)试图优化感知质量,以产生人眼愉悦的图像。最后,我们来看几个与 GAN 相关的架构。
斯尔甘
Source
SRGAN 使用基于 GAN 的架构来生成视觉上令人愉悦的图像。它使用 SRResnet 网络架构作为后端,并使用多任务丢失来优化结果。损失由三项组成:
- MSE 损失捕捉像素相似性
- 感知相似性损失,其用于通过使用深层网络来捕获高级信息
- 歧视者的对抗性损失
尽管所获得的结果具有相对较低的 PSNR 值,但是该模型实现了更多的 MOS,即结果中更好的感知质量。
增强型网络
EnhanceNet 使用带有残差学习的完全卷积网络,该网络在损失函数中使用了一个额外的项来捕捉更精细的纹理信息。除了上述 SRGAN 中的损失,类似于风格转换中的纹理损失被用于捕捉更精细的纹理信息。
Source
ESRGAN 在 SRGAN 的基础上增加了一个相对论鉴别器。其优势在于,网络经过训练,不仅可以分辨出哪个图像是真的还是假的,还可以让真实的图像看起来比生成的图像更不真实,从而有助于欺骗鉴别者。SRGAN 中的批处理规范化也被删除,密集块(灵感来自 DenseNet )被用于更好的信息流。这些致密的块体被称为 RRDB。
资料组
以下是一些用于训练超分辨率网络的常用数据集。
- DIV2K : 800 次训练,100 次验证,100 次测试。提供 2K 分辨率图像,包括具有 2 倍、3 倍和 4 倍缩减系数的高分辨率和低分辨率图像。在 NTIRE17 挑战赛中提出。
- Flickr2K : 来自 Flickr 的 2650 张 2K 图片。
- 滑铁卢 : 滑铁卢探索数据库包含了 4744 张原始的自然图像和 94880 张由它们创建的扭曲图像。
损失函数
在这一节中,我们将讨论可以用来训练网络的各种损失函数。
- 像素损失:这是在训练超分辨率网络中使用的最简单和最常见的损失函数类型。L2、L1 或一些差异度量被用来评估模型。像素丢失的训练优化了 PSNR,但没有直接优化感知质量,因此生成的图像可能不符合人眼的要求。
- 感知损失:感知损失试图将生成图像中的高级特征与给定的 HR 输出图像进行匹配。这是通过采用预先训练的网络,如 VGG,并使用预测和输出图像之间的特征输出的差异作为损失来实现的。SRGAN 中引入了这个损失函数。
- 夏邦尼尔损失:该损失函数用于 LapSRN,而不是通用的 L2 损失。结果表明,与通常更平滑的 L2 损失相比,夏邦尼尔损失更好地处理异常值并产生更清晰的图像。
- 纹理损失:在 EnhanceNet 中引入,这个损失函数试图优化特征输出的 Gram 矩阵,其灵感来自于风格转移损失函数。该损失函数训练网络来捕捉 HR 图像中的纹理信息。
- 对抗损失:在所有 GAN 相关架构中使用,对抗损失有助于欺骗鉴别器,并且通常产生具有更好感知质量的图像。ESRGAN 通过使用相对论鉴别器增加了一个额外的变体,从而指示网络不仅使假图像更真实,而且使真实图像看起来更假。
韵律学
在本节中,我们将讨论用于比较各种模型性能的各种指标。
- PSNR: 峰值信噪比是确定结果质量最常用的技术。可以使用下面的公式从 MSE 直接计算,其中 L 是可能的最大像素值(8 位图像为 255)。
- SSIM: 该指标用于使用以下公式比较两幅图像的感知质量,两幅图像的平均值(\(\mu\))、方差(\(\sigma\))和相关性( c )。
Source
- MOS: 平均意见得分是一种手动确定模型结果的方法,要求人们在 0 到 5 之间对图像进行评分。将结果汇总,并将平均结果用作衡量标准。
此时你可能会想:
Source
让我们编写一个到目前为止我们已经讨论过的流行架构,ESPCN。
inputs = keras.Input(shape=(None, None, 1))
conv1 = layers.Conv2D(64, 5, activation="tanh", padding="same")(inputs)
conv2 = layers.Conv2D(32, 3, activation="tanh", padding="same")(conv1)
conv3 = layers.Conv2D((upscale_factor*upscale_factor), 3, activation="sigmoid", padding="same")(conv2)
outputs = tf.nn.depth_to_space(conv3, upscale_factor, data_format='NHWC')
model = Model(inputs=inputs, outputs=outputs)
众所周知,ESPCN 由用于特征提取的卷积层和用于上采样的亚像素卷积组成。我们使用 TensorFlow depth_to_space
函数来执行亚像素卷积。
def gen_dataset(filenames, scale):
# The model trains on 17x17 patches
crop_size_lr = 17
crop_size_hr = 17 * scale
for p in filenames:
image_decoded = cv2.imread("Training/DIV2K_train_HR/"+p.decode(), 3).astype(np.float32) / 255.0
imgYCC = cv2.cvtColor(image_decoded, cv2.COLOR_BGR2YCrCb)
cropped = imgYCC[0:(imgYCC.shape[0] - (imgYCC.shape[0] % scale)),
0:(imgYCC.shape[1] - (imgYCC.shape[1] % scale)), :]
lr = cv2.resize(cropped, (int(cropped.shape[1] / scale), int(cropped.shape[0] / scale)),
interpolation=cv2.INTER_CUBIC)
hr_y = imgYCC[:, :, 0]
lr_y = lr[:, :, 0]
numx = int(lr.shape[0] / crop_size_lr)
numy = int(lr.shape[1] / crop_size_lr)
for i in range(0, numx):
startx = i * crop_size_lr
endx = (i * crop_size_lr) + crop_size_lr
startx_hr = i * crop_size_hr
endx_hr = (i * crop_size_hr) + crop_size_hr
for j in range(0, numy):
starty = j * crop_size_lr
endy = (j * crop_size_lr) + crop_size_lr
starty_hr = j * crop_size_hr
endy_hr = (j * crop_size_hr) + crop_size_hr
crop_lr = lr_y[startx:endx, starty:endy]
crop_hr = hr_y[startx_hr:endx_hr, starty_hr:endy_hr]
hr = crop_hr.reshape((crop_size_hr, crop_size_hr, 1))
lr = crop_lr.reshape((crop_size_lr, crop_size_lr, 1))
yield lr, hr
我们将使用 DIV2K 数据集来训练模型。我们将 2k 分辨率的图像分割成 17×17 的小块,作为训练的模型输入。作者将 RGB 图像转换为 YCrCb 格式,然后使用 ESPCN 放大 Y 通道输入。使用双三次插值对 Cr 和 Cb 通道进行放大,并将所有放大的通道拼接在一起,以获得最终的 HR 图像。因此,在训练时,我们只需要向模型提供低分辨率数据和高分辨率图像的 Y 通道。
完整的代码可以在 Gradient Community (Jupyter)笔记本上免费运行。
摘要
在本文中,我们讨论了什么是超分辨率,它的应用,超分辨率算法的分类,以及它们的优点和局限性。然后,我们查看了一些公开可用的数据集,以及可以使用的不同类型的损失函数和指标。最后,我们浏览了 ESPCN 架构的代码。
如果你想继续学习超分辨率,我推荐这个 repo ,它包括一个研究论文的集合和它们相应代码的链接。
基于 Keras 的Implementing Seq2Seq 文本摘要模型
原文:https://blog.paperspace.com/implement-seq2seq-for-text-summarization-keras/
到目前为止,我们已经在这个两部分系列的第 1 部分中介绍了以下内容:
- Seq2Seq 型号介绍
- Seq2Seq 架构和应用
- 使用编码器-解码器序列到序列模型的文本摘要
- 步骤 1 -导入数据集
- 步骤 2 -清理数据
- 步骤 3 -确定最大允许序列长度
- 步骤 4 -选择合理的文本和摘要
- 步骤 5 -标记文本
- 步骤 6 -删除空文本和摘要
在本教程中,我们将涵盖这个系列的第二部分编码器-解码器序列到序列的 RNNs:如何建立,训练和测试我们的 seq2seq 模型使用 Keras 的文本摘要。
不要忘记,你可以跟随本系列的所有代码,并从Gradient Community Notebook在一个免费的 GPU 上运行它。
我们继续吧!
步骤 7:创建模型
首先,导入所有必需的库。
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.layers import Input, LSTM, Embedding, Dense, \
Concatenate, TimeDistributed
from tensorflow.keras.models import Model
from tensorflow.keras.callbacks import EarlyStopping
接下来,定义编码器和解码器网络。
编码器
编码器接受的输入长度等于您已经在步骤 3 中估算的最大文本长度。然后,这被给予尺寸为(total number of words captured in the text vocabulary) x (number of nodes in an embedding layer)
(在步骤 5 中计算)的嵌入层;x_voc
变量)。接下来是三个 LSTM 网络,其中每一层返回 LSTM 输出,以及在先前时间步骤观察到的隐藏和单元状态。
解码器
在解码器中,定义了嵌入层,随后是 LSTM 网络。LSTM 网络的初始状态是从编码器获取的最后的隐藏和单元状态。LSTM 的输出被提供给被时间分布层包裹的密集层,该时间分布层具有附加的 softmax 激活功能。
总之,模型接受编码器(文本)和解码器(摘要)作为输入,并输出摘要。预测是通过从摘要的前一个单词预测摘要的下一个单词来进行的(见下图)。
Consider the summary line to be “I want every age to laugh”. The model has to accept two inputs - the actual text and the summary. During the training phase, the decoder accepts the input summary given to the model, and learns every word that has to follow a certain given word. It then generates the predictions using an inference model during the test phase.
添加以下代码来定义您的网络体系结构。
latent_dim = 300
embedding_dim = 200
# Encoder
encoder_inputs = Input(shape=(max_text_len, ))
# Embedding layer
enc_emb = Embedding(x_voc, embedding_dim,
trainable=True)(encoder_inputs)
# Encoder LSTM 1
encoder_lstm1 = LSTM(latent_dim, return_sequences=True,
return_state=True, dropout=0.4,
recurrent_dropout=0.4)
(encoder_output1, state_h1, state_c1) = encoder_lstm1(enc_emb)
# Encoder LSTM 2
encoder_lstm2 = LSTM(latent_dim, return_sequences=True,
return_state=True, dropout=0.4,
recurrent_dropout=0.4)
(encoder_output2, state_h2, state_c2) = encoder_lstm2(encoder_output1)
# Encoder LSTM 3
encoder_lstm3 = LSTM(latent_dim, return_state=True,
return_sequences=True, dropout=0.4,
recurrent_dropout=0.4)
(encoder_outputs, state_h, state_c) = encoder_lstm3(encoder_output2)
# Set up the decoder, using encoder_states as the initial state
decoder_inputs = Input(shape=(None, ))
# Embedding layer
dec_emb_layer = Embedding(y_voc, embedding_dim, trainable=True)
dec_emb = dec_emb_layer(decoder_inputs)
# Decoder LSTM
decoder_lstm = LSTM(latent_dim, return_sequences=True,
return_state=True, dropout=0.4,
recurrent_dropout=0.2)
(decoder_outputs, decoder_fwd_state, decoder_back_state) = \
decoder_lstm(dec_emb, initial_state=[state_h, state_c])
# Dense layer
decoder_dense = TimeDistributed(Dense(y_voc, activation='softmax'))
decoder_outputs = decoder_dense(decoder_outputs)
# Define the model
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
model.summary()
# Output
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 100)] 0
__________________________________________________________________________________________________
embedding (Embedding) (None, 100, 200) 5927600 input_1[0][0]
__________________________________________________________________________________________________
lstm (LSTM) [(None, 100, 300), ( 601200 embedding[0][0]
__________________________________________________________________________________________________
input_2 (InputLayer) [(None, None)] 0
__________________________________________________________________________________________________
lstm_1 (LSTM) [(None, 100, 300), ( 721200 lstm[0][0]
__________________________________________________________________________________________________
embedding_1 (Embedding) (None, None, 200) 2576600 input_2[0][0]
__________________________________________________________________________________________________
lstm_2 (LSTM) [(None, 100, 300), ( 721200 lstm_1[0][0]
__________________________________________________________________________________________________
lstm_3 (LSTM) [(None, None, 300), 601200 embedding_1[0][0]
lstm_2[0][1]
lstm_2[0][2]
__________________________________________________________________________________________________
time_distributed (TimeDistribut (None, None, 12883) 3877783 lstm_3[0][0]
==================================================================================================
Total params: 15,026,783
Trainable params: 15,026,783
Non-trainable params: 0
__________________________________________________________________________________________________
步骤 8:训练模型
在此步骤中,编译模型并定义EarlyStopping
以在验证损失度量停止下降时停止训练模型。
model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy')
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=2)
接下来,使用model.fit()
方法拟合训练数据,您可以将批量定义为 128。发送文本和摘要(不包括摘要中的最后一个单词)作为输入,以及包含每个单词(从第二个单词开始)的重新整形的摘要张量作为输出(这解释了在给定前一个单词的情况下,将智能注入模型以预测单词)。此外,为了在训练阶段启用验证,也要发送验证数据。
history = model.fit(
[x_tr, y_tr[:, :-1]],
y_tr.reshape(y_tr.shape[0], y_tr.shape[1], 1)[:, 1:],
epochs=50,
callbacks=[es],
batch_size=128,
validation_data=([x_val, y_val[:, :-1]],
y_val.reshape(y_val.shape[0], y_val.shape[1], 1)[:
, 1:]),
)
# Output
Train on 88513 samples, validate on 9835 samples
Epoch 1/50
88513/88513 [==============================] - 426s 5ms/sample - loss: 5.1520 - val_loss: 4.8026
Epoch 2/50
88513/88513 [==============================] - 412s 5ms/sample - loss: 4.7110 - val_loss: 4.5082
Epoch 3/50
88513/88513 [==============================] - 412s 5ms/sample - loss: 4.4448 - val_loss: 4.2815
Epoch 4/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 4.2487 - val_loss: 4.1264
Epoch 5/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 4.1049 - val_loss: 4.0170
Epoch 6/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 3.9968 - val_loss: 3.9353
Epoch 7/50
88513/88513 [==============================] - 412s 5ms/sample - loss: 3.9086 - val_loss: 3.8695
Epoch 8/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 3.8321 - val_loss: 3.8059
Epoch 9/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 3.7598 - val_loss: 3.7517
Epoch 10/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.6948 - val_loss: 3.7054
Epoch 11/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 3.6408 - val_loss: 3.6701
Epoch 12/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.5909 - val_loss: 3.6376
Epoch 13/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 3.5451 - val_loss: 3.6075
Epoch 14/50
88513/88513 [==============================] - 412s 5ms/sample - loss: 3.5065 - val_loss: 3.5879
Epoch 15/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 3.4690 - val_loss: 3.5552
Epoch 16/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.4322 - val_loss: 3.5308
Epoch 17/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.3981 - val_loss: 3.5123
Epoch 18/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.3683 - val_loss: 3.4956
Epoch 19/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.3379 - val_loss: 3.4787
Epoch 20/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.3061 - val_loss: 3.4594
Epoch 21/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.2803 - val_loss: 3.4412
Epoch 22/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.2552 - val_loss: 3.4284
Epoch 23/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.2337 - val_loss: 3.4168
Epoch 24/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.2123 - val_loss: 3.4148
Epoch 25/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.1924 - val_loss: 3.3974
Epoch 26/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.1727 - val_loss: 3.3869
Epoch 27/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.1546 - val_loss: 3.3853
Epoch 28/50
88513/88513 [==============================] - 408s 5ms/sample - loss: 3.1349 - val_loss: 3.3778
Epoch 29/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.1188 - val_loss: 3.3637
Epoch 30/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.1000 - val_loss: 3.3544
Epoch 31/50
88513/88513 [==============================] - 413s 5ms/sample - loss: 3.0844 - val_loss: 3.3481
Epoch 32/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 3.0680 - val_loss: 3.3407
Epoch 33/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.0531 - val_loss: 3.3374
Epoch 34/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 3.0377 - val_loss: 3.3314
Epoch 35/50
88513/88513 [==============================] - 408s 5ms/sample - loss: 3.0214 - val_loss: 3.3186
Epoch 36/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 3.0041 - val_loss: 3.3128
Epoch 37/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 2.9900 - val_loss: 3.3195
Epoch 38/50
88513/88513 [==============================] - 407s 5ms/sample - loss: 2.9784 - val_loss: 3.3007
Epoch 39/50
88513/88513 [==============================] - 408s 5ms/sample - loss: 2.9655 - val_loss: 3.2975
Epoch 40/50
88513/88513 [==============================] - 410s 5ms/sample - loss: 2.9547 - val_loss: 3.2889
Epoch 41/50
88513/88513 [==============================] - 408s 5ms/sample - loss: 2.9424 - val_loss: 3.2923
Epoch 42/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 2.9331 - val_loss: 3.2753
Epoch 43/50
88513/88513 [==============================] - 411s 5ms/sample - loss: 2.9196 - val_loss: 3.2847
Epoch 44/50
88513/88513 [==============================] - 409s 5ms/sample - loss: 2.9111 - val_loss: 3.2718
Epoch 45/50
50688/88513 [================>.............] - ETA: 2:48 - loss: 2.8809
接下来,绘制在培训阶段观察到的培训和验证损失指标。
from matplotlib import pyplot
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
Train and Validation Loss (Loss v/s Epoch)
步骤 9:生成预测
既然我们已经训练了模型,要从给定的文本片段生成摘要,首先将索引反向映射到单词(之前已经在步骤 5 中使用texts_to_sequences
生成)。此外,将单词映射到 summaries 标记器中的索引,该标记器用于检测序列的开始和结束。
reverse_target_word_index = y_tokenizer.index_word
reverse_source_word_index = x_tokenizer.index_word
target_word_index = y_tokenizer.word_index
现在定义编码器和解码器推理模型,开始进行预测。使用tensorflow.keras.Model()
对象创建您的推理模型。
编码器推理模型接受文本并返回从三个 LSTMs、隐藏和单元状态生成的输出。解码器推理模型接受序列标识符(sostok)的开始,并预测即将到来的单词,最终导致预测整个摘要。
添加以下代码来定义推理模型的架构。
# Inference Models
# Encode the input sequence to get the feature vector
encoder_model = Model(inputs=encoder_inputs, outputs=[encoder_outputs,
state_h, state_c])
# Decoder setup
# Below tensors will hold the states of the previous time step
decoder_state_input_h = Input(shape=(latent_dim, ))
decoder_state_input_c = Input(shape=(latent_dim, ))
decoder_hidden_state_input = Input(shape=(max_text_len, latent_dim))
# Get the embeddings of the decoder sequence
dec_emb2 = dec_emb_layer(decoder_inputs)
# To predict the next word in the sequence, set the initial states to the states from the previous time step
(decoder_outputs2, state_h2, state_c2) = decoder_lstm(dec_emb2,
initial_state=[decoder_state_input_h, decoder_state_input_c])
# A dense softmax layer to generate prob dist. over the target vocabulary
decoder_outputs2 = decoder_dense(decoder_outputs2)
# Final decoder model
decoder_model = Model([decoder_inputs] + [decoder_hidden_state_input,
decoder_state_input_h, decoder_state_input_c],
[decoder_outputs2] + [state_h2, state_c2])
现在定义一个函数decode_sequence()
,它接受输入文本并输出预测的摘要。从sostok
开始,继续生成单词,直到遇到eostok
或者达到摘要的最大长度。通过选择具有最大附加概率的单词,从给定单词中预测即将到来的单词,并相应地更新解码器的内部状态。
def decode_sequence(input_seq):
# Encode the input as state vectors.
(e_out, e_h, e_c) = encoder_model.predict(input_seq)
# Generate empty target sequence of length 1
target_seq = np.zeros((1, 1))
# Populate the first word of target sequence with the start word.
target_seq[0, 0] = target_word_index['sostok']
stop_condition = False
decoded_sentence = ''
while not stop_condition:
(output_tokens, h, c) = decoder_model.predict([target_seq]
+ [e_out, e_h, e_c])
# Sample a token
sampled_token_index = np.argmax(output_tokens[0, -1, :])
sampled_token = reverse_target_word_index[sampled_token_index]
if sampled_token != 'eostok':
decoded_sentence += ' ' + sampled_token
# Exit condition: either hit max length or find the stop word.
if sampled_token == 'eostok' or len(decoded_sentence.split()) \
>= max_summary_len - 1:
stop_condition = True
# Update the target sequence (of length 1)
target_seq = np.zeros((1, 1))
target_seq[0, 0] = sampled_token_index
# Update internal states
(e_h, e_c) = (h, c)
return decoded_sentence
定义两个函数- seq2summary()
和seq2text()
,分别将数字表示转换为摘要和文本的字符串表示。
# To convert sequence to summary
def seq2summary(input_seq):
newString = ''
for i in input_seq:
if i != 0 and i != target_word_index['sostok'] and i \
!= target_word_index['eostok']:
newString = newString + reverse_target_word_index[i] + ' '
return newString
# To convert sequence to text
def seq2text(input_seq):
newString = ''
for i in input_seq:
if i != 0:
newString = newString + reverse_source_word_index[i] + ' '
return newString
最后,通过发送文本来生成预测。
for i in range(0, 19):
print ('Review:', seq2text(x_tr[i]))
print ('Original summary:', seq2summary(y_tr[i]))
print ('Predicted summary:', decode_sequence(x_tr[i].reshape(1,
max_text_len)))
print '\n'
以下是 RNN 模型得出的一些值得注意的总结。
# Output
Review: us president donald trump on wednesday said that north korea has returned the remains of 200 us troops missing from the korean war although there was no official confirmation from military authorities north korean leader kim jong un had agreed to return the remains during his summit with trump about 700 us troops remain unaccounted from the 1950 1953 korean war
Original summary: start n korea has returned remains of 200 us war dead trump end
Predicted summary: start n korea has lost an war against us trump end
Review: pope francis has said that history will judge those who refuse to accept the science of climate change if someone is doubtful that climate change is true they should ask scientists the pope added notably us president donald trump who believes global warming is chinese conspiracy withdrew the country from the paris climate agreement
Original summary: start history will judge those denying climate change pope end
Predicted summary: start pope francis will be in paris climate deal prez end
Review: the enforcement directorate ed has attached assets worth over ã¢ââ¹33 500 crore in the over three year tenure of its chief karnal singh who retires sunday officials said the agency filed around 390 in connection with its money laundering probes during the period the government on saturday appointed indian revenue service irs officer sanjay kumar mishra as interim ed chief
Original summary: start enforcement attached assets worth ã¢ââ¹33 500 cr in yrs end
Predicted summary: start ed attaches assets worth 100 crore in india in days end
Review: lok janshakti party president ram vilas paswan daughter asha has said she will contest elections against him from constituency if given ticket from lalu prasad yadav rjd she accused him of neglecting her and promoting his son chirag asha is paswan daughter from his first wife while chirag is his son from his second wife
Original summary: start will contest against father ram vilas from daughter end
Predicted summary: start lalu son tej pratap to contest his daughter in 2019 end
Review: irish deputy prime minister frances fitzgerald announced her resignation on tuesday in bid to avoid the collapse of the government and potential snap election she quit hours before no confidence motion was to be proposed against her by the main opposition party the political crisis began over fitzgerald role in police whistleblower scandal
Original summary: start irish deputy prime minister resigns to avoid govt collapse end
Predicted summary: start pmo resigns from punjab to join nda end
Review: rr wicketkeeper batsman jos buttler slammed his fifth straight fifty in ipl 2018 on sunday to equal former indian cricketer virender sehwag record of most straight 50 scores in the ipl sehwag had achieved the feat while representing dd in the ipl 2012 buttler is also only the second batsman after shane watson to hit two successive 90 scores in ipl
Original summary: start buttler equals sehwag record of most straight 50s in ipl end
Predicted summary: start sehwag slams sixes in an ipl over 100 times in ipl end
Review: maruti suzuki india on wednesday said it is recalling 640 units of its super carry mini trucks sold in the domestic market over possible defect in fuel pump supply the recall covers super carry units manufactured between january 20 and july 14 2018 the faulty parts in the affected vehicles will be replaced free of cost the automaker said n
Original summary: start maruti recalls its mini trucks over fuel pump issue in india end
Predicted summary: start maruti suzuki recalls india over ã¢ââ¹3 crore end
Review: the arrested lashkar e taiba let terrorist aamir ben has confessed to the national investigation agency that pakistani army provided him cover firing to infiltrate into india he further revealed that hafiz organisation ud dawah arranged for his training and that he was sent across india to carry out subversive activities in and outside kashmir
Original summary: start pak helped me enter india arrested let terrorist to nia end
Predicted summary: start pak man who killed indian soldiers to enter kashmir end
Review: the 23 richest indians in the 500 member bloomberg billionaires index saw wealth erosion of 21 billion this year lakshmi mittal who controls the world largest steelmaker arcelormittal lost 5 6 billion or 29 of his net worth followed by sun pharma founder dilip shanghvi whose wealth declined 4 6 billion asia richest person mukesh ambani added 4 billion to his fortune
Original summary: start lakshmi mittal lost 10 bn in 2018 ambani added 4 bn end
Predicted summary: start india richest man lost billion in wealth in 2017 end
结论
我们构建的编码器-解码器序列到序列模型(LSTM)根据它在训练文本中学习的内容生成了可接受的摘要。尽管在 50 个时代之后,预测的摘要与预期的摘要并不完全一致(我们的模型还没有达到人类水平的智能!),我们的模型所获得的智能肯定是有价值的。
要从该模型中获得更准确的结果,您可以增加数据集的大小,调整网络的超参数,尝试使其更大,并增加历元的数量。
在本教程中,您已经训练了一个编码器-解码器序列到序列模型来执行文本摘要。在我的下一篇文章中,你可以学到所有关于注意力机制的知识。直到那时,快乐学习!
参考: Sandeep Bhogaraju
如何利用 Levenshtein 距离在 Android 上实现文本推荐
原文:https://blog.paperspace.com/implement-text-recommendation-android-levenshtein-distance/
在手机上输入文本可能不如在电脑上书写舒服。这就是为什么大多数将文本作为输入的应用程序为用户提供建议的更正和预测的单词,以使键入更容易。本教程将向您展示如何实现一个应用程序,可以做到这一点。
在本教程中,我们将使用名为 Kivy 的跨平台 Python 框架构建一个 Android 应用程序,用于在用户键入时推荐文本。如果目标单词在推荐列表中可用,用户可以只选择它而不用写下它的所有字符。该教程首先简要概述了如何构建桌面 Kivy 应用程序,然后开始为 Android 构建文本推荐应用程序。
以下是概要:
- 准备 Kivy 开发环境
- 构建一个简单的桌面 Kivy 应用程序
- 导出 Android 应用程序
- Levenshtein 距离的 Python 实现
- 构建 Android 文本推荐应用程序
请注意,本教程是建立在另外两个基础之上的。要深入了解 Levenshtein 距离以及如何计算它,请查看使用 Levenshtein 距离测量文本相似性。在第 2 部分中,我们将看到如何用 Python 实现Levenshtein 距离。
准备 Kivy 开发环境
像任何其他 Python 库一样,您可以使用pip
安装程序安装 Kivy。您需要做的就是发出下面的命令。如果你使用 Python 3,记得用pip3
代替pip
。该命令适用于所有桌面平台,使您能够用 Python 创建跨平台的桌面应用程序。要在 Kivy 应用之外构建 Android 应用,必须使用 Linux。
pip install kivy
前面的命令会将 Kivy 安装到 Python 的主安装中,但是建议创建一个可以安装 Kivy 的虚拟环境。要创建虚拟机,请确保您的机器已经安装了virtualenv
和setuptools
库。
pip install --upgrade pip virtualenv setuptools
要创建虚拟环境,只需发出以下命令并指定您选择的名称。我选择了名字KivyVirtualEnv
。
virtualenv --no-site-packages KivyVirtualEnv
在当前目录中,您可以找到一个名为KivyVirtualEnv
的文件夹,如下图所示。Python,无论是第 2 版还是第 3 版,都将安装在这个文件夹中。
在环境目录中有一个名为bin
的文件夹,其中有一个名为activate
的脚本。该脚本用于根据下一个命令激活虚拟环境。只需指定与您的路径相匹配的路径。
. KivyVirtualEnv/bin/activate
发出命令后,虚拟环境将被激活,如下图所示。
如前所述,在环境内部,将使用pip
安装 Kivy。安装后,您可以通过导入它来测试它是否正常工作,如下所示。
在确保一切按预期运行后,我们可以继续构建一个简单的 Kivy 应用程序。
构建一个简单的桌面 Kivy 应用
Kivy 很容易学会用 GUI 构建 Python 应用程序。如果你想更全面地了解 Kivy 入门,你可以阅读我的书使用 Kivy 和 Android Studio 在 Python 中构建 Android 应用程序:使用 Pyjnius、Plyer 和 Buildozer ,这本书从头开始构建桌面和 Android 应用程序。
下面的代码实现了一个基本的 Kivy 应用程序,其中 GUI 只包含一个按钮。创建一个 Kivy 应用程序所需要做的就是创建一个新的类(在这个例子中是KivyApp
),它从 Kivy 扩展了kivy.app.App
类。在那之后,你只需要实现方法build()
,它保存出现在屏幕上的 GUI 部件。在这种情况下,只有一个按钮小部件被创建为来自kivy.uix.button.Button
类的实例。在类构造函数内部,参数text
被设置为Hello
。
import kivy.app
import kivy.uix.button
class KivyApp(kivy.app.App):
def build(self):
return kivy.uix.button.Button(text="Hello")
app = KivyApp()
app.run()
如果这段代码保存在一个名为main.py
的文件中,那么您可以按照下一个命令照常运行它:
python3 main.py
之前的命令必须在之前创建的虚拟环境中发出。下图显示了应用程序的屏幕。
现在我们有了一个在桌面上运行的基本 Kivy 应用程序,让我们在下一节中导出 Android 应用程序。
导出安卓应用
有一个名为 python-for-android 的项目,旨在从 Kivy 中导出一个 android 应用程序。这个项目的顶端是一个名为 Buildozer 的工具,只需三个步骤就可以轻松导出 Android 应用程序。
第一步是使用pip
安装 Buildozer,如下所示:
pip3 install buildozer
安装完成后,第二步是创建一个名为buildozer.spec
的文件,其中保存了 Android 应用程序的一些规范。您可以手动创建该文件,也可以使用以下命令生成模板。
buildozer init
该文件中的一些字段包括用于保存 Android 应用程序标题的title
;package.name
代表包的名称;orientation
指定方向是横向还是纵向;requirements
,它保存了要打包到 Android 应用程序中的库的列表;还有更多。稍后,我们将需要把NumPy
库打包到 Android 应用程序中,因此它必须被列为一个需求。
在导出 Android 应用程序之前,您必须确保 Kivy 应用程序的代码保存在名为main.py
的文件中,并且该文件位于buildozer.spec
文件旁边的应用程序文件夹的根目录中。如果 app 文件夹名为app
,那么项目的层次结构如下:
app
main.py
buildozer.init
构建 Android 应用程序和创建 APK 文件的最后一步是发出以下命令来创建调试 APK:
buildozer android debug
当第一次发出这个命令时,除了准备 Android 开发环境的 SDK 和 NDK 之外,下载 Kivy 应用程序的所有要求也需要花费大量时间。
该命令完成后,将在主应用程序文件夹的根目录下的bin
文件夹中找到 APK。这是项目的层次结构。
app
bin
APK FILE
main.py
buildozer.init
下图显示了运行 Android 应用程序后的结果。
现在我们能够从 Kivy 应用程序中创建一个 Android 应用程序。下一节讨论 Python 中的 Levenshtein 距离实现。
Levenshtein 距离的 Python 实现
在之前一篇名为实现单词自动完成和自动更正的 Levenshtein 距离的文章中,Levenshtein 距离是用 Python 实现的。下面列出了代码,其中一个名为levenshteinDistanceMatrix()
的函数接受两个表示单词的参数。该函数返回这两个单词之间的距离。距离越短,单词越相似。
def levenshteinDistanceMatrix(token1, token2):
distances = numpy.zeros((len(token1) + 1, len(token2) + 1))
for t1 in range(len(token1) + 1):
distances[t1][0] = t1
for t2 in range(len(token2) + 1):
distances[0][t2] = t2
a = 0
b = 0
c = 0
for t1 in range(1, len(token1) + 1):
for t2 in range(1, len(token2) + 1):
if (token1[t1-1] == token2[t2-1]):
distances[t1][t2] = distances[t1 - 1][t2 - 1]
else:
a = distances[t1][t2 - 1]
b = distances[t1 - 1][t2]
c = distances[t1 - 1][t2 - 1]
if (a <= b and a <= c):
distances[t1][t2] = a + 1
elif (b <= a and b <= c):
distances[t1][t2] = b + 1
else:
distances[t1][t2] = c + 1
return distances[len(token1)][len(token2)]
下面是一个计算两个单词cane
和man
之间距离的例子。
print(levenshteinDistanceMatrix("cane", "man"))
2.0
上一篇教程还构建了一个名为calcDictDistance
的函数,它接受两个参数:word
和numWords
。该函数通过调用levenshteinDistanceMatrix()
函数来计算word
和字典之间的距离,并返回最近单词的列表。列表中的字数根据numWord
参数中的值设置。例如,calcDictDistance("conr", 4)
只返回与单词conr
最近的 4 个单词。
下面是calcDictDistance()
函数的实现。使用的字典只是一个名为1-1000.txt
的包含 1000 个单词的文本文件。该文件可在此链接下载。
def calcDictDistance(word, numWords):
file = open('1-1000.txt', 'r')
lines = file.readlines()
file.close()
dictWordDist = []
wordIdx = 0
for line in lines:
wordDistance = levenshteinDistanceMatrix(word, line.strip())
if wordDistance >= 10:
wordDistance = 9
dictWordDist.append(str(int(wordDistance)) + "-" + line.strip())
wordIdx = wordIdx + 1
closestWords = []
wordDetails = []
currWordDist = 0
dictWordDist.sort()
for i in range(numWords):
currWordDist = dictWordDist[i]
wordDetails = currWordDist.split("-")
closestWords.append(wordDetails[1])
return closestWords
下面是一个使用这个函数返回与单词conr
最近的 4 个单词的例子。
print(calcDictDistance("conr", 4))
['bone', 'can', 'car', 'cent']
到目前为止,我们已经介绍了如何创建一个 Kivy 应用程序,如何构建一个 Android 应用程序并导出 APK,以及如何用 Python 实现 Levenshtein 距离。在下一节中,我们将使用所有这些来创建一个向用户推荐文本的 Android 应用程序。
打造安卓文字推荐 App
首先,我们需要通过放置以下 Kivy 小部件来创建应用程序的 GUI:
- 用户可以在其中输入单词的文本字段。
- 显示推荐的按钮(仅使用 3 个按钮来显示与用户输入最接近的 3 个单词)
下面列出了用这 4 个小部件创建屏幕的 Kivy 代码。
import kivy.app
import kivy.uix.boxlayout
import kivy.uix.textinput
import kivy.uix.button
class KivyApp(kivy.app.App):
def build(self):
mainBoxLayout = kivy.uix.boxlayout.BoxLayout(orientation="vertical")
self.textInput = kivy.uix.textinput.TextInput(hint_text="Enter a word")
mainBoxLayout.add_widget(self.textInput)
buttonsBoxLayout = kivy.uix.boxlayout.BoxLayout(orientation="horizontal")
self.btn1 = kivy.uix.button.Button(text="Word 1")
self.btn2 = kivy.uix.button.Button(text="Word 2")
self.btn3 = kivy.uix.button.Button(text="Word 3")
buttonsBoxLayout.add_widget(self.btn1)
buttonsBoxLayout.add_widget(self.btn2)
buttonsBoxLayout.add_widget(self.btn3)
mainBoxLayout.add_widget(buttonsBoxLayout)
return mainBoxLayout
app = KivyApp()
app.run()
运行应用程序后,我们可以在下图中看到它的屏幕。
我们将遵循的策略是将一个侦听器绑定到TextInput
小部件,以便为文本中的每次更改调用一个回调方法。计算TextInput
中的当前值和字典中所有单词之间的距离。最接近的 3 个单词的文本将反映在 3 个按钮上。一旦用户点击一个按钮,它的文本就会自动移动到TextInput
中。
为了将监听器附加到TextInput
小部件,在build()
方法的末尾添加了下面一行。这意味着对于TextInput
小部件的text
属性的每一次改变,都将调用calcDictDistance()
方法。
self.textInput.bind(text=self.calcDictDistance)
calcDictDistance()
方法接受 3 个参数:
self
:KivyApp
类的实例。
widget
:小部件TextInput
的实例。
text
:当前TextInput
小工具中的文本值。
为了适应这些变化,calcDictDistance()
编辑如下。请注意,levenshteinDistanceMatrix()
方法将保持不变。
def calcDictDistance(self, widget, word):
numWords=3
file = open('1-1000.txt', 'r')
lines = file.readlines()
file.close()
dictWordDist = []
wordIdx = 0
for line in lines:
wordDistance = levenshteinDistanceMatrix(word, line.strip())
if wordDistance >= 10:
wordDistance = 9
dictWordDist.append(str(int(wordDistance)) + "-" + line.strip())
wordIdx = wordIdx + 1
wordDetails = []
currWordDist = 0
dictWordDist.sort()
self.btn1.text = dictWordDist[0].split("-")[1]
self.btn2.text = dictWordDist[1].split("-")[1]
self.btn3.text = dictWordDist[2].split("-")[1]
在calcDictDistance()
方法的末尾,下面 3 行负责显示 3 个按钮上最接近的 3 个单词。
self.btn1.text = dictWordDist[0].split("-")[1]
self.btn2.text = dictWordDist[1].split("-")[1]
self.btn3.text = dictWordDist[2].split("-")[1]
下图显示了建议如何显示在 3 个按钮上。
到目前为止,点击按钮没有任何作用。为了将用户单击的按钮中的文本移动到TextInput
小部件,下面 3 行将被添加到build()
方法的末尾。简单来说,每按一次按钮(即点击),就会调用回调方法selectWord()
。
self.btn1.bind(on_press=self.selectWord)
self.btn2.bind(on_press=self.selectWord)
self.btn3.bind(on_press=self.selectWord)
下面列出了selectWord()
方法的实现。它将TextInput
中的text
属性设置为被点击的Button
小部件的相同属性。
def selectWord(self, widget):
self.textInput.text = widget.text
既然我们已经讨论了所有的代码,这里是应用程序的完整实现。
import kivy.app
import kivy.uix.boxlayout
import kivy.uix.textinput
import kivy.uix.button
import numpy
def levenshteinDistanceMatrix(token1, token2):
distances = numpy.zeros((len(token1) + 1, len(token2) + 1))
for t1 in range(len(token1) + 1):
distances[t1][0] = t1
for t2 in range(len(token2) + 1):
distances[0][t2] = t2
a = 0
b = 0
c = 0
for t1 in range(1, len(token1) + 1):
for t2 in range(1, len(token2) + 1):
if (token1[t1-1] == token2[t2-1]):
distances[t1][t2] = distances[t1 - 1][t2 - 1]
else:
a = distances[t1][t2 - 1]
b = distances[t1 - 1][t2]
c = distances[t1 - 1][t2 - 1]
if (a <= b and a <= c):
distances[t1][t2] = a + 1
elif (b <= a and b <= c):
distances[t1][t2] = b + 1
else:
distances[t1][t2] = c + 1
return distances[len(token1)][len(token2)]
class KivyApp(kivy.app.App):
def calcDictDistance(self, widget, word):
numWords=3
file = open('1-1000.txt', 'r')
lines = file.readlines()
file.close()
dictWordDist = []
wordIdx = 0
for line in lines:
wordDistance = levenshteinDistanceMatrix(word, line.strip())
if wordDistance >= 10:
wordDistance = 9
dictWordDist.append(str(int(wordDistance)) + "-" + line.strip())
wordIdx = wordIdx + 1
wordDetails = []
currWordDist = 0
dictWordDist.sort()
self.btn1.text = dictWordDist[0].split("-")[1]
self.btn2.text = dictWordDist[1].split("-")[1]
self.btn3.text = dictWordDist[2].split("-")[1]
def selectWord(self, widget):
self.textInput.text = widget.text
def build(self):
mainBoxLayout = kivy.uix.boxlayout.BoxLayout(orientation="vertical")
self.textInput = kivy.uix.textinput.TextInput(hint_text="Enter a word")
mainBoxLayout.add_widget(self.textInput)
buttonsBoxLayout = kivy.uix.boxlayout.BoxLayout(orientation="horizontal")
self.btn1 = kivy.uix.button.Button(text="Word 1")
self.btn2 = kivy.uix.button.Button(text="Word 2")
self.btn3 = kivy.uix.button.Button(text="Word 3")
buttonsBoxLayout.add_widget(self.btn1)
buttonsBoxLayout.add_widget(self.btn2)
buttonsBoxLayout.add_widget(self.btn3)
mainBoxLayout.add_widget(buttonsBoxLayout)
self.textInput.bind(text=self.calcDictDistance)
self.btn1.bind(on_press=self.selectWord)
self.btn2.bind(on_press=self.selectWord)
self.btn3.bind(on_press=self.selectWord)
return mainBoxLayout
app = KivyApp()
app.run()
在确保桌面应用程序正常工作后,下一步是构建 Android 应用程序。在此之前,确保将numpy
列为buildozer.spec
文件的requirements
属性中的一个值。该属性应该如下所示:
requirements = kivy, numpy
另一个重要的注意事项是,该应用程序使用名为1-1000.txt
的文本文件,因此它必须打包到 Android 应用程序中。我们如何做到这一点?
在buildozer.spec
文件中,有一个名为source.include_exts
的属性,它指定了将被打包到 APK 文件中的文件扩展名。该属性的默认值如下:
source.include_exts = py,png,jpg,kv,atlas
如果我们需要包含.txt
扩展名,只需将其添加到列表中,这样新值如下所示:
source.include_exts = py,png,jpg,kv,atlas,txt
现在,我们可以使用 buildozer 通过以下命令导出 APK 文件:
buildozer android debug
APK 导出后,只需将其安装在 Android 设备上。下图显示了安装后的应用程序屏幕。
结论
本教程使用开源 Python 框架 Kivy 来构建一个 Android 应用程序,该应用程序使用 Levenshtein 距离向用户推荐文本。
在本教程中,我们首先准备了构建 Kivy 应用程序的环境,然后讨论了使用两个项目从 Kivy 应用程序构建 Android 应用程序的步骤:python-for-android
和buildozer
。
在 NumPy 中实现了一个计算 Levenshtein 距离的函数,它接受代表两个文本单词的两个参数,并返回它们之间的距离。
最后,使用 Kivy 我们创建了一个 Android 应用程序,允许用户键入一个单词,然后 3 个推荐显示在 3 个按钮上,用户可以自动完成单词。