深度学习魔法:小企业类型
企业情报很难,NAICS 代码很糟糕。一些公司按行业购买公司列表,以保持他们的潜在客户数据库最新(例如 D+B )。购买清单的主要问题是它们马上就过时了,而且它们不能很好地捕捉小公司的数据。我们如何使用机器学习来猜测一个小企业仅根据他们的企业名称做什么?
大企业通常在公司简介上标注得很好。我们可以从免费来源或商业级 API 获得这些,如彭博和路透社。甚至的小型公司也有现成的数据。但是小企业没有被很好的记录。更糟糕的是,大多数企业都是小企业。让我们把注意力集中在小企业上,看看我们如何从它们的名字中猜出它们是做什么的。
作为一个人,你自动完成这项任务。你看到一家企业的名称“渥太华街角超市”,并根据你的经验得出结论,这是一家便利店。如何从公司名称转换成公司标签?我们认为一个很好的方法是在 Google Places API 中读取 类型。事实证明,这是一个很好的数据源,但还不够好。对于谷歌没有标签的 80%+的小企业,我们能做什么?
我们决定发布一个深度学习的 keras 模型来解决这个问题。DNN 模型在超过 500,000 个公司名称及其相关类别上进行了训练。您想要分类的公司名称被编码到一个 word2vec (gensim)单词嵌入模型中,该模型对大约 250 万个公司名称进行了训练。以下是我们训练模型识别的所有 127 个类别的列表:
我们做了大量的设计空间探索研究,以找出在我们的神经网络模型中使用的正确的层数和每层神经元数。
Trying various model hyperparameters in a shallow search yielded a 1024-neuron wide, 3-layer deep (input, hidden, output) DNN. The model we picked corresponds to the top pink line.
The model scored 60% on test data it never saw before. That’s pretty good for a 127 class classification problem.
原来宽浅才是这个模式要走的路。以下是一个示例数据集的结果:
以下是你如何得到这款机型的方法( GitHub link ):
git clone [https://github.com/dcshapiro/smallCompanyType.git](https://github.com/dcshapiro/smallCompanyType.git)
cd smallCompanyType/
pip3 install h5py
pip3 install keras --upgrade
pip3 install tensorflow --upgrade
python3 setup.py install
以下是您使用它的方法:
cd test
python3 test_text.py
python3 test_pandas.py
有趣的是,这里有一个视频展示了在 DigitalOcean 机器学习 droplet 上的成功安装。就是我之前贴的几篇文章里提到的一键安装镜像。
The smallCompanyType package installs cleanly in a new VM. It depends on the latest sklearn, h5py, keras, numpy, and tensorflow. The tests use glob, pandas, and some more fun stuff.
你能把这个模型用于商业目的吗?是的。没有性能保证,但请继续。尝试一下,让我知道你的想法。
试用拍手工具。轻点那个。关注我们的媒体。分享这篇文章的链接,或者 GitHub repo 的链接。去吧。
编码快乐!
-丹尼尔
丹尼尔@lemay.ai ←打个招呼。
LEMAY . AI
1(855)LEMAY-AI
您可能喜欢的其他文章:
深度学习和医疗诊断
越来越多的领域正在将机器学习应用于基于医学成像的诊断。
在过去的几个月里,已经有许多研究发现宣布,声称深度学习已经被应用于某个特定的诊断领域,并且经常是立即胜过医生。
我最初开始写这篇博文是为了跟踪他们——我打算以草稿的形式发表这篇博文,并希望定期更新。
医学影像诊断中的深度学习试图做什么?
在深入研究具体结果之前,我想强调一下,下面的方法(到目前为止)具有相同的共同模式。我试着用下面一句话来概括:
通过机器学习的诊断在病症可以被简化为对生理数据的 分类任务 时起作用,在这些领域中,我们当前依赖于临床医生能够视觉上 识别指示病症的存在或类型的模式
把它分解成细节:
- 分类。结果是医学诊断的领域,可以简化为一个分类问题:给定一些数据,诊断可以简化为将该数据映射到 N 个不同结果之一的问题。在某些情况下,N = 2:任务仅仅是识别数据(例如,x 射线)是否显示条件。请注意,还有其他问题(如图像分割)可以用深度学习来解决,但我还没有看到它们被单独使用用于诊断*(而不是,比如说,仅用于分析)。***
- 生理数据。下面的结果倾向于使用医学成像数据或来自其他类型传感器的数据。这些领域结果的激增在很大程度上归因于数据集的创建(例如这些数据集),这些数据集远远大于以前可用的数据集。注释数据集(例如,标记 x 射线是否包含肿瘤)的一种常见方法是让一组临床医生给出他们的意见并核对响应。
- 我们依靠视觉识别模式。自动诊断系统的替代方案是让专业临床医生查看您的数据(也许与一些同行专家讨论一下)来确定结果。这一点抓住了为什么深度学习应该在这一领域取得成功:深度学习自动化了在这种“非结构化”数据中提取模式和学习关系的整个过程。还有很多深度学习的非医疗应用(如人脸识别)也有类似的需求;正因为如此,技术已经相当成熟。事实上,甚至训练有素的医学图像模型现在也被开源。
对于该领域的优秀评论,请查看我在本文底部的参考资料部分添加的评论论文和博客文章。
这种方法而不是应该在哪里起作用?
*上面的模式让我们对这种方法*目前不适用的领域有了一些了解。就像这篇文章提到的一样,深度学习并没有告诉我们应该如何治疗患者,或者在治疗时他们会有多好。然而,与上述各点特别相关的是,有些领域:
- ****不是分类问题。如果我们对疾病不够了解,我们就无法创建数据来训练任何算法。例如,有些情况没有很好理解的进展,可以列举成一组阶段。在这些情况下,建立一个可靠的模型来告诉我们患者处于哪个进展阶段将是非常具有挑战性的——因为我们不知道应该是哪个阶段。
- ****缺乏(或有主观)数据。如果很少或没有数据,我们就无法训练模型。诚然,这种情况正在开始改变——有深度学习实验证明从极小的数据集中学习。如果有数据,但其中的数据和/或模式是主观的(例如,痛苦或压力的瞬间体验),那么我认为下面的方法需要重新设想。
- 不依赖医疗器械。类似地,无法通过将患者连接到某种机器并收集单个数据“样本”来得出诊断的领域(例如,需要长期跟踪或通过排除进行诊断)。这可能是因为(a)我们还没有开发出一种检测疾病的方法——因此,如上所述,需要更多的基础研究,或者(b)我们还没有开发出可行的产品来进行长期、非侵入性的监测,以收集支持机器学习的数据。
医疗条件列表
我添加到这个列表的标准是:(a)一个数据集已经发布,(b)研究已经发表,©一个公司或研究小组已经写了关于进展中的工作,或者(d)有描述解决问题的博客文章。我已经按字母顺序把条件分类了。
我错过了什么吗?你可以在推特上 @我,我来加。
痴呆症
…”是一种慢性神经退行性疾病,通常开始缓慢,并随着时间的推移而恶化伦敦的研究人员发表了一篇论文,报告使用来自ADNI的数据来训练一个具有单一卷积层的三层神经网络,该网络可以预测核磁共振扫描是否是一个健康的大脑,一个有轻度认知障碍的大脑和一个有老年痴呆症的大脑。
心律不齐
…”是一组心跳不规则的情况斯坦福大学的研究人员发表了一篇论文,报告称他们开发的 34 层卷积神经网络“在从单导联可穿戴监护仪记录的心电图中检测各种心律失常方面,超过了委员会认证的心脏病专家的性能”(项目页面,博客文章)。
孤独症
“是一种以社交互动受损为特征的神经发育障碍”一组研究人员发表了一篇论文,报告称“一种主要使用来自 6 个月和 12 个月大的大脑 MRI 的表面积信息的深度学习算法预测了自闭症高家族风险儿童的 24 个月自闭症诊断”(通过 @datarequena 在推特上)。
乳腺癌
…“是一种从乳腺组织发展而来的癌症。DeepMind Health 发布了一篇博客帖子 t,其中他们宣布他们已经与英国癌症研究所合作,对来自 7500 名女性的匿名乳房 x 光片进行分析并应用机器学习。
牙洞
…”是由于细菌产生的酸而导致的牙齿损坏 ParallelDots 的研究人员已经发表了一篇论文报道了一个 100+层的卷积网络对牙齿 x 光片进行像素级的二进制分类(有龋/无龋)。
糖尿病视网膜病
…”是一种因糖尿病导致视网膜受损的医学状况。两年多前,有一场 kaggle 竞赛,试图将眼睛的图像分为 5 类(从无糖尿病视网膜病变,到轻度,中度,重度,以及增生性)。获胜的解决方案使用了稀疏卷积网络和随机森林的组合,从一对图像(左眼和右眼)对结果进行预测。
革兰氏染色
…是否有一种染色的方法用于区分和分类细菌种类为两大类这是一种实验室技术,在怀疑感染时对体液(如血液)进行检测。研究人员发表了一篇论文(本文引用)描述了使用卷积神经网络将显微镜图像分类为革兰氏阳性、革兰氏阴性和背景(无细胞)。在论文中,他们描述他们没有从零开始培养一个 CNN 他们为这个任务微调了 Inception v3 。
肺癌
…”是一种以肺组织中不受控制的细胞生长为特征的恶性肿瘤这个 2017 kaggle 竞赛包括一组 CT 扫描的数据,目标是预测肺癌的可能性。这里有一些有趣的挑战,包括数据是三维的——获奖解决方案的文章描述了一些有趣的解决方法。这篇博客文章从临床的角度概述了这个竞赛的一些局限性。另外,Enlitic 似乎也在研究一种肺癌筛查解决方案。
甲真菌病
…”是指甲的真菌感染。正如这条推文所指出的,韩国的研究人员发表了一篇论文,报告称使用 CNN(VGG-19,ResNet-152)创建了一个训练数据集(例如,从临床照片中提取手和脚的图像)并将指甲分为六类(如下所示:(甲癣、指甲营养不良、甲松脱、黑甲、正常和其他),以实现“使用深度学习对甲癣的诊断准确性,优于大多数皮肤病的诊断准确性
肺炎
“是肺部的一种炎症状态,主要影响称为肺泡的小气囊。”斯坦福大学的研究人员发表了一篇论文,报告称他们开发的 121 层卷积神经网络“可以在超过执业放射科医生的水平上从胸部 x 光片中检测肺炎”(项目页面)。
皮肤癌
…“是由于有能力侵入或扩散到身体其他部位的异常细胞的发展。”斯坦福大学的研究人员发表了一篇论文,报道了使用“皮肤科医生标记的 129,450 张临床图像的数据集,包括 3,374 张皮肤镜图像”,对 Inception v3 进行微调,以分类 757 种疾病类别通过不同的预测任务检查结果,准确率似乎与临床医生的分数相当。
…我相信这个名单会越来越长。
参考资料和资源
**[## [1702.05747]医学图像分析中的深度学习调查
摘要:深度学习算法,特别是卷积网络,已经迅速成为一种选择方法…
arxiv.org](https://arxiv.org/abs/1702.05747)** ** [## 加速医学成像领域的深度学习
当前方法的概述,可公开获得的数据集,领域的方向,和机会…
medium.com](https://medium.com/the-mission/up-to-speed-on-deep-learning-in-medical-imaging-7ff1e91f6d71) [## 医学成像中的深度学习:概述
机器学习(ML)被定义为一组自动检测数据中模式的方法,然后利用这些模式进行学习
www.ncbi.nlm.nih.gov](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5447633/) [## 医学图像分析中的深度学习——science direct 综述
深度学习算法,特别是卷积网络,已经迅速成为一种方法学的选择…
www.sciencedirect.com](http://www.sciencedirect.com/science/article/pii/S1361841517301135) [## albarquni/医学应用深度学习
面向医学应用的深度学习-医学图像分析的深度学习论文
github.com](https://github.com/albarqouni/Deep-Learning-for-Medical-Applications)**
深度学习遇上物理学:受限玻尔兹曼机器第一部分
受限玻尔兹曼机背后的理论——重组系统的有力工具
本教程是关于受限玻尔兹曼机器的两部分系列的第一部分,这是一种用于协作过滤的强大深度学习架构。在这一部分,我将介绍受限玻尔兹曼机背后的理论。第二部分由一个模型的实际实现的逐步指导组成,该模型可以预测用户是否喜欢一部电影。
实用的部分现在可以在这里得到。
目录:
- 0。简介
- 1。受限玻尔兹曼机器
- 1.1 架构
- 1.2 基于能量的模型
- 1.3 概率模型
- 2。使用受限玻尔兹曼机器的协同过滤
- 2.1 识别数据中的潜在因素
- 2.2 利用潜在因素进行预测
- 3。培训
- 3.1 吉布斯采样
- 3.2 对比差异
如果你喜欢这篇文章,想分享你的想法,问问题或保持联系,请随时通过 LinkedIn 与我联系。
0.介绍
受限玻尔兹曼机器(RBM)是属于所谓的基于能量的模型的神经网络。这种类型的神经网络可能不像前馈或卷积神经网络那样为本文的读者所熟悉。然而,这种神经网络近年来在 Netflix 奖的背景下大受欢迎,RBM 在协同过滤方面取得了最先进的表现,并击败了大多数竞争对手。
1.受限玻尔兹曼机器
1.1 架构
在我看来,RBM 是所有神经网络中最简单的架构之一。如图 1 所示,RBM 由一个输入/可见层(v1,…,v6)、一个隐藏层(h1,h2)和相应的偏置向量 Bias a 和 Bias b 组成。输出层的缺失是明显的。但是正如后面可以看到的,输出层是不需要的,因为预测的方式与常规前馈神经网络不同。
1.2 基于能源的模型
能量是一个可能一开始就不会与深度学习联系在一起的术语。相反,能量是物理学的一个定量属性。重力能量描述了一个有质量的物体由于重力而相对于另一个大质量物体的势能。然而,一些深度学习架构使用能量的概念作为衡量模型质量的指标。
Fig. 2. Gravitational energy of two body masses.
深度学习模型的一个目的是编码变量之间的依赖关系。通过将标量能量与变量的每个配置相关联来捕获依赖性,这用作兼容性的度量。高能量意味着差的兼容性。基于能量的模型总是试图最小化预定义的能量函数。RBMs 的能量函数定义为:
Eq. 1. Energy function of a Restricted Boltzmann Machine
可以注意到,能量函数的值取决于可见/输入状态、隐藏状态、权重和偏差的配置。RBM 的训练在于寻找给定输入值的参数,使得能量达到最小。
1.3 一个概率模型
受限玻尔兹曼机器是概率性的。与分配离散值相反,该模型分配概率。在每个时间点,RBM 都处于特定的状态。状态是指可见层和隐藏层的神经元的值 v 和 h 。可以观察到 v 和 h 的某一状态的概率由以下联合分布给出:
Eq. 2. Joint Distribution for v and h.
这里 Z 被称为“配分函数”,它是所有可能的可见和隐藏向量对的总和。
这是受限玻尔兹曼机器第二次与物理学相遇的地方。这种联合分布在物理学中被称为玻尔兹曼分布,它给出了在能量为 E 的状态下可以观察到粒子的概率。正如在物理学中我们分配一个概率来观察一个状态 v 和 **h,**这取决于模型的总能量。不幸的是,由于配分函数 Z 中 v 和 h 的大量可能组合,计算联合概率非常困难。给定状态 v 时状态 h 的条件概率以及给定状态 h 时状态 v 的条件概率的计算要容易得多:
Eq. 3. Conditional probabilities for h and v.
应该事先注意到(在实际例子上证明这个事实之前)RBM 中的每个神经元只能以 0 或 1 的二进制状态存在。最有趣的因素是隐藏或可见层神经元处于状态 1 的概率——因此被激活。给定输入向量 v ,单个隐藏神经元 j 被激活的概率为:
Eq. 4. Conditional probability for one hidden neuron, given v.
这是 Sigmoid 函数。这个等式是通过将贝叶斯规则应用于等式 3 并进行大量扩展而得到的,这里不做介绍。
类似地,可见神经元 i 的二进制状态被设置为 1 的概率为:
Eq. 5. Conditional probability for one visible neuron, given h.
2。使用受限玻尔兹曼机器的协同过滤
2。1 识别数据中的潜在因素
让我们假设一些人被要求对一组电影进行 1-5 颗星的评分。在经典因素分析中,每部电影都可以用一组潜在因素来解释。例如,像《哈利·波特》、《速度与激情》、这样的电影可能与奇幻和动作的潜在因素有很强的关联。另一方面,喜欢玩具总动员和瓦力的用户可能会对潜在的皮克斯因素产生强烈的联想。成果管理制被用来分析和找出这些潜在的因素。在训练阶段的一些时期之后,神经网络已经多次看到每个用户的训练数据集中的所有评级。此时,该模型应该已经基于用户偏好和所有用户的相应协作电影品味学习了潜在的隐藏因素。
隐藏因素的分析以二进制方式进行。用户不是给模型用户连续的评级(例如 1-5 颗星),而是简单地告诉他们是否喜欢(评级 1)特定的电影(评级 0)。二进制等级值表示输入/可见图层的输入。给定输入,人民币然后试图发现数据中的潜在因素,可以解释电影的选择。每个隐藏的神经元代表一个潜在的因素。给定一个由成千上万部电影组成的大数据集,很确定用户只观看和评价了其中的一小部分。有必要给尚未分级的电影也赋予一个值,例如-1.0,以便网络可以在训练时间期间识别未分级的电影,并忽略与它们相关联的权重。
让我们考虑下面的例子,其中用户喜欢指环王和哈利波特,但是不喜欢黑客帝国、搏击俱乐部和泰坦尼克号。《霍比特人》还没有被看过,所以它的评级是-1。给定这些输入,玻尔兹曼机器可以识别对应于电影类型的三个隐藏因素戏剧、幻想和科幻。
Fig. 3. Identification of latent factors.
给定电影,人民币分配一个概率 p(h|v) (等式。4) 为每个隐藏神经元。神经元的最终二进制值是通过使用概率 p 从伯努利分布中采样获得的。
在这个例子中,只有代表流派幻想的隐藏神经元被激活。给定电影分级,受限玻尔兹曼机器正确地识别出用户最喜欢幻想。
2.2 利用潜在因素进行预测
在训练阶段之后,目标是预测尚未看过的电影的二进制评级。给定特定用户的训练数据,网络能够基于该用户的偏好识别潜在因素。由于潜在因素由隐藏的神经元表示,我们可以使用 p(v|h) (等式。5)并从伯努利分布中取样以找出哪些可见神经元现在变得活跃。
Fig. 4. Using hidden neurons for the inference.
图 4 示出了在使用隐藏神经元值进行推断之后的新评级。电视网的确将《T32》奇幻片《T33》确定为首选电影类型,并将《霍比特人》评为用户喜欢的电影。
总之,从训练到预测阶段的过程如下:
- 根据所有用户的数据训练网络
- 在推理期间,获取特定用户的训练数据
- 使用这些数据来获得隐藏神经元的激活
- 使用隐藏神经元值获得输入神经元的激活
- 输入神经元的新值显示了用户对尚未看过的电影的评价
3.培养
受限玻尔兹曼机器的训练不同于通过随机梯度下降的常规神经网络的训练。RBM 训练程序的偏差不在这里讨论。相反,我将给出两个主要训练步骤的简要概述,并建议本文的读者查阅关于受限玻尔兹曼机器的原始论文。
3.1 吉布斯采样
训练的第一部分叫做吉布斯采样。给定输入向量 v ,我们使用 p(h|v) (等式 4)来预测隐藏值 h. 已知隐藏值,我们使用 p(v|h) (等式 5)来预测新的输入值 v 。这个过程重复 k 次。在 k 次迭代之后,我们获得了另一个输入向量 v_k ,它是从原始输入值 v_0 重新创建的。
3.2 对比分歧
权重矩阵的更新发生在对比发散步骤期间。向量 v_0 和 v_k 用于计算隐藏值 h_0 和 h_k 的激活概率(等式 4)。这些概率的外积与输入向量 v_0 和 v_k 之间的差导致更新矩阵:
Eq. 6. Update matrix.
使用更新矩阵,可以用梯度**上升、**计算新的权重,由下式给出:
Eq. 7. Update rule for the weights.
参考
- https://www.cs.toronto.edu/~rsalakhu/papers/rbmcf.pdf
- 【https://www.cs.toronto.edu/~hinton/absps/guideTR.pdf
深度学习遇上物理学:受限玻尔兹曼机器第二部分
建造你自己的受限玻尔兹曼机器
本文是**的续篇,第一部分 在这里我介绍了受限玻尔兹曼机背后的理论。该第二部分包括通过受限玻尔兹曼机器的实际实现的逐步指导,该受限玻尔兹曼机器用作推荐系统,并且可以基于用户的喜好来预测用户是否喜欢电影。**
(1)在本文中,我不会涉及我所做的步骤背后的理论,我将只解释实际的部分。请务必通过复习本系列第一部分来更新您的理论知识。
(2)我在本文中展示的代码来自我在 GitHub 上的项目资源库。因为我只关注模型的实现,所以我跳过了一些预处理步骤,比如将数据分成训练/测试集以及构建输入管道。可以在存储库中检查这些步骤。
先决条件
- Python 3.6
- 张量流 1.5 或更高
- 1.11 或更高版本
资料组
我们使用的是 MovieLens 1M 数据集。该集合包含由大约 6000 个用户制作的大约 4000 部电影的 100 万个评级。该模型将在这个数据集上进行训练,并将学习预测用户是否喜欢随机电影。数据集需要一些重新处理步骤。因为通常的受限玻尔兹曼机器只接受二进制值,所以有必要给等级 1-2 一个 0 值——因此用户不喜欢这部电影。相应地,等级 3-5 的值为 1。尚未分级的电影的值为-1。
在下一步中,转换后的原始数据被分成两个独立的训练和测试数据集。这是必要的两个完全相同的用户在两个数据集,但不同的电影评级。图 1 示出了将原始数据集划分成训练和测试数据的简单示例。在此示例中,前 5 个评级被放入训练集,而其余的用-1 屏蔽,表示尚未评级。相应地,测试集接收剩余的 5 个等级。
在训练时间期间,受限的 Boltzmann 机器学习每个用户的前 5 部电影评级,而在推断时间期间,该模型试图预测后 5 部电影的评级。然后,将这些预测的评级与放入测试集的实际评级进行比较。
两个数据集都以二进制 TFRecords 格式保存,这使得数据输入管道非常高效。
Fig. 1. Partitioning of the data into training and test datasets.
模型架构
该模型以面向对象的方式实现。受限玻尔兹曼机器是一个具有所有必要操作的类,如训练、丢失、精确、推理等。在里面。一些助手功能被外包到一个单独的脚本中。
构造函数为权重和偏差设置内核初始化器。在下一步中,网络中的所有权重和偏差都被初始化。权重呈正态分布,平均值为 0.0,方差为 0.02,而偏差在开始时都设置为 0.0。可以注意到,网络仅由一个隐藏层组成。结果,只需要一个权重矩阵。
****采样隐藏的状态
Eq. 1. Probability that a hidden neuron is activated.
给定二进制输入 v ,下面的函数_sample_h(self)
获得隐藏神经元被激活的概率(等式 1)。这是通过将输入 v 乘以权重矩阵、添加偏置并应用 s 形激活来实现的。获得的概率用于从伯努利分布中采样。采样值 1.0 或 0.0 是隐藏神经元的状态。
可视状态的采样
Eq. 2. Probability that a visible neuron is activated.
给定隐藏状态 h ,我们可以使用这些来获得可见神经元活动的概率(等式 2)以及相应的状态值。这是在_sample_v(self)
中实现的。
吉布斯采样
Fig.2. Gibbs Sampling.
训练的第一部分是一个叫做吉布斯采样的操作。简而言之,我们取一个输入向量 v_0 并用它来预测隐藏状态 h_0 的值。另一方面,隐藏状态用于预测新的输入状态 v 。这个过程重复 k 次。这个过程如图 2 所示。
**吉布斯采样在下面截取的代码中实现。迭代发生在 while 循环体中。体内重要的一步是Vk=tf.where(tf.less(V,0),V,Vk)
。该操作确保在每次迭代中,对于每个 v_k ,为-1 的 v 中的评级(意味着还没有看过的电影)保持为-1。在 k 迭代之后,我们得到 v_k 和相应的概率 **p(h_k|v_k)。连同 v_0 和 h_0 这些值可用于在下一个训练步骤中计算梯度矩阵。
计算梯度
在前一步骤中获得的值可以用于计算梯度矩阵和梯度向量。根据等式计算梯度。3 是直截了当的。请注意,这个等式中的符号 a 和 b 分别代表隐藏的可见偏差,与我在代码中使用的不同符号形成对比。
唯一棘手的是 TensorFlow 1.5 不支持外积。但是这个问题可以通过临时整形和应用通常的点乘来解决。
Eq. 3. Computation of gradients for the weights and biases.
注意,梯度的计算发生在 while 循环中。这仅仅是因为培训是小批量进行的。这意味着循环为小批量中的每个数据样本计算梯度,并将它们添加到先前定义的梯度占位符中。最后,梯度的总和除以小批量的大小。
更新步骤
在计算梯度之后,所有的权重和偏差可以根据等式通过梯度上升来更新。4.对于这个过程,我们必须在_update_parameter(self).
中创建一个赋值操作
Eq. 4. Update step of the parameters through gradient ascent.
**整个训练操作在名称范围“操作”下用optimize(self)
方法计算。在此之下,执行更复杂的训练精度操作。基本上,该操作从吉布斯采样 **期间获得的 v_k 中减去原始输入值 v_0 。减法只发生在 v_0 ≥ 0 时。在此之后,相减之和除以所有等级数≥ 0。准确度给出了训练期间正确预测的二进制电影分级的比率。
推理
在推理时间期间,方法inference(self)
接收输入 v. 该输入是特定用户的一个训练样本,用于激活隐藏的神经元(用户电影品味的潜在特征)。隐藏的神经元再次用于预测新的输入 v 。在最好的情况下,这种新的输入包括对已经存在的评级以及尚未评级的电影的评级的重新创建。
在 TensorFlow 会话之外,将做出的预测与相应的测试数据进行比较,以进行验证。
网络图
为了概述前面的步骤,这里是主网络图的定义和执行训练和推理步骤的会话的开始。
模型的性能
在训练过程中,我们可以在训练集和测试集上检查准确性的进展。准确度给出了正确预测的二进制电影分级的比率。可以看出,在 6 个时期之后,如果用户喜欢或不喜欢随机电影,该模型正确预测的时间为 78%。
epoch_nr: 0, batch: 50/188, acc_train: 0.721, acc_test: 0.709
epoch_nr: 1, batch: 50/188, acc_train: 0.767, acc_test: 0.764
epoch_nr: 2, batch: 50/188, acc_train: 0.772, acc_test: 0.773
epoch_nr: 3, batch: 50/188, acc_train: 0.767, acc_test: 0.725
epoch_nr: 4, batch: 50/188, acc_train: 0.768, acc_test: 0.717
epoch_nr: 5, batch: 50/188, acc_train: 0.772, acc_test: 0.769
epoch_nr: 6, batch: 50/188, acc_train: 0.774, acc_test: 0.771
epoch_nr: 7, batch: 50/188, acc_train: 0.779, acc_test: 0.780
参考
https://github . com/artem-opper Mann/Restricted-Boltzmann-Machine/blob/master/readme . MD
深度学习模型训练循环
用 Python、PyTorch 和 TorchVision 实现一个简单的神经网络训练循环。
几个月前,我开始探索 py torch——一个奇妙且易于使用的深度学习框架。在的上一篇文章中,我描述了如何使用 MovieLens 数据集实现一个简单的推荐系统。这一次,我想把重点放在对任何机器学习管道都至关重要的主题上——训练循环。
PyTorch 框架为您提供了构建机器学习模型的所有基本工具。它给你 CUDA 驱动的张量计算,优化器,神经网络层,等等。然而,要训练一个模型,你需要把所有这些东西组装成一个数据处理管道。
最近开发人员发布了 PyTorch 的 1.0 版本,已经有很多很棒的解决方案帮助你训练模型,而不需要钻研张量和层的基本操作。(在下一节中简要讨论)。然而,我相信每隔一段时间,大多数软件工程师都有一种“从零开始”实现东西的强烈愿望,以更好地理解底层过程,并获得不依赖于特定实现或高级库的技能。
在接下来的部分中,我将展示如何使用torch
和torchvision
Python 包实现一个简单但有用的训练循环。
TL;DR: 请跟随这个链接直接进入资源库,在那里你可以找到这篇文章中讨论的源代码。此外,这里有一个到笔记本的链接,它包含了所有的实现,以及帖子中没有包含的其他信息,以使其简洁。
现成的解决方案
正如前面提到的,有一些构建在框架之上的高级包装器,它们极大地简化了模型训练过程。按照复杂性增加的顺序,从最简单到非常复杂:
- Ignite—py torch 的官方高级接口
- torch sample——一个类似 Keras 的包装器,带有回调、增强和方便的实用程序
- Skorch —一个 scikit-learn 兼容的神经网络库
- fastai —一个强大的端到端解决方案,以高精度和计算速度训练各种复杂性的深度学习模型
高级库的主要好处是,不用编写定制的实用程序和包装程序来读取和准备数据,人们可以专注于数据探索过程本身——不需要在代码中寻找错误,辛勤工作的维护人员可以改进库,并在您有问题时随时提供帮助。不需要实现定制的数据扩充工具或训练参数调度,一切都已经在这里。
如果您正在开发生产就绪的代码,或者参加数据科学竞赛并需要搜索最佳模型,而不是坐在调试器前试图找出这个内存错误的来源,那么使用维护良好的库无疑是一个选择。如果您正在学习新的主题,并且希望更快地获得一些工作解决方案,而不是花费许多天(或几周)来编写 ResNets 层和编写 SGD 优化器,情况也是如此。
然而,如果你像我一样,那么有一天你会想测试你的知识,用更少的抽象层来构建一些东西。如果是这样,让我们进入下一部分,开始重新发明轮子!
核心实现
训练循环的最基本实现并不困难。 pytorch 包已经包含了允许实例化数据集访问器和迭代器的便利类。所以本质上,我们需要做一些如下面代码片段所示的事情。
我们可以停止对这一部分的讨论,节省一些时间。然而,通常我们需要的不仅仅是简单的损失计算和更新模型权重。首先,我们希望使用各种性能指标来跟踪进展。第二,初始设置的优化器参数应该在训练过程中被调整以改善收敛性。
一种简单的方法是修改循环代码,使其包含所有这些附加特性。唯一的问题是,随着时间的推移,我们可能会因为添加越来越多的技巧而失去实现的清晰性,引入回归错误,并最终得到杂乱无章的代码。我们如何在代码的简单性和可维护性以及训练过程的效率之间找到一个折衷?
附加物
答案是使用软件设计模式。观察者是面向对象语言中众所周知的设计模式。它允许将一个复杂的系统分解成更容易维护的片段。我们不试图将所有可能的特性封装到一个类或函数中,而是将调用委托给从属模块。每个模块负责对收到的通知做出适当的反应。如果消息是发给其他人的,它也可以忽略通知。
该模式有不同的名称,反映了实现的各种特性:观察器、事件/信号调度器、回调。在我们的例子中,我们使用回调,这种方法在 Keras 和(特别是) fastai 库中有所体现。 ignite 包的作者采用的解决方案有点不同,但本质上,它归结为相同的想法。看看下面这张图。它显示了我们改进的训练循环的图解组织。
每个彩色部分是委托给回调组的一系列方法调用。每个回调都有类似epoch_started
、batch_started
等方法,通常只实现其中的几个。例如,考虑损失度量计算回调。它不关心向后传播之前运行的方法,但是一旦收到batch_ended
通知,它就会计算一个批处理丢失。
下一个片段展示了这个想法的 Python 实现。
仅此而已,并不比原始版本复杂多少,对吗?它仍然干净简洁,但功能更多。现在训练算法的复杂度完全由委托调用决定。
回调示例
我们可以实现许多有用的回调(参见 keras.io 和 docs.fast.ai 获取灵感)。为了保持文章的简洁,我们将只描述其中的几个,并将其余的几个移到 Jupyter 笔记本中。
失败
当谈论机器学习模型训练时,首先想到的是损失函数。我们用它来指导优化过程,并希望看到它在培训过程中如何变化。因此,让我们实现一个回调来跟踪这个指标。
在每一批结束时,我们计算一个运行损失。计算可能看起来有点复杂,但主要目的是平滑损失曲线,否则会很颠簸。公式a*x + (1 — a)*y
是新旧值之间的一个线性插值。
Geometric interpretation of linear interpolation between vectors A and B
分母有助于我们计算开始时的偏差。查看这篇文章详细描述了平滑损失计算公式。
准确(性)
accuracy
指标可能是机器学习中最著名的指标之一。虽然在很多情况下它不能给你一个好的模型质量评估,但是它非常直观,易于理解和实现。
请注意,回调在每批结束时以及训练时期结束时接收通知。它迭代地计算精度度量,因为否则,我们将需要在整个训练时期将输出和目标保存在存储器中。
由于我们计算的这种迭代性质,我们需要批量计算大量样本。我们使用该值在该时期结束时调整我们的计算。实际上,我们正在使用下图所示的公式。
其中 b(i) 是迭代 i 时的批量大小,a(i) — 根据批量 b(i) , N 计算的准确度—样本总数。如最后一个公式所示,我们的代码计算出一个精确度的样本均值。查看这些有用的参考资料,了解有关迭代指标计算的更多信息:
参数调度程序
现在最有趣的事情来了。现代神经网络训练算法不使用固定的学习速率。最近的论文(一篇、两篇和三篇)显示了一种调整深度学习模型训练参数的受过教育的方法。其思想是使用循环调度器,在单个或几个训练时期调整模型的优化器参数幅度。此外,这些调度程序不仅会随着处理批次数量的增加而降低学习率,还会在一定数量的步骤内或周期性地增加学习率。
例如,考虑以下函数,它是经过缩放和移位的余弦函数:
Half-period of shifted and scaled cosine function
如果我们重复这个函数几次,使其周期加倍,我们将得到一个余弦退火调度程序,如下图所示。
Cosine annealing with restarts scheduler
将优化器的学习率乘以这个函数值,我们有效地获得了一个随机梯度,它允许我们从局部极小值中逃脱。下面的片段显示了如何实现余弦退火学习率。
还有一个更令人兴奋的调度器,叫做单周期策略。该时间表的想法是在整个训练过程中使用单周期的学习率递增-递减*,如下图所示。*
One-cycle policy scheduler
在训练过程的最开始,模型权重不是最优的,因此我们可以允许自己使用更大的更新步长(即,更高的学习率),而没有错过最优值的风险。经过几个训练时期后,权重变得越来越好,越来越适合我们的数据集,所以我们放慢了学习速度,更仔细地探索学习表面。
如果我们使用前面显示的类,单周期策略有一个非常简单的实现。我们只需要在余弦衰减之前添加一个线性段,如线27-30
所示。
最后一步是用回调接口包装调度程序。为了使这篇文章简洁易读,这里没有给出实现的例子。然而,你可以在前面提到的Jupyter 笔记本中找到完整的功能代码。
流记录器
我们想添加的最后一件事是一些日志记录,以查看我们的模型在训练过程中的表现如何。最简单的方法是将统计数据打印到标准输出流中。然而,你可以把它保存成 CSV 文件,甚至把它作为通知发送到你的手机。
好了,最后,我们准备开始使用我们的训练循环!
你最喜欢的数据集
现在,当试听准备就绪,是时候展示我们的训练循环是如何工作的了。为此,让我们选择无处不在的 MNIST 数据集。你甚至可以在几分钟内在 CPU 上轻松训练它。
数据集对于现代深度学习架构和算法来说非常简单。因此,我们可以使用相对较浅的架构,有几个卷积和线性层。
我们这里不使用迁移学习,但是你在日常工作中绝对应该使用。与从头开始的训练相比,它使你的网络收敛得更快。
接下来,我们使用torchvision
包来简化数据集加载和迭代。此外,我们还应用了一些增强方法来提高模型的质量。然后,我们建立了一个回调组,它向我们的基本训练循环添加了一些特性。最后,我们做了一些小的准备工作,并调用训练函数来优化模型。
您应该会得到类似如下所示的输出。
Epoch: 1 | train_loss=0.8907, train_accuracy=0.6387, valid_loss=0.1027, valid_accuracy=0.9695Epoch: 2 | train_loss=0.4990, train_accuracy=0.8822, valid_loss=0.0828, valid_accuracy=0.9794Epoch: 3 | train_loss=0.3639, train_accuracy=0.9086, valid_loss=0.0723, valid_accuracy=0.9823
注意,上面显示的代码包括这里没有显示的make_phases()
功能。请参考笔记本查看其实现。本质上,它用瘦结构包装数据加载器,有助于在模型训练期间跟踪性能指标。
结论
深度学习工程师的最终目标是为特定的数据集和任务建立一个健壮而准确的解决方案。实现这一目标的最佳方式是使用世界各地的用户在许多用例中测试过的经过验证的工具和维护良好的框架和库。
然而,如果你想精通数据科学并最终构建你的定制解决方案,你可能“应该了解 backprop ”。充分了解您的工具使您能够根据您的特定需求定制它们,添加新功能并更快地学习新工具。
我相信,在使用经过验证的 API 和理解“低级”细节之间保持平衡,可以让你成为一名更好的工程师,可以轻松地将获得的知识转移到新的平台、语言和接口上。
对 Python 语言感兴趣?离不开机器学习?看过网上的其他东西吗?
那么你可能会对我的博客 感兴趣,我的博客 在这里我谈论了各种编程话题,并提供了我感兴趣的教科书和指南的链接。
基于深度学习的自动驾驶车辆和高级驾驶员辅助系统的对象分类模型
基于 Python 的对象分类模型,使用 tensorflow 在自制数据集上进行训练,并部署在嵌入式计算平台上,用于向驾驶员实时传输数据
Figures: Artistic rendition of an autonomous vehicle’s object detection system versus our algorithm working in real-time
专用对象检测系统需要快速、准确,并且专用于对少数但相关数量的对象进行分类。我们的目标是集成一个系统,该系统利用 Inception 庞大的启发式映射图像预测树,以及一个实时系统,该系统足够精确和健壮,能够在各种处理能力下工作,并给用户足够的信心用单个帧识别和检测对象。由于这种特性可以在大量依赖实时检测的地方使用,它可能不仅限于驾驶辅助或自动驾驶系统,而是超出了本项目的范围。
为了提出一种新颖的数据集,该数据集将具有具有足够权重和多样性的图像树,以便以高准确度和精确度预测被识别的对象,该数据集被用来建立 softmax 层的初始状态,该初始状态先前被现有的 ImageNet 数据集加权。结果是令人信服的识别准确性和预测的信心与实时测试帧的视频。随着自动驾驶汽车的出现,人们也越来越担心行人的安全。这也已经使用实时单帧行人识别器以令人满意的准确度解决了。通过我们的算法,我们打算在这一领域做出重大贡献,因为我们提出了一种集成了物体检测和安全的驾驶员辅助系统,这有助于提高道路安全,并有助于满足车辆中自动和智能驾驶员辅助系统领域不断增长的需求。
Figure : How a basic deep neural network performs weight distribution and classification of images
Figure: Inception v3 architechture that is utilized here for training classifier
在我们的工作中,我们提出了一个算法,可用于创建一个智能驾驶辅助系统。该算法分两个阶段实现,以下部分将描述该算法每个阶段的实现。该项目还延伸到一个描述自主安全和监控系统的子部分,该系统包括一个用于识别车辆类型的车辆分类器,以及一个用于捕获和存储车辆注册号码的车牌识别系统。它还有一个部分专门描述我们的实时对象检测系统,该系统用于识别每个视频帧中常见的路上对象。
之前的相关工作
Figure : Accuracy performance of various object classification models
当前关于实时对象检测和跟踪的工作涉及使用传统的特征提取算法,如 SURF 和背景减法,以便识别运动对象。但是我们的算法使用了一个更有效的物体检测系统,名为 YOLO,它比 SURF 有明显的优势。SURF 是一种可变形零件模型(DPM ),因为它采用滑动窗口来执行对象检测。DPM 使用不相交的管道来提取特征、对区域进行分类以及预测高分区域的边界框等。使用 YOLO,这些不同的部分被执行特征提取、边界框预测等任务的单个卷积神经网络代替。,并发。这导致更快和更准确的对象检测系统。车辆检测和识别是一个重要的,但具有挑战性的任务,因为车辆图像是扭曲的,并受到多种因素的影响。几种车辆检测算法主要将任何车辆分类为汽车或其他。他们还采用传统的分类算法,如 SVM,并使用滑动窗口技术进行特征提取。一些研究人员还研究了使用正面车辆图像的车辆标志检测和识别,以获取将揭示车辆制造商的信息。但是这并不总是提供足够的功能来满足用户的需求。
Figure : New dataset of cars created in order to tune the max pooling layer weights of Inception v3 model
另一方面,我们的系统能够将任何车辆分为三类,即 SUV、轿车和小型车。这是通过创建印度道路上 750 幅车辆图像的数据集来实现的,这使得将该系统集成到现有车辆中变得更加简单。此外,我们的系统采用 CNN 进行分类,这使得它成为一个更快和更有效的系统。一些系统还采用雷达来执行目标检测。
然而,雷达也有自己的缺点。当大型车辆过于靠近雷达系统时,雷达接收器可能会饱和。此外,与计算机视觉算法相比,雷达系统速度较慢,因为它们最多需要 2 秒钟才能锁定。因此,我们的系统采用计算机视觉来检测和跟踪车辆。驾驶辅助系统在过去几年中获得了巨大的欢迎,这主要是由于谷歌和特斯拉等公司所做的出色工作。这些公司是电子自动驾驶汽车领域的主要贡献者,这些汽车采用计算机视觉技术进行物体检测、识别和跟踪,并采用激光雷达技术在低能见度条件下工作。
通过我们的算法,我们打算在这个新的和创新的研究领域做出重大贡献。我们的驾驶员辅助系统集成了用于自动驾驶功能的物体检测和用于改善道路安全的安全性。
Figure : Cross-entropy and weights output graphs versus epochs for the trained model
我们即将推出另一个部分,用一个集成了物体探测器的自动车牌识别系统来扩展这个项目。正如我们在一些交通事故数据中研究的那样,这在紧急情况下可能是至关重要的。
Figure : Number plate text recognition capabilities integrated with the program
如需了解更多信息,请随时前往关于自动驾驶汽车的链接和更多有趣的项目以及同一项目的github 回购。
此外,训练目标识别模型并将其转移到 Raspberry-pi 模块,以利用远程目标检测。虽然 R-pi 响应不是实时的(大约 71 秒检测一帧中的对象),但它可以通过更好的板载计算能力来提高。
Figure : YOLO v3 object detection system trained and deployed on a Raspberry-pi embedded module
该项目的另一个版本计划使用 NVIDIA Jetson tx2 嵌入式 GPU,以便在没有远程高级驾驶辅助系统所需的连接性的情况下具有边缘功能。
Getting to play with this cool thing for the upcoming projects on autonomous driving based on intelligent cameras and object detection !
此外,这是我的 youtube 教程视频,用于安装和启动带有外围设备和摄像头模块的 Nvidia Jetson tx2 套件:
希望你们喜欢并评论,你们的反馈很有价值!
汽车模拟器上的深度学习
这个项目的代码可以在: Github 上找到。
这篇文章也可以在我的网站这里找到。
在 udacity 自动驾驶汽车工程师课程的第三个项目中,我使用了课程组织者提供的汽车模拟器来收集预期驾驶行为的数据,然后使用这些数据来训练深度学习 CNN 模型( NVIDIA 卷积神经网络架构)在模拟环境中驾驶汽车。
该项目包括以下几个阶段:
- 通过在模拟器周围驾驶汽车来收集训练数据。
- 实现 NVIDIA 神经网络。
- 训练和测试模型性能。
收集培训数据
模拟器可以在两种模式下运行:训练模式和自主模式。
在训练模式中,用户使用转向角、油门和刹车来控制汽车的移动。你可以用键盘或鼠标控制汽车。在训练模式下,你可以点击记录键,模拟器将开始保存驾驶数据到一个特定的位置。行驶数据包括来自汽车左、右和前摄像机(160x320x3 尺寸)的每幅图像,以及相应的转向、油门和刹车测量值。
Figure 1. Left: center lane driving on simulator. Right: record mode in simulator.
为了收集训练数据,汽车在训练模式下使用中心车道驾驶行为绕赛道行驶两圈。然后,通过在弯道周围的选择性驾驶行为进一步加强数据,以解决转向过度和转向不足的问题,这使得模型能够学习在这些弯道周围行驶所需的更大转向角度。
从训练运行中总共收集了 7,098 个数据点。通过水平翻转图像并找到转向角的附加倒数来执行进一步的增强。扩充后,数据点的总数增加了一倍,达到 14,196 点。
Figure 2. Left: view from right side camera. Right: flipped image.
实施 NVIDIA 神经网络
对于这个项目,使用了 Keras 深度学习库。
作为初始模型,我实现了一个只有一个卷积层和一个密集连接层的神经网络。转向角是作为模型输出的唯一测量值;油门和制动测量的增加增加了驾驶行为的噪音。这种基本模型能够在赛道的直道段保持汽车在道路上行驶,但无法在弯道处正确驾驶汽车。
为了改进我的解决方案,我决定使用他们的开发者博客上描述的 NVIDIA 神经网络架构。引自他们的文章:
在一项新的汽车应用中,我们使用卷积神经网络(CNN)将来自前置摄像头的原始像素映射到自动驾驶汽车的转向命令。这种强大的端到端方法意味着,通过最少的人类训练数据,系统可以在当地道路和高速公路上学习驾驶,无论有没有车道标志。
以下是取自他们博客的 NVIDIA 神经网络架构图:
Figure 3. NVIDIA’s CNN Architecture (source: link)
上面的 NVIDIA 神经网络由 9 层组成:1 个归一化层,5 个卷积层,3 个全连接层。
我对 CNN 的实现略有不同,我总共有 12 层:1 个裁剪层,1 个归一化层,4 个卷积层,3 个分离层,3 个全连接层。
裁剪图层被用来移除场景(树、水等)…)从输入数据中提取,以便模型可以关注道路特征。输入图像从顶部裁剪 70 像素,从底部裁剪 25 像素。此外,根据多伦多大学研究员 Nitish Srivastava 题为 dropout:一种防止神经网络过度拟合的简单方法的论文的建议,添加了 Dropout 层以减少过度拟合。
下面是每一层的详细描述:
Figure 4. Description of layers used in neural network.
而下面是以上各层在 Keras 中的代码实现:
培训和测试性能
训练数据被拆分,80%的数据用于训练,20%的数据用于验证。数据在分割前被打乱。所用的性能指标是均方误差损失,目的是尽可能将其最小化。
经过实验,我发现训练模型的理想时期数是 5 个时期,大于 10 个时期会过度拟合模型。
使用 adam 优化器对模型进行拟合,因此不必手动调整学习速率。
下图显示了每个历元的训练集和验证集的均方误差损失:
Figure 5. model mean squared error loss for deep learning model.
正如预期的那样,训练集和验证集的均方误差损失随着模型被训练的次数(时期)而降低。
在训练过程结束时,该模型能够在不离开道路的情况下,在赛道上自主驾驶车辆。以下是模型运行的视频:
数字海洋堆栈上的深度学习?还没有
所以你想要一个更便宜的解决方案来运行你的深度学习代码。AWS 每月给你大约 1K 的账单,但你的业务逻辑真的需要深度学习的魔力。更糟糕的是,你不能仅仅调用一个 API 就让它消失。不,不。你处理的数据太多了。但是你喜欢云。你想待在云中。让我们一起踏上尝试新事物的旅程。
Docker 是一种虚拟化(特别是容器化)技术。docker 上有很多很棒的程序可以运行。非常棒,但是记住 DigitalOcean 仍然没有 GPU 支持,所以在这些容器中的机器学习将会非常慢。
也许如果我们在一个高 CPU 水滴上运行我们的深度学习代码,性能会足够好,价格会更低?我记得看过一篇文章,说 DigitalOcean CPU 实例在一些工作负载上可以在成本和性能上打败 AWS。也许是 word 嵌入模型工作得很好。我记不清了,也找不到文章了…那我们去了解一下吧!科学!
首先让我们看看成本。今天(2017 年 8 月 10 日)AWS 上的一个 p2.xlarge 费用为 0.900 美元/小时。它拥有惊人的 K80 GPU。DigitalOcean 中的 32 CPU“高 CPU”水滴费用为 0.952 美元/小时。这台机器有 48 GB 内存,适合我们想要的任何单词嵌入模型。系统也有 SSD。事实上,数字海洋只是固态硬盘。我喜欢它。让我们在两个平台上运行诗人的 TensorFlow,看看我们在执行时间方面的表现如何。
DigitalOcean droplet 是从 Docker 的一次点击安装开始的。这加速了张量流设置。当它旋转起来的时候,跑上去就很有趣,感觉所有这些 CPU 的原始力量就像某种自大狂巫师一样。好的。回去工作。TensorFlow 安装在 Docker 上又快又好。“你好,世界!”验证脚本有效。
在指令中,我们跳过了“我赶时间”这一部分,因为我们真的想处理一些数字。瓶颈文件的创建速度比在 p2 实例上慢一些。我可以告诉你,我过去经常这么做。结果将是不可思议的。让我们通过运行来量化:
time python retrain.py \
--bottleneck_dir=bottlenecks \
--how_many_training_steps=500 \
--model_dir=inception \
--summaries_dir=training_summaries/basic \
--output_graph=retrained_graph.pb \
--output_labels=retrained_labels.txt \
--image_dir=flower_photos
高 CPU DigitalOcean 虚拟机上的结果是:
2017-08-11 00:01:07.773065: Step 480: Train accuracy = 92.0%
2017-08-11 00:01:07.773155: Step 480: Cross entropy = 0.345928
2017-08-11 00:01:07.825522: Step 480: Validation accuracy = 88.0% (N=100)
2017-08-11 00:01:08.424932: Step 490: Train accuracy = 91.0%
2017-08-11 00:01:08.425017: Step 490: Cross entropy = 0.354291
2017-08-11 00:01:08.477905: Step 490: Validation accuracy = 90.0% (N=100)
2017-08-11 00:01:09.032948: Step 499: Train accuracy = 94.0%
2017-08-11 00:01:09.033039: Step 499: Cross entropy = 0.262198
2017-08-11 00:01:09.084237: Step 499: Validation accuracy = 90.0% (N=100)
Final test accuracy = 89.8% (N=353)
Converted 2 variables to const ops.**real 6m42.118s**
user 86m49.452s
sys 30m17.000s
因此,在不到 7 分钟的时间内(6*60+42 = 360+42 = 402 秒),我们生成了重新训练 CNN 最后一层的瓶颈文件,以获得大约 90%的分类准确率。
让我们在 AWS 上运行同样的测试。在 AWS 上用 p2 在 Docker 之外运行要快得多。对于相同的命令和相同的数据集,我得到了以下时间:
2017-08-11 00:02:32.848994: Step 480: Train accuracy = 94.0%
2017-08-11 00:02:32.849077: Step 480: Cross entropy = 0.273793
2017-08-11 00:02:32.907405: Step 480: Validation accuracy = 85.0% (N=100)
2017-08-11 00:02:34.094489: Step 490: Train accuracy = 87.0%
2017-08-11 00:02:34.094567: Step 490: Cross entropy = 0.433323
2017-08-11 00:02:34.153172: Step 490: Validation accuracy = 88.0% (N=100)
2017-08-11 00:02:34.694696: Step 499: Train accuracy = 90.0%
2017-08-11 00:02:34.694771: Step 499: Cross entropy = 0.370968
2017-08-11 00:02:34.753167: Step 499: Validation accuracy = 94.0% (N=100)
Final test accuracy = 89.8% (N=353)
Converted 2 variables to const ops.**real 4m10.831s**
user 3m27.290s
sys 0m29.820s
相同的结果,但只是超过 4 分钟,而不是不到 7 分钟(4*60+11 = 240+11 = 251)。考虑到 DigitalOcean droplet 每小时比 AWS 实例贵一点,我认为这里有一个明显的赢家。AWS 的加速比是的 1.6 倍(402/251),而的成本减少了 5.7%(0.952 比 0.900)。坚持用 AWS 做你的 CNN 工作,把数字海洋水滴留给非 GPU /更小的东西。
坚持住!我找到文章了!
[## 云 CPU 上的 TensorFlow 基准测试:比云 GPU 更便宜的深度学习
我一直在与 Keras 和 TensorFlow 合作几个个人深度学习项目。然而,培训模式的深度…
minimaxir.com](http://minimaxir.com/2017/07/cpu-or-gpu/)
那为什么他们说牛逼我说扯淡?让我们开始吧!
- 本文作者 Max Woolf 从源代码中编译了 tensorflow,以利用 AVX 和 SSE 指令。懒惰的我,我没有那样做。接得好。
- 他的工作量是 MLP 的 MNIST 分类,然后是 CNN,还有其他东西,而我的工作量是《盗梦空间 v3》的 CNN。
- 总的来说,他有点同意我的结果,CPU 在运行时间方面与 GPU 不匹配。他运行的每一个 CPU 测试都比 GPU 慢。
- 对于成本比较,我们不同意。他使用了谷歌的超级 CPU 怪兽,定价为 0.509 美元/小时,GPU 定价为 0.745 美元/小时。这远远低于 DigitalOcean 的高 CPU 0.952 美元/小时和 AWS 的 p2 实例 0.900 美元/小时。平心而论,我们可以将我们的 p2 降级为 g 2.2x 大,并从那里继续下滑,而不是尖叫着跑到数字海洋。是的,给谷歌云平台一个旋转。关于 GCP 的单位工作成本,马克斯说得很有道理。
所以,最后我们都是对的。你可以在 CPU 上做 ML,但是要确保它确实能为你省钱,因为它肯定不会为你节省任何时间。
希望这对于正在考虑更便宜的机器学习选项的解决方案架构师来说是一个很好的比较。如果你喜欢这篇文章,那么请推荐它,分享它,或者给它一些爱(❤).
编码快乐!
-丹尼尔
丹尼尔@lemay.ai ←打个招呼。
LEMAY . AI
1(855)LEMAY-AI
您可能喜欢的其他文章:
边缘的深度学习
在移动和边缘设备上执行深度学习的概述。
Photo by Alexandre Debiève on Unsplash
可扩展的深度学习服务取决于几个约束。根据您的目标应用,您可能需要低延迟、增强的安全性或长期成本效益。在这种情况下,将深度学习模型托管在云上可能不是最佳解决方案。
Computing on the Edge (Source)
边缘深度学习缓解了上述问题,并提供了其他好处。这里的 Edge 指的是在消费者的产品上本地执行的计算。这篇博客探讨了使用边缘计算进行深度学习的好处,以及与之相关的问题。
为什么是 edge?为什么不用云呢?
有太多令人信服的理由支持边缘计算而不是云计算。
1.带宽和延迟
毫无疑问,对远程服务器的 API 调用有一个有形的往返时间(RTT)。要求近乎即时推断的应用程序无法在这种延迟下正常运行。
Latency and Power consumption stats for Object Detection (DET), Tracking (TRA) and Localization (LOC) on four different edge devices (Source)
以自动驾驶汽车为例。足够大的潜伏期会显著增加事故的风险。此外,意外事件,如动物穿越或 jay walking 可能发生在短短几帧。在这些情况下,响应时间极其重要。这就是为什么 Nvidia 让他们定制的板载计算设备在边缘执行推理。
此外,当大量设备连接到同一个网络时,有效带宽会减少。这是因为使用通信信道的固有竞争。如果在边缘上进行计算,这可以显著减少。
Bandwidth requirement for various applications. (Source)
以在多台设备上处理 4K 高清视频为例。在本地处理它们将大大节省带宽的使用。这是因为我们不需要将数据上传到云中进行推断。因此,我们可以相对容易地扩展这个网络。
2.安全和权力下放
商业服务器容易受到攻击和黑客攻击。当然,如果您使用可信的供应商,风险可以忽略不计。但是,为了您收集的数据和您的知识产权(IP)的安全,您需要信任第三方。边缘设备让你对你的 IP 拥有绝对的控制权。
Centralized vs Decentralized vs Distributed. (Source)
如果你听说过区块链,你可能对分权或分配很熟悉。尽管如此,在边缘拥有几个设备可以获得去中心化的所有好处。使用单一 DDoS 攻击来摧毁整个隐藏设备网络比中央服务器更难。这对于使用无人机进行边境巡逻等应用尤其有用。
3.特定于工作的用途(定制)
想象一下,你有一个生产玩具的工厂。它有几百个工作站。每个工作站都需要图像分类服务。问题是,每个工作站都有一组不同的对象,训练单个分类器可能是无效的。此外,在云上托管多分类器将会昂贵。
成本有效的解决方案是训练针对云上每个部分的分类器,并将训练的模型运送到边缘设备。现在,这些设备是为他们的工作站定制的。它们将比在所有工作站上预测的分类器具有更好的性能。
4.群体智能
继续上面提到的想法,边缘设备也可以帮助训练机器学习模型。这对于强化学习特别有用,你可以在并行中模拟大量的“情节”。
Multiple agents trying to grasp objects. (Source)
此外,边缘设备可用于收集数据,用于在线学习(或继续学习)。例如,我们可以使用多架无人机来勘测一个区域进行分类。使用诸如异步 SGD 之类的优化技术,可以在所有边缘设备中并行训练单个模型。它也可以仅仅用于聚集和处理来自各种来源的数据。
5.裁员
冗余对于强健的内存和网络架构极其重要。网络中一个节点的故障会对其他节点产生严重影响。在我们的例子中,边缘设备可以提供很好的冗余度。如果我们的一个边缘设备(这里是一个节点)发生故障,它的邻居可以暂时接管。这极大地确保了可靠性,并大大减少了停机时间。
6.从长远来看,成本效益高
从长远来看,云服务将比拥有一套专用的推理设备更加昂贵。如果您的设备具有较大的占空比(也就是说,它们大部分时间都在工作),这一点尤其正确。此外,如果批量生产,边缘设备会便宜得多,从而显著降低成本。
边缘深度学习的限制
深度学习模型以大和计算昂贵而闻名。将这些模型安装到通常具有节省内存的边缘设备中是一个挑战。有许多方法可以解决这些问题。
1.参数有效的神经网络
神经网络的一个显著特征是其庞大的规模。边缘设备通常不能处理大型神经网络。这促使研究人员在保持准确性的同时,最小化神经网络的规模。两种流行的参数高效神经网络是 MobileNet 和 SqueezeNet 。
SqueezeNet 采用了许多策略,如后期下采样和滤波器数量减少,以在低参数数量下获得高性能。他们引入了具有“挤压”和“扩展”层的“点火模块”,优化了网络的参数效率。
Fire module in the SqueezeNet. (Source)
MobileNet 将普通卷积分解为深度方向卷积和 1x1 卷积的组合。这种安排大大减少了所涉及的参数数量。
Top 1 accuracy in the ImageNet dataset with respect to number of Multiply-Accumulates (MACs). (Source)
2.修剪和截断
经过训练的网络中的大量神经元是良性的,对最终的准确性没有贡献。在这种情况下,我们可以修剪这样的神经元来节省一些空间。谷歌的 Learn2Compress 发现,我们可以通过因子 2 获得大小 缩减,同时保留 97%的准确率。
而且,大多数神经网络参数都是 32 位浮点值。另一方面,边缘设备可以被设计为在 8 位值或更少的值上工作。降低精度可以显著减小模型大小。例如,将 32 位型号减少到 8 位型号理想情况下会将型号大小减少到的 4 倍。
3.蒸馏
蒸馏是使用更大的“教师”网络来教授更小的网络的过程。谷歌的 Learn2Compress 在他们的尺寸缩减过程中融入了这一点。结合迁移学习,这成为一种在不损失太多准确性的情况下减少模型大小的强大方法。
Joint training and distillation approach to learn compact student models. (Source)
4.优化的微处理器设计
到目前为止,我们已经讨论了缩小神经网络以适应我们的边缘设备的方法。一种替代(或补充)方法是提升微处理器的性能。
最简单的解决方案是在微处理器上安装 GPU,比如广受欢迎的 Nvidia Jetson T1。然而,当大规模部署时,这些设备可能不具有成本效益。
Nvidia Jetson (Source)
一个更有趣的解决方案是使用视觉处理单元(vpu)。英特尔声称他们的 Movidius VPUs 具有“超低功耗的高速性能”。谷歌的 AIY 套件和英特尔的神经计算棒内部使用这种 VPU。
Google AIY’s Vision Bonnet using a Movidius VPU. (Source)
或者,我们可以使用 FPGAs。它们比 GPU 具有更低功耗,且可以适应更低位(< 32 位)的架构。然而,由于其较低的 FLOPs 等级,与 GPU 相比,性能可能会略有下降。
对于大规模部署,定制 ASICs 将是最佳解决方案。制造类似 Nvidia V100 的微架构来加速矩阵乘法可以大大提高性能。
Pascal vs Volta architecture; Nvidia. (Source)
深度学习性能备忘单
简单和复杂的技巧可以帮助您提高深度学习模型的准确性
我从新的和有经验的机器学习工程师那里得到最多的问题是“我如何才能获得更高的准确性?”
很有意义,因为机器学习对商业最有价值的部分通常是它的预测能力。提高预测的准确性是从现有系统中获取更多价值的简单方法。
该指南将分为四个不同的部分,每个部分都有一些策略。
- 数据优化
- 算法调整
- 超参数优化
- 全体,全体,全体
并不是所有的这些想法都会提高性能,你将会看到你对同样的问题应用的越多,回报就越有限。试了几个之后还是卡住了?这表明你应该重新思考你的业务问题的核心解决方案。这篇文章只是一个备忘单,所以我将在每一节中为您链接更详细的信息来源。
数据优化
平衡您的数据集
对于表现不佳的深度学习模型,提高性能的最简单方法之一是平衡数据集,如果你的问题是分类。现实世界的数据集往往是倾斜的,如果你想要最好的准确性,你就希望你的深度学习系统学习如何根据特征在两个类别之间进行选择,而不是通过复制其分布
常见的方法包括:
- **二次抽样多数类:**您可以通过二次抽样多数类来平衡类分布。
- **过采样少数民族:**替换采样可以用来增加你的少数民族比例。
更多数据
我们很多人都熟悉这个图表。它显示了深度学习和经典机器学习方法的数据量和性能之间的关系。如果你不熟悉这个图表,那么这个教训是清楚而直接的。如果您希望模型具有更好的性能,则需要更多的数据。根据您的预算,您可能会选择创建更多的标记数据或收集更多的未标记数据,并更多地训练您的特征提取子模型。
开源贴标软件
生成更多数据
或者假装,直到你成功。一个经常被忽视的提高准确性的方法是从你已经有的数据中创建新的数据。以照片为例;工程师通常会通过旋转和随机移动现有图像来创建更多图像。这种变换还增加了训练集的减少的过拟合。
算法调整
复制研究人员
你正在解决一个背后有大量研究的问题吗?你很幸运,因为 100 个工程师可能已经对如何获得这个问题的最佳精度进行了大量思考。
阅读一些关于这个主题的研究论文,记下他们用来获得结果的不同方法!他们甚至可能有一个代码的 git-hub 供您使用。
谷歌学术是开始搜索的绝佳地点。他们还提供许多工具来帮助你找到相关的研究。
对于研究论文的存储和组织,我使用门德利
接收每周人工智能研究论文,资源和趋势。免费订阅 AI 书生
算法抽查
不要让你的自负战胜你。不可能知道哪种机器学习算法最适合你的问题。每当我着手解决一个新问题时,在没有太多研究的情况下,我会寻找一些可用的方法,并尝试所有的方法。深度学习(CNN 的,RNN 的等等。)和经典的机器学习方法(随机森林、梯度推进等)。)
对你所有实验的结果进行排序,对表现最好的算法加倍下注。
超参数优化
学习率
亚当优化算法是可靠的。经常在所有深度学习问题上给出惊人的结果。即使它有极好的性能,它仍然会使你陷入问题的局部最小值。一个更好的算法是带热重启的随机梯度下降,它具有 Adam 的优点,有助于消除陷入局部最小值的机会。
批次大小和时期数
一个标准的程序是对现代深度学习实现使用具有大量时期的大批量大小,但是共同的策略产生共同的结果。试验你的批次的大小和训练时期的数量。
提前停止
这是减少你的深度学习系统的泛化错误的一个极好的方法。持续训练可能会提高数据集的准确性,但在某个点上,它会开始降低模型对模型尚未看到的数据的准确性。要提高实际性能,请尝试提前停止。
网络体系结构
如果你想尝试一些更有趣的东西,你可以试试高效神经架构搜索(ENAS) 。该算法将创建一个定制的网络设计,最大限度地提高数据集的准确性,并且比 cloud ML 使用的标准神经架构搜索更有效。
正规化
停止过度拟合的一个健壮方法是使用正则化。
有几种不同的方法来使用正则化,你可以在你的深度学习项目中进行训练。如果你还没有尝试过这些方法,我会开始把它们包含在你做的每个项目中。
- 辍学 : 他们在训练过程中随机关闭一定比例的神经元。辍学有助于防止神经元群体过度适应自己。
- 重量惩罚 L1 和 L2: 重量爆炸式增长可能是深度学习中的一个真正问题,并降低准确性。解决这个问题的一个方法是给所有的权重增加衰减。这些方法试图使网络中的所有权重尽可能小,除非有大的梯度来抵消它。除了经常提高性能之外,它还有使模型更容易解释的好处。
全体,全体,全体
在选择最佳型号时遇到困难?通常,您可以组合不同模型的输出,从而获得更高的准确性。每种算法都有两个步骤。
- 在原始数据的子集上产生简单 ML 模型的分布
- 将分布组合成一个“聚合”模型
组合模型/视图(Bagging)
在这种方法中,您对相同的数据训练几个不同的模型,这些模型在某些方面是不同的,然后对输出进行平均以创建最终输出。
装袋具有减少模型中方差的效果。你可以直观地把它想成是让不同背景的多个人思考同一个问题,但出发点不同。就像在团队中一样,这可能是获得正确答案的有力工具。
堆叠
这类似于装袋,不同的是,你没有一个综合产量的经验公式。您创建了一个元级学习器,它基于输入数据选择如何权衡来自不同模型的答案,以产生最终输出。
还是有问题吗?
重构你的问题
停下来看看你的屏幕,喝杯咖啡。这个解决方案就是从头开始重新思考你的问题。我发现坐下来集思广益,找出解决问题的不同方法会有所帮助。也许从问自己一些简单的问题开始:
- 我的分类问题会变成回归问题还是反过来?
- 你能把你的问题分解得更小吗?
- 您收集的数据中是否有任何可能改变问题范围的观察结果?
- 你的二进制输出能变成 softmax 输出吗,反之亦然?
- 你是用最有效的方式来看待这个问题的吗?
反思你的问题可能是提高绩效的最难的方法,但它通常是产生最佳结果的方法。与在深度学习方面有经验的人聊天会有所帮助,可以让你对你的问题有新的看法。
如果你想找人聊天, 我将在下个月抽出时间与你就你的项目进行 30 分钟的交谈。我对这 30 分钟的通话收取 5 美元,以此作为屏障,阻止那些不认真的人浪费我们的时间。
报名一个时间段
感谢阅读:)如果你喜欢它,尽可能多的点击下面的按钮!这对我意义重大,鼓励我写更多这样的故事
深度学习;个人笔记第 1 部分第 2 课,学习率,数据扩充,退火,测试时间扩充
这个博客系列将会更新,因为我有第二次采取快速人工智能的教训。这些是我的个人笔记,我努力把事情理解清楚并解释好。没什么新意,只活出了这个 博客 。
第一课复习
我们使用了三行代码来构建图像分类器:
PATH
下的数据组织包括一个 train
文件夹和一个valid
文件夹。每个文件夹下都有分类标签,即cats
和dogs
,其中有相应的图像。
训练输出:*epoch number*
*training loss*
*validation loss*
*accuracy*
****
***0 0.157528 0.228553 0.927593***
选择一个好的学习率
学习速度决定了我们对解决方案的关注速度。这涉及到寻找一个可能有许多参数的函数的最小值。
我们从一个随机的点开始,然后找到梯度来决定哪条路是向上或向下的。我们到达最小值的距离与梯度成正比;如果更陡,我们就离得更远。我们在一个点选择一个梯度,然后乘以一个数。(学习率/步)。
如果学习率太小,就需要非常长的时间才能触底。如果学习率太大,它可能会远离底部振荡。如果训练一个神经网络,你发现损失或准确性正加速到无穷大,学习率太高。
我们使用学习率查找器(learn.lr_find
)来查找合适的学习率。每次小批量(即我们有效利用 GPU 的并行处理能力时,每次查看多少张图像,通常一次查看64
或128
张图像)。我们逐渐成倍地增加学习率,最终学习率会太大,损失会开始变得更严重。
我们绘制学习率与损失的关系图来确定最低点。然后,我们回溯一个数量级,并选择该数量级作为我们的学习率,因为这是损失减少的地方0.01
。
python 中的数学符号
学习率是要设置的关键数字。快速人工智能为您挑选其余的超级参数。为了获得稍微好一点的结果,我们还可以做更多的调整。
这种学习率查找技术是基于 Adam optimiser 之上的。动量和 Adam 是改进梯度下降的其他方法。
要让模型变得更好,你能做的最重要的事情就是给它更多的数据。由于这些模型有数百万个参数,如果你训练它们一段时间,它们就开始“过度拟合”。
过度拟合—模型开始看到训练集中图像的特定细节,而不是学习可以转移到验证集的一般信息。
我们可以收集更多的数据或者使用数据扩充**。**
数据扩充
这指的是以不影响图像解读的方式随机改变图像。例如水平翻转、缩放和旋转。
我们可以通过将aug_tfms
( 增强变换)传递给tfms_from_model
来做到这一点,其中带有一个函数列表,以便按照我们希望的方式将随机变化应用于图像。对于大部分从侧面拍摄的照片(例如,大多数狗和猫的照片,而不是从上到下拍摄的照片,如卫星图像),我们可以使用预定义的功能列表,如transforms_side_on
。我们还可以通过添加max_zoom
参数来指定图像的随机缩放比例。
你构建一个数据类 6 次,每次你画的都是同一只猫。我们来看一些数据增强的猫图。
场景:
您希望对不同类型的图像使用不同类型的数据增强(水平翻转、垂直翻转、放大、缩小、改变对比度和亮度等等)。例如,您想要识别不想水平翻转的字母和数字,因为它们有不同的含义。你不希望猫和狗垂直翻转,因为图像大多是垂直的。对于卫星图像中的冰山,你可能想把它们翻转过来,因为拍摄图像时卫星是哪一边并不重要。
transfrom side_on
—用于从侧面拍摄的图像,轻微改变照片缩放比例,轻微旋转照片,并改变对比度和亮度。
它并不完全是在创建新数据,而是允许卷积神经网络学习如何从不同角度识别猫或狗。
tsf
—包含数据扩充。
data
对象包括增强。最初,增强并不做任何事情,因为precompute=True
在上面的图片中,每个不同的层都有这样的激活,寻找任何像花的中间或鸟的眼球(用红色圈出)等。这个卷积神经网络的后面几层具有激活(数字),例如在上面的图片中指定了鸟的眼球的位置和置信度(概率)。
我们有预先训练的网络,已经学会识别特征(某些类型的东西,如梯度,边缘圆等)。我们采用倒数第二层,这一层包含了识别这些特定事物的所有必要信息,例如“眼珠子”、“毛绒绒的认真程度”等。我们为每个图像保存激活,称之为预先计算的激活。然后,我们可以创建一个新的分类器,利用这个预先计算的激活。我们可以基于预先计算的激活快速训练简单的线性模型。这就是precompute=True
的意思。
这就是为什么当您第一次训练您的模型时,它需要更长的时间-它是预先计算这些激活。
尽管我们每次都尝试展示不同版本的猫,但我们已经预先计算了特定版本猫的激活量,也就是说,我们不会用修改后的版本重新计算激活量。当precompute=True
数据增强不起作用时。我们必须将它设置为learn.precompute=False
,数据扩充才能工作。
**坏消息是accuracy
没有改善,好消息是训练损失(trn_loss
),一种衡量该模型误差是否变好的方法,正在减少。验证误差(val_loss
)没有减少,但是我们没有过度拟合。过度拟合意味着训练损失远低于验证损失。换句话说,当你的模型在训练集上比在验证集上表现得更好时,这意味着你的模型没有泛化。
循环长度参数。这是什么?
cycle_len=1
这使 S 能够以重启(SGDR) 的方式快速梯度下降。基本的想法是,当你越来越接近损失最小的点时,你可能想要开始降低学习速率(采取较小的步骤)以便精确地到达正确的点。在训练时降低学习率的想法被称为学习率退火。这很有帮助,因为随着我们越来越接近最佳重量,我们希望迈出更小的步伐。
逐步退火——你用一定的学习率训练一个模型一段时间,当它停止提高时,手动下拉学习率。选择另一个学习率,手动重复这个过程。
余弦退火——这是一种更好的方法,只需选择某种函数形式——事实证明,真正好的函数形式是余弦曲线的一半,它在开始时以很高的学习速率开始,然后当你更接近时迅速下降。
在训练期间,梯度下降有可能卡在局部最小值而不是全局最小值。
在局部最小值处,损失更严重,并且对于稍微不同的数据集,它不会一般化。在全局最小值时,模型将更好地概括。
请注意,退火不一定与重启相同
我们不是每次都从零开始,但我们会“跳跃”一点,以确保我们处于最佳状态。
然而,我们可能会发现自己处于一个弹性不大的重量空间,即重量的小变化可能会导致损失的大变化。我们希望鼓励我们的模型找到既准确又稳定的权重空间部分。因此,我们不时地增加学习速率(这是“SGDR”中的“重启”),如果当前区域是“尖峰”,这将迫使模型跳到权重空间的不同部分。这是一张图片,展示了如果我们将学习率重置 3 次(在这篇文章中,他们称之为“循环 LR 时间表”):
通过突然增加学习速率,梯度下降可以跳出局部极小值并找到通向全局极小值的路。这样做被称为带重启的随机梯度下降(SGDR),这个想法在这篇论文中被证明是非常有效的。
重置学习率之间的周期数由cycle_len
设置,这种情况发生的次数称为周期数,实际上是我们作为第二个参数传递给fit()
的。这是我们实际学习率的样子:
The learning rate is restored to its original value after each epoch.
学习率在每个时段开始时被重置为您作为参数输入的原始值,然后在该时段内再次降低,如上文余弦退火中所述。
每次学习率下降到它的最低点(上图中每 100 次迭代),我们称之为一个周期**。**
使用随机起点可以得到同样的效果吗?在 SGDR 诞生之前,人们习惯于创造“群体”,他们会重新学习一个全新的模型十次,希望其中一个会变得更好。在 SGDR,一旦我们足够接近最佳和稳定的区域,重置实际上不会“重置”,但权重会变得更好。所以 SGDR 会给你更好的结果,而不仅仅是随机尝试几个不同的起点。
我们选择最高的学习速率1e-2 (0.01)
供 SGD 使用。我们改变每一个小批量的学习率。我们重置它的次数由cycle_len=1
参数定义。1
意思是每隔一个纪元就重置一次。
我们的主要目标是一般化,而不是在狭窄的最优中结束。在这种方法中,我们是否跟踪最小值,对它们进行平均和组合?我们目前没有这样做,但如果你想让它更好地概括,你可以在重置前保存权重并取平均值。但是现在,我们只选择最后一个。(在 1000 次迭代时)
您可以添加一个名为cycle_save_name
的参数以及cycle_len
,它将在每个学习率周期结束时保存一组权重,然后您可以集成它们。
我们的确认损失没有多大改善。因此,可能没有必要单独进一步训练最后一层。
保存并加载模型
不时保存你的权重调用learn.save
并传递文件名224_lastlayer
预计算的激活和调整大小的图像保存在 tmp 文件的数据文件夹中。删除 tmp 文件夹相当于打开和关闭
调用learn.save
时,模型保存在模型文件夹中
**如果您想从头开始重新培训一名模特,该怎么办?通常没有理由删除预先计算的激活,因为预先计算的激活没有任何训练。
微调和差分学习率退火
到目前为止,我们所做的一切都没有改变预先训练的过滤器。我们使用了一个预先训练好的模型,它知道如何找到边缘和渐变(第 1 层)、拐角和曲线(第 2 层),然后是重复的伙伴、文本(第 3 层),最后是眼球(第 4 层和第 5 层)。我们没有在卷积核中重新训练任何更具体的激活权重。我们所做的只是在顶部添加了一些新层,并学习了如何混合和匹配预先训练的功能。
与 ImageNet 图像相比,卫星图像、CT 扫描等图像具有完全不同的特征。所以你需要重新训练很多层。对于狗和猫,图像与模型预先训练的图像相似,但我们仍然会发现稍微调整一些后面的层是有帮助的。
既然我们已经训练了一个好的最终层,我们可以尝试微调其他层。要告诉学习者我们想要解冻剩余的层,只需调用unfreeze()
。这告诉学习者我们想要开始改变卷积滤波器。
**learn.unfreeze()**
冻结层是指未被训练的层,即未被更新的层。
unfreeze()
解冻所有层。
检测边缘和梯度的第一层和检测曲线和拐角的第二层不需要太多的学习,它们不需要太多的改变。而更后面的层需要改变。当训练其他图像识别时,这是普遍适用的。
我们所做的是创建一个学习率数组。
**lr=np.array([1e-4,1e-3,1e-2])**
早期的层(如我们所见)有更多的通用特性。因此,我们预计他们对新数据集的微调需求会减少。出于这个原因,我们将对不同的层使用不同的学习速率:最初的几层将在1e-4
用于基本几何特征和最接近像素的层,中间层在1e-3
用于中间复杂的卷积层,而1e-2
如前所述用于我们添加在顶部的层(完全连接的层)。我们称之为 差异学习率,,尽管据我们所知,在文献中没有这种技术的标准名称。
为什么是 3?实际上它们是 3 个 ResNet 块,但是现在,把它想象成一组层。
**差分学习率与 网格搜索 有何不同?与网格搜索没有相似之处。网格搜索是您试图找到最佳超参数的地方。对于不同的学习率,它尝试了许多学习率,试图找到最好的学习率。对于整个训练,它对每一层使用不同的学习率。
如果我的图像比模型训练的大怎么办?有了这个图书馆和我们正在使用的现代建筑,我们可以使用任何我们喜欢的大小。
**我们可以只解冻特定的层吗?我们还没有这样做,但是如果你愿意,你可以做learn.unfreeze_to(n)
来解冻从层n
开始的层。它几乎从来没有帮助,因为使用不同的学习率,优化器可以学习它所需要的。如果你使用一个非常大的内存密集型模型,如果你用完了 GPU,解冻的层数越少,占用的内存和时间就越少。
注意;您不能解冻一个特定的层。
前面我们说3
是历元数,但实际上是个周期**。在这种情况下,学习是做1
纪元的3
周期。(cycle_len=1
)**
如果cycle_len=2
,它将做 3 个循环,其中每个循环是 2 个时期(即 6 个时期)。
**那它为什么做了 7 个纪元?这是因为cycle_mult
使得每个周期的长度加倍。(1 个历元+ 2 个历元+ 4 个历元= 7 个历元)。
使用差分学习率,我们有一个 99.05%准确的模型。
如果循环长度太短,它开始向下寻找一个好点,然后弹出,再向下寻找一个好点,然后弹出,如此循环,这样它就永远找不到一个好点。早些时候,你希望它这样做,因为它试图找到一个更光滑的地方,但后来,你希望它做更多的探索。这就是为什么cycle_mult=2
似乎是一个好方法。
我们正在引入越来越多的超参数,已经告诉过你们并不多。你可以选择一个好的学习速度,但是增加这些额外的调整可以帮助你不费吹灰之力就达到额外的水平。一般来说,好的起点是:
n_cycle=3, cycle_len=1, cycle_mult=2
n_cycle=3, cycle_len=2
(无cycle_mult
)
为什么更平滑的表面与更一般化的网络相关联?
x 轴显示了当你改变这个特定的参数时,识别狗和猫的能力有多强。一般化意味着当我们给它一个稍微不同的数据集时,我们希望它能够工作。稍有不同的数据集在该参数和它有多像猫和多像狗之间可能具有稍有不同的关系。相反,它可能看起来像红线。换句话说,如果我们在X or Z
结束,那么它不会在这个稍微不同的数据集上做得很好。否则,如果我们在Y,
结束,它仍然会在红色数据集上做得很好。
让我们来看看我们预测错误的图片
当我们做验证集时,我们对模型的所有输入必须是正方形的。如果不同的图像有不同的尺寸,GPU 不会运行得很快。它需要保持一致,以便 GPU 的每个部分都可以做相同的事情。
为了使它成为正方形,我们只需挑出中间的正方形,正如您在下面看到的,可以理解为什么这张图片被错误地分类。
The dogs head was not identified
我们将使用测试时间增强(TTA)** 或推理时间或测试时间它不仅对您的验证集中的图像进行预测,还对它们的许多随机增强版本进行预测(默认情况下,它使用原始图像以及 4 个随机增强版本,假设它们四处移动)。然后,它从这些图像中提取平均预测,并将其用作我们的最终预测。要在验证集上使用 TTA,我们可以使用学习者的TTA()
方法。**
准确率提高到 99.25%。神经网络得到同一张图片的多个参数,使准确率上升。
注;TTA 用于验证/测试设置。训练时,我们不做 TTA。**
**为什么不加边框或者填充,让它变成方形?对神经网络没有太大帮助,因为猫的形象没有变化。变焦可以工作。反射填充通过在外部添加边框来反射图像,使图像变大,适用于卫星图像。一般来说,使用 TTA 加数据增强,最好的办法是尽量使用大图像。如果你裁剪,你会失去例如狗的脸。
非图像数据集的数据扩充?似乎没有人知道。这似乎会有所帮助,但很少有例子。例如,在自然语言处理中,人们试图替换同义词,但总的来说,这个领域还没有得到充分的研究和开发。
我们可以使用滑动窗口生成其他图像吗?例如,从一张狗的图片生成 3 个图像部分?对于训练来说,这不会更好,因为我们不会得到更好的变化,因为你有三种标准的方法来查看数据。你想给它尽可能多的方法来查看数据。固定的作物位置加上随机的对比度、亮度、旋转变化可能对 TTA 更好。
分析结果
混淆矩阵
评估分类算法的快速方法是使用混淆矩阵。它有助于确定你对哪一组分类有困难。
**preds = np.argmax(probs, axis=1)
probs = probs[:,1]**from** **sklearn.metrics** **import** confusion_matrix
cm = confusion_matrix(y, preds)plot_confusion_matrix(cm, data.classes)**
我们有 987 只预测正确的猫和 13 只预测错误的猫。993 只狗我们预测对了,7 只我们预测错了。
训练世界级图像分类器的步骤
- 启用数据增强(
side_on
或top_down
取决于您在做什么),以及precompute=True
- 使用
lr_find()
找到损失仍在明显改善的最高学习率。 - 从预计算的激活中训练最后一层,持续 1-2 个时期。
- 关闭预计算(
precompute=False
),这允许我们使用cycle_len=1
对 2-3 个时期进行数据扩充 - 解冻所有层。
- 将前几层的学习速率设置为下一层的 3-10 倍。经验法则:对于预先训练的 ImageNet 类图像为 10 倍,对于卫星或医学成像为 3 倍。
- 再次使用
lr_find()
(注意:如果你调用lr_find
已经设置了不同的学习率,它会打印出最后一层的学习率。) - 用
cycle_mult=2
训练全网,直到过拟合。
让我们再做一次
这个挑战是确定图像中狗的品种。
使用 kaggle CLI 下载数据。是一个非官方的 kaggle 命令行工具。在使用云虚拟机实例(如 AWS 或 paperspace)下载数据时非常有用。在使用 CLI 之前,请先单击下载按钮,确保您接受竞赛规则。如果您的帐户与另一个帐户连接登录,您必须忘记您的密码,并选择第三个选项来设置新密码和链接您的两个帐户。
**$ kg download -u <username> -p <password> -c dog-breed-identification -f <name of file>**
其中dog-breed-identification
是比赛名称,你可以在比赛 URL 的末尾 /c/
部分[https://www.kaggle.com/c/dog-breed-identification](https://www.kaggle.com/c/dog-breed-identification)
后找到比赛名称。
下面是实际的命令;
**$ kg download -u gerald -p mypassword -c dog-breed-identification**
文件下载完成后,我们可以使用以下命令提取文件。
**#To extract .7z files
7z x -so <file_name>.7z | tar xf -#To extract.zip files
unzip <file_name>.zip**
dogbreeds 文件夹的结构
这与我们之前的数据集不同。不同于train
文件夹,每种狗都有一个单独的文件夹,它有一个带有正确标签的 CSV 文件。
进口货
**from fastai.imports import *
from fastai.torch_imports import *
from fastai.transforms import *
from fastai.conv_learner import *
from fastai.model import *
from fastai.dataset import *
from fastai.sgdr import *
from fastai.plots import *PATH = "data/dogbreeds/"
sz = 224
arch = resnext101_64
bs = 58**
我们将阅读熊猫的 CSV 文件。用于进行结构化数据分析。
**label_csv = f'{PATH}labels.csv'
n = len(list(open(label_csv))) - 1 # header is not counted (-1)
val_idxs = get_cv_idxs(n) # random 20% data for validation setn
10222val_idxs
array([2882, 4514, 7717, ..., 8922, 6774, 37])len(val_idxs) #20% of 10222
2044**
n = len(list(open(label_csv)))-1
:打开 CSV 文件,创建一个行列表,然后取长度。-1
因为第一行是表头。因此n
是我们拥有的图像/行数。
val_idxs = **get_cv_idxs**(n)
:“获取交叉验证索引”——默认情况下,这将返回随机 20%的行(索引)作为验证集。您也可以发送val_pct
来获得特定的百分比,例如val_idxs = get_cv_idxs(n, val_pct=1.0)
获得 100%,但默认为 20%。
这包括图像名称或 id 和标签。
下面是一个熊猫的框架来分组不同品种的狗的数量。
有 120 排代表 120 个品种。
经历这些步骤;
**tfms = tfms_from_model(arch, sz, aug_tfms=transforms_side_on, max_zoom=1.1)
data = ImageClassifierData.from_csv(PATH, 'train', f'{PATH}labels.csv', test_name='test', # we need to specify where the test set is if you want to submit to Kaggle competitions
val_idxs=val_idxs, suffix='.jpg', tfms=tfms, bs=bs)**
支持数据扩充;
**tfms = tfms_from_model(arch, sz, aug_tfms=transforms_side_on, max_zoom=1.1)**
调用tfms_from_model
并通过aug_tfms=transforms_side_on
大概有一面在拍照。
max_zoom
—我们将以 1.1 倍放大图像
**data = ImageClassifierData.from_csv(PATH, 'train', f'{PATH}labels.csv', test_name='test', val_idxs=val_idxs, suffix='.jpg', tfms=tfms, bs=bs)**
ImageClassifierData.from_csv
—上一次,我们使用了from_paths
(它表示文件夹的名称是标签的名称),但是由于标签在 CSV 文件中,我们将改为调用from_csv
并调用包含标签的f’{PATH}labels.csv
csv 文件。PATH
是包含所有数据的文件夹,train
是包含训练数据的文件夹。test_name
指定测试集在哪里,如果你稍后提交给 Kaggle 的话。
val_idx
—没有validation
文件夹,但我们仍想跟踪我们在本地的表现有多好。分离出图像并将其放入验证集中。
suffix=’.jpg’
—文件名末尾有.jpg
,但 CSV 文件没有。因此,我们将设置suffix
,让它知道完整的文件名。
使用包含文件名的trn_ds
获取数据对象中的训练数据集。下面是一个文件名(fnames)
的例子。
**img = PIL.Image.open(fn); img**
我们需要检查图像的大小。然后我们需要知道如何根据它们是太大还是太小来处理它们。大多数 ImageNet 模型都是通过224
对224
或299
图像对299
进行训练的。
创建字典理解;
**size_d = {k: PIL.Image.open(PATH + k).size for k in data.trn_ds.fnames}**
浏览所有文件并创建一个字典,将文件名映射到文件的大小。
**row_sz, col_sz = list(zip(*size_d.values()))**
获取字典并将其转换为行和列。然后将它们转换成 numpy 数组,如下所示:
**row_sz = np.array(row_sz); col_sz = np.array(col_sz)**
以下是前五种行大小:
**row_sz[:5]
array([500, 500, 500, 500, 500])**
用 matplotlib 绘图。图像和像素数量:
从直方图来看,大多数图像都在500
像素左右。
在图上绘制那些小于 1000 像素的像素以放大:
4599
图像位于451 pixels.
内
**验证集中应该有多少幅图像?验证集的大小取决于数据集的大小。不应该总是 20%。如果多次训练同一个模型,并且得到非常不同的验证集结果,那么您的验证集太小了。
狗的形象似乎在中心,占据了画面的最大部分。因此,我们不需要裁剪,这对于医学成像来说是不同的,因为有时肿瘤可能在帧的一侧,因此需要缩放。
初始模型
下面是常规的两行代码。当我们开始处理新数据集时,我们希望一切都超快。所以我们可以指定大小,从运行速度快的 64 开始。稍后,我们将使用更大的图像和更大的架构,在这一点上,你可能会耗尽 GPU 内存。如果您看到 CUDA 内存不足错误,您需要做的第一件事是重启内核,然后减小批处理大小。
预计算
我们将使用预先计算的分类器。
对于 120 个类别,我们获得了 91%的准确率。没有数据增加或解冻。
让我们关闭预计算,再来几个纪元。
准确率提高到 92%。
一个epoch
是一次通过数据,一个cycle
是你说的一个周期中有多少个历元。这是学习率从你要求的下降到零。在这种情况下,从cycle_len=1
开始,时期和周期是相同的。
让我们节约
**learn.save(‘224_pre’)
learn.load(‘224_pre’)**
增加图像尺寸
如果您在较小尺寸的图像上训练了模型,那么您可以调用learn.set_data
并传入较大尺寸的数据集。这将采取你的模型,但是它已经训练到目前为止,它将让你继续在更大的图像上训练。
**learn.set_data(get_data(299, bs))
learn.freeze()**
开始在小图像上训练几个时期,然后切换到更大的图像,继续训练是避免过度拟合的惊人有效的方法。
set_data
完全不改变模式。它只是给了它新的数据来训练。
验证损失(0.239)
远低于培训损失(0.297)
。这是 欠拟合 的标志。Cycle_len=1
可能太短了。学习率在有机会放大之前就被重置了。
再加上cycle_mult=2
(即第一周期为 1 个历元,第二周期为 2 个历元,第三周期为 4 个历元= 7 个历元)
验证损失和训练损失越来越接近,越来越小,几乎相同。暗示我们在正确的轨道上。
测试时间增加(TTA)
要尝试的其他事情:
- 运行两个时期的另一个循环
- 解冻;在这种情况下,训练卷积层没有丝毫帮助,因为图像实际上来自 ImageNet。
- 删除验证集,重新运行相同的步骤,然后提交。这让我们可以使用 100%的数据。
**我们如何处理不平衡的数据集?这个数据集并不是完全平衡的,每个类别(即品种)有 60 到 100 张图片,但还没有不平衡到让你三思的地步。最近的一篇论文称,处理非常不平衡的数据集的最好方法是复制罕见的案例。
*precompute=True*
*unfreeze*
的区别?**
我们从一个预先训练好的网络开始,这个网络通过丰富的功能来发现激活。我们在它的末端添加了几层,开始是随机的。随着一切冻结和precompute=True
,我们正在学习的是我们已经添加的层。使用precompute=True
,我们实际上预先计算了图像看起来有多像激活,因此数据增强没有做任何事情,因为我们每次都显示完全相同的激活。
然后我们设置precompute=False
,这意味着我们仍然只训练我们添加的最后一层,因为它是冻结的,但数据增强现在正在工作,因为它实际上正在从头开始经历和重新计算所有的激活。最后,我们解除冻结,这意味着“好了,现在您可以开始更改所有这些早期的卷积滤波器”。
为什么不一开始就设定precompute=False
?拥有precompute=True
的唯一原因是它快了 10 倍甚至更多。如果您正在处理一个相当大的数据集,它可以节省相当多的时间。使用precompute=True
没有任何准确性理由
能给你带来好结果的最低版本;
- 使用
lr_find()
找到损失仍在明显改善的最高学习率。 - 使用
cycle_len=1
对最后一层进行 2-3 个时期的数据扩充(即precompute=False
)训练(默认情况下,一切从一开始就被冻结) - 解冻所有层。
- 将前几层的学习速率设置为下一层的 3-10 倍。(使用不同的学习率)
- 用
cycle_mult=2
训练全网,直到过拟合。
**减少批量只影响训练速度吗?是的,如果你每次显示的图像越少,那么它计算梯度的图像就越少,因此也就越不准确。换句话说,知道朝哪个方向走以及朝那个方向走多远就不那么准确了。所以当你把批量变小的时候,你会让它变得更加不稳定。它会影响您需要使用的最佳学习速率,但在实践中,将批量大小除以 2 比 4 似乎不会改变太多。如果批量大小发生了很大变化,您可以重新运行学习率查找器来检查它是否发生了变化。
如果狗跑到角落或者很小,你会怎么做?这将在第 2 部分中讨论,但是有一种技术可以让你大致判断出图像的哪些部分最有可能包含有趣的东西。然后你可以把那部分剪掉。
进一步的改进
- 假设您正在使用的图像的大小小于您得到的图像的平均大小,您可以增加图像的大小。正如我们之前看到的,你可以在训练中增加它。
- 使用更好的架构。有不同的方法来组合卷积滤波器的尺寸以及它们之间的连接方式。不同的架构具有不同的层数、内核大小、过滤器数量等。
我们使用 ResNet34,它没有太多的参数,适合小数据集。与 ResNet34 相比,ResNext50 需要两倍的时间和 2-4 倍的内存。
我遇到了RuntimeError: cuda runtime error (2) : out of memory.
尝试重启你的内核,使用较小的批量,我用了10
使用 ResNext50 达到了 99.65%的准确率。
感谢阅读!跟随@ itsmuriuki。
回归学习!
深度学习;个人笔记 Part 1 第三课:CNN 理论;卷积过滤器,最大池,激活,softmax,sigmoid &提交结果给 Kaggle。
随着我对快速人工智能课程的第二次尝试,这个博客系列将会更新。以上是我的个人笔记;a 努力把事情理解清楚,解释好。没什么新意,只活出了这个 博客 。
快狗 Vs 猫
这里是一个端到端的过程,以获得狗与猫的最先进的结果:
PATH = "data/dogscats/"
我们假设您的数据在data
文件夹中。但是你可能想把它们放在别的地方。在这种情况下,你可以使用符号链接或简称符号链接。
注意:我们没有设置pre_compue=True
。这是一种快捷方式,可以缓存一些中间步骤,不必每次都重新计算,也可以省去。当pre_compute=True
时,数据增强不起作用。
learn.unfreeze()
learn.**bn_freeze**(**True**)
%time learn.fit([1e-5, 1e-4,1e-2], 1, cycle_len=1)
bn_freeze
—如果您正在使用更大更深的模型,如 ResNet50 或 ResNext101,即在与 ImageNet 非常相似的数据集上编号大于 34 的任何东西,即尺寸在 200-500 像素之间与 ImageNet 相似的标准对象的侧面照片,您应该添加此行。它导致批量标准化移动平均值不被更新。
使用其他库— Keras
就像 fast ai 坐在 pytorch 上面,keras 坐在 TensorFlow,MXNet,CNTK 等上面。
您需要安装 Keras 或 tensorflow 作为后端:
pip install tensorflow-gpu keras
进口:
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing import image
from keras.layers import Dropout, Flatten, Dense
from keras.applications import ResNet50
from keras.models import Model, Sequential
from keras.layers import Dense, GlobalAveragePooling2D
from keras import backend as K
from keras.applications.resnet50 import preprocess_input
数据路径:
PATH = "data/dogscats/"
sz=224
batch_size=64train_data_dir = f'{PATH}train'
validation_data_dir = f'{PATH}valid'
Keras 使用 train 文件夹和 validation 文件夹的概念,子文件夹带有标签名称。
Keras 需要更多的代码和设置更多的参数。
train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input,
shear_range=0.2, zoom_range=0.2, horizontal_flip=True)test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)train_generator = train_datagen.flow_from_directory(train_data_dir,
target_size=(sz, sz),
batch_size=batch_size, class_mode='binary')validation_generator = test_datagen.flow_from_directory(validation_data_dir,
shuffle=False,
target_size=(sz, sz),
batch_size=batch_size, class_mode='binary')
不是创建一个单一的数据对象,而是在 Keras 中定义DataGenerator
,它指定如何生成数据,还指定我们希望它做什么样的数据扩充(shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
以及做什么样的规范化(preprocessing_function=preprocess_input)
)。换句话说,在 Fast.ai 中,我们可以只说“无论 ResNet50 需要什么,请为我这样做”,但在 Keras 中,您需要知道期望什么。没有标准的扩充集。
train_generator
—通过从目录中查找、设置图像的大小以及小批量和小类的大小来生成图像。当训练时,你随机地重新排序图像,显示它们以不同的顺序显示,通过混洗使它们随机。
class_mode= ‘binary’
——如果你有两个可能的输出,就用二进制,如果是倍数,就用‘categorical’
train_generator
—通过从目录中查找、设置图像大小和小批量的大小来生成图像。
然后创建一个验证数据生成器validation_generator
,这个生成器没有数据扩充。还要告诉它不要为了验证而打乱数据集,因为否则你无法跟踪你做得有多好。
创建模型
base_model = ResNet50(weights='imagenet', include_top=False)
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
predictions = Dense(1, activation='sigmoid')(x)
我们使用 ResNet50,因为 keras 没有 ResNet34。对于 keras,您不能告诉它创建一个适合特定数据集的模型。你必须用手来做。
首先创建一个base_model
,然后构建你想要添加的层,即x
,在这种情况下,我们添加 3 层。
冻结图层并编译模型
model = Model(inputs=base_model.input, outputs=predictions)for layer in base_model.layers: layer.trainable = Falsemodel.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
layer.trainable=False
—循环浏览各层,并使用.trainable = False
冻结
通过传递您想要使用的optimizer
、要查找的loss
和要使用的metric
来编译模型。
适合的
%%time
model.fit_generator(train_generator, train_generator.n // batch_size, epochs=3, workers=4,
validation_data=validation_generator, validation_steps=validation_generator.n // batch_size)
调用 fit_generator 并传递train_generator
和validation _generator
因为 keras 希望您告诉它每个时期有多少批,所以 bathes 的数量=大小是生成器除以批大小,告诉它有多少个时期,以及有多少个工人,即处理器。
微调
解冻一些层,编译,然后再次适合。
没有层组或差异学习率或部分取消冻结的概念,你必须打印出所有的层,并决定有多少你想要微调。我们从140
开始微调。
split_at = 140**for** layer **in** model.layers[:split_at]: layer.trainable = **False**
**for** layer **in** model.layers[split_at:]: layer.trainable = **True**model.compile(optimizer='rmsprop', loss='binary_crossentropy',
metrics=['accuracy'])%%time
model.fit_generator(train_generator, train_generator.n // batch_size, epochs=1, workers=3,
validation_data=validation_generator, validation_steps=validation_generator.n // batch_size)
微调后,你必须重新编译模型,然后拟合。
向 Kaggle 提交结果
在 kaggle 竞赛中,有一个称为评估的部分描述了如何对竞赛进行评估:
对于标签位于不同文件夹的数据集,使用ImageClassifierData.from_paths
。如果您有带标签的 aCSV 文件,您可以使用ImageClassifierData.from_csv
。
要创建提交,您需要使用:
data.classes
:包含所有不同的类
data.test_ds.fnames
:包含测试文件名
使用测试时间增加(TTA)是一个好主意。通过使用is_test=True
,它将给出测试集的预测,而不是验证集的预测。
log_preds, y = learn.TTA(is_test=True)
probs = np.exp(log_preds)
大多数 PyTorch 模型会给你返回预测的日志,所以你需要做np.exp(log_preds)
来得到概率。
probs.shape #(n_images, n_classes(breeds))
(10357, 120)
将矩阵转换成 kaggle 格式,我们使用熊猫数据帧:
df = pd.DataFrame(probs)
df.columns = data.classes
创建一个熊猫数据框架并传递矩阵(probs
)。将列名设置为data.classes
df.insert(0, 'id', [o[5:-4] for o in data.test_ds.fnames])
在零位置插入一个名为id
的包含文件名的新列。这是文件名的样子。test/ab2520c527e61f197be228208af48191.jpg’
。删除前 5 个和最后 4 个字母以获得 id。
查看数据帧:
SUBM = f'{PATH}/subm/'
os.makedirs(SUBM, exist_ok=True)
df.to_csv(f'{SUBM}subm.gz', compression='gzip', index=False)
调用df.to_csv
创建一个 CSV 文件,并使用compression=’gzip’
对其进行压缩。这将文件保存到服务器。您可以使用 kaggle CLI 通过使用此命令$ kg submissions
进行提交,或者使用FileLink(f’{SUBM}subm.gz’)
将其下载到您的计算机并上传到 kaggle。这将为您提供一个链接,将文件从服务器下载到您的计算机上。
个体预测
通过模型运行单个图像以获得预测。
打开验证集中的第一个文件:
fn = data.val_ds.fnames[0]
fn
'train/000bec180eb18c7604dcecc8fe0dba07.jpg'
Image.open(PATH + fn).resize((150, 150))
运行预测:
trn_tfms, val_tfms = tfms_from_model(arch, sz)
im = val_tfms(open_image(PATH + fn)) # open_image() returns numpy.ndarray
preds = learn.predict_array(im[None])
np.argmax(preds)
这个形象必须改变。tfms_from_model
返回定型转换和验证转换。在这种情况下,我们将使用验证转换。
传递给模型或从模型返回的所有东西通常都被认为是小批量的。这里我们只有一个图像,但是我们必须把它变成一个小批量的单个图像。换句话说,我们需要创建一个不只是[rows, columns, channels]
而是[number of images, rows, columns, channels]
的张量。通过使用im[None]
索引到一个数组,在开始处添加一个额外的单位轴,从而将其从一个图像转换为一个小批量的图像。
卷积神经网络理论
这是一个卷积神经网络可视化视频,由 Word Lens 的优秀创作者奥塔维奥制作。
如果你输入一幅图像,计算机会把它们识别为数字(像素)。
输入层
7 号图像数据来自 MNIST 数据库,我们假设您使用预训练模型进行分类。
隐藏层 1
隐藏图层是对输入进行转换,以便从输出图层的数据中识别更复杂的要素,从而做出更好的评估。
我们应用一个过滤器/内核来检测大部分 3×3 的水平边缘。注意,内核A
顶部的1’s
和中间的0’s
以及底部的-1’s
:
1, 1, 1
0, 0, 0
-1, -1, -1
如果滤波器与输入相乘,我们将为高数值分配高数值,因为它们与 1 相乘,而对低数值几乎不分配任何数值,因为它们与 0 或-1 相乘。因此,我们得到了称为 激活 的第一卷积的输出,它基本上是一个通过从输入中取出某个数字,然后应用某种线性运算(即卷积核)来计算输出(激活)的数字。
Conv1 显示了对输入的 3x3 部分进行处理并乘以卷积内核后两者的激活情况。
我们假设网络经过训练,在训练结束时,它创建了一个卷积滤波器,其中有 9 个数字。
卷积在深度学习中,我们几乎总是有一个 3×3 的小矩阵,并将该矩阵的每个元素乘以图像的 3×3 部分的每个元素,然后将它们加在一起,以在一个点上获得卷积的结果。
让我们应用第二个卷积滤波器B
,它也有 9 个数。滤镜B
找到垂直边缘,作为隐藏层输出。
1, 0, -1
1, 0, -1
1, 0, -1
Pytorch 不会将它们存储为两个独立的 9 位数组,而是将其存储为张量。这是多维数组。额外的轴允许我们将额外的过滤器堆叠在一起。滤镜和内核意思相同它指的是 3x3 矩阵。
How convolution work: Activation -3 equals the sum of matrix product of the kernel and input
隐藏层(conv1)的大小为 2,因为它有 2 个过滤器/内核。
接下来,我们将应用另一个过滤器,即隐藏层 2 中的过滤器C
,它是两个 2x3x3 内核,然后应用校正线性单元(ReLu) 来消除负像。从而创建第二隐藏层 conv2。
整流线性单元(ReLU)-是一种非线性,即扔掉底片。
建筑
架构意味着第一层的内核有多大,第一层的内核中有多少滤波器,第一层有一个 3x3,第二层有一个 3x3,这种架构从两个 3x3 卷积内核开始。第二层有两个 2x3x3 内核。
最大统筹
convolved feature vs pooled feature
这意味着用最大值替换输出矩阵中的最大值。例如最大 2x2 池。注意:它是不重叠的,因此会降低分辨率。
下面是最大池层:
最大池的结果得到适合密集权重一个完全连接的层。
全连接层挑选每一个最大汇集激活,给它们一个权重,并挑选最大汇集激活和 3 维张量的 2 级权重的和积。向外放一个密集激活
这与卷积不同,在卷积中,我们使用 3×3 内核一次激活几次。但是在完全连接的层中,我们创建了一个相当于整个输入的大权重矩阵。
大量使用全连接层的架构可能会有很多权重,因此可能会有过度拟合和速度慢的问题。例如,VGG 有多达 19 层,包含一个完全连接的层,4096 个权重连接到 4096 个隐藏层激活,因此(4096 x 4096 x 内核数),这几乎是 3 亿个权重。
*如果我们有 3 个输入通道,滤波器会是什么形状?*如果我们有 3 个输入通道,它看起来就像 conv1,它有 2 个通道,因此有 2 个滤波器,因此每个通道有 2 个滤波器。
当我们从零开始训练时,我们不是从精心训练的过滤器开始,而是从随机数开始,并使用随机梯度下降来改善这些数字,使它们不那么随机。
实际上,我们必须为十位数计算不止一个数。我们会有十次密集激活。
Softmax
预测图像是猫、狗、飞机、鱼还是建筑物。
在全连接层之外,我们将有 5 个数字。请注意,在最后一层没有 Relu,所以我们可以有底片。你想把 5 个数字转换成从 0 到 1 的概率,如果是猫、狗、飞机、鱼或建筑物,就匹配它。这 5 个类别的概率应该在 0 和 1 之间,并且它们的总和应该为 1。为此,我们使用一个 激活函数 这是一个应用于激活的函数。它接受一个数字,吐出另一个数字。
我们需要把线性和非线性函数叠加在一起做深度学习。我们在每个隐藏层之后使用的非线性是一个 ReLu。激活函数是非线性函数。
Softmax 激活发生在最后一层。softmax 吐出介于 *0*
和 *1*
之间的数字,这些数字加起来就是 *1*
。
为了让 softmax 发挥作用,我们需要消除所有负面因素。我们将使用对数和指数。他们出现了很多机器和深度学习。
我们做e^-0.36
得到exp
,然后把它们加起来得到51.31
。为了得到softmax
,我们除以51.31
,得到0.01
。softmax 的总和应该等于1.
exp
的一个特点是,如果一个数字(output
)比另一个稍大,它会使exp
变得更大。softmax 倾向于选择一个强有力的东西,也就是说,它将一个高概率分配给一个类。
如果要把图片归类为猫和狗,我们用什么样的激活函数?Softmax 不喜欢预测多个事物。它想挑一样东西。我们可能想要这样做的一个原因是进行多标签分类。
星球竞赛:从太空了解亚马逊
对于猫和狗比赛的单标签分类,图像要么是猫,要么是狗,不是两者都是。对于卫星竞赛来说,图像按照天气(阴霾和晴朗)、农业、原始雨林和水(河流)进行分类。在这种情况下,我们需要预测多种情况,softmax 不会很好,因为它想选择一种情况。
拟人化你的激活功能(赋予个性) softmax 倾向于挑选一个特定的东西。
如果有多个标签,Fast.ai 库将自动切换到多标签模式。所以你什么都不用做。但这是幕后发生的事情:
**from** **planet** **import** f2 metrics=[f2]
f_model = resnet34label_csv = f'**{PATH}**train_v2.csv'
n = len(list(open(label_csv)))-1
val_idxs = get_cv_idxs(n)**def** get_data(sz):
tfms = tfms_from_model(f_model, sz,aug_tfms=transforms_top_down,
max_zoom=1.05)
**return** ImageClassifierData.from_csv(PATH, 'train-jpg',label_csv, tfms=tfms,suffix='.jpg', val_idxs=val_idxs, test_name='test-jpg')data = get_data(256) #gets images of 256x256
我们使用from_csv
,因为多标签分类不能使用 Keras 风格的方法,其中子文件夹是标签的名称。
transform_top_down
:它不仅仅是一个垂直翻转。正方形有八种可能的对称。它可以旋转 0 度、90 度、180 度和 270 度,并且可以翻转( 二面角 八组 )
x,y = next(iter(data.val_dl))
使用ds
(数据集)我们可以得到一个单独的图像*。*
dl
是一个数据加载器,它会给你一个小批量,特别是转换的小批量。使用数据加载器,您不能请求特定的小批量,您只能取回next
小批量。在 Python 中,它被称为“生成器”或“迭代器”。
PyTorch 真正利用了现代 Python 方法。如果你很了解 python,PyTorch 来的很自然。如果你不太了解 python,PyTorch 是学好 python 的一个很好的理由。
x
是小批量的图像,y
是小批量的标签。打印 y:
默认情况下,批大小为 64,可能的类为 17。
让我们看看0
的图片标签:
list(zip(data.classes, y[0]))
*[('agriculture', 1.0),
('artisinal_mine', 0.0),
('bare_ground', 0.0),
('blooming', 0.0),
('blow_down', 0.0),
('clear', 1.0),
('cloudy', 0.0),
('conventional_mine', 0.0),
('cultivation', 0.0),
('habitation', 0.0),
('haze', 0.0),
('partly_cloudy', 0.0),
('primary', 1.0),
('road', 0.0),
('selective_logging', 0.0),
('slash_burn', 1.0),
('water', 1.0)]*
这是农业,清晰,主要刀耕火种和水。
PyTorch 和 fast.ai 是把标签变成one-hot-encoded标签。如果实际标签是 dog,它看起来会像这样:
我们采用softmax
并与actuals
进行比较来实际预测。actuals
和softmax
之差加在一起给出了误差,即损失函数。
One-hot-encoding 对于排序来说效率非常低,所以我们将存储一个index
值(单个整数),而不是目标值的 0 和 1(y
)。如果您查看狗品种竞赛的y
值,您实际上不会看到一个由 1 和 0 组成的大列表,但是您会看到一个单一的整数。
PyTorch 在内部将索引转换为 one-hot-encoded vector(尽管您实际上永远看不到它)。PyTorch 对于热编码的和非热编码的有不同的损失函数——但是这些细节被 fast.ai 库隐藏了,所以你不必担心。但要意识到的一件很酷的事情是,我们正在为单标签分类和多标签分类做完全相同的事情。
*为 softmax 更改 log 的基数有意义吗?*不,改变基底只是神经网络可以轻松学习的线性缩放:
注意:图像只是数字的矩阵。图像被洗掉了,所以为了让它更明显(“亮一点”)我们乘以*1.4
。
像这样试验图像是很好的,因为这些图像一点也不像 ImageNet。你所做的绝大多数涉及卷积神经网络的事情实际上不会像 ImageNet 一样,它们将是医学成像,对不同种类的钢管、卫星图像等进行分类
sz=64data = get_data(sz)data = data.resize(int(sz*1.3), 'tmp')
我们不会用sz=64
来进行猫和狗的比赛,因为我们是从预先训练的 ImageNet 网络开始的,它开始时近乎完美。如果我们用 64 乘 64 的图像重新训练整个集合,我们会破坏已经非常好的权重。请记住,大多数 ImageNet 模型都是用 224×224 或 299×299 的图像训练的。
image net 中没有与上面的卫星图像相似的图像。imageNet 唯一有用的部分是发现边缘和渐变的第一层,发现纹理和重复图案的第二层。
当使用卫星图像时,首先用小图像进行训练效果很好。
learn = ConvLearner.pretrained(f_model, data, metrics=metrics)lrf=learn.lr_find()
learn.sched.plot()
适合的
使用[lr/9, lr/3, lr]
是因为图像不同于 ImageNet 图像,并且早期的图层可能没有接近它们需要的样子。
绘制损失图:
让我们先将图像尺寸翻倍至 128,然后调整,再翻倍至 256,这样可以达到 93.5%的准确率:
测试时间增加使我们达到 93.6%;
data = data.resize(int(sz*1.3), 'tmp')
这是做什么的?
当我们指定要应用什么转换时,我们传递一个大小:
tfms = tfms_from_model(f_model, sz,
aug_tfms=transforms_top_down, max_zoom=1.05)
数据加载器做的一件事是按需调整图像的大小。这与data.resize
无关。如果初始输入图像是 1000 乘 1000,读取 JPEG 并将其调整为 64 乘 64 比训练卷积网络花费更多的时间。
data.resize
告诉它我们不会使用比sz*1.3
更大的图像,所以遍历一次并创建这个大小的新 JPEG,它是矩形的,所以最小边的新 JPEG 的大小是sz*1.3
中心裁剪的。这类似于循环调整图像大小的批处理脚本。这会节省你很多时间。data.resize
是加速便利功能。
metrics=[f2]
我们没有使用accuacy
,而是使用了 F-beta ,这是一种衡量假阴性和假阳性的方法。我们使用它的原因是因为这个特别的 Kaggle 比赛想要使用它。查看 planet.py,了解如何使用 Skitlearn 创建自己的度量函数。这是最后打印出来的。在每个时期之后[ 0\. 0.08932 0.08218 **0.9324** ]
多标签分类的激活函数
Fast.ai 检查标签以查看一个图像是否有多个标签,并自动选择激活功能。
多标签分类的激活函数称为 sigmoid。
为了找到乙状结肠,选择exp
除以1+exp
,即:
0.01/1+0.01 = 0.01
0.08/1+0.08 = 0.07
这使得可以一次预测多种情况。如果某个值小于 0,则其 sigmoid 小于 0.5,如果大于 0,则其大于 0.5。
与其单独训练最后几层,不如从差别学习率开始训练?你可以跳过最后一层的训练,直接学习不同的学习率,但你可能不想这样做。卷积层都包含预先训练好的权重,所以不是随机的——对于接近 ImageNet 的东西,真的很好;对于不接近 ImageNet 的东西,有总比没有好。然而,我们所有完全连接的层都是完全随机的。因此,您总是希望通过先对它们进行一点训练,使完全连接的权重优于随机权重。否则,如果你直接去解冻,那么你实际上是在摆弄那些早期的层权重,而后面的层权重仍然是随机的——这可能不是你想要的。
解冻时,你想改变什么?内核(过滤器)和权重。训练的意思是设置过滤器和密集权重。密集权重用于完全连接的层,过滤器/内核权重用于卷积。另一方面,激活是根据权重和先前层激活或输入计算的。
如何获得 64 乘 64 的图像尺寸?这取决于变换。默认情况下,我们的变换采用最小的边缘,将其缩小,并为其拍摄中心裁剪,但当使用数据增强时,它会随机选择。
准确性不是模型试图优化的。它优化了损失函数,例如交叉熵,这个指标是打印出来的,让我们可以看到发生了什么。
当您使用差异学习率时,这三个学习率是否在各层之间均匀分布? fast.ai 库有一个“图层组”的概念。在类似 ResNet50 的东西中,有数百个层,您可能不希望编写数百个学习率,因此库为您决定如何拆分它们,最后一个总是指的是我们随机初始化并添加的完全连接的层,其余的学习率在层之间拆分。
可视化图层
learn.summary()*[('Conv2d-1',
OrderedDict([('input_shape', [-1, 3, 64, 64]),
('output_shape', [-1, 64, 32, 32]),
('trainable', False),
('nb_params', 9408)])),
('BatchNorm2d-2',
OrderedDict([('input_shape', [-1, 64, 32, 32]),
('output_shape', [-1, 64, 32, 32]),
('trainable', False),
('nb_params', 128)])),
('ReLU-3',
OrderedDict([('input_shape', [-1, 64, 32, 32]),
('output_shape', [-1, 64, 32, 32]),
('nb_params', 0)])),
('MaxPool2d-4',
OrderedDict([('input_shape', [-1, 64, 32, 32]),
('output_shape', [-1, 64, 16, 16]),
('nb_params', 0)])),
('Conv2d-5',
OrderedDict([('input_shape', [-1, 64, 16, 16]),
('output_shape', [-1, 64, 16, 16]),
('trainable', False),
('nb_params', 36864)]))*
...
conv2d-1
是层的名称。
‘input_shape’, [-1, **3, 64, 64**]
— PyTorch 在图像尺寸64, 64
前列出频道3
。按照这个顺序,一些 GPU 计算会运行得更快。这是一个 4 维的小批量。
-1
是指无论批量有多大,都可以改变。Keras 使用None
‘output_shape’ , [-1, **64**, **32, 32**]
64 是内核数,32 乘 32 是**步距。**像 maxpooling 一样改变大小。
一个非常小的数据集的学习率查找器返回奇怪的数字,图为空。学习率查找器将一次完成一个小批量。如果你有一个很小的数据集,就没有足够的小批量。所以诀窍是让你的批量非常小。
结构化和时间序列数据
有两种类型的数据集。
非结构化 —音频、图像、自然语言文本,其中一个对象内部的所有东西都是同一种东西——像素、波形幅度或单词。
结构化 —损益表,关于脸书用户的信息,其中每一列在结构上不同。“结构化”指的是列数据,您可能会在数据库或电子表格中找到,其中不同的列代表不同种类的事物,每行代表一个观察值。
结构化数据在学术界经常被忽视,因为如果你有一个更好的物流模型,它很难在花哨的会议记录中发表。但它让世界运转,让每个人都赚钱,提高效率。我们不会忽视它,因为我们正在进行实用的深度学习,Kaggle 也不会,因为人们把奖金放在 Kaggle 上,以解决现实世界的问题。
探索这种架构背后的动机是它与现实世界应用的相关性。工业中用于日常决策的大多数数据是结构化或时间序列数据。我们将通过实际的结构化数据问题来探索使用神经网络的端到端过程。
示例:
预测大型杂货连锁店的销售额。此处
使用商店、促销和竞争对手数据预测销售。此处
罗斯曼商店销售
进口:
**from** **fastai.structured** **import** *
**from** **fastai.column_data** **import** *
np.set_printoptions(threshold=50, edgeitems=20)
PATH='data/rossmann/'
structured
—不特定于 PyTorch,也用于机器学习课程中,在完全没有 PyTorch 的情况下进行随机森林。它可以在没有 Fast.ai 库的任何其他部分的情况下单独使用。
fastai.column_data
—允许我们用柱状结构化数据做 Fast.ai 和 PyTorch 之类的东西。
对于结构化数据,我们需要大量使用熊猫。不熟悉熊猫,这里的(用于数据分析的 Python)是熊猫作者写的一本好书。
创建数据集
除了提供的数据,我们还将使用 Kaggle 竞赛参与者收集的外部数据集,例如 google trends 和 weather。你可以在这里下载所有的。
有大量的数据预处理,这个笔记本包含了第三名获胜者的整个管道(分类变量的实体嵌入)。本课程不涉及数据处理,但机器学习课程会详细介绍,因为特征工程非常重要。
查看 CSV 文件:
StoreType
—你经常会得到一些列包含“代码”的数据集。代码是什么意思并不重要。远离了解太多,先看看数据怎么说。然后稍后查看经常随数据一起提供的数据字典。
连接表格
这是一个关系数据集,您已经将相当多的表连接在一起——这对于使用merge
的 Pandas 来说很容易做到:
fast.ai 还提供以下功能:
add_datepart(train, "Date", drop=False)
获取一个日期,提取一系列列,如“星期几”、“季度初”、“一年中的月份”等,并将它们全部添加到数据集中。
持续时间部分将计算距离下一个假期还有多长时间,距离上一个假期还有多长时间,等等。
保存包含数据的整个结构化文件:
joined.to_feather(f'{PATH}joined')
to_feather
:将一个熊猫的数据帧保存为“羽毛”格式,这种格式将它保存在 RAM 中,并将其转储到磁盘中。所以它真的真的很快。厄瓜多尔杂货比赛有 3.5 亿条记录,所以你会关心用羽毛节省多长时间,它需要 6 秒。
该数据包含特定日期商店中特定商品的销售数量。我们的目标是预测在未来的某一天,在某个特定的商店里会卖出多少这样的商品。
下一课
我们将列分为两种类型:分类的和连续的。
分类变量:是store_id
1 和store_id
2 在数值上没有关系。它们是类别。一周中的每一天;周一(第 0 天)和周二(第 1 天)的电子贸易条件分类数据将被热编码,并且
**连续变量:**像到最近的竞争对手的公里距离这样的东西是我们用数字处理的数字。连续数据将按原样馈入全连接层。
步骤:
- 创建验证集。
ColumnarModelData.from_data_frame
是我们加载列数据的方式。基本的 API 概念与图像识别相同。.get_learner
- 找出我们的最佳学习率并画出来。
.fit
带着一个metric
.fit
同一个cycle_len
感谢阅读!跟随@ itsmuriuki。
回归学习!
深度学习;个人笔记第 1 部分第 4 课:结构化学习,自然语言处理,协同过滤。辍学,嵌入,通过时间支持。
随着我对快速人工智能课程的第二次尝试,这个博客系列将会更新。以上是我的个人笔记;a 努力把事情理解清楚,解释好。没什么新意,只活出了这个 博客 。
Fast.ai 采取的方法是“这里是如何使用软件做一些事情,然后通过查看细节来查看幕后。”
拒绝传统社会的人
learn = ConvLearner.pretrained(arch, data, ps=0.5, precompute=True)
预计算来自最后一个卷积层的激活。激活是基于一些权重计算的数字,这些权重也被称为构成应用于先前层激活的核心或过滤器的参数,否则这些权重可能是其他计算的输入或结果。
通过键入学员对象的名称,您可以实际看到其中的层:
learn*Sequential(
(0): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True)
(1): Dropout(p=0.5)
(2): Linear(in_features=1024, out_features=512)
(3): ReLU()
(4): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True)
(5): Dropout(p=0.5)
(6): Linear(in_features=512, out_features=120)
(7): LogSoftmax()
)*
*Linear(in_features=1024, out_features=512)*
一个线性层意味着一个矩阵乘数。在这种情况下,我们有一个 1024 行 512 列的矩阵。它接收 1024 个激活,并发出 512 个激活。
ReLu
。用零代替负数。
*Linear(in_features=512, out_features=120)*
第二线性层获取第一线性层激活 512 个激活,并将它们通过新矩阵乘以 120 并输出 120 个激活。
*Softmax*
—一个激活函数,返回加起来为 1 的数字,每个数字都在 0 和 1 之间。由于较小的数值精度原因,直接取 softmax 的对数比 softmax 更好。这就是为什么当我们从模型中获得预测时,我们必须做np.exp(log_preds)
。
*Dropout(p=0.5)*
是做什么的?
应用 dropout 意味着在层中选择激活并删除它们。p
是删除一个激活的概率。输出激活变化不大。
随机丢弃一个层中的一半激活有一个有趣的效果,它迫使它不要过度拟合。如果一个特定的激活已经学习了确切的狗或猫,它迫使模型找到一个没有随机一半激活的表示。
如果你已经完成了所有的数据扩充,或者为模型提供了足够的数据,那么你能做的事情就很少了,但在很大程度上,你会陷入困境。Geofrey Hinton 等人提出了辍学,这完全是受大脑工作方式的启发。
你放弃了 1%的激活,这根本不会改变什么,因此它不会防止过度拟合,也就是说,不会一般化。
p=0.99
你会丢掉 99%的激活。它也不会过度拟合,因此对概括来说很好,但会杀死你的准确性。
高 *p*
值可以很好地概括,但会降低您的训练精度,低 *p*
值将不能很好地概括,但会提高您的精度。
在训练的早期,为什么验证损失比训练损失好,因为它没有看到数据集,你不希望损失好得多?这是因为当我们查看验证集时,我们关闭了辍学。当进行推理时,即预测时,我们希望尽可能使用最好的模型。训练中会出现辍学现象。
你需要做些什么来适应你丢弃激活的事实吗?我们不(fast.ai)当我们说p=0.5
在幕后 pytorch 扔掉一半的激活,并使激活加倍,因此平均激活没有变化。
在 Fast.ai 中,您可以传入ps
,这是所有添加层的p
值。它不会改变预训练网络中的辍学,因为它应该已经以某种适当的辍学水平被训练:
learn = ConvLearner.pretrained(arch, data, **ps=0.5**, precompute=True)
我们可以通过设置ps=0
来去除掉音。但是过了几个时代后,我们开始过度适应。训练损失小于验证损失。With ps=0
dropout 根本没有添加到模型中。
你可能已经注意到了,它已经增加了两个Linear
层。我们不必这样做。您可以设置xtra_fc
(额外全连接层)参数。你可以传递一个列表,告诉它你希望每个额外的全连接层有多大。您确实需要至少一个将卷积层的输出(本例中为 1096)转换为 120 个狗品种的类数,即由您的问题定义的类数:
如果xtra_fc
是空的,这意味着不要添加任何额外的线性层,只是我们必须有一个,从而给我们一个最小的模型。
默认情况下,我们是否应该使用特定的 p 值?对于第一层,我们有p=0.25
,对于第二层,我们有p=0.5
,这似乎适用于大多数情况。如果你发现它过量,继续增加,比如 0.2。如果不合适,减少它。
ResNet34 的参数较少,因此不会过度拟合,但对于像 ResNet50 这样的大型架构,通常需要增加压差。
有没有一种特别的方法可以确定它是否过度装配?是的,您可以看到培训损失远低于验证损失。你看不出是不是太过过度。零过拟合通常不是最佳的。您唯一要做的事情是降低验证损失,所以您需要尝试一些东西,看看是什么使验证损失降低。
*为什么平均激活很重要?*如果我们删除一半的激活,那么下一个将它们作为输入的激活也将减半,以及之后的所有激活。比如蓬松耳朵激活大于 0.6 就蓬松,现在大于 0.3 才蓬松,就是改了意思。这里的目标是在不改变含义的情况下删除激活。
*我们可以逐层设置不同的退学等级吗?*是的,这就是它被称为ps
的原因,我们可以传递一个数组:ps =[0.1,0.2]
还没有经验法则来确定何时前一层或后一层应该具有不同的漏失量。如果有疑问,对每个完全连接的层使用相同的压差。人们通常只在最后一个线性图层上放置 dropout。
*为什么显示器损耗而不是准确度?*对于验证集和训练集来说,损失是我们唯一可以看到的,我们能够对它们进行比较。我们将在后面了解到,损失是我们实际上正在优化的东西,因此更容易监控和理解这意味着什么。
通过添加辍学,我们似乎添加了一些随机噪声,这意味着我们没有做太多的学习,我们需要调整学习率吗?它对学习速度的影响似乎不足以引起注意。理论上,它可能会影响我们,但还不足以影响我们。
结构化和时间序列数据
数据中有两种类型的列:
分类—它有多个“级别”,例如StoreType
、Assortment
连续——它有一个数,该数的差或比有某种意义。例如CompetitionDistance
我们不会涵盖数据清理和功能工程,我们将假设已经完成。我们需要转换成与神经网络兼容的输入。
这包括将分类变量转换成连续的整数或一键编码,将连续特征标准化为标准常态,等等…
像year
、month
和day
这样的数字虽然我们可以把它们看作是连续的,但我们不必这样。当我们决定将一些事情分类时,我们是在告诉神经网络不同地对待每一个层次。但是,当说它是连续的时,我们是在告诉它用一个光滑的函数来拟合它们。
很多时候,事物实际上是连续的,但没有很多不同的层次(例如Year
、DayOfWeek
),把它们看作是分类的会更好。因为每一天都可能表现出不同的性质。
我们要说哪些变量是分类的,哪些是连续的,这是你必须做出的建模决定。
如果在你的数据中某个东西被编码为“a,b,c ”,你必须称它为分类的,如果它开始是连续的,你必须选择是分类的还是连续的。
注意,连续变量是实际的浮点数。浮点数有许多级别,即它有很高的基数,例如DayOfWeek
的基数是 7。
*你曾经 bin 过连续变量吗?*目前没有,但我们可以做的一件事,比如说Max_Temperature
,是分组为 0-10,10-20,20-30,称之为分类。一组研究人员发现,有时宁滨是有帮助的。
*如果你是用年份作为一个范畴,当一个模型遇到一个它从未见过的年份会发生什么?*将被视为未知类别。熊猫有一个特殊的类别叫做未知,如果它看到一个以前没有见过的类别,它会被视为未知。
*如果我们的训练数据集没有类别,但我们的测试不知道模型会做什么,它会预测吗?*它将是未知类别的一部分,它仍将预测它将处理 seen 后面的值 0,如果训练集中有未知,它将学会预测它们,如果没有,它将有一个随机向量。
n = len(joined); n
844338
我们有 844338 行,这是每个商店的日期。
通过cat_vars
循环,将适用的数据框列转换为分类列。
循环遍历contin_vars
并将它们设置为float32
(32 位浮点),因为这是 PyTorch 所期望的。例如Promo
、SchoolHoliday
从样本开始
我们倾向于从数据集的小样本开始。对于图像,这意味着将图像大小调整为 64 x 64 或 128 x 128。但是对于结构化数据,我们从随机的行样本开始。
因此给我们150000
行数据作为开始。
查看数据:
即使我们将一些列设置为“类别”,例如内部保存的‘StoreType’
、‘Year’.
,熊猫在笔记本中仍然显示为字符串。
proc_df
(处理数据帧)是一个快速人工智能功能,它:
取一个数据框,告诉它你的因变量是什么,把它放入一个单独的变量,并从原始数据框中删除它。换句话说,df
没有Sales
列,而y
包含了Sales
列。
do_scale
神经网络确实希望输入数据都在零附近,标准偏差在 1 左右。为了做到这一点,我们取数据,减去平均值,然后除以标准差。它返回一个特殊的对象mapper
,该对象跟踪它用于标准化的平均值和标准偏差,因此您可以在以后对测试集做同样的事情。
nas
它还处理缺失值,对于分类变量,它变成 ID: 0,其他类别变成 1、2、3 等等。对于连续变量,它用中值替换缺失值,并创建一个新的布尔值列,表明它是否缺失。
Before preprocessing
After preprocessing
例如,2014 年变为 2,因为分类变量已被从零开始的连续整数替换。原因是,我们稍后会将它们放入一个矩阵中,我们不希望矩阵有 2014 行长,因为它可能只有两行。
我们现在有一个不包含因变量的数据框架,其中所有内容都是一个数字。这就是我们需要进行深度学习的地方。
在这种情况下,我们需要预测接下来两周的销售额,因此我们应该创建一个验证集,它是我们训练集的最后两周。
在时间序列数据中,交叉验证不是随机的。相反,我们的维持数据通常是最新的数据,就像在实际应用中一样。这个问题在这里详细讨论。一种方法是将最后 25%的行(按日期排序)作为我们的验证集。
组装我们的模型
重要的是,你要对自己的衡量标准有一个很好的理解,即别人会如何评价你。在这场比赛中,我们将接受均方根百分比误差(RMSPE)的评判。
这意味着我们将实际销售额减去单个预测的预测值,求出其百分比,然后求出平均值。
当你取数据的对数时,得到均方根误差实际上会得到均方根百分比误差:
我们可以直接从外部数据框创建模型数据对象:
md = **ColumnarModelData.from_data_frame**(PATH, val_idx, df,
yl.astype(np.float32), cat_flds=cat_vars, bs=128,
test_df=df_test)
我们将从创建模型数据对象md
开始,它内置了验证集、训练集和可选测试集。从那里,我们将得到一个学习者,然后我们可以选择调用lr_find
,然后调用learn.fit
,以此类推。
这里的不同之处在于,我们没有使用ImageClassifierData.from_csv
或from_paths
,我们需要一种不同的模型数据,称为ColumnarModelData
,我们称之为from_data_frame
。
Path
指定存储模型文件的位置
val_idx
我们将放入验证集中的行的索引列表。
df
数据框是自变量。
yl
因变量,是proc_df
即yl = np.log(y)
返回的y
的日志
cat_flds
指定您希望被视为分类的事物。一切都变成了数字,除非我们指定,它会把它们都视为连续的。通过名单cat_vars
bs
批量大小
现在我们有了一个包含train_dl
、val_dl
、train_ds
、val_ds
等的标准模型数据对象。
创建适合我们模型数据的学习者:
m = md.get_learner(emb_szs, len(df.columns)-len(cat_vars),
0.04, 1, [1000,500], [0.001,0.01],
y_range=y_range)
0.04
:一开始要用多少退学者[1000,500]
:每层有多少激活[0.001,0.01]
:在后面的层使用多少个漏失- emb_szs 嵌入
嵌入
让我们暂时撇开分类变量,看看连续变量:
你永远不要把 ReLU 放在最后一层,因为 softmax 需要负数来创造低概率。
完全连接层的简单视图:
以秩 1 张量的形式接收输入,将其通过线性层(矩阵乘积),通过激活(ReLu),再次通过线性层,然后是 softmax,最后是输出。我们可以添加更多的线性层或辍学。
对于回归问题,跳过 softmax。
分类变量
例如,我们创建一个 7 行和 4 列的新矩阵,并用浮点数填充它。为了将“Sunday”添加到我们的具有连续变量的秩 1 张量中,我们查找这个矩阵,它将返回 4 个浮点数,我们将它们用作“Sunday”。
最初,这些数字是随机的。但是我们可以把它们放入神经网络,用梯度下降法更新它们,以减少损失。这个矩阵只是我们的神经网络中的另一组权重,称为嵌入矩阵
嵌入矩阵是指我们从零到该类别最大层数之间的整数开始。我们对矩阵进行索引以找到一个特定的行,并将它附加到我们所有的连续变量上,之后的一切都和之前一样(线性→ ReLU →线性等)。
那 4 个数字代表什么?它们只是我们正在学习的参数,碰巧最终会给我们带来好的损失。我们稍后会发现,这些特定的参数通常是人类可以解释的,而且非常有趣,但这是一个副作用。它们是我们正在学习的一组 4 个随机数。
你对嵌入矩阵的维数有好的启发吗?我们使用每个变量的基数(即其唯一值的数量)来决定其嵌入的大小。
上面是每个分类变量及其基数的列表。
我们一周有 7 天,但是看起来基数是 8,额外的基数是为了防止在测试集中有一个未知数,除以 2,因此有 4 个随机数。即使原始数据中没有缺失值,也应该留出一个值以备不时之需。年份也是 3,但我们为未知的 e.t.c .加 1
确定嵌入大小的经验法则是基数大小除以 2,但不大于 50。
查看存储,我们将有1116
个存储用于查找,返回长度为50
的秩 1 张量。我们必须为每个分类变量建立一个嵌入矩阵。
然后,我们将嵌入大小emb_szs
传递给get_learner
,这告诉学习者对于每个分类变量,该变量使用哪种嵌入。
m = md.get_learner(emb_szs, len(df.columns)-len(cat_vars),
0.04, 1, [1000,500], [0.001,0.01], y_range=y_range)
m.summary()
*除了 random 还有初始化嵌入矩阵的方法吗?*基本想法是,如果 Rossmann 的其他人已经训练了一个神经网络来预测奶酪销售,你也可以从他们嵌入的商店矩阵开始预测酒类销售。举例来说,Pinterest 和 Instacart 就是如此。Instacart 使用这种技术为他们的购物者选择路线,Pinterest 使用这种技术决定在网页上显示什么。他们有产品/商店的嵌入矩阵,在组织中共享,因此人们不必培训新的产品/商店。
*使用嵌入矩阵比一次热编码有什么优势?*对于上面的星期几示例,我们可以轻松地传递 7 个数字,而不是 4 个数字,例如[0,1,0,0,0,0,0],表示星期天。这也是一个浮动列表,将完全工作,这就是如何,一般来说,分类变量已被用于统计多年所谓的“虚拟变量”。问题是,星期天的概念只能与一个浮点数相关联。所以它得到了这种线性的行为,它说星期天或多或少是一件单一的事情。有了嵌入,星期天就是一个四维空间的概念。所发生的是这些嵌入向量倾向于得到这些丰富的语义概念。例如,如果发现周末有不同的行为,您会看到周六和周日会有一些特定的数字较高,或者例如一周中的某些天往往与较高的销售额相关联,例如在周五购买酒。
通过拥有更高维度的向量而不仅仅是单个数字,它给了深度学习网络学习这些丰富表示的机会。
嵌入的想法就是所谓的“分布式表示”——神经网络最基本的概念。这是一种想法,即神经网络中的概念具有难以解释的高维表示。这个向量中的这些数字不一定只有一个意义。如果这个是低的,那个是高的,这可能意味着一件事,如果那个是高的,那个是低的,这可能意味着另一件事,因为它经历了这个丰富的非线性函数。正是这种丰富的表征让它能够学习到如此有趣的关系。
*嵌入适合某些类型的变量吗?*嵌入适用于任何范畴变量。唯一不能很好工作的是基数太高的东西。如果你有 600,000 行,一个变量有 600,000 个级别,那就不是一个有用的分类变量。但总的来说,这场比赛的第三个获胜者真的决定了所有不太高的基数,他们把它们都定为绝对的。好的经验法则是,如果你能制造一个分类变量,你也可以,因为这样它就能学习丰富的分布式表示;如果你让它保持连续,它最多能做的就是尝试找到一个适合它的单一函数形式。
矩阵代数如何在幕后工作:
进行查找等同于在独热编码向量和嵌入矩阵之间进行矩阵乘积。
相乘后,你只剩下一个向量。现代的库实现这一点的方式是取一个整数并在数组中查找。
你能谈谈使用日期和时间作为分类,以及这如何影响季节性吗?以下代码从一个完整的日期时间中提取特定的日期字段,用于构造类别。在处理日期-时间时,您应该始终考虑这个特征提取步骤。如果不将您的日期-时间扩展到这些额外的字段中,您就无法在任何这些粒度上捕捉任何趋势/周期行为作为时间的函数。我们将为每个表添加一个日期字段。
add_datepart
函数接受一个数据框和一个列名。它可选地从数据框中移除该列,并用表示关于该日期的所有有用信息的许多列来替换它,例如星期几、月几、月几、是四分之一开始还是四分之一结束等等。我们最后列出了我们的功能:
例如,DayOfWeek
现在变成了 8 行乘 4 列的嵌入矩阵。从概念上讲,这允许我们的模型创建一些有趣的时间序列模型。如果有一个七天周期的东西,在周一上升,在周三下降,但只在柏林,它完全可以做到这一点,它有它需要的所有信息。
这是一个处理时间序列的绝妙方法。*您只需确保您的时间序列中的周期指标以列的形式存在。*如果没有一个名为星期几的列,神经网络将很难学会进行除法 mod 并在嵌入矩阵中查找。这不是不可能,但真的很难。
例如,如果您预测三藩市的饮料销售,您可能想要美国电话电报公司公园的球赛何时开始的列表,因为这将影响到索玛有多少人在喝啤酒。所以你需要确保基本指标或周期性在你的数据中,只要它们在,神经网络就要学会使用它们。
学习者
m = md.get_learner(emb_szs, len(df.columns)-len(cat_vars),
0.04, 1, [1000,500], [0.001,0.01], y_range=y_range)
m.summary()
emb_szs
嵌入尺寸
len(df.columns)-len(cat_vars)
数据帧中连续变量的数量。
0.04
嵌入矩阵有自己的漏失,这就是漏失率。
1
我们想要创造多少产出,即销售这一最后线性层的产出。
[1000, 500]
第一线性层和第二线性层的激活次数。
[0.001,0.01]
第一线性层和第二线性层的脱落。
我们有一个 leaner,让我们找出学习率(lr = 1e-3
):
合适的
从样本数据开始:
metrics
这是一个自定义指标,它指定了一个函数exp_rmspe
,该函数在每个时期结束时被调用并打印出结果。
拟合所有数据:
m.fit(lr, 1, metrics=[exp_rmspe], cycle_len=1)*[ 0\. 0.00676 0.01041 0.09711]*
通过使用所有的训练数据,我们得到 0.09711 的 RMSPE。这会让我们在排行榜上名列前茅。
所以这是一种处理时间序列和结构化数据的技术。有趣的是,与使用这种技术的小组分类变量的实体嵌入相比,第二名获胜者做了更多的特征工程。这场比赛的获胜者实际上是物流销售预测方面的专家,所以他们有自己的代码来创建大量的功能。
Pinterest 的人们建立了一个非常相似的推荐模型,他们也说*当他们从梯度推进机器转向深度学习时,他们做了更少的特征工程,这是一个更简单的模型,需要更少的维护。*所以这是使用这种方法进行深度学习的一大好处,你可以获得最先进的结果,但工作量要少得多。
*我们是否使用了任何时间序列?*间接的,是的。正如我们刚刚看到的,我们的列中有一个DayOfWeek
、MonthOfYear
等,它们中的大多数被视为类别,因此我们正在构建一月、星期日等的分布式表示。我们没有使用任何经典的时间序列技术,我们所做的只是神经网络中两个完全连接的层。与任何标准的时间序列技术相比,嵌入矩阵能够以更丰富的方式处理诸如星期几的周期性之类的事情。
图像模型和这个模型有什么区别?我们称呼get_learner
的方式有所不同。在成像中,我们只是做了Learner.trained
并传递数据:
learn = ConvLearner.pretrained(arch, data, ps=0., precompute=True)
对于这些类型的模型,事实上对于很多模型,我们建立的模型依赖于数据。在这种情况下,我们需要知道我们有什么嵌入矩阵。所以在这种情况下,数据对象创建了学习者:
m = md.get_learner(emb_szs, len(df.columns)-len(cat_vars), 0.04, 1,
[1000,500], [0.001,0.01], y_range=y_range)
步伐
第一步。列出分类变量名称,列出连续变量名称,并将它们放入 Pandas 数据帧中:
cat_vars = ['Store', 'DayOfWeek', 'Year', 'Month', 'Day', 'StateHoliday', 'CompetitionMonthsOpen',
'Promo2Weeks', 'StoreType', 'Assortment', 'PromoInterval', 'CompetitionOpenSinceYear', 'Promo2SinceYear',
'State', 'Week', 'Events', 'Promo_fw', 'Promo_bw', 'StateHoliday_fw', 'StateHoliday_bw',
'SchoolHoliday_fw', 'SchoolHoliday_bw']contin_vars = ['CompetitionDistance', 'Max_TemperatureC', 'Mean_TemperatureC', 'Min_TemperatureC',
'Max_Humidity', 'Mean_Humidity', 'Min_Humidity', 'Max_Wind_SpeedKm_h',
'Mean_Wind_SpeedKm_h', 'CloudCover', 'trend', 'trend_DE',
'AfterStateHoliday', 'BeforeStateHoliday', 'Promo', 'SchoolHoliday']
第二步。创建您希望在验证集中包含哪些行索引的列表:
val_idx = np.flatnonzero(
(df.index<=datetime.datetime(2014,9,17)) & (df.index>=datetime.datetime(2014,8,1)))
第三步。调用这一行代码:
md = ColumnarModelData.from_data_frame(PATH, val_idx, df,
yl.astype(np.float32), cat_flds=cat_vars, bs=128,
test_df=df_test)
第四步。创建一个列表,列出您希望每个嵌入矩阵有多大:
emb_szs = [(c, min(50, (c+1)//2)) for _,c in cat_sz]
emb_szs
[(1116, 50),
(8, 4),
(4, 2),
(13, 7),
(32, 16),
(3, 2),
(26, 13),
(27, 14),
(5, 3),
(4, 2),
(4, 2),
(24, 12),
(9, 5),
(13, 7),
(53, 27),
(22, 11),
(7, 4),
(7, 4),
(4, 2), ...
第五步。打电话给get_learner
——你可以用这些精确的参数开始,如果它不合适,你可以摆弄它们。
m = md.get_learner(emb_szs, len(df.columns)-len(cat_vars), 0.04, 1,
[1000,500], [0.001,0.01], y_range=y_range)
第六步。呼叫m.fit
m.fit(lr, 3, metrics=[exp_rmspe])
如何对这种类型的数据使用数据扩充,以及 dropout 是如何工作的?不知道。可能它必须是特定领域的,但他(杰里米)从未见过任何论文或行业中的任何人使用结构化数据和深度学习进行数据增强。他认为这是可以做到的,但还没有看到它的实现。dropout 实际上是抛出了一阶张量的一半激活。这也适用于嵌入矩阵。
有什么坏处?几乎没有人在用这个。为什么不呢?基本上答案是,学术界没有人在研究这个,因为这不是人们发表的东西。因此,还没有真正伟大的例子,人们可以看着并说“哦,这里有一种技术工作得很好,让我们的公司实现它吧”。但也许同样重要的是,直到现在有了这个 Fast.ai 库,还没有任何方法可以方便地做到这一点。如果您想要实现这些模型中的一个,您必须自己编写所有的定制代码。现在是 6 步流程。有很多巨大的商业和科学机会来利用这一点,解决以前没有很好解决的问题。
自然语言处理
这是深度学习最有前途的领域,比计算机视觉落后两三年。软件的状态和一些概念远没有计算机视觉成熟。
你在 NLP 中发现的一件事是你可以解决特定的问题,它们有特定的名字。在自然语言处理中有一种特殊的问题叫做**“语言建模”——**这意味着给定一个句子中的几个单词,建立一个模型来预测下一个单词会是什么。
例如,当你输入时,按下空格键,下一个单词是一个语言模型。
为了这个练习,我们从 arXiv.org 下载了 18 个月的论文
数据:
<cat>
—纸张的类别。CSNI 是计算机科学和网络。
<summ>
—论文摘要。
让我们看看一个经过训练的语言模型的输出,我们传递了类别,在这种情况下是 csni(计算机科学和网络)和一些启动文本:
该模型通过阅读 arXiv 论文了解到,某个写计算机网络的人会这样说话。记住,一开始根本不懂英语。它从随机的每个英语单词的嵌入矩阵开始。通过阅读大量的 arXiv 论文,它学会了跟随他人的是什么样的单词。
该模型不仅学会了如何很好地书写英语,而且在你说出类似“卷积神经网络”的东西后,它会使用括号来指定一个缩写词“(CNN)”。
我们将尝试创建一个预训练模型,用于执行其他一些任务。例如,给定 IMDB 电影评论,我们将计算出它们是正面的还是负面的。这是一个分类问题。
我们想使用一个预先训练的网络,至少知道如何阅读英语。因此,我们将训练一个预测句子下一个单词的模型,即语言模型,就像在计算机视觉中一样,在末尾添加一些新层,并要求它预测某事是积极的还是消极的。
基本上,创建一个语言模型,并使其成为分类模型的预训练模型。
*为什么直接做自己想做的事不会更好?*事实证明,它的表现并不理想。有几个原因。首先,我们知道微调一个预先训练好的网络是非常强大的。因此,如果我们能让它先学习一些相关的任务,那么我们就可以利用所有的信息来帮助它完成第二项任务。另一个原因是 IMDB 电影评论长达 1000 字。因此,在阅读了 1000 个单词(整数)后,你对英语的结构或单词或标点符号的概念一无所知,你得到的只是 1 或 0(肯定或否定)。试图学习英语的整个结构,然后用一个数字来表达积极和消极的情绪,这实在是太难了。通过建立语言模型,我们首先建立了一个理解电影评论英语的神经网络,然后我们希望它学习的东西将有助于决定评论是积极还是消极。
*这与卡帕西的夏尔-RNN 相似吗?*这有点类似于夏尔-RNN,它在给定多个前一个字母的情况下预测下一个字母。语言模型通常在单词级工作,但它们不是必须,我们将集中在单词级建模上。
这些生成的单词和句子在多大程度上是它在训练数据集中找到的内容的实际副本?这些单词肯定是它以前见过的单词,因为它不在字符级别,所以它只能给我们它以前见过的单词。句子,有几种严格的方法,但最简单的方法是看上面的例子,它创建了两个类别,看着它们,它创建了相似的概念。
最重要的是,当我们训练语言模型时,我们将有一个验证集,以便我们试图预测从未见过的东西的下一个单词。
文本分类示例:
- 对冲基金,可能会在文章或 Twitter 中找出过去导致市场大幅下跌的原因。
- 识别客户服务查询,这些查询往往与下个月取消订阅的人有关。
- 将 if 文档分类为它们是否属于法律查询的一部分。
IMDB
进口
PyTorch 的 NLP 图书馆。
数据
大型电影观看数据集包含来自 IMDB 的 50,000 条评论。数据集包含偶数个正面和负面评论。作者只考虑了高度两极分化的评论。负面评价得分≤ 4 分(满分 10 分),正面评价得分≥ 7 分(满分 10 分)。中立评论不包括在数据集中。数据集分为训练集和测试集。训练集是同样的 25,000 个带标签的评论。
情感分类任务包括预测给定文本的极性(积极或消极)。
然而,在我们尝试对情感进行分类之前,我们将简单地尝试创建一个语言模型;也就是可以预测句子下一个单词的模型。为什么?因为我们的模型首先需要理解英语的结构,然后我们才能期望它识别积极情绪和消极情绪。
因此,我们的攻击计划与我们用于狗对猫的计划相同:预训练一个模型来做一件事(预测下一个单词),并微调它来做其他事情(对情绪进行分类)。
不幸的是,没有好的预训练语言模型可供下载,所以我们需要创建自己的模型。
在这种情况下,我们没有单独的测试和验证。培训目录中有许多代表电影评论的文件:
看一个电影评论的例子:
让我们分别检查训练数据集和测试数据集中有多少个单词:
在我们分析文本之前,我们必须先对它进行 标记化 。这指的是将一个句子拆分成一个单词数组,或者更一般地说,拆分成一个记号数组的过程。
一个好的标记器会很好的识别你句子中的片段。每个分隔的标点符号都将被分隔,多部分单词的每个部分也将被适当分隔。下面是标记化的一个示例:
我们使用 Pytorch 的 torchtext 库来预处理我们的数据,告诉它使用 spacy 库来处理标记化。
创建一个字段
首先,我们创建一个 torchtext 字段,它描述了如何预处理一段文本——在本例中,我们告诉 torchtext 将所有内容都变成小写,并用 spacy 对其进行标记。
TEXT = data.Field(lower=True, tokenize="spacy")
lower=True
—小写文本
tokenize=spacy_tok
—用spacy_tok
来标记
我们利用LanguageModelData
为语言建模创建一个 ModelData 对象,向它传递我们的 torchtext 字段对象,以及我们的训练、测试和验证集的路径。在这种情况下,我们没有单独的测试集,所以我们也将使用VAL_PATH
。
bs=64; bptt=70FILES = dict(train=TRN_PATH, validation=VAL_PATH, test=VAL_PATH)
md = LanguageModelData.from_text_files(PATH, TEXT, **FILES, bs=bs, bptt=bptt, min_freq=10)
PATH
数据在哪里,模型就保存在哪里
TEXT
torchtext 关于如何预处理文本的字段定义。
我们拥有的所有文件的列表:培训、验证和测试
bs
批量大小
bptt
背道具穿越时间。意思是我们一次在 GPU 上贴多长的句子
min_freq=10
:一会儿,我们将把单词替换成整数,即每个单词都有一个唯一的索引。如果有什么单词出现次数少于 10 次,就叫不认识。
在构建了我们的ModelData
对象之后,它会自动为TEXT
对象填充一个非常重要的属性:TEXT.vocab
。这是一个词汇表,它存储了文本中出现过的单词(或记号),以及每个单词将如何映射到一个唯一的整数 id。我们以后还需要使用这些信息,所以我们保存了这些信息。
(技术说明:python 的标准 *Pickle*
库无法正确处理这个,所以在这个笔记本的顶部我们用了 *dill*
库代替,导入为 *pickle*
) 。
这是从整数 id 到唯一令牌的映射的开始:
vocab
让我们将一个单词与一个整数匹配,一个整数与一个单词匹配。我们将使用整数。
做词干分析或词干分析很常见吗?不完全是,不。一般来说,标记化是我们想要的。为了尽可能地概括,我们想知道接下来会发生什么,所以不管它是将来时还是过去式,是复数还是单数,我们都不知道哪些事情会变得有趣,哪些事情不会,所以似乎最好是尽可能不去管它。
在处理自然语言的时候,语境不重要吗?为什么我们要标记和查看单个单词?不,我们不是在看单个单词,它们仍然是有序的。只是因为我们用数字 12 代替了 I,它们还是那个顺序。
有一种不同的处理自然语言的方式叫做“单词袋”,他们确实抛弃了顺序和上下文。在机器学习课程中,我们将学习如何使用单词包表示法,但它们不再有用或即将变得不再有用。我们开始学习如何使用深度学习来恰当地使用上下文。
批量大小和回柱通过时间( BPTT)
在语言模型中发生的事情是,即使我们有许多电影评论,它们都被连接在一起成为一大块文本。因此,我们预测这个巨大的东西的下一个词是所有的 IMDB 电影评论串联在一起。
我们从一大块文本开始,例如 6400 万字。我们将串联的评论分成批次。在这种情况下,我们将把它分成 64 个部分。然后,我们将每个部分移动到前一部分的下方,并对其进行移调。我们最终得到一个 100 万乘 64 的矩阵。然后,我们在时间上抓取一个小块,这些块的长度大约等于 BPTT(这随着每个时期而变化)。我们抓取一个 70 长的小片段,这是我们第一次放入 GPU,也就是批处理。
我们的LanguageModelData
对象将创建具有 64 列的批处理(这是我们的批处理大小),以及大约 80 个令牌的不同序列长度(这是我们的bptt
参数- 通过时间反向传播)。
每个批次还包含与标签完全相同的数据,但在文本中晚了一个单词,因为我们总是试图预测下一个单词。标签被展平成一维数组。
我们通过用iter
包装数据加载器(md.trn_dl
)然后调用next
来获取第一批训练数据
我们得到一个 77 乘 64 的张量,大约有 70 行,但不完全是。
Torchtext 每次都会随机改变bptt
的数字,因此每个时期它都会获得稍微不同的文本比特——类似于计算机视觉中的混洗图像。我们不能随机打乱单词的顺序,因为它们需要有正确的顺序,所以相反,我们随机地移动它们的断点一点点。
77 代表第一篇影评的前 77 个字。
为什么不按句子拆分?不尽然。记住,我们使用的是列。所以我们的每一列都有大约一百万的长度,虽然这些列并不总是以句号结尾,但是它们太长了,我们并不在乎。每列包含多个句子。
创建模型
现在,我们有了一个模型数据对象,它可以为我们提供批处理,我们可以创建一个模型。首先,我们要创建一个嵌入矩阵。
4602
批次数量
34945
在vocab
中唯一令牌的数量(唯一字必须至少出现 10 次min_freq=10
,否则它们将被替换为Unk
)
1
数据集的长度,即整个语料库
20621966
语料库中的字数。
34945 用于创建嵌入矩阵:
每个单词都有一个嵌入向量。这是一个分类变量,它是一个基数很高的分类变量。这是 34945 分类变量,这就是为什么我们为它创建一个嵌入矩阵。
我们需要设置许多参数:
em_sz = 200 *# size of each embedding vector*
nh = 500 *# number of hidden activations per layer*
nl = 3 *# number of layers*
嵌入大小为 200,比我们之前的嵌入向量大得多。这并不奇怪,因为一个词比星期天的概念有更多的细微差别。
一般来说,一个单词的嵌入大小在 50 到 600 之间。
研究人员发现,大量的动量不能很好地与这些类型的 *RNN(递归神经网络)*模型一起工作,因此我们创建了一个版本的 Adam 优化器,其动量小于默认的0.9
。
opt_fn = partial(optim.Adam, betas=(0.7, 0.99))
Fastai 使用了由 Stephen Merity 开发的最新的 AWD·LSTM 语言模型的变体。该模型的一个关键特征是,它通过退出提供了出色的正则化。没有已知的简单方法(还没有!)要找到以下辍学参数的最佳值,您只需进行实验…
然而,其他参数(alpha
、beta
和clip
)通常不需要调整
如果你试图建立一个 NLP 模型,并且你是欠拟合的,那么减少所有这些漏失,如果是过拟合的,那么大约以这个比例增加所有这些漏失。
还有其他方法可以避免过度拟合。目前,learner.reg_fn = partial(seq2seq_reg, alpha=2, beta=1)
工作可靠,所以所有的 NLP 模型可能都需要这条特别的线。
learner.clip=0.3
当你查看你的梯度,将它们乘以学习率,决定你的权重更新多少,这不会让它们超过 0.3。防止我们迈出太大的步伐(高学习率)。
有一些单词嵌入其中,例如 Word2vec 或 GloVe。它们和这个有什么不同?为什么不一开始就用这些来初始化权重呢?人们在做各种其他任务之前已经预先训练了这些嵌入矩阵。它们不被称为预训练模型;它们只是一个预先训练好的嵌入矩阵,你可以下载。我们没有理由不下载它们。以这种方式建立一个完整的预训练模型似乎不会从使用预训练的单词向量中受益太多;在其他地方,使用整个预先训练的语言模型会产生更大的差异。也许我们可以把两者结合起来,使它们更好一点。
*模型的架构是什么?*它是一个 递归神经网络 使用一种叫做 **LSTM (长短期记忆)**的东西。
拟合模型
learner.fit(3e-3, 4, wds=1e-6, cycle_len=1, cycle_mult=2)
learner.save_encoder('adam1_enc')
learner.load_encoder('adam1_enc')learner.fit(3e-3, 1, wds=1e-6, cycle_len=10)
在情感分析部分,我们只需要语言模型的一半——编码器 ,所以我们保存这部分:
learner.save_encoder('adam3_10_enc')
learner.load_encoder('adam3_10_enc')
语言建模精度一般用度量 困惑 来衡量,简单来说就是我们使用的损失函数的exp()
。
math.exp(4.165)*64.3926824434624*pickle.dump(TEXT, open(f'**{PATH}**models/TEXT.pkl','wb'))
测试
我们可以对我们的语言模型进行一些试验,以检查它是否工作正常。首先,让我们创建一小段文本来“启动”一组预测。我们将使用 torchtext 字段对其进行数值化,这样我们就可以将其输入到我们的语言模型中。
我们还没有添加方法来简化语言模型的测试,所以我们需要手动完成以下步骤:
让我们看看短文后的下一个单词的十大预测是什么:
让我们看看我们的模型是否可以自己生成更多的文本:
情感分析
我们有一个预训练的语言模型,现在我们想对它进行微调,以进行情感分类。我们需要从语言模型中保存 vocab,因为我们需要确保相同的单词映射到相同的 id。
TEXT = pickle.load(open(f'{PATH}models/TEXT.pkl','rb'))
sequential=False
告诉 torchtext 应该对文本字段进行标记化(在这种情况下,我们只想存储‘正’或‘负’单个标签)。
我们不需要把整件事当作一大段文字,但每篇评论都是独立的,因为每篇评论都有不同的情感。
splits
是一个创建训练集、测试集和验证集的 torchtext 方法。IMDB 数据集内置于 torchtext 中,因此我们可以利用这一点。看一看lang_model-arxiv.ipynb
,了解如何定义自己的 fastai/torchtext 数据集。
Splits 允许我们查看单个标签t.label
和一些文本t.text
fastai 可以直接从 torchtext 分割中创建一个 ModelData 对象,我们可以在md2
上训练它:
get_model
获取我们的学习者,然后我们可以将预先训练好的语言模型m3.load_encoder(f’adam3_10_enc’)
加载到其中。
因为我们正在微调一个预训练的模型,我们将使用不同的学习率,并增加剪辑的最大梯度,以使 SGDR 更好地工作。
我们要确保除了最后一层之外的都被冻结了。然后我们训练一下,解冻它,训练一下。好的一面是,一旦你有了一个预先训练好的语言模型,它实际上训练得非常快。
Bradbury 等人最近的一篇论文,在翻译中学习:语境化的词向量,对解决 IMDB 情感分析问题的最新学术研究进行了方便的总结。这里展示的许多最新算法都是针对这个特定问题进行调整的。
正如你所看到的,我们刚刚在情感分析中获得了一个新的最先进的结果,将误差从 5.9%降低到了 5.5%!使用相同的基本步骤,您应该能够在其他 NLP 分类问题上获得类似的世界级结果。
有很多机会可以进一步改善这一点:
例如,我们可以开始训练查看大量医学期刊的语言模型,然后制作一个可下载的医学语言模型,然后任何人都可以使用它来微调医学文献的前列腺癌子集。
我们也可以将这与预先训练的单词向量相结合。
我们可以预先训练维基百科语料库语言模型,然后将其微调为 IMDB 语言模型,然后将其微调为 IMDB 情感分析模型,我们会得到比这更好的结果。
协同过滤
数据:
电影镜头数据
http://files . group lens . org/datasets/movie lens/ml-latest-small . zip
path='data/ml-latest-small/'
它包含了userId
电影movieId
rating
和timestamp
我们的目标将是一些我们以前没有见过的用户-电影组合,我们必须预测他们是否会喜欢它。这就是推荐引擎的构建方式。
让我们也来读一下电影名:
我们将(在下一课中)创建一种电影用户交叉标签:
感谢阅读!跟随@ itsmuriuki。
回归学习!