分位数回归简介
Distribution of the lengths of ant bodies, from Wikimedia Commons
普通最小二乘回归是应用最广泛的统计方法之一。然而,它是一个参数模型,依赖于经常不满足的假设。分位数回归对残差的分布不做任何假设。它也让你探索因变量和自变量之间关系的不同方面。
动机
分位数回归至少有两个动机:假设我们的因变量是双峰或多峰的,也就是说,它有多个峰。如果我们知道是什么导致了多模态,我们就可以分离那个变量并进行分层分析,但是如果我们不知道,分位数回归可能是好的。在这里,OLS 回归会像依赖平均值作为双峰分布中心性的度量一样具有误导性。
如果我们的 DV 是高度倾斜的,例如,收入在许多国家,我们可能会对预测中位数(第 50 百分位)或其他分位数感兴趣;就像我们通常报告中位数收入而不是平均数收入一样。
另一个例子是,我们真正感兴趣的是处于最高或最低分位数的人。例如,如果研究性传播疾病的传播,我们可能会记录一个人在给定时间段内拥有的性伴侣数量。我们可能最感兴趣的是预测有很多伴侣的人,因为他们将是传播疾病的关键部分。
对于那些对商业感兴趣的人来说,花在商店或网站上的钱可能是不真实的。作为商店的老板,您可能更感兴趣的是找出预测最高分位数的因素,而不是平均值。
示例:出生体重
预测低出生体重很重要,因为出生时体重低的婴儿比正常体重的婴儿更容易出现健康并发症。通常的方法是使用 OLS 回归将平均出生体重建模为各种因素的函数,或者对出生体重进行二分法或其他分类,然后使用某种形式的逻辑回归(正常或有序)。这两者都是不够的。
对平均值建模是不够的,因为,正如我们将看到的,不同的因素在对平均值和下分位数建模时是重要的。我们经常感兴趣的是预测哪些母亲可能生下体重最低的婴儿,而不是特定母亲群体的平均出生体重。
对因变量进行分类很少是一个好主意,主要是因为它丢弃了有用的信息,并且对不同类别的人一视同仁。低出生体重的典型临界值是 2.5 公斤。然而,这意味着出生时体重为 2.49 公斤的婴儿与出生时体重为 1.0 公斤的婴儿相同,而出生时体重为 2.51 公斤的婴儿与出生时体重为 4 公斤的婴儿相同。事实显然并非如此。
出生体重的一个模型(由 SAS 提供,改编自 Koenker)包括孩子的性别、母亲的婚姻状况、母亲的种族、母亲的年龄(作为二次方)、她的教育状况、她是否进行过产前护理,如果是的话,在哪个孕期,她是否吸烟,如果是的话,每天吸多少烟,以及她的体重增加(作为二次方)。
母亲的婚姻状况被编码为已婚与未婚;种族要么是黑人要么是白人(不清楚其他种族的母亲是否被排除在外),母亲的教育程度被编码为高中以下(参考类别),高中毕业,某个学院,或大学毕业。产前护理编码为无、第一个三个月(参考类别)、第二个
三个月或第三个三个月。母亲的体重增加和年龄集中在方法上。
我们现在可以看看这些变量在每个分位数的影响。最好的方法可能是图形化。该程序的输出是常规回归的 9 倍长(因为我们有 9 个分位数),阅读起来很费力。但是,可以打印出来。下面显示的 SAS 代码生成四个图形面板。第一个面板:
First panel of quantile regression plots
显示截取的效果,母亲是黑人,母亲结婚,孩子是男孩。截距是未婚白人妇女所生女婴的每个分位数的平均出生体重,该妇女受教育程度不到高中,不吸烟,是平均年龄,体重平均增加。这些婴儿中只有大约 5%的体重低于 2500 克的标准体重。黑人女性所生的婴儿比白人女性所生的婴儿更轻,这种影响在低端比在其他地方更大——差异在 5%瓦片处约为 280 克,在中位数
处为 180 克,在 95%瓦片处为 160 克。母亲已婚的婴儿比母亲未婚的婴儿体重更重,而且这种影响在各个分位数上相对稳定。男孩比女孩重,这种影响在高端更大:在 5%的比例下,男孩比女孩重约 50 克,但在 95%的比例下,差异超过 100 克。
下一个面板:
2nd panel of quantile regression plots
显示了产前护理的效果,以及教育的第一部分,可以用与第一个面板相似的方式阅读。
第三个小组:
3rd panel of quantile regression plots
显示了其他教育影响和吸烟的影响。第四个也是最后一个面板:
4th panel of quantile regression plot
显示了母亲年龄和体重增加的影响。最后两个有点难以解释,因为与线性效应相比,二次效应总是如此。改善这种混乱的一种方法是绘制
不同母亲年龄或体重增加的婴儿出生体重预测图,保持其他变量的平均值或最常见值不变。下面是完成这项工作的 SAS 代码。母亲体重增加的结果:
Plot for maternal weight gain
这是一个迷人的图表!请注意,极端分位数是二次效应显著的分位数。进一步注意,体重减轻或大量增加的母亲比体重增加适度的母亲有更高的机会生下低体重婴儿。此外,体重增加较多的女性生下超大婴儿的几率更高。这种发现证实了医学观点,但不是我们可以用普通最小二乘回归发现的。
我们可以对母亲的年龄做同样的事情,但是年龄的影响不是很大,二次效应很小,我们可以考虑通过消除它来简化模型。另一方面,如果文献说母亲年龄应该有很强的二次效应,那么要么这个数据集有些奇怪,要么我们有证据反驳这种说法。需要注意的一点是,这个数据集涵盖了有限的年龄范围——所有的母亲都在 18 岁到 45 岁之间。年轻和年长的年龄可能会产生强烈的影响。
用于分析出生体重数据的 SAS 代码
程序 1:基本分位数回归
proc quantreg ci=sparsity/iid algorithm=interior(tolerance=1.e-4)
data=new;
class visit MomEdLevel;
model weight = black married boy visit MomEdLevel MomSmoke
cigsperday MomAge MomAge*MomAge
MomWtGain MomWtGain*MomWtGain/
quantile= 0.05 to 0.95 by 0.05
plot=quantplot;
run;
程序 2:创建二次曲线
获取预测值:
**proc quantreg ci=sparsity/iid algorithm=interior(tolerance=1.e-4)
data=new;
class visit MomEdLevel;
model weight = black married boy visit MomEdLevel MomSmoke
cigsperday MomAge MomAge*MomAge
MomWtGain MomWtGain*MomWtGain/
quantile= 0.05 to 0.95 by 0.05;
output out = predictquant p = predquant;
run;**
然后我们对其进行子集划分,只得到其他值是它们的均值或模式的情况。首先,对于产妇年龄:
**data mwtgaingraph;
set predictquant;
where black = 0 and married = 1 and boy = 1 and MomAge = 0 and MomSmoke = 0 and visit = 3 and MomEdLevel = 3;
run;**
然后排序:
**proc sort data = mwtgaingraph;
by MomWtGain;
run;**
然后画出来。
**proc sgplot data = mwtgaingraph;
title ’Quantile fit plot for maternal weight gain’;
yaxis label = “Predicted birth weight”;
series x = MomWtGain y = predquant1 /curvelabel = “5 %tile”;
series x = MomWtGain y = predquant2/curvelabel = “10 %tile”;
series x = MomWtGain y = predquant5/curvelabel = “25 %tile”;
series x = MomWtGain y = predquant10/curvelabel = “50 %tile”;
series x = MomWtGain y = predquant15/curvelabel = “75 %tile”;
series x = MomWtGain y = predquant18/curvelabel = “90 %tile”;
series x = MomWtGain y = predquant19/curvelabel = “95 %tile”;
run;**
用于分位数回归的其他统计软件
我选择使用 SAS。但是,R 提供了 quantreg 包,Python 在 statsmodels 包中有分位数回归,STATA 有 qreg。其他软件也可能提供它。
结论
分位数回归对于不满足 OLS 回归假设的情况和感兴趣的是分位数的情况是一个有价值的工具。
Scikit Learn 简介:Python 机器学习的黄金标准
A comparison Scikit Learn’s many Machine Learning models
想获得灵感?快来加入我的 超级行情快讯 。😎
机器学习金牌
如果你打算用 Python 做机器学习, Scikit Learn 是金标准。Scikit-learn 提供了监督和非监督学习算法的广泛选择。最棒的是,它是迄今为止最简单、最干净的 ML 库。
Scikit learn 是以软件工程的思维方式创建的。它的核心 API 设计围绕着易于使用、功能强大,并且仍然保持研究工作的灵活性。这种健壮性使得它非常适合用于任何端到端的 ML 项目,从研究阶段一直到生产部署。
Scikit Learn 提供了什么
Scikit Learn 建立在几个常见的数据和数学 Python 库之上。这样的设计使得它们之间的集成变得非常容易。您可以将 numpy 数组和 pandas 数据帧直接传递给 Scikit 的 ML 算法!它使用以下库:
Scikit Learn 专注于机器学习,例如数据建模。It 与数据的加载、处理、操作和可视化无关。因此,对于那些额外的步骤,使用上面的库,尤其是 NumPy,是自然和常见的做法;他们是天生的一对!
Scikit 强大的算法产品包括:
- ****回归:拟合线性和非线性模型
- ****聚类:无监督分类
- ****决策树:用于分类和回归任务的树归纳和修剪
- ****神经网络:用于分类和回归的端到端训练。可以很容易地在元组中定义层
- ****支持向量机:用于学习决策边界
- 朴素贝叶斯:直接概率建模
除此之外,它还有一些其他库通常不提供的非常方便和高级的功能:
- 集成方法: Boosting、Bagging、随机森林、模型投票和平均
- 特征操作:降维、特征选择、特征分析
- ****异常值检测:用于检测异常值和剔除噪声
- ****模型选择和验证:交叉验证、超参数调整和指标
味觉测试
为了让您体验使用 Scikit Learn 来训练和测试 ML 模型是多么容易,这里有一个关于决策树分类器的示例。
在 Scikit Learn 中,用于分类和回归的决策树非常容易使用,内置了一个类。我们将首先加载我们的数据集,它实际上是内置在库中的。然后我们将初始化我们的分类决策树,这也是一个内置的类。跑步训练是一个简单的小程序!.fit(X, Y)
函数训练模型,其中 X 是输入的 numpy 数组,而 Y 是相应的输出的 numpy 数组
Scikit Learn 还允许我们使用 graphviz 库可视化我们的树。它附带了一些选项,有助于可视化模型学习到的决策节点和拆分,这对理解其工作原理非常有用。下面我们将根据特性名称给节点着色,并显示每个节点的类别和特性信息。
除此之外,Scikit Learn 的文档非常精美!每个算法参数都有清晰的解释和直观的命名。此外,他们还提供带有示例代码的教程,介绍如何训练和应用该模型、其优缺点以及实际应用技巧!
喜欢学习?
在 twitter 上关注我,我会在这里发布所有最新最棒的人工智能、技术和科学!也在 LinkedIn 上与我联系!
用 Python 例子介绍 t-SNE
简介
我一直对学习充满热情,并认为自己是一个终身学习者。作为一名数据科学家,在 SAS 工作让我能够学习和尝试我们定期向客户发布的新算法和功能。通常,算法在技术上并不新,但对我来说是新的,这让我觉得很有趣。
最近,我有机会了解更多关于 t 分布随机邻居嵌入(t-SNE)的知识。在这篇文章中,我将给出 t-SNE 算法的一个高层次的概述。我还将分享一些示例 python 代码,其中我将对数字和 MNIST 数据集使用 t-SNE。
什么是 SNE 霸王龙?
t-分布式随机邻居嵌入(t-SNE)是一种无监督的非线性技术,主要用于数据探索和可视化高维数据。更简单地说,t-SNE 给你一种感觉或直觉,告诉你数据是如何在高维空间中排列的。它是由劳伦斯·范德·马滕斯和杰弗里·辛顿在 2008 年开发的。
t-SNE vs PCA
如果你熟悉主成分分析 (PCA),那么和我一样,你可能想知道 PCA 和 t-SNE 之间的区别。首先要注意的是,PCA 是在 1933 年开发的,而 t-SNE 是在 2008 年开发的。自 1933 年以来,数据科学领域发生了很多变化,主要是在计算和数据规模方面。第二,PCA 是一种线性降维技术,它寻求最大化方差并保持大的成对距离。换句话说,不同的事物最终会相距甚远。这可能导致较差的可视化,尤其是在处理非线性流形结构时。把流形结构想象成任何几何形状,比如:圆柱形、球形、曲线形等等。
t-SNE 不同于 PCA,它只保留小的成对距离或局部相似性,而 PCA 关心的是保留大的成对距离以最大化方差。劳伦斯使用图 1 [1]中的瑞士滚动数据集很好地说明了 PCA 和 t-SNE 方法。您可以看到,由于这个玩具数据集(流形)的非线性和保留大距离,PCA 将错误地保留数据的结构。
Figure 1 — Swiss Roll Dataset. Preserve small distance with t-SNE (solid line) vs maximizing variance PCA [1]
t-SNE 的工作原理
现在我们知道了为什么我们可能使用 SNE 霸王龙而不是 PCA,让我们讨论一下 SNE 霸王龙是如何工作的。t-SNE 算法计算高维空间和低维空间中的实例对之间的相似性度量。然后,它尝试使用成本函数来优化这两个相似性度量。让我们把它分成 3 个基本步骤。
1.步骤 1,度量高维空间中的点之间的相似性。想象一下散布在 2D 空间中的一堆数据点(图 2)。对于每个数据点(xi ),我们将在该点上以高斯分布为中心。然后,我们测量高斯分布下所有点的密度(xj)。然后对所有点进行重正化。这给了我们所有点的一组概率(Pij)。这些概率与相似性成正比。这意味着,如果数据点 x1 和 x2 在这个高斯圆下有相等的值,那么它们的比例和相似性是相等的,因此你在这个高维空间的结构中有局部相似性。高斯分布或圆可以使用所谓的困惑进行操作,这将影响分布的方差(圆的大小)以及最近邻的数量。困惑的正常范围在 5 到 50 之间[2]。
Figure 2 — Measuring pairwise similarities in the high-dimensional space
2.第 2 步与第 1 步类似,但不是使用高斯分布,而是使用一个自由度的学生 t 分布,也称为柯西分布(图 3)。这给了我们低维空间中的第二组概率(Qij)。正如你所看到的,学生 t 分布比正态分布有更重的尾部。厚重的尾部可以更好地模拟相距较远的距离。
Figure 3 — Normal vs Student t-distribution
3.最后一步是,我们希望这些来自低维空间(Qij)的概率集尽可能好地反映高维空间(Pij)的概率集。我们希望这两个映射结构相似。我们使用 Kullback-Liebler 散度(KL)来度量二维空间的概率分布之间的差异。我不会过多地讨论 KL,只知道它是一种不对称的方法,可以有效地比较大的 Pij 和 Qij 值。最后,我们使用梯度下降来最小化我们的 KL 成本函数。
t-SNE 的使用案例
现在你知道了 t-SNE 是如何工作的,让我们快速讨论一下它在哪里使用。Laurens van der Maaten 在他的视频演示中展示了许多例子[1]。他提到了 t-SNE 在气候研究、计算机安全、生物信息学、癌症研究等领域的应用。t-SNE 可以用于高维数据,然后这些维度的输出成为其他分类模型的输入。
此外,t-SNE 可以用来调查,学习,或评估分割。很多时候,我们在建模之前选择分段的数量,或者在结果之后迭代。t-SNE 常常能在数据中显示出清晰的分离。这可以在使用您的分段模型选择聚类数之前使用,或者在评估您的分段实际上是否成立之后使用。然而,t-SNE 不是一种聚类方法,因为它不像 PCA 那样保留输入,并且值可能经常在运行之间改变,所以它纯粹是为了探索。
代码示例
下面是一些 python 代码(下图带有 GitHub 的链接),在这里你可以看到 PCA 和 t-SNE 在数字和 MNIST 数据集上的可视化比较。我选择这两个数据集是因为维度的不同,因此结果也不同。我还在代码中展示了一项技术,您可以在运行 t-SNE 之前运行 PCA。这样做可以减少计算量,你通常会减少到 30 维左右,然后运行 t-SNE。
我使用 python 并调用 SAS 库来运行这个。它可能看起来与你习惯的略有不同,你可以在下面的图片中看到。我用 Seaborn 来做我的视觉效果,我认为这很棒,但是用 t-SNE 你可能会得到非常紧凑的集群,需要放大。如果您需要放大或操纵绘图对象,另一个可视化工具(如 Plotly)可能会更好。此外,假设您使用 Matplotlib,那么在绘图之前进行简单的%matplotlib notebook
调用也是可行的。
在 GitHub 查看完整的笔记本,这样你就可以看到中间的所有步骤并获得代码:
步骤 1——加载 Python 库。创建到 SAS 服务器的连接(称为“CAS”,它是一个分布式内存引擎)。加载 CAS 动作集(把它们想象成库)。读入数据并查看形状。
第 2 步——到目前为止,我仍然在我的本地机器上工作。我将把这些数据加载到我提到的 CAS 服务器中。这有助于我利用分布式环境并提高性能效率。然后我对数字和 MNIST 数据进行主成分分析。
第 3 步——可视化两位数和 MNIST 的 PCA 结果
PCA actually does a decent job on the Digits dataset and finding structure
As you can see PCA on the MNIST dataset has a ‘crowding’ issue
步骤 4——现在让我们尝试与上面相同的步骤,但是使用 t-SNE 算法
现在是 MNIST 数据集…
结论
我希望你喜欢这个概述和 t-SNE 算法的例子。我发现 t-SNE 是一个非常有趣和有用的可视化工具,因为几乎所有我处理过的数据都是高维的。我会把我发现的超级有用的资源贴在下面。对我来说,最好的资源是劳伦斯的 YouTube 视频。差不多 1 个小时,有点长,但是解释得很好,我找到了最清晰的详细解释。
我发现有用的其他资源:
1.t-SNE vs PCA:https://www . quora . com/What-advantages-the-t-SNE 算法优于 PCA
2.kull back-Liebler Divergence:https://en . Wikipedia . org/wiki/kull back % E2 % 80% 93 lei bler _ Divergence
3.t-SNE 维基百科:https://en . Wikipedia . org/wiki/T-distributed _ random _ neighbor _ embedding
4.t-SNE 演练:https://www . analyticsvidhya . com/blog/2017/01/t-SNE-implementation-r-python/
5.好的超参数信息:https://distill.pub/2016/misread-tsne/
6.劳伦·范德·马腾的 GitHub 页面:https://lvdmaaten.github.io/tsne/
7.t-SNE 动作集和信用的 SAS 开发者: Jorge Silva
参考文献
1.YouTube。(2013 年 11 月 6 日)。使用 t-SNE[视频文件]可视化数据。从 https://www.youtube.com/watch?v=RJVL80Gg3lA取回
2.范德马滕和辛顿。使用 t-SNE 可视化高维数据。机器学习研究杂志 9(十一月):2579–2605,2008。
MXNet API 介绍—第 4 部分
在第三部中,我们建立并训练了我们的第一个神经网络。我们现在知道的足够多,可以举更高级的例子。
最先进的深度学习模型非常复杂。他们有数百层**,需要几天——如果不是几周——来训练大量的数据。构建和调整这些模型需要大量的专业知识。**
幸运的是,使用这些模型要简单得多,只需要几行代码**。在本文中,我们将使用一个名为 Inception v3 的预训练图像分类模型。**
盗梦空间 v3
Inception v3 发布于 2015 年 12 月,是 GoogleNet 模型的一个进化版本(该模型赢得了 2014 ImageNet 挑战赛)。我们不会深入研究论文的细节,但转述其结论,Inception v3 比当时可用的最佳模型精确 15-25%,同时在计算上便宜 6 倍,使用的参数至少少 5 倍(即使用模型需要更少的 RAM)。
那真是一头野兽。那么我们如何让它发挥作用呢?
MXNet 模型动物园
模型动物园是一群预先训练好的模型随时可以使用。你会发现模型定义**、模型参数(即神经元权重)和指令(可能)。**
让我们下载定义和参数(您可能需要更改文件名)。随意打开第一个文件:你会看到所有层的定义。第二个是二进制文件,别管它;)
$ wget [http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-symbol.json](http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-symbol.json)$ wget [http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-0126.params](http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-0126.params)$ mv Inception-BN-0126.params Inception-BN-0000.params
由于这个模型已经在 ImageNet 数据集上训练过,我们还需要下载相应的图像类别列表(其中 1000 个)。
$ wget [http://data.dmlc.ml/models/imagenet/synset.txt](http://data.dmlc.ml/models/imagenet/synset.txt)$ wc -l synset.txt
1000 synset.txt$ head -5 synset.txt
n01440764 tench, Tinca tinca
n01443537 goldfish, Carassius auratus
n01484850 great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias
n01491361 tiger shark, Galeocerdo cuvieri
n01494475 hammerhead, hammerhead shark
好了,完成了。现在我们开始工作吧。
加载模型
我们需要做的是:
- 从保存的状态加载模型:MXNet 称之为检查点**。作为回报,我们得到输入符号和模型参数。**
import mxnet as mx
sym, arg_params, aux_params = mx.model.load_checkpoint('Inception-BN', 0)
- 创建一个新的模块,并将其指定为输入符号。我们还可以使用一个上下文参数来指示我们想要在哪里运行模型:默认值是 cpu(0) ,但是我们将使用 gpu(0) 在 gpu 上运行它。
mod = mx.mod.Module(symbol=sym)
- 将输入符号*绑定到输入数据。我们称它为‘data ’,因为这是它在网络的输入层中的名字(请看 JSON 文件的前几行)。*
- 将“数据”的形状定义为 1 x 3 x 224 x 224。不要慌;)‘224 x 224’是图像分辨率,模型就是这么训练的。‘3’是通道数:红、绿、蓝(按此顺序)。“1”是批量大小:我们将一次预测一个图像。
mod.bind(for_training=False, data_shapes=[('data', (1,3,224,224))])
- 设置模型参数。
mod.set_params(arg_params, aux_params)
这就够了。四行代码!现在需要把一些数据放进去,看看会发生什么。嗯……还没有。
准备我们的数据
数据准备:让我们的生活变得悲惨自从七十年代以来…从关系数据库到机器学习到深度学习,在这方面没有什么真正改变。很无聊但是很有必要。让我们完成它。
请记住,该模型需要一个 4 维的n 数组来保存一张 224 x 224 图像的红色、绿色和蓝色通道。我们将使用流行的 OpenCV 库从我们的输入图像构建这个n 数组。如果没有安装 OpenCV,运行“pip install OpenCV-python”在大多数情况下应该足够了:)
以下是步骤:
- ****读取图像:这将返回一个 numpy 数组,形状为(图像高度,图像宽度,3),三个通道按照 BGR 的顺序排列(蓝、绿、红)。
img = cv2.imread(filename)
- ****将图像转换为 RGB 。
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
- ****将图像调整到 224 x 224 。
img = cv2.resize(img, (224, 224,))
- ****将数组从(图像高度,图像宽度,3)整形为(3,图像高度,图像宽度)。
img = np.swapaxes(img, 0, 2)
img = np.swapaxes(img, 1, 2)
- 添加一个第四维并构建n 数组
img = img[np.newaxis, :]
array = mx.nd.array(img)>>> print array.shape
(1L, 3L, 224L, 224L)
头晕?让我们看一个例子。这是我们的输入图片。
Input picture 448x336 (Source: metaltraveller.com)
一旦经过处理,这张图片已经被调整大小并被分割成 RGB 通道,存储在数组【0】(这里的是用来生成下面图像的代码)。
array[0][0] : 224x224 red channel
array[0][1] : 224x224 green channel
array[0][2] : 224x224 blue channel
*如果批处理大小大于 1,那么我们将在*数组【1】中有第二个图像,在数组【2】中有第三个图像,依此类推。
这很有趣吗?现在我们来预测一下!
预测
您可能还记得第三部分中的内容,一个模块对象必须在批次中向一个模型提供数据:通常的做法是使用一个数据迭代器(具体来说,我们使用了一个 NDArrayIter 对象)。
在这里,我们想要预测一个单个图像,所以尽管我们可以使用数据迭代器,但这可能是多余的。相反,我们将创建一个名为 tuple 的,名为 Batch ,当它的数据属性被引用时,它将通过返回我们的输入 NDArray 来充当伪迭代器。****
from collections import namedtuple
Batch = namedtuple('Batch', ['data'])
现在我们可以把这个“批”传递给模型,让它预测。
mod.forward(Batch([array]))
该模型将输出一个n 数组,包含 1000 个概率,对应于 1000 个类别。它只有一行,因为批大小等于 1。
prob = mod.get_outputs()[0].asnumpy()>>> prob.shape
(1, 1000)
让我们用挤压()把这个变成一个数组。然后,使用 argsort (),我们创建第二个数组,保存这些概率的索引**,按照降序排序。**
prob = np.squeeze(prob)>>> prob.shape
(1000,)
>> prob
[ 4.14978594e-08 1.31608676e-05 2.51907986e-05 2.24045834e-05
2.30327873e-06 3.40798979e-05 7.41563645e-06 3.04062659e-08 *etc.*sortedprob = np.argsort(prob)[::-1]>> sortedprob.shape
(1000,)
根据模型,这张图最有可能的类别是 #546 ,概率 58% 。
>> sortedprob
[546 819 862 818 542 402 650 420 983 632 733 644 513 875 776 917 795
*etc.* >> prob[546]
0.58039135
让我们找到这个类别的名称。使用 synset.txt 文件,我们可以构建一个类别列表**,并在索引 546 处找到它。**
synsetfile = open('synset.txt', 'r')
categorylist = []
for line in synsetfile:
categorylist.append(line.rstrip())>>> categorylist[546]
'n03272010 electric guitar'
第二高的类别呢?
>>> prob[819]
0.27168664
>>> categorylist[819]
'n04296562 stage
很好,你不觉得吗?
所以你走吧。现在你知道如何使用一个预先训练好的,最先进的模型进行图像分类。它只需要 4 行代码 …剩下的只是数据准备。
你可以在下面找到完整的代码。玩得开心,请继续关注:D
接下来:
如何使用 Selenium 和 Docker 刮测网站
你想不费吹灰之力就能访问大量数据并测试你的网站吗?你是否有堆积如山的计算机程序可以处理的在线繁重工作?然后你需要浏览器自动化和 Selenium WebDriver 。Selenium 是测试和抓取 Javascript 呈现的网页的首要工具,本教程将涵盖在任何操作系统上设置和使用它所需的一切。Selenium 依赖项可以下载到运行在 Linux 虚拟机上的 Docker 容器中;因此,这些技术被介绍和讨论。最后,介绍了 Docker 中的编程,以及设置 Selenium 并将其绑定到 RStudio 的分步协议。准备好进入浏览器自动化的世界吧,在那里你单调乏味的任务将被委托给一个守护进程,你的前额叶官能将被解放出来专注于哲学。
为什么
为什么要用 Selenium 来自动化浏览器?
为什么要使用 Selenium 来自动化 web 浏览器?如上所述,两个主要原因是 web 测试和数据抓取。如果没有网络测试,像苹果这样的公司的程序员将无法在新功能上线前检查它们是否如预期那样工作,这可能会给用户带来不幸的错误,(就像发生在 iOS 12 T21 更新中的那些错误一样)。虽然当苹果这样的公司发布有问题的软件时,客户通常会感到震惊,但 iPhone 的复杂性和每次更新的新功能或更新功能的数量(iOS 12 几乎有 100 个)至少极有可能发生一些事故。不仅每个新组件都必须测试,而且它与手机其他部分的交互也必须检查。
然而,通过彻底的测试可以避免错误,这就是浏览器自动化的用武之地。虽然手动测试仍然是测试协议不可或缺的组成部分,但是完全手动测试如此多的复杂功能及其交互是不切实际的。有了浏览器自动化,用例可以在不同的环境中被测试成千上万次,从而找出只在不寻常的情况下才会出现的错误。然后,当苹果推出另一个重大更新时,它可以重新运行一个保存的测试协议,而不是设计一个新的,称为回归测试。因此,自动化测试允许公司提高客户满意度并避免错误。
Photo by Jeremy Bezanger on Unsplash
在编程环境中驱动 web 浏览器的第二个原因是 web 抓取,这是从 web 页面中提取内容以便在您自己的项目或应用程序中使用的过程。虽然 web 抓取可以在没有 Selenium 这样的 web 驱动程序的情况下执行,但这种工具的功能是有限的。这些“无驱动”包,包括 R 中的 Rvest 和 Python 中的 beautiful soup ,不能在浏览器中执行 Javascript,因此不能访问任何 Javascript 渲染的元素。虽然他们可以下载一个网站的源代码作为 HTML 文档,但是他们不能访问任何来自用户交互的数据。这是由于 HTML、CSS 和 Javascript 合作构建现代网页的特殊方式。
当你第一次打开一个网站时,你看到的内容来自于它的源代码,在谷歌 Chrome 中按 Ctrl+Shift+I 就可以随时查看,这个源代码主要是用 HTML 和 CSS 编写的,前者负责网站的结构,后者负责网站的风格。虽然对 HTML 和 CSS 的详细讨论超出了本文的范围,但我们需要知道的是, HTML 标签和 CSS 选择器格式化 web 元素,两者的结合赋予每个 web 元素自己唯一的标识符。这些唯一的标识符允许无驱动 web 抓取器区分 web 元素,并仅从源代码中提取相关信息。
然而,当用户与网站交互时,Javascript 会形成新元素并改变现有元素。这些元素不包含在页面的源代码中,因此不能被上面提到的包访问。例如,大多数网站要求用户在访问信息前登录,这意味着登录后的一切都将超出无人驾驶网络抓取器的范围。如果互联网真的是“信息高速公路”,源代码只是一个入口,它在入口就结束了。有了 Selenium,只要我们知道如何驾驶,我们就可以驶向目的地数据,人口无限。
Photo by Nick Fewings on Unsplash
怎么做
虚拟机和容器
Selenium 不能简单地下载和运行,因为它需要 1)特定的库和 2)特定的操作系统(例如,它不能在 OS X 上运行)。虽然在过去虚拟机(VM)提供了这样的依赖关系,但是今天使用容器和 Docker 的组合要高效得多。为了理解为什么,我们首先需要回顾一下这些工具的用途和基础。
虚拟机是一台计算机在另一台计算机中的模拟。一台主机内可以运行多个虚拟机,主机和仿真之间的交互由一个 虚拟机管理程序 管理。因为虚拟机包含了物理计算机所做的一切,所以它们经常被用来" T6 "测试新的操作系统(OS ),而不必购买新的硬件。其原因是,尽管许多人认为操作系统实际上是可以互换的,但它们之间可能存在显著差异。操作系统不仅仅是主板上的一个徽章;相反,它是您必须用来让硬件做您想要做的事情的工具集。如果操作者是一个画家,那么操作系统就是画笔,是从思想到现实的翻译者,可以限制或扩展可能性。因此,由于您选择的操作系统可能会产生深远的影响,因此轻松测试新操作系统的能力是非常宝贵的。
虽然虚拟机对于测试新的操作系统仍然有用,但是容器是为软件提供库的更好的方法。因为你下载的每个 VM 都有自己的操作系统,使用一个来简单地提供库就像为周末野营旅行打包你的整个衣柜:当然你可能有你需要的一切,但是四个纽带和翼尖看起来肯定是多余的。(也许就拿四分之一布洛克鞋吧,老兄。)这就是容器的用武之地。如果虚拟机是贪婪的过度包装者,容器就是残酷的随身携带的填鸭式包装,因为每个容器只带有运行一个软件所必需的库。机器上的每个容器共享 1 (Linux) OS ,这大大减少了它们的存储空间和运行时间。但是他们从哪里得到这个 Linux 操作系统来运行呢?这就是 Docker 的用武之地。
码头工人
Docker 是运行和分发容器的领先软件,它的主要目的是提供容器运行的 Linux 操作系统。这个 Linux 操作系统在 Windows 和 Mac 中由每个操作系统的本机管理程序管理(分别是 Hyper-V 和 HyperKit )。因此,在启动和运行时,容器可以利用 Linux 操作系统的各个方面,包括它的文件系统和内核。两个重要的 Linux 组件,守护进程和命令行接口(CLI) ,共同组成了 Docker 引擎 ,它用于执行容器的大部分任务。docker 守护进程是一个在后台运行的服务器,等待特定的事件或者用户调用它。当我们想要在 Docker 中完成一些事情时,我们使用 CLI 向守护进程(也称为dockerd
)发送消息。调用 Docker 守护进程的命令称为 Docker 命令,它们的使用遵循一个通用模板,我将在下面介绍。
大多数 docker 命令包含一个动作,一个路径,和选项。****动作被写成 docker,后跟我们希望守护进程做的事情。例如,如果我们希望守护进程启动一个容器,它必须运行一个映像,所以动作是docker run
。(图像只是一个文件,当它被执行时,启动容器——如果容器是一个蛋糕,那么图像就是食谱。)路径指定了我们希望守护进程对哪个文件执行操作,以及文件的位置。在docker run
中,路径将告诉守护程序在哪里可以找到图像,(默认情况下是 Docker Hub ,Docker 的基于云的图像存储库以及图像文件的名称。如果一个文件有不同的版本,可以通过提供一个标签来选择一个;如果未指定标签,则自动提取最新版本。最后,选项修改命令。以docker run
为例,有数百个选项(你可以在它的参考页上看到)。虽然您可以忽略或使用大多数的缺省值,但是有些确实需要指定,正如我们将在下面看到的。
docker 运行中的选项
因为docker run
启动我们的容器,所以它是最重要的 Docker 命令之一。因此,它有如此多的选择是有道理的。这可能会使代码看起来复杂,正如您在下面启动 Selenium ChromeDriver 的示例中看到的那样:
docker run -d -v LOCAL_PATH://home/seluser/Downloads -p 4445:4444 — shm-size = 2g — name YOUR_CONTAINER_NAME selenium/standalone-chrome
事实上,上面的代码非常简单,在动作和路径之间只指定了 5 个选项。下面我们来回顾一下这 5 个选项。
-d
选项告诉容器以分离模式运行,这意味着在后台运行。这使得应用程序的输出保持隐藏,允许我们继续使用终端。**-v**
选项被称为**绑定挂载,**对于数据抓取是必不可少的。这个选项告诉 Docker 将容器运行所在的 Linux VM 中的某个目录绑定到主机(即我们的家庭计算机)中的某个文件夹。这意味着下载到那个 Linux 目录的任何东西都将被转移到我们在机器上指定的文件夹中。当你关闭 Docker 和你正在运行的容器时,保存到它们的数据不会持久,所以这是保存我们数据的一个非常重要的步骤!(另一个选项是使用永久卷。)要使用-v
选项,首先在您的家庭计算机上指定您想要将数据转移到的文件夹,然后在 Linux VM 上指定您想要使用的目录,用冒号分隔。当您真正开始运行 Selenium 时,请确保让您的代码将数据保存到您指定的 Linux 目录中!**— shm-size**
选项增加了/dev/shm 目录的大小,这是一个临时文件存储系统。这是因为容器上的默认共享内存对于 Chrome 来说太小了。在这次 github 讨论之后,我成功地将大小设置为 2g。-p
选项指定容器和 Linux VM 应该通过哪些端口连接。我们首先在 Linux VM 上指定端口,然后在容器上指定端口。Selenium 映像默认公开端口 4444 ,这里我们使用 4445 作为主机端口。当我们稍后将 Linux VM 和其中的 Selenium 容器绑定到 RStudio 时,我们将使用这个面向外部的端口 4445。**— name**
选项允许我们给容器一个特定的名称。如果我们不指定名称,Docker 将使用其默认命名系统为我们的容器命名,这实际上非常酷。UUID 是一长串难以阅读和记忆的数字和字母,Docker 没有使用它,而是随机合并一个形容词和一位著名的科学家,不知何故,组合总是很吸引人。(在一个名为 kickass_chandrasekhar 的容器中编码感觉也很好。)
就是这样!这 5 个选项是你创建上面复杂的docker run
命令所需要的。
既然我们已经牢牢掌握了 Selenium、VMs、containers 和 Docker,是时候最终下载并设置 Selenium ChromeDriver 了。我们走吧!
下载和设置 Docker 和 Selenium ChromeDriver 的步骤
- 下载适合您的操作系统和工作类型(商务、个人等)的 Docker 版本。).Docker 同时提供企业和社区版 s (CE)。对于那些想更深入了解容器的人来说,莫比把容器的组件分开,允许用户像组装乐高玩具一样单独组装。对于我们的目的来说,Docker CE 可以很好地工作。
- 下载T22chrome driver。
- 在 Windows 中,您需要确保启用了虚拟化,以便 Docker 可以启动 Linux VM。您可以通过导航到 BIOS 并启用虚拟化来实现这一点(在 BIOS 中称为 VT-AMD)。(要访问 BIOS,请在 Windows 启动时按 F10,然后转到系统配置。)
- 执行安装和设置 Docker 的步骤。最后,码头工人的特色鲸鱼将出现在码头。
- 通过在终端中键入
docker pull selenium/standalone-chrome
来获取 Selenium ChromeDriver 的图像。因为我们没有指定版本,所以将提取最新的版本。你应该看到Using default tag: latest: Pulling from selenium/standalone-chrome
。然后你会看到Status: Downloaded newer image for selenium/standalone-chrome:latest
- 使用上面的命令运行 Selenium Chromedriver。记得用您想要使用的文件夹和名称替换 LOCAL_PATH 和 YOUR_CONTAINER_NAME。
docker run -d -v LOCAL_PATH://home/seluser/Downloads -p 4445:4444 — shm-size = 2g — name YOUR_CONTAINER_NAME selenium/standalone-chrome
现在我们已经设置并运行了 Docker,我将向您展示如何使用 RSelenium 将它绑定到 RStudio。如果你不是 R 用户,有关于如何将 Selenium 绑定到其他编程语言的文章,比如 Python 和 Ruby ,或者你可以简单地在 Docker CLI 中编写脚本。
下载硒元素
- 在 RStudio 的控制台中键入
install.packages(RSelenium
。 - 然后:
library(RSelenium)
- 设置 Chrome 驱动程序的选项。还有其他的可以设置,但是这三个是必不可少的。第一个将阻止弹出窗口,第二个将确保文件下载不需要您的提示,第三个将决定下载文件的最终位置(应该是您之前在
docker run
中指定的 Linux VM 中的目录)。
eCaps <- list(
chromeOptions =
list(prefs = list(
“profile.default_content_settings.popups” = 0L,
“download.prompt_for_download” = FALSE,
“download.default_directory” = “home/seluser/Downloads”
)
)
)
4.创建从 R 到 Linux 虚拟机的绑定。浏览器名称是 Chrome,端口是在docker run
中指定的端口,extra capabilities
是在上面的步骤 3 中指定的,而remoteServerAddr
是 Linux VM 的 IP。
remDr <- remoteDriver(browserName= “chrome”, port=4445L, extraCapabilities = eCaps, remoteServerAddr = "192.168.99.100",)
5.最后,键入remDr$open
将把 R 绑定到虚拟 OS。在您的全球环境中,您应该看到 remDr 是一个<Object containing active binding>
。
那都是乡亲们!现在,您已经准备好开始使用 Docker 进行令人惊叹的 web 测试和数据抓取项目了!非常感谢您的阅读,如果有任何问题,请随时关注我的 twitter 账号@halfinit。下次见!
Python 网页抓取简介
让我们用 BeautifulSoup 刮一个虚构的书店网站吧!
Photo by Darwin Vegher on Unsplash
介绍
作为一名数据科学家,我经常发现自己在寻找可能与我的机器学习项目相关的外部数据源。问题是,很难找到与您所寻找的完全对应的开源数据集,或者允许您访问数据的免费 API。在这种情况下,web 抓取可以成为获取更多数据的一种解决方案。
什么是网页抓取?
网络搜集包括收集网站上的可用数据。这可以由人类用户或机器人手动完成。后者当然可以比人类用户更快地收集数据,这就是为什么我们要关注这一点。因此,这种机器人在几分钟内收集一个网站的所有数据在技术上是可能的吗?然而,这种做法的合法性没有明确界定。网站通常会在使用条款和 robots.txt 文件中说明是否允许使用刮刀。
它是如何工作的?
网页抓取器收集网站数据的方式与人类相同:抓取器进入网站的网页,获取相关数据,然后前进到下一个网页。每个网站都有不同的结构,这就是为什么网站抓取器通常是用来搜索一个网站的。在 web 刮刀的实现过程中出现的两个重要问题如下:
- 包含相关数据的网页的结构是什么?
- 我们如何能到达那些网页?
为了回答这些问题,我们需要了解一点网站是如何工作的。网站是使用 HTML(超文本标记语言)以及 CSS(层叠样式表)和 JavaScript 创建的。HTML 元素由标签分隔,它们直接将内容引入网页。下面是一个基本 HTML 文档的样子:
Basic HTML page
我们可以看到第一个标题的内容包含在“h1”标记之间。第一段包含在“p”标记之间。在真实的网站上,我们需要找出相关数据在哪些标签之间,并告诉我们的刮刀。我们还需要指定应该浏览哪些链接,以及它们在 HTML 文件中的位置。有了这些信息,我们的刮刀应该能够收集所需的数据。
我们要用什么工具?
在本教程中,我们将使用 Python 模块 requests 和 BeautifulSoup。
请求将允许我们发送 HTTP 请求来获取 HTML 文件。
申请文件链接:http://docs.python-requests.org/en/master/
Requests module
BeautifulSoup 将用于解析 HTML 文件。这是最常用的网页抓取库之一。它使用起来非常简单,并且有许多功能可以帮助有效地收集网站数据。
链接到 BeautifulSoup 文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/
BeautifulSoup module
先决条件
- python 2.7
- 要求
- beautifulsoup4
- 熊猫
目标
我们要刮一个网上书店的数据:【http://books.toscrape.com/
这个网站是虚构的,所以我们可以随意抓取。
在本教程中,我们将收集网站所有产品的以下信息:
- 书名
- 价格
- 有效性
- 图像
- 种类
- 等级
预热:获取主页的内容
首先,让我们使用请求模块来获取网站主页的 HTML。
u'<!DOCTYPE html>\n<!--[if lt IE 7]> <html lang="en-us" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->\n<!--[if IE 7]> <html lang="en-us" class="no-js lt-ie9 lt-ie8"> <![endif]-->\n<!--[if IE 8]> <html lang="en-us" class="no-js lt-ie9"> <![endif]-->\n<!--[if gt IE 8]><!--> <html lang="en-us" class="no-js"> <!--<![endif]-->\n <head>\n <title>\n All products | Books to Scrape - Sandbox\n</title>\n\n <meta http-equiv="content-type" content="text/html; charset=UTF-8" />\n <meta name="created" content="24th Jun 2016 09:29" />\n <meta name="description" content="" />\n <meta name="viewport" content="width=device-width" />\n <meta name="robots" content="NOARCHIVE,NOCACHE" />\n\n <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->\n <!--[if lt IE 9]>\n <script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>\n <![endif]-->\n\n \n <link rel="shortcut icon" href="static/oscar/favicon.'
结果还挺乱的!让我们让这更具可读性:
<html class="no-js" lang="en-us">
<!--<![endif]-->
<head>
<title>
All products | Books to Scrape - Sandbox
</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta content="24th Jun 2016 09:29" name="created"/>
<meta content="" name="description"/>
<meta content="width=device-width" name="viewport"/>
<meta content="NOARCHIVE,NOCACHE" name="robots"/>
<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
<!--[if lt IE 9]>
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="static/oscar/favicon.ico" rel="shortcut icon"/>
<link href="static/oscar/css/styles.css" rel="stylesheet" type="tex
函数*pretify()*使 HTML 更具可读性。但是,我们不会直接使用它来探索相关数据在哪里。
让我们定义一个函数来请求和解析一个 HTML 网页,因为在本教程中我们会经常用到它:
在主页上查找图书 URL
现在让我们开始更深入地探讨这个问题。为了获得图书数据,我们需要能够访问他们的产品页面。第一步是找到每本书产品页面的 URL。
在浏览器中,进入网站主页,右键单击产品名称,然后单击 inspect。这将向您显示对应于该元素的网页的 HTML 部分。恭喜你,你已经找到了第一个图书链接!
请注意 HTML 代码的结构:
Inspecting HTML code
你可以对页面上的其他产品进行尝试:结构总是一样的。产品的链接对应于“a”标签的“href”属性。这属于 a 类值为“product_pod”的“article”标记。这似乎是发现产品网址的可靠来源。
BeautifulSoup 使我们能够找到那些特殊的“文章”标签。我们可以屏蔽 find() 函数,以便在 HTML 中找到这个标签的第一次出现:
<article class="product_pod">\n<div class="image_container">\n<a href="catalogue/a-light-in-the-attic_1000/index.html"><img alt="A Light in the Attic" class="thumbnail" src="media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg"/></a>\n</div>\n<p class="star-rating Three">\n<i class="icon-star"></i>\n<i class="icon-star"></i>\n<i class="icon-star"></i>\n<i class="icon-star"></i>\n<i class="icon-star"></i>\n</p>\n<h3><a href="catalogue/a-light-in-the-attic_1000/index.html" title="A Light in the Attic">A Light in the ...</a></h3>\n<div class="product_price">\n<p class="price_color">\xc2\xa351.77</p>\n<p class="instock availability">\n<i class="icon-ok"></i>\n \n In stock\n \n</p>\n<form>\n<button class="btn btn-primary btn-block" data-loading-text="Adding..." type="submit">Add to basket</button>\n</form>\n</div>\n</article>
我们还有太多的信息。
让我们通过添加其他子标签来更深入地研究树:
<a href="catalogue/a-light-in-the-attic_1000/index.html"><img alt="A Light in the Attic" class="thumbnail" src="media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg"/></a>
好多了!但是我们只需要包含在’ href '值中的 URL。
我们可以通过相加。get("href ")到上一条指令:
u'catalogue/a-light-in-the-attic_1000/index.html'
好了,我们设法用 BeautifulSoup 得到了我们的第一个产品 URL。
现在让我们使用 findAll() 函数一次性收集主网页上所有产品的 URL:
20 fetched products URLs
One example:
u'catalogue/a-light-in-the-attic_1000/index.html'
这个函数对于一次找到所有的值非常方便,但是您必须检查收集的所有信息是否相关。有时一个相同的标签可以包含完全不同的数据。这就是为什么在选择标签时尽可能具体是很重要的。在这里,我们决定依赖带有“product_pod”类的标签“article ”,因为这似乎是一个非常具体的标签,我们不太可能在其中找到除产品数据之外的数据。
前面的 URL 对应于它们相对于主页的路径。为了使它们完整,我们只需要在它们前面加上主页面的网址:http://books.toscrape.com/index.html(去掉 index.html 部分后)。
现在让我们用它来定义一个函数,在网站的任何给定页面上检索图书链接:
在主页上查找图书类别 URL
现在让我们尝试检索不同产品类别对应的 URL:
Inspecting HTML code
通过检查,我们可以看到它们遵循相同的 URL 模式:“目录/类别/书籍”。
我们可以告诉 BeautifulSoup 匹配包含此模式的 URL,以便轻松检索类别 URL:
50 fetched categories URLs
Some examples:[u'http://books.toscrape.com/index.htmlcatalogue/category/books/travel_2/index.html',
u'http://books.toscrape.com/index.htmlcatalogue/category/books/mystery_3/index.html',
u'http://books.toscrape.com/index.htmlcatalogue/category/books/historical-fiction_4/index.html',
u'http://books.toscrape.com/index.htmlcatalogue/category/books/sequential-art_5/index.html',
u'http://books.toscrape.com/index.htmlcatalogue/category/books/classics_6/index.html']
我们成功地检索了 50 个类别的网址!
记住要经常检查你获取的信息,以确保所有的信息都是相关的。
如果我们想要抓取网站的某个特定部分,获取网站各个部分的 URL 会非常有用。
收集所有书籍数据
在本教程的最后一部分,我们将最终解决我们的主要目标:收集网站上所有书籍的数据。
我们知道如何在给定的页面中找到书籍的链接。如果所有的书都放在同一个页面上,这就容易了。然而,这种情况不太可能发生,因为在同一个页面上向用户显示所有目录对用户来说不是很友好。
通常产品会显示在多页或一页上,但通过滚动显示。在主页底部,我们可以看到有 50 个产品页面和一个“下一步”按钮,可以进入下一个产品页面。
End of the main page
在接下来的页面上,还有一个“上一页”按钮,可以返回到上一个产品页面。
End of the second page
获取所有页面的 URL
为了获取所有产品的网址,我们需要能够通过所有的网页。为此,我们可以反复查看所有“下一步”按钮。
Inspecting HTML code
“下一步”按钮包含“页面”模式。我们可以用它来检索下一页的 URL。但是我们要小心:上一步按钮也包含这种模式!
如果我们在匹配“页面”时有两个结果,我们应该采用第二个结果,因为它将对应于下一页。对于第一页和最后一页,我们将只有一个结果,因为我们将有“下一页”按钮或“上一页”按钮。
50 fetched URLs
Some examples:
['http://books.toscrape.com/index.html',
u'http://books.toscrape.com/catalogue/page-2.html',
u'http://books.toscrape.com/catalogue/page-3.html',
u'http://books.toscrape.com/catalogue/page-4.html',
u'http://books.toscrape.com/catalogue/page-5.html']
我们成功地获得了 50 页的网址。有趣的是,这些页面的 URL 是高度可预测的。我们可以通过将“page-X.html”递增到 50 来创建这个列表。
这个解决方案在这个例子中是可行的,但是如果页面数量发生变化(例如,如果网站决定在每页上打印更多的产品,或者如果目录发生变化),这个解决方案就不再可行了。
一种解决方案是增加这个值,直到我们得到一个 404 页面。
404 error page
这里我们可以看到,试图转到第 51 页实际上会导致 404 错误。
幸运的是,请求的结果有一个非常有用的属性,可以向我们显示 HTML 请求的返回状态。
status code for page 50: 200
status code for page 51: 404
代码 200 表示没有错误。404 代码告诉我们没有找到该页面。
我们可以使用这些信息来获取所有页面的 URL:我们应该迭代直到得到 404 代码。
现在让我们试试这个方法:
50 fetched URLs
Some examples:
['http://books.toscrape.com/catalogue/page-1.html',
'http://books.toscrape.com/catalogue/page-2.html',
'http://books.toscrape.com/catalogue/page-3.html',
'http://books.toscrape.com/catalogue/page-4.html',
'http://books.toscrape.com/catalogue/page-5.html']
我们设法用这个更简单的方法获得了相同的 URL!
获取所有产品的 URL
现在,下一步是获取每个页面的所有产品 URL。这一步非常简单,因为我们已经有了所有页面的列表和从页面获取产品 URL 的功能。
让我们遍历页面并应用我们的函数:
1000 fetched URLs
Some examples:
[u'http://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html',
u'http://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html',
u'http://books.toscrape.com/catalogue/soumission_998/index.html',
u'http://books.toscrape.com/catalogue/sharp-objects_997/index.html',
u'http://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html']
我们终于得到了 1000 本书的网址。这与网站上显示的号码相对应!
获取产品数据
最后一步是收集每个产品的数据。让我们先来看看产品页面上的信息是如何组织的:
A product page
我们可以很容易地检索到每本书的大量信息:
- 书名
- 价格
- 有效性
- 图像
- 种类
- 等级
我们开始吧!
Result dataframe
我们得到了我们的数据:我们的网络抓取实验是成功的。
在使用之前,一些数据清理可能是有用的:
- 将评级转换成数值
- 删除 product_category 列中的数字
包裹
我们已经看到了如何使用自动化的 web 抓取工具浏览网站并收集每个网页上的数据。为了建立有效的网页抓取工具,一个关键的事情是理解你想要抓取信息的网站的结构。这意味着如果你想在网站更新后仍然有用,你可能需要维护你的刮刀。
这个书店网站是一个简单的例子,但是在现实生活中,您可能不得不处理更复杂的网站,这些网站使用 Javascript 呈现一些内容。你可能想使用类似 Selenium 的自动浏览器来完成这些任务(https://www.seleniumhq.org/)。
以下是 Jupyter 笔记本的原始链接:
[## jonathanoheix/scraping _ basics _ with _ beautiful soup
在 GitHub 上创建一个帐户,为 jonathanoheix/scraping _ basics _ with _ beautiful soup 开发做贡献。
github.com](https://github.com/jonathanoheix/scraping_basics_with_beautifulsoup)
我的 LinkedIn 个人资料:【https://www.linkedin.com/in/jonathanoheix/
用 Hyperopt 实现 Python 中贝叶斯优化的介绍性示例
学习强大优化框架基础的实践示例
虽然寻找一个函数的最小值可能看起来很平常,但这是一个延伸到许多领域的关键问题。例如,优化机器学习模型的超参数只是一个最小化问题:这意味着搜索验证损失最低的超参数。
贝叶斯优化是一种基于概率模型的方法,用于找到任何返回实值度量的函数的最小值。该函数可以简单到 f(x) = x,也可以复杂到深度神经网络关于数百个模型架构和超参数选择的验证误差。
最近的结果表明机器学习模型的贝叶斯超参数优化比手动、随机或网格搜索更有效,具有:
- 测试集上更好的整体性能
- 优化所需的时间更少
很明显,这么强大的方法一定很难使用,对吗?幸运的是,有许多 Python 库,比如 Hyperopt ,允许简单应用贝叶斯优化。其实一行就可以做基本的贝叶斯优化!
Bayesian Optimization of a 1-D polynomial
如果你能理解上面代码中的一切,那么你大概可以停止阅读,开始使用这种方法。如果你想要更多的解释,在这篇文章中,我们将通过一个 Hyperopt 程序的基本结构,以便稍后我们可以将这个框架扩展到更复杂的问题,如机器学习超参数优化。这篇文章的代码可以在 GitHub 上的 Jupyter 笔记本中找到。
贝叶斯优化入门
优化是找到产生最低输出值的目标函数的输入值或一组值,称为“损失”。目标函数 f(x) = x 具有单个输入,并且是一维优化问题。通常,在机器学习中,我们的目标函数是多维的,因为它接受一组模型超参数。对于低维中的简单函数,我们可以通过尝试许多输入值并查看哪一个产生最低损失来找到最小损失。我们可以创建一个输入值的网格,并尝试所有的值—网格搜索—或者随机选取一些值—随机搜索。只要目标函数的评估(“evals”)是廉价的,这些不知情的方法可能是足够的。然而,对于复杂的目标函数,如神经网络的 5 倍交叉验证误差,目标函数的每次评估意味着训练网络 5 次!
对于需要几天训练的模型,我们想要一种方法来限制对评估函数的调用。对于高维问题,随机搜索实际上比网格搜索更有效,但仍然是一种统一方法,其中搜索不使用先前的结果来挑选下一个输入值进行尝试。让我们看看你是否比随机搜索更聪明。假设我们从为回归任务训练随机森林中得到以下结果:
如果你选择下一批要评估的树,你会集中在哪里?很明显,最好的选择是 100 棵树左右,因为数量越少,损失越小。你基本上已经在你的头脑中完成了贝叶斯优化:使用先前的结果,你形成了目标函数的概率模型,该模型表示更少数量的树可能导致更低的误差。
贝叶斯优化,也称为基于序列模型的优化(SMBO) ,通过建立目标函数的概率模型来实现这一思想,该模型将输入值映射到损失概率:p(损失|输入值)。概率模型,也称为代理或响应面,比实际的目标函数更容易优化。贝叶斯方法通过将一个标准(通常是预期改进)应用于代理来选择下一个要评估的值。其概念是通过花更多时间选择下一个要尝试的值来限制目标函数的评估。
贝叶斯推理意味着基于新的证据更新模型,并且,随着每次评估,替代物被重新计算以结合最新的信息。算法运行的时间越长,代理函数就越接近实际的目标函数。贝叶斯优化方法在构造替代函数的方式上有所不同:常见的选择包括高斯过程、随机森林回归,以及在 Hyperopt 中的选择 Tree Parzen 估计器(TPE) 。
这些方法的细节可能有点难以理解(我在这里写了一个高层次的概述),并且也很难找出哪个工作得最好:如果你阅读算法设计者的文章,每个人都声称他们的方法是优越的!然而,特定的算法并不像从随机/网格搜索升级到贝叶斯优化那样重要。使用任何库(留兰香,远视, SMAC )都可以上手!记住这一点,让我们看看如何将贝叶斯优化付诸实践。
Hyperopt 中的优化示例
在 hyperpt 中公式化一个优化问题需要四个部分:
- **目标函数:**接受一个输入并返回一个损失以最小化
- **域空间:**要评估的输入值的范围
- **优化算法:**用于构造代理函数和选择下一个要评估的值的方法
- **结果:**算法用来建立模型的分数、值对
一旦我们知道如何指定这四个部分,它们就可以应用于任何优化问题。现在,我们将讨论一个基本问题。
目标函数
目标函数可以是任何返回我们想要最小化的真实值的函数。(如果我们有一个想要最大化的值,比如精度,那么我们只需要让函数返回这个度量的负值。)
这里我们将使用多项式函数,代码和图形如下所示:
这个问题是一维的,因为我们正在优化单个值 x。在 Hyperopt 中,目标函数可以接受任意数量的输入,但必须返回单个损失以最小化。
领域空间
域空间是我们想要搜索的输入值。作为第一次尝试,我们可以在函数定义的范围内使用均匀分布:
from hyperopt import hp# Create the domain space
space = hp.uniform('x', -5, 6)
为了可视化该域,我们可以从空间中抽取样本并绘制直方图:
Uniform domain space
如果我们知道最佳值在哪里,那么我们可以创建一个更智能的域,将更多的概率放在得分更高的区域。(关于在这个问题上使用正态分布的例子,见笔记本。)
最优化算法
虽然这在技术上是最困难的概念,但在 Hyperopt 中创建一个优化算法只需要一行代码。我们正在使用树形结构的 Parzen 评估模型,我们可以让 Hyperopt 使用suggest
方法为我们配置它。
from hyperopt import tpe# Create the algorithm
tpe_algo = tpe.suggest
幕后有很多我们不必担心的理论!在笔记本中,我们也使用随机搜索算法进行比较。
结果(试验)
这并不是绝对必要的,因为 Hyperopt 会在内部跟踪算法的结果。然而,如果我们想要检查算法的进展,我们需要创建一个Trials
对象来记录值和分数:
from hyperopt import Trials# Create a trials object
tpe_trials = Trials()
最佳化
现在问题定义好了,就可以最小化我们的目标函数了!为此,我们使用fmin
函数,该函数接受上述四个部分,以及最大数量的试验:
**{'x': 4.878208088771056}**
对于这次运行,该算法在不到 1000 次试验中找到了 x 的最佳值(使损失最小化的值)。最佳对象只返回使函数最小化的输入值。虽然这是我们正在寻找的,但它并没有给我们太多的方法。要获得更多细节,我们可以从 trials 对象获得结果:
可视化对于直观理解正在发生的事情很有用。例如,让我们按顺序绘制 x 的值:
随着时间的推移,输入值聚集在红线所示的最佳值周围。这是一个简单的问题,因此算法在寻找 x 的最佳值时没有太大的困难。
为了与简单的搜索形成对比,如果我们用随机搜索运行同样的问题,我们会得到下图:
随机搜索基本上是随机尝试值!当我们查看 TPE 算法和随机搜索的 x 值的直方图时,这些值之间的差异变得更加明显:
在这里,我们看到了基于贝叶斯模型的优化的主要好处:更加关注有希望的输入值。当我们搜索几十个参数,并且每个评估需要几个小时或几天的时间时,减少评估的次数是至关重要的。贝叶斯优化通过基于以前的结果推理将来应该尝试什么输入值来最小化评估的次数。
(在这种情况下,由于基本的一维目标函数和评估次数,随机搜索实际上找到了非常接近最优的 x 值。)
后续步骤
一旦我们掌握了如何最小化一个简单的函数,我们就可以把它扩展到任何需要优化一个返回实值的函数的问题。例如,调整机器学习模型的超参数只需要对基本框架进行一些调整:目标函数必须接受模型超参数并返回验证损失,域空间需要特定于模型。
为了了解这看起来像什么,我写了一个笔记本,在那里我调整了梯度推进机器的超参数,这将是下一篇文章!
结论
基于贝叶斯模型的优化是直观的:根据过去的结果选择下一个输入值进行评估,以集中搜索更有希望的值。最终结果是,与无信息随机或网格搜索方法相比,减少了搜索迭代的总次数。虽然这只是一个简单的例子,但是我们可以将这里的概念用于各种有用的情况。
这篇文章的要点是:
- 贝叶斯优化是一种通过构建目标函数的概率(替代)模型来寻找函数最小值的有效方法
- 代理由过去的搜索结果通知,并且通过从该模型中选择下一个值,搜索集中在有希望的值上
- 这些方法的总体结果是减少搜索时间和更好的价值
- 这些强大的技术可以在 Hyperopt 这样的 Python 库中轻松实现
- 贝叶斯优化框架可以扩展到复杂的问题,包括机器学习模型的超参数调整
一如既往,我欢迎反馈和建设性的批评。可以通过推特 @koehrsen_will 联系到我。
政策梯度的直观解释
Source: https://hiveminer.com/User/Julian%20Veron
这是一系列教程的第 1 部分,我希望有 2 或 3 部分。下一部分将讨论 A2C,如果时间允许,我希望能完成一部分关于各种形式的政策外政策梯度的讨论。
这些帖子的笔记本可以在git repo中找到。
介绍
政策梯度的问题
当今一些最成功的强化学习算法,从 A3C 到 TRPO 再到 PPO,都属于策略梯度算法家族,通常更具体地说属于演员兼评论家家族。显然,作为一个 RL 爱好者,你应该对策略梯度方法有一个很好的理解,这就是为什么有这么多教程试图描述它们。
然而,如果你曾经试图遵循这些教程中的一个,你可能会面临一个如下的等式:
或者相关的更新规则:
或者甚至损失函数:
很有可能伴随着一个非常笨拙的解释,一大堆复杂的数学,或者根本没有解释。
的确,我最喜欢的一些关于强化学习的教程对此感到内疚。雅罗米鲁的让我们做一个 A3C 声明:
期望中的第二项,∇θlogπ(a|s,告诉我们在状态 s 中采取行动 a 的概率上升的方向。简单的说,如何让这种背景下的行动更有概率。
而 Arthur Juliani 在用 Tensorflow 进行简单强化学习中指出:
直观地说,这个损失函数允许我们增加产生正回报的行动的权重,减少产生负回报的行动的权重。
这两种解释都不能令人满意。具体来说,他们完全无法解释这些公式中神秘的对数函数的存在。如果你只记住了他们的直观解释,你就不能自己写下损失函数。这是不对的。
令人惊讶的是,实际上有对这个公式的完美直观的解释!然而,据我所知,这种直觉的唯一清晰表述隐藏在萨顿&巴尔托的强化学习:导论第 13 章第 3 节。
一旦我向你详细描述了它,你将能够解释对数函数的存在,并且你应该能够从基本原理写下公式。事实上,你也可以想出你自己的变种政策梯度!
在这篇文章中,我将假设你已经对强化学习有了一些基本的概念,特别是你熟悉 Q-Learning,理想情况下,熟悉 dqn。
如果你还不熟悉 Q-Learning,我建议你至少阅读一下 DQN 众多优秀教程中的一本。一些建议:
激励政策梯度
在继续之前,让我们了解一下策略梯度方法相对于 Q-Learning 的一些优势,作为尝试真正理解策略梯度的额外动力:
- Q-Learning 隐含的策略是确定性的。这意味着 Q-Learning 不能学习随机策略,这在某些环境下是有用的。这也意味着我们需要创建自己的探索策略,因为遵循策略不会执行任何探索。我们通常在ϵ-greedy 探索中这样做,这可能是非常低效的。
- 在 Q-Learning 中没有直接的方法来处理连续的动作。在策略梯度中,处理连续动作相对容易。
- 顾名思义,在政策梯度中,我们遵循政策本身的梯度,这意味着我们在不断改进政策。相比之下,在 Q-Learning 中,我们改进了对不同行动的价值的估计,这只是隐含地改进了策略。你可能会认为直接改善政策会更有效,事实也的确如此。
总的来说,在玩雅达利游戏这样的现代任务中,政策梯度方法经常击败基于价值的方法,如 DQNs。
解释政策梯度
简单的政策梯度上升
尽管本文旨在提供关于政策梯度的直觉,从而避免过于数学化,但我们的目标仍然是解释一个等式,因此我们需要使用一些数学符号,我将根据需要在整篇文章中介绍。让我们从第一个基本符号开始:
字母π将象征一项政策。我们称之为πθ(a|s)在状态 s 下采取行动 a 的概率,θ代表我们政策的参数(我们神经网络的权重)。
我们的目标是将θ更新为使ωθ成为最优策略的值。因为θ会变化,所以我们将使用符号θt 来表示迭代 t 时的θ。我们希望找出从θt 到θt+1 的更新规则,以便最终达到最佳策略。
通常,对于离散动作空间,ωθ将是具有 softmax 输出单元的神经网络,因此输出可以被认为是采取每个动作的概率。
显然,如果动作 a∫是最优动作,我们希望ωθ(a∫| s)尽可能接近 1。
为此,我们可以简单地对πθ(a∑| s)执行梯度上升,因此在每次迭代中,我们以如下方式更新θ:
我们可以将梯度∇πθt(a∗|s 视为“移动θt 的方向,以便最快地增加ωθt(a∑| s)的值”。请注意,我们确实在使用梯度上升,因为我们想要增加 a 值,而不是减少a 值,这在深度学习中是常见的。
因此,看待这一更新的一种方式是,我们在我们的政策中不断“推动”更多的行动 a*,这确实是我们想要的。
在下面的例子中,我们知道第一个动作是最好的动作,所以我们对它运行梯度上升。对于这个运行的例子,我们将假设单个状态 s,以便更容易绘制策略的演变。在本系列后面介绍 A2C 时,我们将推广到多个状态。
上面的 gif 显示了我们算法的结果。每个条形的高度是我们算法运行时采取每个动作的概率,箭头显示了我们每次迭代所遵循的梯度。在这种情况下,我们只在第一个动作上应用渐变,它恰好具有最大的值(10),这就是为什么它是唯一一个以其他动作为代价增加的动作。
权衡梯度
当然,在实践中,我们不会知道哪一个行动是最好的…毕竟这是我们首先要解决的问题!
回到“推动”的比喻,如果我们不知道哪个行动是最优的,我们可能会“推动”次优的行动,我们的政策将永远不会收敛。
一种解决方案是“推动”行动,其方式与我们对这些行动的价值的猜测成比例。这些猜测可能是高度近似的,但只要它们在现实中有所依据,更多的整体推动将发生在最优行动 a *上。这样就保证了我们的策略最终会收敛到 a∑= 1!
我们称我们对状态 s Q̂ (s,a)中动作 a 的值的猜测为。事实上,这与我们从 Q-Learning 中了解到的 Q 函数非常相似,尽管有一个微妙而重要的区别,这将使学习变得更容易,我们将在后面学习。现在,让我们假设这个 Q 函数是给定的。
我们得到下面的梯度上升更新,我们现在可以依次应用于每个动作,而不仅仅是最佳动作:
让我们使用不同行动的 Q 值的嘈杂版本(行动 a 为 10,行动 b 为 5,行动 c 为 2.5)来衡量我们对模拟策略梯度的更新,这一次,我们将随机更新每个不同的可能行动,因为我们假设我们不知道哪一个是最好的。正如我们所看到的,第一个动作最终仍然获胜,即使我们对值的估计(如箭头的长度所示)在迭代中变化很大。
政策修正
当然,在实践中,我们的代理人不会随机选择一致的行动,这是我们到目前为止隐含的假设。相反,我们将遵循我们正在努力培养的政策ρθ!这被称为培训政策。我们可能希望进行政策培训有两个原因:
- 即使在训练中,我们也积累了更多的奖励,这是我们在某些情况下可能看重的东西。
- 它允许我们探索状态空间中更有希望的区域,而不是纯粹随机地探索,而是更接近我们当前对最优行动的猜测。
然而,这给我们当前的训练算法带来了一个问题:尽管我们将更强烈地“推动”具有更好值的动作,但我们也将更经常地“推动”那些碰巧一开始就具有更高ωθ值的动作(这可能由于偶然或错误的初始化而发生)!尽管这些行为是不好的,但最终可能会赢得通往顶峰的竞赛。
让我们通过使用相同的更新规则来说明这种现象,但是根据概率而不是统一地对动作进行采样:
**
在这里我们可以看到,第三个动作,尽管比其他两个动作的值低,但最终还是赢了,因为它的初始化值要高得多。
这意味着我们需要对更有可能被更频繁地采取的行动进行补偿。我们如何做到这一点?简单:我们用我们的更新除以行动的概率。这样,如果一个动作比另一个动作更有可能被采取 4 倍,我们将有 4 倍多的梯度更新,但每个将会小 4 倍。**
这为我们提供了以下更新规则:
让我们来试试:
**
我们现在看到,尽管行动 3 一开始有很大优势,但行动 1 最终胜出,因为当它的概率较小时,它的更新要大得多。这正是我们想要的行为!
基础加固
我们现在已经解释完了政策梯度背后的直觉!这篇文章的其余部分只是简单地填写一些细节。这是一个重大的成就,你应该为自己理解到这一步而感到自豪!
“但是,等一下”,你说,“我以为你会告诉我们如何理解神秘的更新规则
但是我们的更新规则看起来完全不同!"
是的,的确我们的更新规则看起来不同,但实际上本质上是一样的!提醒一下,我们规则的关键点是
虽然我们试图解释的规则包含
下一节将解释 Â和 Q̂之间的区别,但现在可以说两者都工作得很好,但使用 Â只是一种优化。
因此,剩下的唯一区别是
和
其实这两种表达是等价的!这是由于链式法则和 log x 的导数是 1/x 的事实,你们可能从微积分中知道。所以一般来说我们有:
所以现在你知道政策梯度更新中神秘日志功能的由来了吧!那么,为什么人们使用对数函数的形式,而不是更直观的除以π(s|a)的形式呢?我认为这有两个原因:
- 它模糊了政策梯度背后的直觉,从而让你看起来更理解它。
- 当进行梯度下降时,可以将更新表示为损失函数,如我们在开始时看到的方程 L=−Â (s,a)logπθ(s|a )(将 Â(s,a)inside 作为损失函数是好的,因为它相对于θ是常数)。这样你就可以使用你最喜欢的深度学习库来训练你的策略(我们很快就会看到怎么做!).
至此,您已经理解了强化算法的基本形式,正如您所料,它代表“奖励增量=非负因子×补偿强化×特征合格性”……(是的,我是认真的,参见原文)。加强是基本的策略梯度算法,几乎所有你可能听说过的高级策略梯度算法都基于它。
优势功能和基线
现在剩下的最后一件要解释的事情,正如承诺的那样,是 Q̂和 Â.之间的区别你应该已经从 Q-Learning 中熟悉了 Q:Q(s,a)是通过在状态 s 采取行动 a,然后在其后遵循我们的政策π而获得的值(确切地说是累积贴现回报)。
请注意,可能的情况是,遵循任何行动都会给我们带来累积奖励,比如说,至少 100,但一些行动会比其他行动更好,因此 Q 可能等于,比如说,行动 a 为 101,行动 b 为 102,行动 c 为 100。正如您所猜测的,这意味着我们更新的几乎所有权重都不会告诉我们当前行动是否更好,这是有问题的。
你可能也熟悉 V(s)函数,它简单地给出了从状态 s 开始一直遵循策略的值。在上面的例子中,V(s)将大于 100,因为所有 Q(s,a)都大于 100。
通过从 Q(s,A)中减去 V(s),我们得到了优势函数* A(s,A)。这个函数告诉我们,在一个状态下采取行动与按照策略行动相比,是好是坏。在上面的例子中,它将从所有动作的 Q 值中减去额外的 100,提供更多的信噪比。*
在本教程中,我总是写 Q̂或 Â,在 q 和 a 上面加上一个“帽子”,以强调我们不是在使用“真实的”q 或 a,而是对它们的估计。如何获得这些估计值将是本系列下一篇文章的主题。
事实证明,如果我们从 Q̂(s,a 中减去任何函数,只要该函数不依赖于动作,强化仍然可以很好地工作。当然,基于我们之前的直觉,这是有意义的,因为唯一重要的是我们在具有更高 Q 值的动作上更努力地“推动”,减去任何不依赖于该动作的值将保留我们在各种动作上推动的努力程度的排名。**
这意味着用 Â函数代替 Q̂函数是完全允许的。事实上,由于上述原因,并且还因为它应该减少梯度更新的方差,所以它被广泛推荐。请注意,Â并不一定是代替 Q̂的最佳函数,但在实践中,它通常工作得相当好。
让我们看看在我们的模拟中使用 Â做了什么:
****
输出:
**Running using A function
Done in 48 iterations
Variance of A gradients: 31.384320255389287
Running using Q function
Done in 30 iterations
Variance of Q gradients: 31.813126235495847**
不幸的是,这里的影响并不显著:方差最终会稍微降低,但是收敛的时间会更长。也就是说,我在比较两者之间相同的学习率,使用优势函数应该会让您使用更高的学习率,而不会发散。此外,这是一个玩具的例子,使用优势函数的好处已经在实践中广泛证明了更大的问题。可悲的是,这是本教程中的一个例子,你必须相信我的话,有些东西工作得很好。
结论
您现在对基本的政策梯度有了全面直观的理解。然而,您可能想知道如何在实践中使用它们:如何用神经网络表示策略?首先,你是如何得到你的 Â估值的?在实践中,还有哪些其他的技巧可以使这种方法奏效?
我们将在第 2 部分中很快了解所有这些,这将解释 A2C 算法。
深层网络架构的直观指南
GoogLeNet, 2014
在过去的几年里,计算机视觉深度学习的许多进展都可以归结为少数几个神经网络架构。抛开所有的数学、代码和实现细节,我想探索一个简单的问题:这些模型如何以及为什么工作?
在撰写本文时,Keras 附带了六个已经内置到库中的预训练模型:
- VGG16
- VGG19
- ResNet50
- 盗梦空间 v3
- 例外
- MobileNet
VGG 网络,以及 2012 年的早期 AlexNet,遵循基本 conv 网络的原型布局:一系列卷积、最大池和激活层,最后是一些全连接的分类层。MobileNet 本质上是针对移动应用程序优化的 Xception 架构的简化版本。然而,剩下的三个真正重新定义了我们看待神经网络的方式。
这篇文章的其余部分将关注 ResNet、Inception 和 Xception 架构背后的直觉,以及为什么它们已经成为计算机视觉中许多后续工作的构建模块。
雷斯内特
ResNet 诞生于一个非常简单的观察:为什么当你不断增加层数时,非常深的网络表现更差?
直觉上,较深的网络应该不会比较浅的网络表现差,至少在训练时(没有过度拟合的风险)。作为一个思想实验,假设我们已经建立了一个具有 n 层的网络,它达到了一定的精确度。至少,具有 n+1 层的网络应该能够实现完全相同的精度,只要通过复制相同的前 n 层并对最后一层执行身份映射即可。类似地, n+2 、 n+3 和 n+4 层的网络都可以继续执行身份映射并达到相同的精度。然而,在实践中,这些更深的网几乎总是降低性能。
ResNet 的作者将这些问题归结为一个假设:直接映射很难学习。他们提出了一个解决方案:不要试图学习从 x 到 H(x)的潜在映射,而是学习两者之间的差异,或者“残差”然后,为了计算 H(x ),我们可以将残差加到输入中。
假设残差是 F(x)=H(x)-x,现在我们的网不是试图直接学习 H(x),而是试图学习 F(x)+x。
这就产生了您可能见过的著名的 ResNet(或“剩余网络”)块:
ResNet block
ResNet 中的每个“块”由一系列层和一个将块的输入添加到其输出的“快捷”连接组成。“添加”操作是按元素执行的,如果输入和输出的大小不同,可以使用零填充或投影(通过 1x1 卷积)来创建匹配的维度。
如果我们回到我们的思想实验,这大大简化了我们身份层的构建。直观地说,学习将 F(x)推至 0 并将输出保留为 x 比从头开始学习一个恒等式转换要容易得多。一般来说,ResNet 给层一个“参考”点——x——来开始学习。
这个想法在实践中非常有效。以前,深度神经网络经常遇到消失梯度的问题,其中来自误差函数的梯度信号随着它们反向传播到更早的层而呈指数下降。本质上,当错误信号一路传回到早期层时,它们已经小到网络无法学习了。然而,由于 ResNets 中的梯度信号可以通过快捷连接直接返回到早期层,我们可以突然构建 50 层、101 层、152 层,甚至(显然)1000+层的网络,仍然表现良好。在当时,这是一个巨大的飞跃,超越了之前以 22 层赢得 ILSVRC 2014 挑战赛的最先进技术。
ResNet 是我个人最喜欢的神经网络领域的发展之一。如此多的深度学习论文在没有考虑模型的底层任务的情况下,从数学、优化和训练过程的黑客攻击中获得了微小的改进。ResNet 从根本上改变了我们理解神经网络及其学习方式的方式。
有趣的事实:
- 1000+层网是开源的!我不会真的建议你重新训练它,但是……
- 如果您觉得功能强大,有点兴奋,我最近将 ResNet50 移植到了开源的 Clojure ML 库 Cortex 。尝试一下,看看它与 Keras 相比如何!
开始
如果说 ResNet 是为了更深入,那么 Inception Family 则是为了更广泛。尤其是,《盗梦空间》的作者对训练大型网络的计算效率感兴趣。换句话说:我们如何在不增加计算成本的情况下扩大神经网络的规模?
最初的论文关注于深度网络的一个新的构建模块,这个模块现在被称为“初始模块”该模块的核心是两个关键见解的产物。
第一个见解与层操作有关。在传统的 conv 网络中,每一层都从前一层提取信息,以便将输入数据转换成更有用的表示。但是,每种图层类型提取不同种类的信息。5x5 卷积内核的输出告诉我们不同于 3x3 卷积内核的输出,3x 3 卷积内核告诉我们不同于 max-pooling 内核的输出,等等。在任何给定的层,我们如何知道什么转换提供了最“有用”的信息?
洞察力#1:为什么不让模型选择?
一个初始模块在同一输入映射上并行计算多个不同的变换,将它们的结果连接成一个输出。换句话说,对于每一层,Inception 做一个 5x5 的卷积变换,和一个 3x3,和一个 max-pool。模型的下一层决定是否(以及如何)使用每条信息。
这种模型架构增加的信息密度带来了一个突出的问题:我们急剧增加了计算成本。不仅大的(例如 5×5)卷积滤波器计算起来固有地昂贵,并排堆叠多个不同的滤波器极大地增加了每层的特征图的数量。这种增长成为我们模型中的致命瓶颈。
这么想吧。对于添加的每个额外滤波器,我们必须对所有输入映射进行卷积,以计算单个输出。参见下图:从单个过滤器创建一个输出贴图涉及到计算来自前一层的每一个贴图。
假设有 M 个输入映射。一个额外的过滤器意味着对更多的地图进行卷积; N 额外的过滤器意味着对 NM* 更多的地图进行卷积。换句话说,正如作者指出的,“任何[过滤器]数量的均匀增加都会导致计算量的二次增加。”我们天真的初始模块只是将过滤器的数量增加了三倍或四倍。从计算上来说,这是一件大坏事。
这导致了洞察力#2:使用 1x1 卷积来执行维度缩减。为了解决计算瓶颈,Inception 的作者使用 1x1 卷积来“过滤”输出的深度。1x1 卷积一次只查看一个值,但在多个通道中,它可以提取空间信息并将其压缩到更低的维度。例如,使用 20 个 1×1 过滤器,大小为 64×64×100(具有 100 个特征地图)的输入可以被压缩到 64×64×20。通过减少输入映射的数量,Inception 的作者能够并行堆叠不同的层转换,从而产生同时深(许多层)和“宽”(许多并行操作)的网络。
这种方法效果如何?《盗梦空间》的第一个版本被称为“GoogLeNet”,是我前面提到的 ILSVRC 2014 竞赛的 22 层冠军。Inception v2 和 v3 是在一年后的第二篇论文中开发的,并在几个方面对原始版本进行了改进——最显著的是通过将较大的卷积重构为更容易学习的连续较小的卷积。例如,在 v3 中,5×5 卷积被替换为 2 个连续的 3×3 卷积。
《盗梦空间》迅速成为一个定义性的模型架构。Inception 的最新版本 v4 甚至在每个模块中加入了剩余连接,创造了 Inception-ResNet 的混合体。然而,最重要的是,Inception 展示了设计良好的“网络中的网络”架构的力量,为神经网络的表现能力又增加了一步。
有趣的事实:
- 最初的《盗梦空间》论文字面上引用了“我们需要更深入”互联网迷因作为其名称的灵感。这一定是 knowyourmeme.com 第一次被列为谷歌论文的第一参考文献。
- 第二篇启始论文(包含 v2 和 v3)是在最初的 ResNet 论文后一天发布的。2015 年 12 月是深度学习的好时机。
例外
Xception 代表“极限盗梦空间”很像我们之前的两个架构,它重新构建了我们看待神经网络的方式——特别是 conv 网络。顾名思义,它将盗梦空间的原则发挥到了极致。
这里有一个假设:“跨通道相关性和空间相关性是充分解耦的,因此最好不要将它们一起映射
这是什么意思?嗯,在传统的 conv 网中,卷积层寻找跨越空间和深度的相关性。让我们再来看看我们的标准卷积层:
在上图中,过滤器同时考虑了空间维度(每个 2x2 彩色正方形)和跨通道或“深度”维度(四个正方形的堆叠)。在图像的输入层,这相当于卷积滤镜在所有三个 RGB 通道上查看一个 2x2 像素块。问题来了:有什么理由需要我们同时考虑图像区域和通道呢?
在《盗梦空间》中,我们开始将两者稍微分开。我们使用 1x1 卷积将原始输入投影到几个独立的更小的输入空间,并从每个输入空间中使用不同类型的过滤器来转换这些更小的 3D 数据块。Xception 更进一步。它不是将输入数据划分为几个压缩块,而是分别映射每个输出通道的空间相关性,然后执行 1x1 深度方向卷积来捕获跨通道相关性。
作者指出,这基本上等同于一种称为“深度方向可分离卷积”的现有操作,它由一个深度方向卷积(为每个通道独立执行的空间卷积)和一个点方向卷积(跨通道的 1x1 卷积)组成。我们可以认为这是首先在 2D 空间寻找相关性,然后在 1D 空间寻找相关性。直观地说,这种 2D + 1D 映射比完全的 3D 映射更容易学习。
而且很管用!在 ImageNet 数据集上,Xception 略微优于 Inception v3,在具有 17,000 个类的更大的图像分类数据集上,xception 远远优于 Inception v3。最重要的是,它与 Inception 具有相同数量的模型参数,这意味着更高的计算效率。Xception 要新得多(它于 2017 年 4 月问世),但如上所述,它的架构已经在通过 MobileNet 为谷歌的移动视觉应用提供支持。
有趣的事实:
- Xception 的作者也是 Keras 的作者。弗朗索瓦·乔莱是活着的上帝。
走向
ResNet,Inception,Xception 就是这样!我坚信对这些网络有很强的直觉理解,因为它们在研究和工业中无处不在。我们甚至可以在自己的应用程序中使用它们,这种应用程序叫做迁移学习。
迁移学习是机器学习中的一种技术,在这种技术中,我们将来自源领域(例如 ImageNet)的知识应用到可能具有明显更少数据点的目标领域。在实践中,这通常包括用来自 ResNet、Inception 等的预训练权重来初始化模型。或者将其用作特征提取器,或者微调新数据集的最后几个图层。通过迁移学习,这些模型可以重新用于我们想要的任何相关任务,从自动驾驶汽车的物体检测到为视频剪辑生成字幕。
为了开始迁移学习,Keras 有一个很棒的微调模型指南这里。如果你觉得这很有趣,那就去看看吧——祝你黑客生涯愉快!
机器人是如何做决定的?
简而言之,我们经常希望赋予机器人对环境进行推理的能力,以便让它们达到目标。例如,自动驾驶汽车必须想出一条从你家到杂货店的好路线,更重要的是,它必须考虑当它到达十字路口时要做什么,以避免撞到汽车、人、标志、建筑物等。
Source: CBS News
那么我们怎样才能让机器人思考呢?这就是这篇文章的切入点。虽然我不能涵盖人工智能、机器人、机器学习和控制理论的每个方面,但让我们来看一个简单的例子。
网格世界
假设我们是一个寻宝者,正在寻找藏在世界某处的一罐金子。为了简单起见,让我们假设我们的世界不是地球,而是一个 5×5 网格。为了寻找宝藏,我们可以通过向北、向东、向南或向西从一个牢房移动到另一个牢房。此外,虽然我们对这个世界一无所知,但我们至少知道其中一个细胞里有宝藏。因此,如果我们能够成功地在世界上航行并找到宝藏,我们将会变得无比富有。不幸的是,有一个陷阱。虽然世界上大多数的牢房都是空的,但也有一些牢房里有陷阱,就像一个怪物或一个刺坑。如果我们不小心进入任何一个细胞,我们都会痛苦地死去。
既然我们想发财而不死,我们需要想出一个既能找到宝藏又能避开陷阱的计划。在我们的例子中,计划只是我们从当前位置到目标州(例如宝藏)可以采取的一系列行动(例如,北、东、南或西)。在我们完成计划中的每一个行动后,我们将会到达宝藏(这意味着我们不是怪物的晚餐)。听起来很简单,对吧?只要(1)想出一个从我们当前位置到宝藏的计划,然后(2)通过做建议的行动来遵循该计划,最后(3)收获无限财富的回报。
可悲的是,这并不容易。即使在这样一个简单的问题中,这可能是微不足道的,但在现实世界中,想出一个计划要难得多。事实上,如果我们有一个更难的问题,为了制定计划,我们需要用数学模型来表示它。因此,在我们可以将一张漂亮的支票存入银行账户之前,我们需要将我们的寻宝场景转换为一个数学模型,我们可以使用许多不同的规划和强化学习算法中的一种来求解该模型。
现在,我们需要解决一个大问题:
我们如何想出一个数学模型来描述我们的场景,这样我们就可以在不被钉子刺穿或被怪物吃掉的情况下抢走宝藏?
MDPs
为了模拟任何这样的场景,我们可以使用一个马尔可夫决策过程 ( MDP )。MDP 是我们刚刚看到的那类问题的正式表示。一般来说,它有四个部分。让我们来看看每个部分。
州
集合 S 包含了世界上的每一个状态。在我们的寻宝世界(提醒一下,它是一个 5×5 网格)中,每个单元格对应一个州。这意味着我们有一组 25 个状态:
S = {s0,s1,…,s24}
为了方便起见,这里有一张标示世界各州的图片:
行动
集合 A 包含了代理在世界上可以采取的每一个动作。对了,如果你不知道什么是经纪人,那只是世界上的演员。它可能是你,一个机器人,一辆汽车,或者任何遵循计划的东西。无论如何,在我们的例子中有一组五个动作:
A = {北,东,南,西,停留}
为什么我们有一个停留行动?一旦找到宝藏,我们就可以坐在那里。在我们努力工作之后,一旦我们发现了宝藏,我们就不想离开它。
请记住,每个状态都有某些限制代理可用操作的约束。例如,如果我们在世界的一个角落,比如说左上角,我们不能向北或向西移动或,因为我们在世界的边界。我们通常将给定状态下可用的动作表示为。因此,如果我们在世界的右上角, s20 ,这些将是代理唯一可用的操作:
A(s20)= {东,南,停留}
这种可视化可能会有所帮助:
过渡
函数 T(s,a) 是转换函数。如果代理在状态 s 中执行动作 a ,它返回世界 S 的每个可能状态的概率分布。例如,如果我们在世界的左下角,比方说, s0 ,我们执行动作北,函数 *T(s0,北)*将返回以下概率分布:
【s0:0.0,s1: 0.0,…,s5: 1.0,…,s24: 0.0,s25:0.0】
如果我们在状态 s0 中执行动作北,这个符号只是分配进入世界每个状态的概率。既然我们的世界是确定性,那么状态 s5 就有 1.0 的概率。看看这个就明白我的意思了:
然而,在现实世界的例子中,行动的结果通常是概率或随机。这意味着一个动作可能会导致不同的状态,而不仅仅是像我们的例子那样。在一个更现实的 MDP,我们也许可以把滑倒的概率与每一个动作联系起来。因此,即使我们执行了动作北,我们也可能会滑倒,并意外地在错误的状态中结束。这在机器人领域经常发生:
现在,让我们假设我们有一个确定性的世界来简化事情。
奖励
函数 R(s,a) 就是奖励函数。它返回代理在状态 s 中执行动作 a 后的奖励。在我们的世界里,假设宝藏在世界的右上角 s23 。因此,如果我们处于状态 s23 ,即 s24 以东的单元格,并且我们执行了动作西,函数 R(s24,西)将返回1000,因为我们找到了宝藏。形式上,我们应该这样写:
R(s23,西)= 1000
在任何空单元格中执行任何动作都将返回奖励 -1 ,因为在空单元格上重复移动应该会有一些相关的成本。如果没有,我们可以在找到宝藏之前绕着世界转几圈,而不会受到任何惩罚。为了节省时间,我们宁愿走最短的路去寻宝。
不幸的是,如果我们降落在一个有怪物或刺坑的州,我们会得到 -100,000 的奖励。这个高负数代表我们的死亡。我们可能会不惜一切代价避免这种情况,除非我们是一个相当虐待狂的寻宝者。
简而言之,这就是我们的奖励世界的样子:
这四个部分完全说明了我们在第一部分中概述的问题。但所有这些都给了我们一种方法,将问题数学建模为 MDP。换句话说,虽然我们现在有了一个代表 MDP 的场景,但是我们仍然没有找到得到宝藏应该遵循的计划。我们如何着手做那件事?也就是我们应该采取什么行动才能得到宝藏,避免死亡?有许多奇特的算法,我们可以用来为我们的 MDP 获得最佳计划。然而,在我们谈论那个之前,让我们多谈一点什么是计划。
政策
在计划中,我们通常制定一个计划作为政策。顺便说一句,这只是一种真实,但现在让我们继续下去。总之,什么是政策?它只是一张地图,将世界上的每一个州与推荐的行动联系起来:
更正式一点,策略 π 是每个状态 s 和动作 a 之间的映射:
π(s) = a
太好了,贾斯汀!我想我现在明白什么是政策了。为什么我们需要一个?为什么我们要将动作映射到状态?策略很重要,因为它指定了代理在世界的每个状态下应该采取的动作。例如,让我们假设代理处于状态 s23 ,它直接位于球门的东面。为了弄清楚要采取什么行动,我们将取出我们的策略来查看在 s23 中要执行什么行动。在一个理想的世界里,我们的政策应该告诉我们这一点(因为这会使我们走向宝藏):
π(s23) =西
然而,重要的是要注意,我们的政策并不一定要将每个州与一个好的行动联系起来。政策只是把状态和行动联系起来的东西,仅此而已。我们可以有很多政策,在所有的垃圾政策中,我们需要找到最好的一个。如果我们遵循一个糟糕的政策,我们可能会避开宝藏,做这样的事情:
注意我们是如何走过宝藏的。是啊,那可不好。另一方面,如果我们找到了最优策略(或者我们所能拥有的最佳策略),这就是我们要做的:
既然我们知道什么是 MDP,什么是政策,现在最大的问题是:
在我们将问题建模为 MDP 之后,我们如何在所有的坏政策中找到最好的政策?
规划和强化学习
虽然我不能深入这个问题,因为我可以写 10 本很长的教科书来解释每个细微差别,但有许多跨越规划和强化学习的算法可以用来解决 MDP。为了让你对那里有什么有个概念,这里有一个我们可以使用的一些基本算法的快速列表:
- 价值迭代
- 策略迭代
- TD 学习
如果您继续关注,我可能会在接下来的几周内就每个算法写一篇文章。
线性回归的直观透视
Credit: Unsplash
线性回归是最简单但有效的机器学习算法
所以,你在互联网上搜寻了解线性回归后来到这里,我必须说你的搜索到此结束。
所以不要再等了,让我们直接开始吧。
“泛化是任何机器学习算法的最终目标”
机器学习算法试图解决的两种主要问题是:
- 回归-预测给定数据点的连续值
- 分类-预测给定数据点的类别
线性回归,直观上是一种采用线性方法的回归算法。我们试图通过对现有数据进行归纳来预测给定数据点的连续值。线性部分表示我们使用线性方法对数据进行归纳。
一个例子
例子让它变得容易理解,所以假设你想通过知道房子的大小来预测它的价格。你有一些房价和相应大小的数据。绘制数据图表并在它们之间拟合一条线将看起来像这样:
概括地说,你画一条穿过最大值点的直线。一旦你得到那条线,对于任何大小的房子,你只要把数据点投影到那条线上,就可以得到房价。
就是这样。你完了。但是等等!
真正的问题是
问题是永远找不到房价。问题是找到能很好地概括数据的最佳拟合线。
使用了相同的旧直线方程: y = mx +c ,并添加了一些专门用于线性回归建模的术语。
我们将逐一检查等式的元素:
- y —要预测的数值
- β₀ — 直线的 y 轴截距是指直线与 y 轴的交点
- β₁-线的斜率或梯度表示线有多陡
- x —数据点的值
- u——不明原因造成的残留或噪音
似乎我们可以通过试凑法很容易地得出最佳参数值,从而找到最佳拟合线。但是当你要解决的问题有更多维度时,事情就没那么简单了,这也被称为维度的诅咒。
Multiple Linear Regression
多元线性回归将有 n 个特征,我们必须找到一条线来拟合所有维度的所有数据点。你已经开始意识到这不再是一个试凑法的解决方案。
数学拯救世界
我们可以画出无限多条线,但永远无法确定这条线是否是最佳拟合线,所以我们的救星来了:成本函数
“数学是科学女王”
我一生都在逃避数学,但你看,你就是无法逃避它。这真的很直观,让你能够看到引擎盖下发生了什么。
成本函数是一种数学构造,通过将误差平方项相加来计算
Mean Squared Error
通过稍微调整上面的等式,我们得到下面的等式:
其中 J 是成本符号, θ 是参数
我们的目标是降低成本函数,从而提高精度。我们可以通过试凑法获得参数值,并计算每个参数组合的 MSE,但这不是一种有效的技术。似乎这个问题有一个微积分解决方案。
梯度下降
梯度下降是一种简单的优化技术,用于寻找任何函数的最小值,在这种情况下,我们希望找到我们的 MSE 函数的最小值。
Gradient Descent
你可以把这个函数想象成一个山谷,你正站在某个随机的点上。你的目标是到达山谷的最底部,梯度下降帮助你到达山谷的最底部。
你可以这样做:
你会看到并弄清楚山谷的斜坡在哪里向下,然后你开始向向下的斜坡走去。一旦你看到你周围的每一个轮廓都比你站的地方高,你就声称你已经到达了最低点。
这里是梯度下降将如何做到这一点:
它必须知道山谷的斜坡在哪里(它不像你一样有眼睛),所以它需要数学的帮助。
要知道函数在任一点的斜率,就要对该点的参数进行微分,因此梯度下降法对上述成本函数进行微分,从而得到该点的斜率。
为了到达最底部的点,它必须沿着与斜率相反的方向,即斜率减小的地方。
它必须一小步一小步地向最低点移动,因此学习率决定了梯度下降将采取的步长。
每次移动后,它验证当前位置是否是全局最小值。这通过该点的斜率来验证,如果斜率为零,则算法已经到达最底部的点。
在每一步之后,它更新参数(或权重),通过重复上述步骤,它到达最底部的点。
获胜
一旦你到达了谷底,这意味着你已经得到了对应于最小均方误差或成本函数的参数,这些参数值将为你带来最高的精度。
现在,您可以使用线性回归模型以非常高的精度预测任何不可预见的数据点的因变量。
如果你还和我在一起,那么你已经了解了线性回归的概念,并准备好应用它来解决现实世界中的问题。
线性回归的应用
- 销售驱动因素分析 —线性回归可用于根据过去的购买行为预测未来的产品销售
- 预测经济增长 —经济学家使用线性回归来预测一个国家或州的经济增长
- 得分预测 —体育分析师根据球员之前的表现,使用线性回归来预测他在接下来的比赛中的得分次数
- 薪资估算——一个组织可以使用线性回归来计算出他们会根据工作经验付给新员工多少钱
- 房价预测 —线性回归分析可以帮助建筑商预测未来几个月能卖出多少房子,价格是多少
- 石油价格预测 —可以使用线性回归来预测石油价格
在接下来的文章中,我们将深入研究数学部分,并用 python 编写一个线性回归模型。
如果我能在任何方面改进这篇文章,请在下面的评论区给我反馈,或者给我发邮件到sarfraz.contact@gmail.com
非数学家监督学习综合入门指南
洞察具有自学能力的计算机
人工智能:一个对人类发动战争的机器人部落。技术启示录。公元 3000 年。
不完全是。下面是更准确的介绍。
人工智能(A.I.): 电脑创作的画作。与机器进行有意义的对话。自动驾驶汽车。甚至,令我们痛苦的是,高度个性化的、有针对性的广告。
这些只是人工智能大规模创新应用的一小部分。
欢迎来到智能机时代。人工智能已经降临到我们头上,幸运的是,它不打算终结人类。当大多数人讨论人工智能时,他们指的是机器学习,通常缩写为 M.L .,这是计算机智能行业内最大的领域。在本指南中,我将专门讨论机器学习及其内部运作,特别是线性回归:一种监督学习的普通形式。
在接下来的教导中,描述和解释了多个数学模型;记住,这些计算都是在机器内部进行的。
目录:
- 监督学习——概述
- 监督学习的注释
- 线性回归预测模型
- 用成本函数评估模型
- 梯度和导数导论
- 梯度下降:监督 M.L .模型如何学习
- 总结
准备好学习了吗?如果是这样,带上手电筒——我们正在进入人工智能的洞穴!
监督学习——概述
在处理一个复杂的主题时,主题的坚实基础非常重要。在深入研究机器学习的机制之前,让我们先了解一下机器学习在做什么。
当我们教计算机做一些事情时,比如识别物体的照片,或者预测明天的房价,我们使用的是监督学习。监督学习是一种方法,在这种方法中,我们为机器提供数据,将数据分类成示例,并为这些数据提供正确的输出。机器自我调整,直到获得可接受的预测精度。
对的注释用于监督学习
让我们通过例子来学习。
假设我们想教我们的计算机预测房价。我们将首先给我们的机器提供该地区其他房屋的价格,以及每所房屋的信息(大小、卧室数量、楼层数量)。
这些大量的信息被称为训练集。我们为任何一个给定的房屋提供的数据被称为训练示例。这由 x(i) 表示,意味着与房子(I)相关的数据(x(2) =训练示例 2)。
包含在训练示例中的每个不同的信息位被称为一个特征。在表示住房属性的数据中,房子的大小可能是一个特征,楼层数和卧室数也是一个特征。
每个训练示例以相同的顺序包含相同的功能,这意味着我们在训练集中获得关于每个房屋或示例的相同信息。
特征由 j 表示。也就是说, x(i)j 是示例 I 的特征 n
在这种情况下,给定房屋的价格将是期望产量或标签。这由 y(i) 表示,表示房子(I)的正确输出(y(2) =示例 2 的正确输出)。
注意: Medium 不支持下标和上标。x(i)j 一般写成 x 上标 (i),下标 j. y(i)应该是 y 上标 (i)。
线性回归预测模型(假设)
这一切都很好,但是我们仍然不知道计算机实际上是如何得出它的输出的。
线性回归是最基本的机器学习模型之一,当然也是学习的必备工具。尽管如此,它在大规模应用中使用非常频繁。象征性地,这是该模型的常见表示:
满足假设。别害怕,他不咬人。
我会为你打破僵局。
hθ(x) 简单地表示假设函数。它说输出的值根据输入 x 而变化,也就是说,根据括号内的值 x 进行预测,意思是 hθ(x(i)) 象征假设对例 x(i)所做的预测。
你看到的时髦希腊符号(θ——这些家伙)是θ值,更专业的说法是参数。这些数字可以是正数,也可以是负数。
来自数据集的每个特征 k 与来自假设的参数 k 相关联。你很快就会明白我说的“相关联”是什么意思。然而,由于惯例,θ0 没有各自的特征。现在,不要太在意θ0。只知道它叫偏置单元。
*注:*上图假设只有一个特征(x)和两个参数(θ0 和θ1)。在实际的监督学习应用中,通常有非常大量的参数和特征。
现在所有的部分都准备好了,让我们来看一下预测过程!当我们为我们的假设提供一个训练示例时,每个特征都乘以其各自的θ值。输出是这些乘积的总和,加上偏置单元。
为了澄清,让我们回到我们的房价例子。假设我们使用的是培训示例 1。我们的例子将只使用一个特征来解释:房子的平方英尺大小。这将由 x(1)1 表示,表示训练示例 1 的特征 1,并且将等于 2,500(平方英尺)。假设我们的假设认为,每平方英尺为房屋增值 50 美元,该地区的房价起价为 20 万美元。θ(0)的值为 200,000,θ(1)的值为 50。
让我们来看看这是为什么。
我们知道,预测是通过将偏差单位与特征及其各自参数的乘积相加而形成的。
当我们将 x(1)1(2500 平方英尺)乘以θ(1)(相关参数为 50 美元/平方英尺)时,我们得到的值为 125000 美元。通过这个乘法,对于每平方英尺(x(1)1),增加了 50 美元的价值(θ(1))。我们将该产品添加到我们的偏差单位$200,000 ( θ(0)代表当地平均起始房价)中,得到最终产值$325,000!
在机器学习的实际应用中,会有 10 到 10,000 个特征。与要素关联的每个θ值将具有正权重或负权重,从而影响每个要素影响最终输出的方式。
哇!这是使用线性回归模型预测值的过程。
在继续之前,看一下如果将预测用图表表示出来,它会是什么样子:
Linear regression hypothesis
这是一个假设,它已经被一个特征的数据所拟合。x 轴显示每个训练示例的该特性的值。y 轴测量该示例的期望输出值。在上图中,θ0 的值为 5,θ1 的值约为 1/7。可以看到,θ0 是 y 轴截距,θ1 是模型的斜率。
我们用来预测房价的模型也可以用同样的方式绘制。
用成本函数评估模型
好了,现在我们明白了什么是监督学习,以及线性回归模型如何根据它们的训练样本进行预测。但是学习是在哪里进行的呢!?
别担心,我们很快就会到达那里。在我们这样做之前,我们必须了解如何衡量我们假设的有效性。*具体地说,我们需要一个数字表示,特别是一个函数,来证明我们的参数θ与我们的数据有多吻合。*这将向我们展示我们的模型做出预测的准确性。
进入成本函数。
它就在那里,光彩夺目!平方误差成本函数!
被恐吓?不要这样。你很快就会对这里发生的事情有一个清晰的印象。
正如我们的假设一样,我们有一个函数句柄: J(θ) 。它用来表示函数及其输出值。因此,当你看到 J(θ)时,回想一下你正在处理的是成本函数(有时称为损失函数),或者与之相关的输出。
在机器学习中,字母 m 是训练样本的数量。如果我们有 5000 套房子的数据,m = 5,000。
我们最新的希腊朋友西格玛(σ)怎么样?这个家伙表示一个求和项,它在一个迭代器上循环并对一个指定的项求和。σ下面的小等式 i = 1 显示了迭代器的初始值,以及变量(I)。(I)表示求和循环中的当前迭代。σ以上是 m,即终值。
那是一个非常抽象的定义。用简单的英语来说,变量 I 取 1 到 m 之间的每一个值。对于这个循环的每一个阶段,我们将 sigma 右边的值加到一个运行总和上。
σ右边的表达式是什么?!
(hθ(x(i)) - y(i)) 称为平方误差项。它的内容应该看起来很熟悉。对于每次迭代,平方误差项使用假设来预测使用示例 x(i)的输出,并从中减去该示例 y(i)的期望输出。这就是 x(i)和 y(i)之间的差异或误差。然后,该值被提升到 2 的幂。因此平方误差。
因为它是求和,其中(I)取 1 和训练样本数量之间的所有值,所以我们对整个数据集完成这个计算。*随着算法的进行,所有平方误差被一个接一个地加在一起。*一旦求和完成,假设的所有误差的总和已经计算出来。
最后,我们将总误差乘以 1/2m ,得到成本函数的输出。除以 2m,结果是模型平均误差的一半。除以 2m 而不是 m 应该被认为是另一种机器学习惯例。这背后有数学上的原因,但是解释超出了本文的范围。
给定成本函数上的任何坐标,可以提取以下信息:
自变量的所有值(θ值),以及那些参数的成本。成本是因变量。
这个总结了关于成本函数的知识。让我们来看看它的几个图像。
Cost function for data with two parameters
请注意成本函数是如何抛物线化的,因为它是二次函数。同样重要的是,要看到 y 轴上显示的成本随着θ值的变化而变化。
Another cost function, but for only one parameter
损失函数的另一种变化如上图所示。它测量的是只包含一个参数的假设,因此是一个二维的可视化。
对于每个θ值,空间维度被添加到函数中。由于这个原因,我们不能绘制任何处理包含两个以上特征的数据的损失表示(第三维是 y 轴上的成本)。记住这一点可能对你的视觉学习者有所帮助。
成本函数经常采用奇特的形状和奇怪的形式。除了数学和视觉上的有趣,损失函数在机器的实际学习过程中也起着至关重要的作用。以下部分将概述这一关键特征。
梯度和导数介绍
导数是微积分的基础之一。我知道微积分会让很多人产生恐惧。请不要逃避。
就像我们已经讨论过的所有内容一样,我将以简单而有条理的方式解释这个概念。
下面是什么导数是一句话:表示函数和该函数在任意点的斜率之间的关系的表达式,给定自变量。
x 的导数是 2x。这意味着在函数 x 上的值为 x 时,该点的斜率为 2 * x。
知道了这一点,我们就可以理解导数代表了瞬时变化率,因为它们揭示了函数上某一点的斜率,而不是两个独立点之间的斜率。
当我们计算一个导数时,通过将独立变量代入导数方程,其输出就是函数上该点的斜率。由于这个原因,导数可以画为图形的切线。
A tangent line to x², at x = 1. The derivative being 2x, the slope of the tangent is 2 * 1.
关于衍生品需要注意的三件事:
- 它们有一个量级(大的表示陡坡,小的表示缓坡)。
- 它们有一个方向(正或负)。
- 导数指向函数求值点的最陡变化方向。
请记住这一点,因为这对下一节很重要。
那么梯度呢?
函数的梯度就像导数一样,代表函数上任意给定点的斜率。然而,梯度可以应用于多元函数。
梯度本质上是一系列导数,每一个导数代表图形一维的斜率。这些导数,每一个都表示多元函数中一个变量的变化率,叫做偏导数。当偏导数组合成一个方向单位(向量)时,它们就被称为梯度。
向量格式的梯度可以用以下格式表示:
【3;-4;5]
这将是三维图形的梯度。此处提供的信息如下:
- 在这个坐标上,x 平面的斜率是+3
- y 平面在这个坐标上的斜率是-4
- 该坐标处 z 平面的斜率为+5
Partial derivative visualization
上面是一个三维函数的偏导数。下图展示了偏导数所代表的含义:函数一维的变化率。
这就是你所需要知道的关于梯度和导数的全部,来理解机器是如何学习的!
从一个方程中提取导数和梯度可能是相当密集的,通常需要整个教科书来解释。因此,不幸的是,在这篇文章中,您将无法深入了解推导。我只是要求你在我变出一个衍生物的时候相信我的话。
此外,如果你没有理解最后一部分的所有内容,也不要担心。最重要的是,梯度代表函数的斜率。偏导数告诉我们关于所述函数的一个变量(或维度)的斜率。
梯度下降:有监督的核磁共振模型如何学习
我非常强烈地感觉到,你已经攻克了机器学习最难的部分。也就是说,把你的脑袋缠在外国符号上,学习假设是如何形成和评估的。如果你理解了前面每个主题的基本概念,理解梯度下降就相对容易了。
话虽如此,我们还是开始吧。
梯度下降的目标是最小化成本函数。这意味着我们要为我们的模型确定最低的成本,因为这样做可以找到误差幅度最小的假设。该值将被表示为函数的最低点,因为图的高度描述了损失。
如果要根据假设来表示这一点,我们需要一个模型来最小化所有数据点和函数本身之间的垂直差异之和,因为误差是根据预测输出与实际输出计算的,并在 y 轴上测量。
Minimizing the distance on the y-axis between the function and data points minimizes the cost.
通常用于解释梯度下降的类比如下:
你站在山顶,周围是山谷。你想通过最有效的路线到达离你很近的最低海拔。你环顾四周,朝最陡的下坡方向迈出一步。你重复这个过程,直到你在一个谷底。
山丘和山谷象征着成本函数。下坡时采取的步骤代表梯度下降的过程。
在该算法中,所述步骤通过递增地改变随机初始化的θ值来实现,以更好地表示训练数据。一旦模型符合数据,它应该能够推广到它还没有见过的例子,并根据新的信息提供合理准确的预测。
下面是梯度下降的公式。再次强调,不要感到不知所措。我将详细介绍它的内部工作原理。
算法说:对于 0 到 n(参数个数)之间的 j 的每一个值,设θ(j)等于θ(j)减去代价函数在当前坐标对θ(j)的偏导数,乘以一个值α。重复,直到成本最小化。
这可能是一句话中包含的大量信息。我明白了。继续读下去,我会澄清。
正如我们所知,导数指向函数中变化最大的方向,导数的值是计算坐标处直线的斜率。通过取导数的负值,我们得到与最陡变化方向相反的方向。这个方向是我们调整θ值的方向。这样做的原因很快就会变得明显。
对于每一个参数θ,我们通过所述参数的负偏导数的比例值来调整θ。我说缩放,是因为在执行减法之前,我们将导数乘以变量α。我将很快解释阿尔法的重要性。这改变了θ,使得它更接近其自身的值,这允许成本函数具有最小值。θ的一次变化称为一个步长。
本质上,如果θ太大,就会倒向最小值的右边。偏导数将是正的。当我们从参数中减去导数值时,会导致θ变小,更接近所需的最小值。
当θ太小时也是一样。当一个参数需要变大时,它位于最小值的左边,这就产生了负导数。减去负数会导致加法,这正是这种情况下所需要的。
让我们仔细看看:
θ的第一个值由成本函数上的紫色点表示。θ开始时过大。如您所见,该值导致损失非常高。该点的斜率相当陡,并且具有很大的正值。这个导数乘以某个小整数α,然后从θ中减去。在减法中,偏导数的大小和方向起着至关重要的作用。斜率为正时,θ(1)变小。斜率较陡或较大时,θ(1)会发生显著变化,这是合适的,因为θ距离其目标值非常远。
随着这一过程的重复,θ(1)接近损耗最小值,函数的斜率降低。由于增量的大小基于斜率,因此步长会变小。最终,变化变得无穷小,只是起到微调的作用。这个方便的细节允许梯度下降以避免超过最小值,并为参数提供非常精确和合适的值。
让我们看看这个过程如何影响太小的θ值。
上面,我们可以看到导数的值在θ(1) = -1 处小于零,因为切线的斜率在该点为负。在这种情况下,我们将反复从θ(1)中减去这个负值,慢慢增加它的值,直到它实现函数的最小化。
这个算法的另一个有趣的特点是,在任何函数的最小值,斜率是 0。这很重要,因为它告诉我们,在收敛时,当采取步骤时,θ值将不再改变。(θ(1) - 0 = θ(1)).
在多元成本函数中,我们一次更新所有的θ值,因此我们朝着最小值的增量步骤类似于梯度,如下所示:
上面,我们看到了成本函数 J(θ)。黑线显示了随着θ值的调整,梯度下降所采取的运动。记住这个过程和二维函数的过程是一样的。它只是对θ(0)和θ(1)同时执行,这是导致下降以梯度的形式出现的原因。
现在让我们回顾一下这个过程是如何用数学方法表示的。这里再一次给出了可供参考的算法:
我们已经知道θ(j)代表什么。
:=符号表示我们将变量设置为等于操作符另一端指定的值。x := 3 意味着我们设 x 等于 3。
你听说的那个阿尔法怎么样了?∝是α。∝就是所谓的超参数。这意味着它不是模型用来进行预测的值之一,而是对我们的最终模型有影响的变量。
Alpha 修改学习率,或梯度下降的增量大小。如果α取大值,梯度下降算法可以更快地收敛(达到最小值)。如果它很小,梯度下降需要更多的迭代来产生精确的模型。
所以越大越好?
嗯,没有。
如果∝太大,梯度下降可能会超过所需的最小值。如果是这种情况,算法就会出现分歧,这意味着成本越来越高。另一方面,如果α太小,可能需要数千甚至数百万的增量来实现准确的假设。
Gradient descent diverging due to an overly large value for alpha
∝有一个非常好的位置。然而,找到它是非常复杂的,模型分析需要一篇文章。现在,你应该想象找到α的最好方法是通过计算试验。
常用值介于 0.001 和 10 之间。
梯度下降方程的最后部分是成本函数相对于θ(j)的导数项。这是公式中求和项右边的部分。
下面是关于某个参数θ(j)的导数的计算方法:
对于参数θ(j):
使用(I)作为迭代器循环通过每个训练示例,从该示例的假设输出 x(i)中减去期望输出 y(i)。这个差值乘以特征 x(i)j(与θ(j)相关的特征)。这个乘积被加到一个累计和中。
*注意:*偏差单位没有任何相关特征,这意味着在计算偏导数时,我们不将误差(x(I)-y(I))乘以 x(i)j 的值。
求和完成后,我们将所得值除以训练示例的总数。这为我们提供了关于θ(j)的偏导数。
在同时更新所有参数之前,我们对每个参数θ重复导数计算。
现在,再读一遍梯度下降的解释:
算法说:对于 0 到 n(参数个数)之间的每个值 j,设θ(j)等于θ(j)减去代价函数在当前坐标对θ(j)的偏导数,乘以值α。重复,直到成本最小化。
我相信这一点更容易理解。
恭喜你!这就是梯度下降——最常见的机器学习算法。现在你对它有了彻底的了解。
一份总结
这是线性回归:
我们有一个数据集 X,包含训练示例 x(i)和标签 y(i)。每个例子具有特征 x(i)1 到 x(i)j,j 是特征的总数。这些特征各自提供独特的信息。
假设 hθ(x)对数据进行预测。它包含与每个特征相关联的θ值,以及偏差单位θ0,总共 j + 1 个参数。此模型通过将训练示例的每个特征与其相应参数的乘积相加,并将偏差单位添加到此总和中,来创建预测输出。
成本函数衡量假设的准确性。通过降低成本,我们获得了更精确的模型。
梯度下降可以通过基于θ值相对于成本函数的偏导数改变θ值来实现这种最小化。通过在损失函数的当前坐标处取负斜率,每个参数逐渐向图表上的最小值移动。重复这一过程,直到达到收敛。
然后,模型能够接受新数据作为输入,并产生准确的输出。
这就是线性回归。你明白吗!太神奇了!
看到我们每天都被人工智能包围,令人震惊的是很少有人理解它在做什么。这项技术发展极其迅速,这一次无知不是福。理解我们周围的世界是人类的天性。重要的是要看到什么样的进步将很快成为主流。
你现在对机器学习的专业领域有了一些了解。让我们最后看一下我们眼中的人工智能是什么:
人工智能(A.I.): 预测模型。平方误差成本函数。梯度下降算法。
出发前
- 在 Linkedin 上与我联系,有任何问题请随时联系我
- 查看吴恩达在 Coursera 上的机器学习课程,这是一个很好的、深入的机器学习入门课程
- 记住学习需要时间!对学习新的东西感到兴奋,而不是对不理解的东西感到沮丧。
假日电影的 NLP 观点——第一部分:使用 Gensim 和 SKlearn 的主题建模
Photo by Tom Coomer on Unsplash
假期是家人、朋友、雪和我妻子关心的:老掉牙的假期电影的时间。
为了尝试帮助她永无止境的假日电影胃口,我们将检查我们是否可以创建一个新的圣诞电影。这篇博文由两部分组成:
- 第一部分:获取数据并尝试识别圣诞电影
- 第二部分:使用这个数据集尝试生成一个新的数据集
第一部分的代码可以在这里找到,完整的回购协议可以在这里找到。
电影
没有特别指向任何电影制作人,但就假日电影而言,霍尔马克是一座金矿。所以让我们试着去挖掘这些。
为此,我:
- 抓取了霍尔马克电影公司的维基百科页面(链接)
- 解析 2000 - 2018 年的所有电影,获取片名和日期(使用 Beautifullsoup)
事实上:圣诞电影似乎正在蓬勃发展:
标题
我对我的妻子做了一个相当大胆的声明,所有的霍尔马克头衔只是“皇家”、“圣诞节”、“婚礼”、“王子”和“爱情”这些词的组合
让我们用数据来证明这一点!在 Spacy 的快速引理化之后,只有 8 个结果支持这个假设😕。其中最精彩的是《圣诞婚礼的尾巴》,谁能忘记《婚礼进行曲 2:诉诸爱情》。
然而,“圣诞节”仍然是迄今为止最流行的术语:
数据
我们没有每部电影的抄本,但我们会尽量使用英文字幕。它可能不像真实的抄本那样具有描述性,但是生成算法应该能够提取对话。
为此,我非常感谢地使用了“imdb”和“open subtitle”API 以及它们各自的 Python 包装器。
一旦有了这些链接,我们就可以用一个简单的脚本来获取数据:
提取圣诞电影:主题建模
虽然不是所有的电影都与训练数据相关,但是我们特别想提取圣诞节的电影。让我们看看是否可以创建一个主题模型来帮助我们:
预处理
在处理文本数据时,总是需要一些预处理,在这种情况下,我们需要:
- 从字幕中删除时间戳
- 删除一些特定的标签
- 让一切变得简单
- 过滤掉“人”实体
- 删除停用词
- 删除短于 1 的字符串元素(不必要的单词和标点)
词汇化和实体抽取使用 Spacy 完成,停用词使用 NLTK 移除。
在文本清理之后,让我们检查一个主题建模分析是否可以拾取电影中隐藏的主题。我预计会出现诸如“圣诞节”、“爱情”、“婚礼”(也许是“食物”)等话题。
我们将使用流行的潜在狄利克雷分配(LDA)算法。因为没有 Python 包应该在圣诞节被冷落,所以让我们同时使用 Gensim 和 Scikit-Learn。
使用 Gensim 的主题建模
从已处理文档的列表开始,在这种情况下,每个文档都是经过过滤的词条列表,我们可以轻松地进行如下分析:
使用 SKlearn
同样,我们可以使用 Scikit-Learn 进行 LDA 分析:
结果呢
对于 Gensim 算法:
**Topic: 0**
Words: 0.046*"christmas" + 0.008*"ice" + 0.007*"la" + 0.006*"horn" + 0.006*"holiday" + 0.006*"ok" + 0.005*"ho" + 0.005*"santa"
**Topic: 1**
Words: 0.015*"christmas" + 0.011*"chuckle" + 0.009*"ok" + 0.008*"sigh" + 0.005*"applause" + 0.004*"york"
**Topic: 2**
Words: 0.026*"christmas" + 0.008*"ok" + 0.008*"wed" + 0.007*"chuckle" + 0.005*"sigh" + 0.004*"mm" + 0.003*"dog" + 0.003*"alright"
**Topic: 3**
Words: 0.052*"christmas" + 0.012*"santa" + 0.011*"la" + 0.007*"wed" + 0.007*"ok" + 0.007*"holiday" + 0.006*"ha" + 0.005*"ho" + 0.004*"york" + 0.004*"merry"
**Topic: 4**
Words: 0.008*"christmas" + 0.008*"sigh" + 0.005*"ok" + 0.004*"chuckle" + 0.004*"wed" + 0.004*"dog"
**Topic: 5**
Words: 0.013*"wed" + 0.012*"%" + 0.008*"ok" + 0.006*"dog" + 0.004*"alright" + 0.004*"movie" + 0.004*"mama"
对于 SKlearn 算法:
**Topic 0:**
christmas mama king window turkey note corner process embarrass tea
**Topic 1:**
group golf hon i. mm birthday pot split pink successful
**Topic 2:**
ok wed alright farm bride cake christmas york marriage camp
**Topic 3:**
kill chase christmas disappoint empty mystery possibility chief awkward dr.
**Topic 4:**
christmas ok wed chuckle la sigh alright santa mm dog
**Topic 5:**
chef silver honestly dog idiot wed race deliver kick climb
所以没有一个能真正抓住我在寻找的东西。
- 在 Gensim 的分析中,“圣诞节”似乎太普遍了
- 在 SKlearn 的分析中,情况有所好转。我怀疑 TFIDF 矢量化方案与此有很大关系。例如,主题 2 看起来主要是婚礼电影。但是,圣诞节仍然无处不在。
计划 B:集群
作为后备计划,我想尝试在 TFIDF 矢量化文档上做一个简单的 K 均值聚类练习。
这非常简单:
然后,我们可以使用 T-SNE 将 TF-IDF 向量的维数降低到 2,并使用新的聚类来给点着色。
结果看起来足够好:
(披露说明:我让我的妻子帮忙命名星团👼)
- 集群 0 看起来主要是婚礼电影
- 集群 1 看起来像爱情电影一般
- 电影看起来像爱情电影,但是有一点奇幻色彩(皇室、女巫)
- 集群 3 电影好像是季节性爱情电影(收获,夏天)
- 集群 4 电影很明显圣诞电影:头奖!
- 电影:还不确定,我妻子坚持让我们看一些电影,以获得更好的感受。不会上当的…
- 第六组电影看起来像是带有“食物”元素的电影
一些很酷的事情打动了我:电影《在圣诞节嫁给我》是圣诞电影和婚礼电影的混合体。K-means 将它标记为婚礼电影,但正如我们所见,它也可能是一部圣诞电影:
总的来说,TF-IDF 确实有助于获得一个好的聚类结果。
好的,这个肯定可以。我们将把所有的“第 4 集”电影带入我们的下一步目标,制作我们自己的圣诞电影,在这篇博文的第二部分。最终数据集(在 pandas 数据框架中)也可以在 repo 中找到。
那里见!
假日电影的自然语言处理观——第二部分:用 LSTM 的《在喀拉斯》生成文本
继续这篇博文的第一部分,让我们看看是否可以用输入序列训练一个 RNN,并用它来生成一些新的序列。
这部分的代码可以在这里找到。完整的代码和数据(在熊猫数据框架中)也在回购中。
这个想法
使用 RNN 的工具生成文本已经做了很多次了(在这里插入安德烈·卡帕西的博客文章)。但是尽管如此,当你有大量的文本时,这仍然是一件有趣的事情。对 AI 古怪博客大声喊出来!
除此之外,我还对算法在不同参数下的行为感兴趣。
数据
我们对第一部中所有确定的圣诞电影都使用小写字幕。这里有一个片段:
" never had. we got bikes, toys, video games, all sorts of stuff. don't those marines or firemen collect toys for those kids? why are we spending the money? it's christmas. oh, bah, humbug. no one ever did anything like that for me when i was a kid. jimmy! when i get out on set, i don't want to see o"
编码数据
很容易做到:我们将语料库分割成 N 个半冗余的文本片段(作为输入),紧随其后的字符作为输出(要预测的实际内容)。
我们每次跳过 5 个字符的事实有望成为一种规则,避免记忆整个连续的句子。
输入序列中的每个输入字符都是一次性编码的:
one-hot encoded input sequence
在这之后,我们将数据分成训练和测试集(20%)。
模型
在摆弄了一些超参数之后,我注意到下面的设置在验证损失方面取得了良好的结果:
👉提示:使用 recurrent_dropout 在处理 RNN 氏症时,不要在中间添加简单的辍学层(如果你感兴趣,这篇文章是一个很好的来源)。
👉注意:当堆叠 LSTM 层时,将 return_sequences 设置为 True,除了最后一个 LSTM 层。
授予:
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_1 (LSTM) (None, 40, 512) 1191936
_________________________________________________________________
lstm_2 (LSTM) (None, 512) 2099200
_________________________________________________________________
dense_1 (Dense) (None, 69) 35397
=================================================================
Total params: 3,326,533
Trainable params: 3,326,533
Non-trainable params: 0
_________________________________________________________________
训练时间
该模型训练了 50 个时期,以及一些回调函数:
参数研究
为了尝试感受模型在各种参数下的表现,我们将对不同模型中的训练测试损失进行比较。
我们每次都从基本模型开始:
- 2 个 LSTM 层,经常性辍学率为 70%
- 顶部 1 个致密层
为了稍微加快训练过程,我们只使用 20%的数据进行训练,另外 20%用于测试。
当然,一些参数也会严重影响训练时间(数据量、隐藏层数等)。).如果这是一个问题,也应该考虑到这一点!
更改落差大小
👉我们可以清楚地看到,增加压差极大地减少了过度拟合
更改堆叠 LSTM 图层的数量
👉总的来说,没有太大的区别,2 层收敛更快,但是训练起来更慢
更改隐藏状态大小
👉单元越多,过度拟合的可能性越大(学习能力越强),但收敛速度更快(尽管训练速度较慢)
更改输入集的大小
对于最后一次运行,我设置了两个实验,一个有 20%的训练数据可用,另一个有 80%的训练数据可用(同样的 20%每次用于测试集):
不出所料:添加更多的数据会带来最大的性能提升。
这让我想起了 Peter Norvig(以及其他人)的一篇著名论文:数据的不合理有效性
输出
摆弄够了,让我们看看输出是否有任何好处!
我们要做的是:
- 取一个 40 个字符的起始种子句子
- 编码它
- 预测下一个第 41 个字符
- 将窗口向前滚动一个字符,以便预测第 42 个字符
- 重复
为了预测下一个字符,我们使用“温度”原理。我们不是简单地选取概率最高的字符,而是从一个概率分布中取样(为什么会这样马上就清楚了):
让我们看看这是如何发挥作用的。起始种子是:
"那是圣诞节的前一天晚上,t . "
使用温度= 0.00
那是圣诞节的前一天晚上,我成了圣诞树的一部分。我不知道我是最好的圣诞树。我不知道我是最好的圣诞树。我不知道我是最好的圣诞树。我不知道我是最好的圣诞树。我不知道我是最好的圣诞树。我不知道我是最好的圣诞树。我不知道我是最好的圣诞树。我不知道
使用温度= 0.25:
那是圣诞节的前一天晚上,我感到非常抱歉。我想我很抱歉我很抱歉。我不知道我和那个问题一样。你猜怎么着我不知道我有多漂亮。我在想,最好的圣诞夜圣诞树是最好的圣诞礼物之一,圣诞晚会的圣诞树让我大吃一惊。对不起,我会说我太美了。我不知道。我不知道
使用温度= 0.50:
那是圣诞节的前一天晚上,几年前我本打算让你参加假日射击赛。我会跟你谈的。你认为我和我最喜欢的人做了那么多,然后我把我们带到了树上。我想我很抱歉。我不知道。他们做了什么?呃,好吧,有个舞会,我得去拿些我不能说的关于树灯的东西。不不不不太好了。现在,我只是一个坏想法
使用温度= 0.75:
那是圣诞节的前一天晚上,t 他看着自己。你有点太固执了。这对你来说是一种力量。嘿,我得走了。先生。好吧,我会和所有新饼干的负责人谈谈。[笑]我不能说不。不,不,不,不,无论如何。嗨。珍妮小姐的艾米丽你不会很好的。那是我父亲的感冒。[笑]什么?有六天时间我要离开他们。是啊,难道你不知道吗?n
使用温度= 0.9:
那是圣诞节的前一天晚上,请给我一棵好圣诞树。你说得对。伙计,提尔不会来帮忙了吗?这是他们从来没有谈过的交易。哦,何亚!呵呵。不,我知道这不是你不了解我的原因。你会没事的。嗯嗯。你知道你去哪里奇怪。哦,嗯,让我看看那一年妈妈可以变身了。欢迎来到
我们看到,使用概率最高的预测字符(温度= 0)只会将我们带入一个重复的循环。
提高温度会赋予模型更多的“创造力和自由”。在某种程度上,[叹息]和[笑声]混在了一起。
气温升高还会增加出现晦涩难懂的单词的风险,比如“goving”和“sost”,以及不连贯的句子。
故事板
嗯,看起来这位模特肯定学到了一些圣诞概念,比如树和灯。
尽管如此,这似乎还不是一个完整的电影剧本😕…
也许如果我们添加一些图纸,事情会看起来更好!让我们试试上面生成的内容(温度 0.5)
👉提示史诗绘画技能
嗯…不是真的,虽然它是…
现在让我们把电影写作交给专业人士吧。
如果有人想玩的话,我也上传了模型的最佳重量到 GitHub。
更多生成的片段(种子每次都以粗体突出显示),它甚至似乎在某个时候制作了一首歌曲:
**what do you mean you forgot the presents?** i think that you would like to see you that parade. ♪ ♪ we wish you a terrible and children ♪ ♪ the annie last night ♪ ♪ i would love to a project ♪ ♪ and the way ♪ ♪ it's a little bit ♪ ♪ we're selling a little bit since when it was a camera for you. i don't know what i mean. i know that i'm going to be pretty good to ask you again. come on, come on. i mean, i was a consider make one of your li**it was the night before christmas, and t**he problem to the prince was that i would have to be the bigger of the family. you were about the boys and the holidays. and i don't know. i love the store. i don't know what i don't know what i see that is a decorating christmas tree and i have to get it. i don't know what i don't know what i was the boys and i can do it for the company of the company this company and the tree is the boys and i have**i don't know jake, the christmas tree** in new york can be a christmas pageant. i mean, i can't take it to the best virgin suits and the holidays were making this year's man. you know, it's been a lot of ready to be the past for us and we'll get back to the store that you can ask you a movie. it's good to make a boyfriend. yeah, i think you're gonna be next week. well, he's a sweet. i could be in the day that you don't mind if i don't want t**are you okay, didn't you hear the jingle**? i mean, i broke my mom so look at this. well, i have to be a sec. i'm so sorry. it's not a basketball, i don't think that's really good. thanks. i love you, and i wish you two pass. i know. i do... you know. i think i'm thinking about your life. the book. so thank you. so let's just the book room. oh, good to get ahead. so you can relat**are you okay, didn't you hear the jingle**? well, i didn't think that's cool. i know it, that's what i was the perfect for the holidays. doesn't see you and i wish you a very nice guy. you know, i told you it mean it cookies. you know, i told you it mean it. we're going to be a surprises. i know. i think i'm the best christmas trees. i think i know what they have been for christmas? maybe i could see why you don't want to go to play that yo
恰当的标题
不亚于一个圣诞奇迹,AI weirdness 刚刚发布了一篇关于为一部圣诞电影命名的博文。以下是其中的一些:
圣诞生物案例
春天的圣诞节
一只圣诞猫
一只圣诞虱子
接近圣诞节
树屋圣诞节
圣诞节的喧闹
就这样吧,如果你有任何反馈、意见或想法,请随时告诉我。
节日快乐!
数据可视化中的色彩颂歌
数据可视化中颜色的使用在讲述一个故事时起着至关重要的作用。使用尽可能多的颜色使可视化看起来“漂亮”是很诱人的,尤其是作为数据可视化领域的初学者。我的建议是尽量避免在视图中使用太多的颜色。
如果使用颜色作为一种方法,将观众的注意力吸引到你想要在视觉上突出的特定点上,会更有效。
让我们看一个例子→如果我们以一个灰度(没有其他颜色)开始我们的可视化,然后思考我们想要讲述什么故事,我们可以使用颜色来帮助我们讲述这个故事。
下面的灰度仪表盘是起点。
这些图表试图回答的三个问题是:
1.哪些州最赚钱?
2.哪些产品类别利润最高?
3.哪种运输方式最有利可图?
由于这三个问题都与盈利能力有关,我们可以为“盈利能力”指定一种特定的颜色,并在整个仪表板中使用该颜色。
让我们用亮蓝色来显示这些不同属性的盈利水平。
将仪表板上的标题更改为**“跨各种属性的盈利能力”,并将单词“盈利能力”**更改为蓝色,有助于突出显示,并减少可能会扰乱视图的彩色图例的需要。它告诉观众,当你看到蓝色时,我们意味着它有更高的盈利能力。
下面是同一个仪表板,有效地使用颜色来讲述一个故事:
谨慎使用颜色非常重要,尤其是当您在仪表板上的多个工作表中使用一种颜色时。人们认为相同的颜色在所有工作表中都是相关的。
这是一个用颜色来讲述故事的小例子。你对色彩的运用有什么想法?下面是该仪表板的前后图像。
深入研究图像分类
了解用于图像分类的不同网络架构是一项艰巨的任务。在这篇博文中,我将讨论目前在 keras 包中可用的主要架构。我将按时间顺序浏览这些架构,并试图从从业者的角度讨论它们的优缺点。
关键概念
尽管计算机视觉领域的不同研究人员倾向于遵循不同的实践,但总体而言,在设置实验时,您可以看到以下趋势。我讨论了如何对图像进行预处理,使用什么类型的数据增强,优化机制和最终层的实现。
预处理
通常,在训练集上计算平均像素值,并从图像中减去。请注意,在将这些模型与 keras 一起使用时,将这一点考虑在内非常重要。Keras 为每个计算机视觉模型提供了不同的“预处理”功能。
数据扩充
影像分类研究数据集通常非常庞大。然而,数据扩充经常被用来提高泛化性能。通常,使用随机裁剪重新缩放的图像以及随机水平裁剪和随机 RGB 颜色和亮度偏移。存在不同的重新缩放和裁剪图像的方案(即单尺度对多尺度训练)。测试期间的多作物评估也经常使用,尽管计算成本更高且性能改善有限。请注意,随机缩放和裁剪的目标是了解每个对象在不同比例和位置下的重要特征。Keras 没有实现所有这些现成的数据扩充技术,但是它们可以通过 ImageDataGenerator 模块的预处理功能轻松实现。安德鲁·霍华德的数据扩充技术更深入地解释了关键方法。
An example of different crops of the same picture (Image taken from Andrew Howard’s paper).
训练机制带动量的 SGD 或 RMSProp 经常被用作优化技术。学习率方案通常相当简单,要么在验证损失或准确度开始稳定时降低学习率,要么在固定间隔降低学习率。使用 keras 中的“ReduceLROnPlateau”回调,您可以很容易地模仿这种行为。
An example of the training procedure where the LR is reduced then a plateauing loss is noticed.
最后一层
传统上,图像分类网络的最后一层是全连通层。这些层是大量的参数猪,因为你需要 NxM 个参数从 N 到 M 个隐藏层节点。如今,这些层已经被需要更少参数和计算时间的平均或最大池层所取代。在 keras 中微调预训练网络时,考虑这一点以限制添加的参数数量是很重要的。
VGGNet
最初由卡伦·西蒙扬和安德鲁·齐泽曼于 2014 年发表, VGGNet 表明,堆叠多层是计算机视觉良好性能的关键组成部分。他们公布的网络包含 16 或 19 层,主要由小型 3×3 卷积和 2×2 汇集操作组成。
作者的主要贡献是表明堆叠多个小滤波器而不合并可以增加网络的代表性深度,同时限制参数的数量。通过堆叠例如三个 3×3 conv。代替使用单一的 7×7 层,克服了几个限制。首先,组合三个非线性函数而不是单个非线性函数,这使得决策函数更具区分性和表达性。第二,参数的数量减少了 81%,而感受野保持不变。因此,使用较小的滤波器也可以作为正则化器,提高不同卷积滤波器的效率。
VGGNet 的缺点是评估起来比浅层网络更昂贵,并且使用更多的内存和参数(140M)。这些参数中的许多可以归因于第一个完全连接的层。已经表明,可以移除这些层而不会降低性能,同时显著减少必要参数的数量。VGG 在 keras 上提供 16 层和 19 层两种预训练重量。
雷斯内特
ResNet 体系结构是由何等人开发的,试图训练具有更大深度的网络。作者指出,增加网络深度会导致更高的训练损失,这表明由于梯度问题(爆炸/消失梯度)而存在潜在的训练收敛问题。
Although the space of potential functions of a 20-layer network is encapsulated within the space of a 56-layer network, with convential gradient descent, it is not possible to achieve the same results. (Image taken from the ResNet paper)
他们的主要贡献是在神经网络体系结构中增加了跳跃连接,使用批量标准化并在网络末端移除完全连接的层。
With a skip connection, the input x of a convolutional layer is added to the output. As a result, the network must only learn ‘residual’ features and existing learned features are easily retained (Image taken from the ResNet paper).
跳过连接是基于这样一种思想,即只要神经网络模型能够将信息从前一层“正确地”传播到下一层,它就应该能够“无限地”深入。在没有通过更深入来聚集附加信息的情况下,具有跳跃连接的卷积层可以充当身份函数。通过向网络添加跳跃连接,卷积层的默认功能变成了识别功能。滤波器学习到的任何新信息都可以从基本表示中减去或添加到基本表示中,因此更容易优化残差映射。跳过连接不会增加参数的数量,但是会导致更稳定的训练和显著的性能提升,因为可以获得更深的网络(例如,深度为 34、50、101 和 152 的网络)。请注意,1x1 卷积用于将图层输入映射到其输出!
除了跳跃连接之外,在每次卷积之后和每次激活之前使用批量归一化。最后,完全连接的层被移除,取而代之的是使用平均池层来减少参数的数量。由于更深的网络而增加的卷积层的抽象能力减少了对完全连接的层的需求。
谷歌网
谷歌网的论文与雷斯网的论文几乎同时发表,但引入了不同的改进。前两篇论文着重于增加分类网络的表示深度。
然而,使用 GoogLeNet,作者仍然试图扩大网络规模(多达 22 层),但同时他们的目标是减少参数数量和所需的计算能力。最初的 Inception 架构由谷歌发布,专注于在大数据场景和移动设置中应用 CNN。该架构是完全卷积的,由初始模块组成。这些模块的目标是通过构建由多个构建块组成的复杂滤波器(例如,网络中的网络—初始)来提高卷积滤波器的学习能力和抽象能力。
An example of an Inception module. The 1x1 convolutions are performed to reduce the dimensions of input/output (Image taken from the GoogLeNet paper).
除了初始模块,作者还使用辅助分类器来促进更稳定和更好的收敛。辅助分类器的思想是使用几种不同的图像表示来执行分类(黄框)。因此,在模型的不同层计算梯度,然后可以用于优化训练。
A visual representation of the GoogLeNet architecture. The yellow boxes indicate the presence of auxiliary classifiers.
Inceptionv3
Inceptionv3 架构结合了几项创新。在 Inceptionv3 中,主要关注的是重用 GoogLeNet 和 VGGNet 的一些原始想法,即使用 Inception 模块,并用一系列较小的卷积更有效地表达大型过滤器。除了小卷积之外,作者还试验了非对称卷积(例如,用 nx1 和 1xn 代替 nxn,而不是多个 2x2 和 3x3 滤波器)。
An example of a 3x3 filter followed by a 1x1 filter, effectively replacing a 5x5 filter (Image taken from the Inceptionv3 paper).
作者通过执行批量标准化和标签平滑改进了网络的正则化。标签平滑是给每个类分配一些权重的做法,而不是给地面真实标签分配全部权重。由于网络在训练标签上的过度拟合会更少,它应该能够更好地概括,这与使用 L2 正则化是类似的做法。
投入了大量精力来确保该模型在高分辨率和低分辨率图像上都表现良好,这是通过分析不同比例的图像表示的初始模块来实现的。因此,当初始网络用于对象检测框架时,它们在分类小的和低分辨率的对象时表现良好。
纳斯网
我将讨论的最后一个图像分类架构是 NASNet ,它是使用神经架构搜索(NAS)框架构建的。NAS 的目标是使用数据驱动的智能方法来构建网络架构,而不是直觉和实验。虽然框架的细节我就不赘述了,但大致思路还是说明了。
在初始文件中,显示了“单元”中过滤器的复杂组合可以显著改善结果。NAS 框架将这种单元的构建定义为优化过程,然后堆叠最佳单元的多个副本来构建大型网络。
最终,构建两个不同的单元并用于训练完整的模型。
结果
下表摘自 Keras ,它很好地概括了我所讨论的不同网络的结果。请注意,VGG 以其大量的参数脱颖而出,而 Resnet50 和 Inceptionv3 以其令人印象深刻的网络深度脱颖而出。在撰写本文时,NASnet(不在表中)在 ImageNet 上以 82.7%的前 1 名和 96.2%的前 5 名准确率实现了最佳性能。
问题
如果你有任何问题,我很乐意在评论中阅读。如果你想收到我博客文章的更新,请在 Medium 或 Twitter 上关注我!