边缘高效强化学习
图片作者。
借助正交持久性,我们可以在边缘设备上实现顺序学习
在之前的帖子中,我们已经展示了如何利用模型训练(即需要所有数据,通常需要多次通过数据集)和预测(即基于具有预训练参数的单个函数)之间的差异,来有效地部署机器学习(ML)和人工智能(AI)模型。通过将训练好的模型传输到 WebAssembly ,可以将受监督和不受监督的模型高效地部署到云和边缘设备。我们已经能够在可用内存小于 64Kb 的设备上部署模型。相当酷。然而,我们经常被问到如何有效地更新模型(T10)(最好是本地更新模型(T11)):在边缘设备上更新模型(T14)状态的简单方法(T15)将允许有效的顺序学习和强化学习应用。
在边缘设备本身上更新模型状态的简单方法将允许高效的顺序学习和强化学习应用。
在这篇文章中,我们将解释如何使用正交持久性来更新 WebAssembly 二进制文件的状态,以实现这样的功能。我们将首先解释模型更新的概念(ML/AI)视图,然后关注 WebAssembly 中的实现。最后,我们将讨论正交持久性提供的可能性。
更新模型的状态
正如我们之前所描述的,经过训练后,大多数机器学习模型由一个简单的(尽管通常是高维的)数学函数和一组参数组成。例如,在神经网络中,所涉及的权重矩阵的条目构成了通过训练模型获得的参数。有点抽象地说,在训练之后,我们可以将模型定义为一个函数 f() ,它将输入 x 映射到(预期的)输出 y 。我们可以明确说明,函数的行为取决于参数的状态*😗
y = f(x;S1)
其中将包含(例如)所有必需的权重。**
在许多情况下,能够随时间更新状态 S1 是有用的。当(例如)存在概念漂移并且模型的性能随时间下降时,或者当部署到边缘设备的模型需要更新以反映本地情况时,这可能是有用的。在这种情况下(以及更多情况,参见我们下面的讨论),将更新为—即所涉及参数的新状态会很有用,而不会有太多麻烦。此外,当部署模型时,应该维护其更新的状态:即当在边缘设备上运行时,更新的模型应该保持。**
虽然概念上很简单——我们只是将新状态 S2 存储在某个地方——但是当使用 WebAssembly 部署模型时,在边缘设备上高效地实现持久状态更改有些棘手。然而这是很有可能的。
虽然在概念上很容易,但在边缘设备上有效实现持久状态改变在实践中往往很棘手。然而这是很有可能的。
更新 WebAssembly 二进制文件的状态
我们通常使用 WebAssembly 将模型部署到边缘设备。当考虑这种模型的持续更新时,对所涉及的一般过程有一个合理的理解是有用的:
- 我们部署 WebAssembly 二进制文件——它有效地包含了 f(。;)—去边设备。这些二进制文件存储在设备的“磁盘”上,包括它们的状态。****
- 边缘设备包含一个 WebAssembly 运行时,这是一个应用程序,它加载二进制文件并将其所谓的
data section
( 参见本文以获得关于 WebAssembly 二进制文件的更多信息)写入其(线性)内存:这个data section
通常用于存储模型的状态。运行时确保在边缘设备上执行预测(在我们的例子中,当。WASM 二进制被称为,见本教程)。**
一般来说,在 WebAssembly 二进制文件之间来回传输数据是相对容易的:我们通常在传递一个指向内存中存储(例如)特征向量的位置的指针时这样做(详见本教程—特征向量是y = f(x;S)以上符号介绍)。然而,传递一个持续的新状态 S 要棘手得多:不允许直接向现有的data section
写入,这样做也不能确保它在磁盘上的持久性。**
我们通过向所有需要持久更新的 WebAssembly 二进制文件中自动添加一个新的custom data section
有效地解决了这个问题。这个custom data section
在功能上与标准的 WebAssembly 数据部分相同,只是这次它可以被更新:我们将一个导出的update()
函数添加到已部署的。WASM 二进制文件,包含对模型状态进行任何更新所需的逻辑。因此,我们现在可以将状态从 S1 更新到 S2 以确保模型的更新。
然而,简单地更新线性存储器中的状态对于持久性来说是不够的;我们需要确保状态的任何变化也反映在磁盘上(即当打开和关闭设备时,我们希望 S2 被保留)。这是通过扩展标准 WebAssembly 运行时来解决的:我们的 edge 运行时主动监控线性内存中的状态 S 。当它改变时,运行时会在磁盘上覆盖所涉及的 WebAssembly 二进制文件的custom data section
。因此,我们现在有了一个新的、持久的状态。
因此,通过扩展 WebAssembly 的数据结构和默认运行时,有可能在边缘上允许 ML/AI 模型的高效持久更新。
因此,通过扩展 WebAssembly 的数据结构和默认运行时,有可能允许边缘上的 ML/AI 模型的高效持久更新。
持久状态更新的可能性
我们已经描绘了一些场景,在这些场景中,对已部署的 WebAssembly 二进制文件进行(本地)更新可能是有意义的:在概念漂移或本地变化的情况下,使用新的状态不时地更新已部署的模型可能是有用的。然而,快速和持久的更新允许更丰富的应用程序:
- 模型状态的快速和持久更新允许边缘设备上的模型的顺序学习。例如,当模型可以用求和形式来表达时,部署的模型可以很容易地用每个新的(标记的)可用数据点来更新。
- 模型状态的快速和持久更新允许在边缘设备上进行强化学习(从而将独立的 WebAssembly 部署扩展到受监督和不受监督的模型之外)。例如,可以使用
predict()
和update()
函数迭代地实现多武装强盗策略(并添加一些主动探索)。
我们将很快向我们的 WebAssembly Javascript 运行时添加持久更新,从而让您看到在此设置中“运行时覆盖到磁盘”的实际实现。然而,我们希望在上面的帖子中已经提供了一个有价值的方法草图。
放弃
值得注意的是我自己的参与:我是 Jheronimus 数据科学院 的数据科学教授,也是Scailable的联合创始人之一。因此,毫无疑问,我对 Scailable 有既得利益;我有兴趣让它成长,这样我们就可以最终将人工智能投入生产并兑现它的承诺。这里表达的观点是我自己的。
数据科学中的因果推理:有效的抽样框架和设计
实践教程
病例对照设计:数学推导、解释、计算模拟和消除误解
1:背景和动机
当应用因果推断方法时,特别是在非随机环境中,病例对照设计家族是一个强有力的工具。它们是一个有效的抽样框架,用于恢复因果效应的无偏估计,这些估计可以从一个完整的队列研究中恢复,而无需实际进行所述队列研究。
像几乎所有的因果推断方法一样,病例对照设计最初是在医学和公共卫生相关领域发展起来的;这些设计范例中的许多语言和术语反映了这一事实。然而,这些都是强大的方法,可以(并且更普遍地)在任何对恢复非随机化设置中因果效应的无偏估计感兴趣的领域中利用,感兴趣的从业者包括统计学家、数据科学家和机器学习从业者。
让我们指定下面的玩具例子。我们希望在非随机化设置中恢复二元干预对二元结果的因果效应的无偏估计。下图显示了我们框架的一个因果 DAG 在 A 对YC无因果效应的空值下。如果这是一项队列研究,我们可以使用标准方法对 C 进行调整,并恢复A*Y 的因果效应的无偏估计。*****
作者图片
然而,假设为这个队列中的每个观察收集数据非常昂贵或耗时?相反,我们只想获取一小部分观察数据用于我们的分析。而如果二元结局 Y 高度不平衡,其中 Y=1 只代表队列人群的一小部分比例呢?因此,简单地对整个队列进行边际随机抽样是不可行的。与传统的机器学习不同,在传统的机器学习中,我们只关心预测,我们不能以优化我们预测目标的能力的方式方便地“平衡”我们的数据集;这可能极大地扭曲了和 Y 之间的经验关系,使得恢复 A 对 Y 的因果效应的无偏估计变得不可能。**
那么,有没有一种有效的方法(或一系列方法)可以用来有效地对我们的队列进行抽样,进行分析,并且仍然能够恢复我们感兴趣的因果效应的无偏估计?答案是肯定的。使用病例对照设计家族,我们在分析中包括病例和对照的有效取样部分;但是我们这样做的方式仍然允许我们无偏地估计利息的因果效应。
作为一个在使用这些范例方面受过良好训练的人,我发现与其他方法相比,关于这些方法的教育材料和资源很缺乏。围绕这些方法还有一些常见的误解和困惑。我的目标是这篇文章有助于缓解这些挑战。
这篇文章的内容如下:
作者图片
所以让我们开始吧。
2:5 个病例对照范例的详细示例
为了便于说明,让我们指定一个简化的玩具示例。假设我们有一个利益的二元干预和利益的二元结果。在我们的玩具示例中,我们的群组中总共有 13 个人。所有个体在随访开始时都以结果 Y=0 开始。每个人都被随访一段可变的时间,总共有 3 个人在随访结束时经历了结果 Y=1 。这 3 个人是观察值 7、9 和 13,在下图中用圆圈标记。我对这 3 个观察结果进行了不同的颜色编码,稍后会变得清晰。**
作者图片
需要记住几个关键点:
- 在下面的 13 个观测值中,一些有二元干涉 A=0 ,其余的有 A=1 。考虑到我们将对独立于干预状态的控制进行采样,我特意没有在下图中标出每个观察的干预状态。这个以后就清楚了。
- 在实际的病例对照分析中,为了恢复干预对结果 Y 的无偏因果估计,我们可能会因 C 而产生混淆,我们希望稍后进行调整。因此,在混杂因素 C 水平内匹配病例和对照可能是有益的。在下面的例子中,为了简单起见,我特意省略了混杂因素 C 。在实际分析中,如果在 C 上匹配是有益的,我们将在混杂 C 的层次内实现下面示例中的匹配标准。
2.1:事件密度采样
在发病率密度抽样中,我们从研究基地中产生病例的人-时间总量中抽取对照样本。取样控件的示例如下所示,取样控件由相应的彩色 x 表示。对于每种情况,我们匹配两个对照。下表显示了三个病例及其相应的六个匹配对照的图。
作者图片
对于事件密度抽样方案,有三个关键点需要记住:
- 随后可能成为病例的观察结果可以作为对照进行采样(注意观察结果 9 如何成为病例,也作为对照进行采样,与观察结果 7 相匹配)。**
- 观察值通过替换进行采样,这意味着它们可以作为对照进行多次采样(注意观察值 8 如何作为对照进行两次采样)*。注意,“替换取样”适用于本文讨论的所有取样方案的*。****
- 假设我们从总的人-时间池中取样,观察值随机取样作为人-时间长度的控制(即加权随机取样)。因此,例如,在上面的图中,观察值 8 比观察值 12 具有更高的被采样作为对照的概率,假设观察值 8 的随访总长度更长。因此,我们使用加权随机抽样法对对照组进行随机抽样,每次观察的权重与观察在队列中所占的总人-时间成比例
我们现在有病例,也有控制手段。我们知道我们可以计算病例对照优势比估计量:
作者图片
我们可以从数学上证明,在利用发病率密度抽样的情况下,病例对照比值比估计值是我们在进行完整队列研究时估计的发病率比(IRR)的无偏估计值。这是有条件的,假设在干预 A=1 和 A=0 总体中,抽样控制的抽样分数是相同的。这就是为什么必须独立于干预状态对质控品进行取样的原因。
上述陈述的数学证明如下所示:
作者图片
2.1.1:病例对照分析中“比值比”的注释
关于病例对照研究的一个常见误解如下:
“病例对照研究估计比值比,需要罕见结果假设来估计风险比”
正如我们从入射密度采样的详细演练中看到的,上述陈述(一般而言)是不正确的。许多困惑源于围绕病例对照研究设计的拙劣语言。
从病例对照分析中估计的优势比与从队列分析中估计的优势比不是同一个经验指标。从算法上来说,它看起来是相同的度量。然而,参照病例对照研究,抽样“对照”并不是真正的对照。更确切地说,抽样“控制”是从一些独立于干预状态的随机抽样的基础研究中抽取的。该“研究基础”可以是处于危险中的人-时间、处于危险中的观察的总人口、在审查之前从未得出结果的处于危险中的观察的总人口等。“研究基础”的详细说明改变了对比值比的解释,该比值比是从病例对照研究中根据经验得出的。
正如我们在上面所看到的,采用发病率密度抽样的病例对照分析恢复了一个经验优势比,这是一个我们在进行全队列研究时估计的发病率比的无偏估计。注意,这和“罕见病”的假设完全没有关系。
请注意,有一个抽样方案(累积密度抽样),其中病例对照比值比估计值是我们进行全队列研究时估计的比值比的无偏估计值(见 2.3 节)*。*
让我们进入下一个抽样方案,风险集抽样
2.2:风险设定抽样
风险集抽样几乎与发病率密度抽样相同,除了对照抽样与人-时间匹配的相应病例。正如发病率密度抽样一样,作为风险集抽样结果的病例对照比值比估计值也是发病率比(IRR)的一个无偏估计值,如果我们进行了一项完整的队列研究,我们就会对其进行估计。
如果人-时间长度是干预 A 和结果 Y 因果关系的混杂因素,并且人-时间需要在分析的后期进行调整,出于统计效率和阳性相关的原因,使用风险集抽样而不是发病率密度抽样可能是有益的。
从同一个玩具示例开始,下面再次显示了该采样方案的示例实现。
作者图片
2.3: 累计发病率抽样
累积发病率抽样是经典的“病例对照”旗帜下的最后一种抽样技术。这也是(我相信)最容易理解的。
对于在随访监测结束时没有成为病例的每个观察结果,我们从观察总体中随机抽取对照样本。请注意,我们不再按时间匹配(如风险集抽样)或随机抽样控制与基于人-时间长度的加权随机抽样(如发病率密度抽样)。相反,到随访结束时,群组中仍为 Y=0 的每个观察值被采样作为对照的可能性相等。
同样,下面显示的是这种采样方案的一个示例实现。
作者图片
在累积发病率抽样的情况下,病例对照比值比估计值是我们进行完全队列研究时估计的实际比值比(or)的无偏估计值。这也是基于这样的假设:在干预 A=1 和 A=0 人群中,抽样控制的抽样分数是相同的。
上述陈述的数学证明如下所示:
作者图片
2.4:基于案例的采样
基于病例的设计与累积密度抽样的病例对照设计有某些相似之处。不同之处在于:
- 使用累积密度抽样,我们等到随访结束时才知道哪些观察结果发展成病例,哪些没有。然后,我们从从未成为病例的观察群体中抽取对照样本。
- 在基于病例的设计中,我们在随访开始时对对照进行取样,不管这些观察结果是否可能(在某一点上)发展成病例。
因此,在基于病例的设计中,我们从引起病例的潜在观察队列中取样。
下面显示的是这种采样方案的一个实现示例。
作者图片
在基于病例的设计中,病例对照比值比估计值是对风险比(RR)的无偏估计,如果我们进行了完整的队列研究,我们就会对 RR 进行估计。这也是基于这样的假设:在干预 A=1 和 A=0 人群中,抽样控制的抽样分数是相同的。
上述陈述的数学证明如下所示:
作者图片
2.5:病例群组抽样
病例队列设计与风险集抽样的病例对照设计有某些相似之处。在某些方面,病例-群组设计抽样方案更简单;尽管它需要比本文讨论的任何其他抽样方案更复杂的统计分析。
病例组设计的步骤如下:
- 我们从随访开始时从总人口中随机抽样一个亚队列开始。这个小组是一个简单的边际随机样本。
- 我们识别整个队列中的所有病例。注意,有些病例可能属于亚组,有些可能不属于。
- 然后,我们随着时间的推移跟踪亚队列中的病例和所有观察结果。当每个病例发展成一个病例时,我们对在该时间点仍在亚队列中的所有其他观察结果进行采样,作为该病例的匹配对照。
下面显示的是这种抽样方案的一个实现示例,其中观察值 2 到 10 作为“子群组”被随机抽样。
请记住以下几点:
- 类似于带有风险集抽样的病例对照设计,我们对每个病例对照进行人-时间匹配的抽样。
- 与风险设定抽样或本文中讨论的任何其他抽样方案不同,我们不一定要对每个案例进行相同数量的控制抽样。在每个病例匹配时,我们对亚队列中的所有观察结果进行采样,而不是对每个病例的恒定数量的对照进行采样。随着时间的推移,随着随访时间的结束,观察结果退出亚队列,与每个病例相匹配的对照组的数量变得越来越少。我们可以在上面的玩具例子中看到这种效果;观察 9 与 8 个对照匹配,观察 7 与 7 个对照匹配,观察 13 仅与 5 个对照匹配。
作者图片
3.病例对照分析中的混杂调整
不管采用何种抽样方案,在病例对照分析中,通常采用我们的抽样程序,在混杂因素水平内匹配病例和对照。就这样做的动机而言,一个常见的误解如下:
作者图片
注意,上述说法是不正确的**。**
这种匹配的目的是而不是来调整混杂(至少在分析的这一步不是这样)。我相信这种混乱很大程度上源于队列研究中的匹配比较。
在队列研究中,在混杂因素 C 的水平内,匹配干预 A=1 和非干预 A=0 观察值(具有一些指定的匹配率)确实会产生一个匹配人群,其中CA***。这也是在一项队列研究中,通过 C 充分调整了混杂因素。假设满足因果推断的条件(一致性、正定性和可交换性),在匹配人群中,对 Y 的经验平均边际效应恢复了对 Y 的平均因果效应的无偏估计。*****
同样,以上是队列研究中匹配的结果。病例对照研究中的情况并非如此。
在病例对照研究中,当抽样控制时,我们通常在混杂水平内匹配病例和控制。这不会通过 C 对混杂进行调整(至少不会在分析的这一步)。相反,在匹配之后,我们仍然需要通过 C 来调整混杂,无论是通过使用标准方法(条件作用)、倾向得分调整、边际结构建模、G 估计等。我们在 C 水平内匹配病例和对照的原因是出于两个重要的研究设计考虑:
- 抽样估计量的统计效率
- 当恢复因果效应的无偏估计时,减少积极性问题的可能性
通过在混杂因素 C 的水平内匹配病例和对照,我们保证在C 的每个水平内至少有一些病例和对照的观察实例,使我们更有可能获得我们所需要的足够数据,甚至有可能在以后对 C 进行调整。这与因果推理的肯定性要求有关。
这也可以通过检查下面的因果有向无环图(Dag)来理解。注意,这些示例 Dag 是在干预对结果没有因果影响的无效假设下绘制的(因此缺少从 A 到 Y 的有向箭头)。**
在图 1 中,我们从讨论中的整个群组开始。我们想要估计干预对结果 Y 的因果效应。我们可以看到,变量 C 是两个和*的共同原因。由 d-分离规则可知,路径从 A 到 C 到 Y 是开放的,用 C 混淆了 A 对 Y 。***
作者图片
在图 2 中,我们通过从一些潜在的研究基础中提取所有病例( Y=1 )和一个对照样本,在混杂因素 C 水平内匹配病例,来创建我们的病例对照人群。假定病例与对照的相同比例在 C 水平内匹配,在这个匹配的人群中 C 不再是结果 Y 的直接原因(因此缺少从 C 到 Y 的箭头)。
然而, C 直接告知我们用于选择控件的标准 S ,正如结果状态 Y 一样,因此定向箭头到 S 和 Y 到 S 。注意 S 是 C 和 Y 之间的碰撞体。此外,假设我们的病例对照分析仅限于我们匹配的人群,我们的条件是选择标准 S (因此围绕 S )。通过 d-分离,通过在碰撞器 S 上的调节,我们打开了从 C 到 Y 通过 S 的联想路径。因此,以我们的抽样标准 S 为条件,从 A 到 C 到 S 到的联想路径仍然是开放的,留下变量 C**
这就是为什么在混杂因素 C 的水平内匹配我们的病例和对照本身并不针对CofAonY的混杂因素进行调整。我们后面还需要针对 C 进行调整。
作者图片
最后,图 3 显示了我们在病例对照人群中使用标准方法(条件反射)对 C 进行调整。以 C 为条件从 A 到 C 到 S 的关联路径现已关闭。因此,如果我们估计在上匹配的总体中 A 对YC的平均条件效应,我们将恢复出对Y 的平均因果效应的无偏估计。******
作者图片
4.5 种范式的计算机模拟
为了巩固上述证明和概念,进行了计算模拟研究。根据以下 DAG 指定了一百万次观察的模拟队列:
在模拟队列中,随访时间的长度被随机分配到 30 到 100 个单位的观察值。目标是恢复对的相对因果影响的无偏估计。按照上面的 DAG,这可以通过在C 上的结果模型中的标准调节方法来实现。因此,AY以 C 为条件的条件估计,恢复了对AY 因果效应的无偏估计。********
在我们的模拟研究中,如果我们可以访问整个队列,我们将受益于恢复真实的“基于队列的”发病率比(IRR)、比率比(RR)和优势比(OR)。在我们的模拟中,我们将实现本文中讨论的五种病例对照抽样方法中的每一种,并展示它们如何产生与其对应的“群组”估计非常接近的估计。
从上面的模拟输出中,我们得到了以下结果。我们可以看到病例对照比值比估计值与他们估计的相应队列相对测量值非常匹配。
作者图片
5.总结和结论
如果你想了解更多的病例对照分析,特别是一级与二级对照,以及对照选择的考虑,我强烈建议你阅读 Wacholder 等人的 3 部分系列。
此外,如果你想了解更多关于因果推理的方法和注意事项,我会推荐哈佛大学的 Miguel Hernan 和 Jamie Robins(我以前的两位教授)的教科书“causalem Inference:What If”,加州大学洛杉矶分校的 Judea Pearl 的教科书“causalem”,以及斯坦福大学的达芙妮·黑仔和耶路撒冷希伯来大学的 Nir Friedman 的教科书“probabilical graphic Models:Principles and technologies”。这些都是很棒的文章。我计划在未来写更多关于因果推理的深度文章。
因果推断:如果会怎样
希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。我打算在未来写一些基础作品,所以请随时在 LinkedIn 上与我联系,并在 上关注我这里的 更新!
TigerGraph 和 Docker 的有效使用
TigerGraph 可以与 Docker 结合,在几乎任何操作系统上运行。在本文中,我们解决了大型 TigerGraph 图像及其未使用潜力的问题。
图片作者。Logos 分别来自 TigerGraph 和 Docker 的网站。
TigerGraph 是我首选的图形数据库和图形分析平台,因为它速度快、可扩展,并且有一个活跃的开源社区。由于附近没有 TigerGraph Cloud 服务器,我经常在本地使用 TigerGraph。
在撰写本文时, TigerGraph 软件要求规定支持以下操作系统:
- Redhat 和 Centos 版本 6.5–6.9、7.0–7.4 和 8.0–8.2
- Ubuntu 14.04、16.04 和 18.04
- Debian 8
对于使用这个列表之外的操作系统的人来说,一个合理的解决方案是使用容器化:Docker,在本文的例子中。
在本文中,我们将涵盖:
- 如何使用官方 TigerGraph 和里面的内容
- 剥离不必要的臃肿的官方码头形象
- 修改入口点以添加:
- 启动时运行
gadmin
- 运行绑定在某个目录下的 GSQL 脚本
- 将日志文件的输出发送到
STDOUT
4.使用 Docker Compose 运行 TigerGraph 图像
官方 TigerGraph 图像
运行开发人员版的官方 TigerGraph 映像可以通过以下命令获得:
docker pull docker.tigergraph.com/tigergraph-dev:latest
运行方式:
docker run -d -p 14022:22 -p 9000:9000 -p 14240:14240 --name tigergraph_dev --ulimit nofile=1000000:1000000 -v ~/data:/home/tigergraph/mydata -t docker.tigergraph.com/tigergraph-dev:latest
该源给出了关于如何构建图像的更深入的说明,但概括来说:
- 使用 Ubuntu 16.04 的基本映像
- 所有必需的软件,如 tar、curl 等。已安装
- 可选软件,如 emacs、vim、wget 等。已安装
- GSQL 101 和 102 教程以及 GSQL 算法库已下载
- SSH 服务器、REST++ API 和 GraphStudio 是 3 个值得注意的端口,它们可以暴露出来并用于与服务器通信
整个映像的下载量接近 1.8-2.0 GB(取决于版本),这给带宽带来了相当大的压力,尤其是对于 CI/CD 这样的资源敏感型使用情形。另一个值得注意的点是,使用 TigerGraph 所需要的只是一个 GSQL 套接字连接,它可以通过诸如 Giraffle 和 pyTigerGraph 之类的工具进行接口。
我已经确定了膨胀的两个主要来源:
- 可选和不必要的软件,如 vim 和 GSQL 教程 101
- TigerGraph Developer Edition 的最小操作不需要 GraphStudio 和二进制文件
剥离老虎图像
我用 Bitnami 的 MiniDeb 映像替换了基本映像ubuntu:16.04
,以便去掉几兆字节的不必要空间。这是黛比·杰西的作品。
下一步是删除在官方映像的apt-get
阶段安装的不必要的二进制文件。我将 Vim 作为唯一的命令行文本编辑器,但保留了诸如wget
、git
、unzip
、emacs
等二进制文件。不再安装。
在 TigerGraph 安装期间,严格执行硬件要求,如果不满足,安装将失败。因为我想让 DockerHub runners 自动构建和推送我的映像,所以我破解了这个检查,以便资源不足的 runners 可以继续构建映像。
这是通过用我的版本替换os_utils
二进制来实现的,这使得check_cpu_number()
和check_memory_capacity()
函数更加宽松。这个二进制文件可以在下面找到:
/home/tigergraph/tigergraph-${DEV_VERSION}-developer/utils/os_utils
这已经减少了大约 400MB 的膨胀,我的 DockerHub 图像报告了 1.52GB 的压缩大小 TigerGraph 3.0.0(我注意到下载这些层表明它大约为 1.62GB)。
注意:我曾试图随意删除 GraphStudio 二进制文件,但这无法通过gadmin start
脚本,因此必须进行更细致的调整,以便从 TigerGraph 中删除更多内容,例如编辑gadmin
Python 脚本。
一旦我下载并解压缩了这两个图像,它们之间的最终结果可以通过调用docker images
来查看:
我构建的源代码可以在这里找到我鼓励任何有建议的人联系我!
更新(2020 年 2 月 11 日):感谢 Bruno imi 在这项工作的基础上精简了 TigerGraph 企业版,并与我分享了他的代码。以下额外的剥离是他的工作,已经在这个形象实施。
安装目录下似乎有文档和不必要的构建工件(比如许多node_modules
)。可以在以下位置找到这些示例:
${INSTALL_DIR}/app/${DEV_VERSION}/document/
${INSTALL_DIR}/app/${DEV_VERSION}/bin/gui/server/node_modules
${INSTALL_DIR}/app/${DEV_VERSION}/bin/gui/node/lib/node_modules
${INSTALL_DIR}/app/${DEV_VERSION}/gui/server/node_modules/
${INSTALL_DIR}/app/${DEV_VERSION}/.syspre/usr/share/
${INSTALL_DIR}/app/${DEV_VERSION}/.syspre/usr/lib/jvm/java-8-openjdk-amd64–1.8.0.171/
其中INSTALL_DIR
是 TigerGraph Developer Edition v3 的/home/tigergraph/tigergraph
,而DEV_VERSION
是特定版本,例如3.0.0
。
执行 tiger graph Developer Edition v3.0.5 的新版本,并将其与我的新版本 v 3 . 0 . 5 进行比较,我们看到使用了以下数量的磁盘空间:
修改入口点
在我们添加功能之前,让我们先来看看最初的ENTRYPOINT
:
ENTRYPOINT /usr/sbin/sshd && su — tigergraph bash -c “tail -f /dev/null”
这做了两件事:
- SSH 服务器通过运行
/usr/sbin/ssh
启动。 - 通过以用户
tigergraph
的身份运行tail
命令,容器保持活动状态。这样做的目的是不断地从/dev/null
读取输出,这也是为什么容器的STDOUT
是空的。
启动时启动“gadmin”
为了改善用户体验,我添加了一行代码,在 Docker 入口点启动gadmin
服务
ENTRYPOINT /usr/sbin/sshd && su - tigergraph bash -c "tail -f /dev/null"
到
ENTRYPOINT /usr/sbin/sshd && su - tigergraph bash -c "/home/tigergraph/tigergraph/app/cmd/gadmin start all && tail -f /dev/null"
一个很简单却很有价值的改变!
使用卷在启动时运行 GSQL 脚本
TigerGraph Docker 映像缺少(MySQL、MariaDB 和 PostgreSQL 等其他数据库映像有)一个名为 Something 的目录,类似于docker-entrypoint-init.d
,用户可以在其中绑定数据库脚本以在启动时运行,例如用于模式创建或数据库填充。
有多种方法可以实现这一点,但我选择了一个相当简单的方法,在gadmin
和tail
命令之间添加下面一行:
该命令的工作原理是:
- 命令
if
将检查一个名为/docker-entrypoint-initdb.d
的目录是否存在,除非存在,否则不会执行下一步。 for file in /docker-entrypoint-initdb.d/*.gsql; do
行将对入口点文件夹中以扩展名gsql
结尾的所有文件开始 for-each 循环。su tigergraph bash -c
行将对 for-each 循环给出的文件运行 GSQL 命令。- 通过添加
|| continue
,如果脚本执行失败,容器和循环都不会停止。
如果放入entrypoint.sh
中,这看起来会更整洁,但这取决于你!最终结果看起来像这样:
将日志路由到标准输出
为了弄清楚日志属于哪里,可以运行gadmin log
,它将返回如下内容
ADMIN : /home/tigergraph/tigergraph/log/admin/ADMIN#1.out
ADMIN : /home/tigergraph/tigergraph/log/admin/ADMIN.INFO
CTRL : /home/tigergraph/tigergraph/log/controller/CTRL#1.log
CTRL : /home/tigergraph/tigergraph/log/controller/CTRL#1.out
DICT : /home/tigergraph/tigergraph/log/dict/DICT#1.out
DICT : /home/tigergraph/tigergraph/log/dict/DICT.INFO
ETCD : /home/tigergraph/tigergraph/log/etcd/ETCD#1.out
EXE : /home/tigergraph/tigergraph/log/executor/EXE_1.log
EXE : /home/tigergraph/tigergraph/log/executor/EXE_1.out
...etc
我最感兴趣的是管理日志,所以我将把tail
命令改为从/home/tigergraph/tigergraph/log/admin/ADMIN.INFO
而不是/dev/null
中读取。
现在,写入管理日志的任何内容都将自动传输到容器的日志中。所有三个步骤的最终产品现在是:
使用 Docker 编写 TigerGraph
请注意,我添加了一个健康检查,它每 5 秒钟调用一次 REST++ echo 端点来确定容器是否健康。如果您使用官方映像,您将需要 SSH 到容器中来手动启动所有服务:
如果您希望 GSQL 脚本在启动时运行,请在volumes
下添加以下条目:
- my_script.gsql:/docker-entrypoint-initdb.d/my_script.gsql
请注意,我添加了一个健康检查,它每 5 秒钟调用一次 REST++ echo 端点来确定容器是否健康。这对于许多应用程序都很有用,在我的用例中,用于在开始测试之前检查容器在集成测试期间是否准备好了。
如果您使用官方映像,您将需要 SSH 到容器中来手动启动所有服务:
默认密码是“tigergraph ”,在此之后您可以调用该命令
gadmin start all (v3.0.0 >=)
gadmin start (v3.0.0<)
GraphStudio 可以在您的网络浏览器的localhost:14240
中找到,Rest++可以在localhost:9000
中找到。
结论
在这篇文章中,我们有:
- 检查了官方文件,
- 识别并删除明显不必要的文件,
- 构建了一个精简版的 TigerGraph 图像,节省了大量的磁盘空间,
- 修改了
ENTRYPOINT
以向容器添加额外的自动化,以及 - 使用 Docker Compose 运行此图像。
如果你对进一步减小图片的大小有任何建议或想法,那么留下评论、问题或分叉,并在 GitHub 库上对本文中的代码提出请求。如果你想看更多 Docker 相关的指南,为数据库如 JanusGraph 建立自定义设置,请在下面留下评论。
你可以在 Docker Hub 上找到这些图片,我会随着新版本的出现继续更新这些图片,或者直到 TigerGraph 推出正式的更瘦版本。
如果您想加入 TigerGraph 社区并做出贡献,或者启动令人敬畏的项目并深入了解 TigerGraph 的未来,请在以下平台上加入我们:
如果你有兴趣看我的其他作品,那就看看我在 https://davidbakereffendi.github.io/的个人主页。
TigerGraph 的 Jon Herke 在社区中发挥了领导作用,让我们能够以有意义的方式做出贡献,Bruno imi 分享了他在精简 TigerGraph 企业版图像方面的发现,这些发现可在https://hub.docker.com/r/xpertmind/tigergraph找到。
使用 Scrapy 进行有效的网页抓取
https://unsplash.com/@markusspiske
Scrapy 的新功能使您的刮削效率
Scrapy 作为一个网页抓取框架是强大的和可扩展的。它有一个活跃的用户群,每次更新都会有新的功能出现。在本文中,我们将介绍其中的一些特性,以充分利用您的抓取项目。
在本文中,您将了解到
- 更有效地跟踪链接
- html 属性的更清晰提取
- Scrapy 中函数间更清晰的变量传递
- 使用 attribute 属性在没有 xpath 或 css 选择器的情况下获取 html 属性
1.以下链接
为了让你的蜘蛛跟踪链接,这是通常的做法
links = response.css("a.entry-link::attr(href)").extract()
for link in links:
yield scrapy.Request(url=response.urljoin(link), callback=self.parse_blog_post)
现在使用 requests 方法就可以了,但是我们可以使用另一个名为 response.follow()的方法来清理这个问题。
links = response.css("a.entry-link")
for link in links:
yield response.follow(link, callback=self.parse_blog_post)
看看我们为什么不必提取链接或使用 urljoin,这是因为 response.follow 接受标签。Response.follow()自动使用 href 属性。
for link in response.css("a.entry-link"):
yield response.follow(link, callback=self.parse_blog_post)
事实上,scrapy 可以使用 follow_all()方法处理多个请求。这样做的好处是 follow_all 将直接接受 css 和 xpath。
yield from response.follow_all(css='a.entry-link', allback=self.parse_blog_post)
2.提取数据
从标签中提取数据的常用方法是extract()
和extract_first()
。我们可以用一个方法get()
和get_all()
,看起来干净一点。
从
def parse_blog_post(self, response):
yield {
"title": response.css(".post-title::text").extract_first(),
"author": response.css(".entry-author::text").extract_first(),
"tags": response.css(".tag::text").extract(),
}
到
def parse_blog_post(self, response):
yield {
"title": response.css(".post-title::text").get(),
"author": response.css(".entry-author::text").get(),
"tags": response.css(".tag::text").getall(),
}
3.使用属性选择数据
如果您不习惯 xpath 或 css 选择器,Scrapy 可以让您以类似字典的方式获取属性。
从
**>>** response.css('a::attr(href)').getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
到
**>>** [a.attrib['href'] **for** a **in** response.css('a')]
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
使用attrib
你可以获取 html 属性,而不是使用 xpath 或 css!
4.从回调传递数据
通常当你做一些网络抓取时,你需要将信息从一个函数传递到另一个函数。这是您通常使用 response.meta 函数的地方。
def parse_blog_post(self, response):
for link in links:
yield scrapy.Request(
link,
meta={"author": author, "date": post_date},
callback=self.parse_full_blog_post,
)
def parse_full_blog_post(self, response):
author = response.meta["author]
post_date = response.meta["post_date]
现在我们可以使用 follow_all()函数和名为cb_kwargs
的新关键字,这允许我们传递一个值的字典,然后我们可以在回调函数中访问它。
def parse_blog_post(self, response):
yield from response.follow_all(
links,
cb_kwargs={"author": author, "date": post_date},
callback=self.parse_full_blog_post,
)
def parse_full_blog_post(self, response, author, post_date):
我们在字典中定义变量author
和post_date
,并在parse_full_blog_post
中声明它们。干净多了!
我希望你会发现这些技巧对充分利用 Scrapy 框架很有用。
参考
- https://stummjr.org/post/scrapy-in-2020/——这篇文章的来源和一篇很棒的博客文章详细介绍了这些观点!
相关文章
今天如何最大限度地学习 python
towardsdatascience.com](/approach-to-learning-python-f1c9a02024f8) [## 你应该知道的 5 个 Python 技巧
如何轻松增强 python 的基础知识
medium.com](https://medium.com/swlh/5-python-tricks-you-should-know-d4a8b32e04db) [## Scrapy:这就是如何轻松成功登录
揭秘用 Scrapy 登录的过程。
towardsdatascience.com](/scrapy-this-is-how-to-successfully-login-with-ease-ea980e2c5901)
请看这里关于我在我的博客和其他帖子上关于项目的更多细节。更多技术/编码相关内容,请点击这里订阅我的简讯
我将非常感谢任何评论,或者如果你想与 python 合作或需要帮助,请联系我。如果你想和我联系,请在这里 asmith53@ed.ac.uk 或在 twitter 上联系。
EfficientNet:卷积神经网络的扩展做得很好
如何智能缩放 CNN 以实现精度增益
在 Unsplash 上由 Lidya Nada 拍摄的照片
自从 Alex net 赢得 2012 年 ImageNet 挑战赛以来,卷积神经网络在计算机视觉领域变得无处不在。他们甚至发现了它们在自然语言处理中的应用,其中最先进的模型使用卷积运算来保留上下文并提供更好的预测。然而,与所有其他神经网络一样,设计 CNN 的关键问题之一是模型缩放,即决定如何增加模型大小以提供更好的准确性。
这是一个繁琐的过程,需要手动反复试验,直到产生满足资源约束的足够精确的模型。该过程耗费资源和时间,并且经常产生具有次优精度和效率的模型。
考虑到这个问题,谷歌在 2019 年发布了一篇论文,涉及一个新的 CNN 家族,即 EfficientNet。与 GPipe 等先进模型相比,这些 CNN 不仅提供了更好的精度,还通过减少参数和 FLOPS(每秒浮点运算)流形提高了模型的效率。本文的主要贡献是:
- 设计一个简单的手机大小的基线架构: EfficientNet-B0
- 提供一种有效的复合缩放方法来增加模型大小,以实现最大的精度增益。
EfficientNet-B0 架构
表 1。基线网络的架构细节
复合缩放方法可以推广到现有的 CNN 架构,例如移动网络和 ResNet。然而,选择一个良好的基线网络对于实现最佳结果至关重要,因为复合比例方法仅通过复制基础网络的底层卷积运算和网络结构来增强网络的预测能力。
为此,作者利用神经架构搜索构建了一个高效的网络架构, EfficientNet-B0 。仅用 5.3M 参数和 0.39B FLOPS 就在 ImageNet 上实现了 77.3%的准确率。(Resnet-50 提供 76%的精度,参数为 26M,触发器为 4.1B)。
该网络的主要构建模块由 MBConv 组成,并添加了压缩和激励优化功能。MBConv 类似于 MobileNet v2 中使用的反向残差块。这些在卷积块的开始和结束之间形成快捷连接。首先使用 1x1 卷积来扩展输入激活图,以增加特征图的深度。随后是 3×3 深度方向卷积和点方向卷积,减少了输出特征图中的通道数量。捷径连接连接较窄的层,而较宽的层存在于跳跃连接之间。这种结构有助于减少所需操作的总数以及模型大小。
图一。反向剩余块
该块的代码可以推测为:
from keras.layers import Conv2D, DepthwiseConv2D, Add
def inverted_residual_block(x, expand=64, squeeze=16):
block = Conv2D(expand, (1,1), activation=’relu’)(x)
block = DepthwiseConv2D((3,3), activation=’relu’)(block)
block = Conv2D(squeeze, (1,1), activation=’relu’)(block)
return Add()([block, x])
复合缩放
图二。模型缩放。(a)是一个基线网络示例;(b)-(d)是仅增加网络宽度、深度或分辨率的一个维度的传统缩放。(e)是我们提出的复合缩放方法,它以固定的比例统一缩放所有三个维度。
卷积神经网络可以在三个维度上扩展:深度、宽度、分辨率。网络的深度对应于网络的层数。宽度与层中神经元的数量相关,或者更确切地说,与卷积层中滤波器的数量相关。分辨率就是输入图像的高度和宽度。上面的图 2 更清晰地展示了这三个方面的扩展。
通过堆叠更多的卷积层来增加深度,允许网络学习更复杂的特征。然而,更深层次的网络往往会受到梯度消失的影响,变得难以训练。虽然诸如批量标准化和跳过连接的新技术在解决这个问题上是有效的,但是经验研究表明,仅通过增加网络的深度而获得的实际精度会很快饱和。例如,Resnet-1000 提供了与 Resnet-100 相同的精度,尽管有额外的层。
缩放网络的宽度允许层学习更精细的特征。这一概念已在众多作品中广泛使用,如广域网络和移动网络。然而,与增加深度的情况一样,增加宽度会阻止网络学习复杂的特征,从而导致精度增益下降。
更高的输入分辨率提供了关于图像的更多细节,因此增强了模型推理更小对象和提取更精细模式的能力。但是像其他缩放维度一样,这本身也提供了有限的精度增益。
图 3。用不同的网络宽度(w)、深度(d)和分辨率®系数放大基线模型。
这导致了一个重要的观察结果:
观察 1 : 按比例增加网络宽度、深度或分辨率的任何维度都会提高精度,但是对于更大的模型,精度增益会减小。
图 4。为不同的基线网络调整网络宽度。
这意味着,为提高精确度而进行的网络缩放应该部分地由这三个维度的组合来贡献。图 4中的经验证据证实了这一点,在图 4中,对于不同的深度和分辨率设置,网络的精度以增加的宽度进行建模。
结果表明,仅缩放一维(宽度)会使精度增益迅速停滞。然而,将此与层数(深度)或输入分辨率的增加相结合,可以增强模型的预测能力。
这些观察多少有些意料之中,可以用直觉来解释。例如,如果输入图像的空间分辨率增加,卷积层的数量也应该增加,使得感受野足够大以跨越现在包含更多像素的整个图像。这导致了第二个观察结果:
观察 2: 为了追求更高的精度和效率,在 ConvNet 扩展期间平衡网络宽度、深度和分辨率的所有维度至关重要。
建议的缩放方法
卷积神经网络可以被认为是各种卷积层的堆叠或组合。此外,这些层可以被划分成不同的级,例如 ResNet 具有五个级,并且每个级中的所有层具有相同的卷积类型。因此,CNN 在数学上可以表示为:
等式 1
其中 n 表示网络,I 表示级数,F ᵢ表示第 I 级的卷积运算,L ᵢ表示 F ᵢ在级 I 中重复的次数。H ᵢ、W ᵢ和 C ᵢ简单地表示级 I 的输入张量形状
从等式 1 可以推导出,L ᵢ控制网络的深度,C ᵢ负责网络的宽度,而 H ᵢ和 W ᵢ影响输入分辨率。找到一组好的系数来缩放每一层的这些维度是不可能的,因为搜索空间是巨大的。因此,为了限制搜索空间,作者制定了一套基本规则。
- 比例模型中的所有层/级将使用与基线网络相同的卷积运算
- 所有层必须以恒定的比例均匀缩放
建立这些规则后,等式 1 可以参数化为:
等式 2
其中 w、d、r 是用于缩放网络宽度、深度和分辨率的系数;F̂ ᵢ、L̂ ᵢ、ĥᵢ、ŵᵢ、ĉᵢ是基线网络中预定义的参数。
作者提出了一种简单但有效的缩放技术,该技术使用复合系数ɸ 以原则方式统一缩放网络宽度、深度和分辨率:
等式 3
ɸ是一个用户定义的全局比例因子(整数),它控制有多少资源可用,而 α 、 β 和 γ 决定如何将这些资源分别分配给网络深度、宽度和分辨率。卷积运算的 FLOPS 与 d,w,r, 成比例,因为深度加倍会使 FLOPS 加倍,而宽度或分辨率加倍会使 FLOPS 增加几乎四倍。因此,使用等式 3 缩放网络将使总 FLOPS 增加(α * β * γ ) ^ɸ。因此,为了确保总 flops 不超过 2^ϕ,应用约束(α * β * γ ) ≈ 2。这意味着,如果我们有两倍的可用资源,我们可以简单地使用复合系数 1 将 FLOPS 缩放 2 倍。
参数- α 、 β 和 γ- 可通过设置 ɸ=1 并找到产生最佳精度的参数,使用网格搜索来确定。一旦找到,这些参数可以固定,复合系数 ɸ 可以增加,以获得更大但更精确的模型。这就是 EfficientNet-B1 到 EfficientNet-B7 的构造方式,名称末尾的整数表示复合系数的值。
结果
这种技术使作者能够生产出比现有的 ConvNets 精度更高的模型,同时也极大地减少了总体 FLOPS 和模型尺寸。
表二。针对 ImageNet 挑战,比较 EfficientNet 与现有网络
这种缩放方法是通用的,可以与其他架构一起使用,以有效地缩放卷积神经网络,并提供更好的精度。
表 3。向上扩展 MobileNets 和 ResNet。
参考资料:
- [高效网](http://Rethinking Model Scaling for Convolutional Neural Networks) , ICML 2019
- MobileNet v2, CVPR 2018
- GPipeNIPS 2019
- 官方发布的代码:https://github . com/tensor flow/TPU/tree/master/models/official/efficient net
EfficientNet 应该是 goto 预训练模型或…
比较不同预训练模型的时间和准确性,并最终创建一个集成来提高结果。
乔恩·泰森在 Unsplash 上的照片
一周前我还没有听说过这个术语,现在我认为 EfficientNet 是最好的预训练模型。在他们的论文中,他们已经展示了它的艺术状态,所以让我们来测试一下,当你为一个模型选择一个主干时,它是否应该是你的选择。我将把它的性能与广泛使用的 MobileNet、Inception 和 Xception 进行比较,比较的基础是每个时期训练和执行推理所用的时间,当然还有准确性。我决定用一场狗对猫的 Kaggle 比赛来做我的裁判,让一切都脱离我的掌控。在我们开始模型和比较之前,如果你想了解更多关于什么是 EfficientNet 和它的所有八个模型的架构,你可以先阅读我以前的文章。
让我们深入了解所有不同高效网络模型的体系结构细节,并找出它们的不同之处…
towardsdatascience.com](/complete-architectural-details-of-all-efficientnet-models-5fd5b736142)
目录
- 要求
- 加载数据集
- 模型
- 结果
- 训练时间
- 推理时间
- 测试集上的性能
5.全体
要求
您将需要 TensorFlow-Nightly,因为 EfficientNet 的稳定版本和 Kaggle 目前不支持下载数据集和提交结果。我将使用 Google Colab,所以如果你想编码,打开笔记本,不要忘记连接到 GPU。
!pip install tf-nightly-gpu
!pip install -q kaggle
您将需要生成一个 Kaggle 的 API 密钥。程序如所示,此处为。执行下面给出的代码来完成设置。
! mkdir ~/.kaggle
! cp kaggle.json ~/.kaggle/
! chmod 600 ~/.kaggle/kaggle.json
加载数据集
! kaggle competitions download -c 'dogs-vs-cats-redux-kernels-edition'
! mkdir train
! unzip train.zip -d train
! mkdir test
! unzip test.zip -d testimport ostrain_dir = os.path.join('/content/train', 'train')
test_dir = os.path.join('/content/test', 'test')
我使用了一个定制的数据生成器来批量加载图像,并定义了几个图像增强函数。
def data_gen(img_names, bat ch_size):
c = 0
n = os.listdir(img_names) #List of training images
random.shuffle(n)while (True):
img = np.zeros((batch_size, 224, 224, 3)).astype('float')
labels = []for i in range(c, c+batch_size):
train_img = cv2.imread(os.path.join(train_dir, n[i]))
train_img = cv2.resize(train_img, (224, 224))
train_img = train_img/255.if random.random() < 0.25:
train_img = cv2.flip(train_img, 1)
rno = random.random()
if rno < 0.1:
train_img = train_img[:196, :196, :]
elif rno < 0.2:
train_img = train_img[28:, 28:, :]
elif rno < 0.3:
train_img = train_img[28:, :196, :]
elif rno < 0.4:
train_img = train_img[:196, 28:, :]
elif rno < 0.5:
train_img = train_img[28:196, 28:196, :]
if rno < 0.5:
train_img = cv2.resize(train_img, (224, 224), cv2.INTER_CUBIC)img[i-c] = train_img
if len(re.findall('dog', n[i])) == 1:
labels.append(1)
else:
labels.append(0)labels = np.array(labels)
c+=batch_size
if(c+batch_size>=len(n)):
c=0
random.shuffle(n)
yield img, labelstrain_gen = data_gen(train_dir, batch_size)
如果您想了解如何创建更多的图像增强功能,请参考本文。
这是一篇详尽的文章,通过使用 OpenCV 的自定义数据生成器,涵盖了所有的图像增强功能。
towardsdatascience.com](/complete-image-augmentation-in-opencv-31a6b02694f5)
模型
我们将创建一个非常基本的模型,即加载预训练的网络,将其层设置为可训练,添加一个全局平均池层和一个密集层。所有的层都被设置为可训练的,这样即使有些层在这里被冻结了,也要花最长的时间来训练。他们将接受 10 个纪元的训练。
def create_model(base_model):
base_model.trainable = True
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()(base_model.output)
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid')(global_average_layer)
model = tf.keras.models.Model(inputs=base_model.input, outputs=prediction_layer)
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.0001), loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=["accuracy", "mse"])
return modeldef fit_model(model):
model.fit(train_gen, batch_size=batch_size, steps_per_epoch=25000 // batch_size, epochs=epochs)IMG_SHAPE = (224, 224, 3)
model_mob = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_inc = tf.keras.applications.InceptionV3(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_xcep = tf.keras.applications.Xception(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_B0 = tf.keras.applications.EfficientNetB0(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_B1 = tf.keras.applications.EfficientNetB1(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_B2 = tf.keras.applications.EfficientNetB2(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_B3 = tf.keras.applications.EfficientNetB3(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_B4 = tf.keras.applications.EfficientNetB4(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")
model_B5 = tf.keras.applications.EfficientNetB5(input_shape=IMG_SHAPE, include_top=False, weights="imagenet")fit_model(model_mob)
fit_model(model_inc)
fit_model(model_xcep)
fit_model(model_B0)
fit_model(model_B1)
fit_model(model_B2)
fit_model(model_B3)
fit_model(model_B4)
fit_model(model_B5)
结果
啊终于到了真相大白的时刻了。你一直在等待的部分。
训练时间
记录下训练时每个时期所用的时间,如下所示。
+-----------------+-------------------------+
| Model | Time per epoch (in sec) |
+-----------------+-------------------------+
| MobileNetV2 | 250 |
| InceptionV3 | 400 |
| Xception | 900 |
| EfficientNet-B0 | 179 |
| EfficientNet-B1 | 250 |
| EfficientNet-B2 | 257 |
| EfficientNet-B3 | 315 |
| EfficientNet-B4 | 388 |
| EfficientNet-B5 | 500 |
+-----------------+-------------------------+
(最后两个 EfficientNets 在 Colab 上抛出内存错误,我无法训练它们。如果你想创建这样的表格,你可以使用这个。)EfficientNet-B0 轻松击败所有人,Xception 是最慢的。
推理时间
为了避免差异,我加载了一个测试图像,并测量了预测它的总时间 100 次,取其平均值。结果如下。
+-----------------+-------------------------+
| Model | Time per epoch (in sec) |
+-----------------+-------------------------+
| MobileNetV2 | 0.034 |
| InceptionV3 | 0.049 |
| Xception | 0.038 |
| EfficientNet-B0 | 0.041 |
| EfficientNet-B1 | 0.048 |
| EfficientNet-B2 | 0.049 |
| EfficientNet-B3 | 0.054 |
| EfficientNet-B4 | 0.061 |
| EfficientNet-B5 | 0.070 |
+-----------------+-------------------------+
现在,这是个惊喜!我曾觉得训练时间会指示推理时间,但一点也不。MobileNet 这次拿了蛋糕,紧随其后的是花了最多时间训练的 Xception。效率网模型中的时间随着代的增加而增加,这是随着参数数量的增加而预期的。
准确(性)
我选择不包含验证集,这样在 Kaggle 上提交 CSV 文件后会有惊喜。使用的度量标准是测井损失。它的价值越低越好。
+-----------------+----------+
| Model | Log loss |
+-----------------+----------+
| MobileNetV2 | 0.238 |
| InceptionV3 | 0.168 |
| Xception | 0.111 |
| EfficientNet-B0 | 0.205 |
| EfficientNet-B1 | 0.160 |
| EfficientNet-B2 | 0.122 |
| EfficientNet-B3 | 0.137 |
| EfficientNet-B4 | 0.126 |
| EfficientNet-B5 | 0.125 |
+-----------------+----------+
例外表现最好!!紧随其后的是其他 EfficientNet 模型,除了 EfficientNet-B0,它真正的比较对象是 MobileNetV2,它名列前茅。EfficientNet-B0 可能是移动模型的有趣选择🤔。这些结果表明,深度学习仍然像彩票一样,任何人都可以表现得更好(在可比模型中表现良好)。
全体
当我知道 EfficientNet 有 8 个模型时,我想为它创建一个整体模型,看看效果如何。我们将制作两个集合模型,一个包含 MobileNet、Inception 和 Xception,另一个包含 6 个 EfficientNet 模型。我们将创建的集合将使用 ANN 来组合这些模型。我已经写了一篇关于如何做到这一点的文章,所以如果你想了解它是如何做到的,请参考。
通过制作预训练网络的集成堆叠集成模型,如…
towardsdatascience.com](/destroy-image-classification-by-ensemble-of-pre-trained-models-f287513b7687)
在为集合模型创建数据生成器而不是产生四维(批量大小、图像高度、图像宽度、通道)的 NumPy 数组时,我们在列表中传递它的次数作为模型的数量。
img = np.zeros((batch_size, 224, 224, 3)).astype('float')
# looping and adding images to img
img = [img]*no_of_models_in_ensemble
现在加载所有的模型放入集合中,冻结它们的权重,改变层的名称,这样没有两层有相同的名称,添加一些密集的层来创建一个人工神经网络,这就完成了。
def ensemble_model(models):
for i, model in enumerate(models):
for layer in model.layers:
layer.trainable = False
layer._name = 'ensemble_' + str(i+1) + '_' + layer.name
ensemble_visible = [model.input for model in models]
ensemble_outputs = [model.output for model in models]
merge = tf.keras.layers.concatenate(ensemble_outputs)
merge = tf.keras.layers.Dense(32, activation='relu')(merge)
merge = tf.keras.layers.Dense(8, activation='relu')(merge)
output = tf.keras.layers.Dense(1, activation='sigmoid')(merge)
model = tf.keras.models.Model(inputs=ensemble_visible, outputs=output)
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.001), loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=["accuracy"])
return model
两个集合模型都被训练 10 个时期,并且它们的对数损失值是:
- MobileNet、Inception 和异常集合:0.104
- 有效净系综:0.078
第一个集合模型确实有所改进,但没有那么多。然而,有效网络集合有了很大的提高。
比较所有这些结果,我们可以看到,我们不能抹杀其他模型相比,有效的网络和提高分数的竞争集成是一条路要走。
使用 Apache Spark 轻松调整超参数
如何在 Spark 上运行随机搜索而不用编写 Spark 代码?
图片由 Gerd Altmann 从 Pixabay 拍摄
我仍然无法决定超参数调优是我在处理机器学习管道时最喜欢还是最不喜欢的步骤之一;一方面,这是通常在项目接近尾声时发生的事情:那些定义问题的会议,花在挖掘数据上的所有时间,ETL 脚本开发的数周… 遥远的记忆,是时候看看事情是否联系在一起了!另一方面,有时候我觉得自己就像 坦塔罗斯,拿不到就挂在我面前的多汁水果!
如果您曾经有过这种感觉,那么这篇文章可能是一个很好的起点,可以加速这个过程并简化您的生活,尤其是有了 Apache Spark 基础设施之后!
超参数调谐 101
我会将学习算法的超参数定义为一条在训练过程之前嵌入模型中的信息,而不是在拟合过程中导出的信息。如果模型是一个随机森林,超参数的例子有:树的最大深度或在构建森林的每个元素时要考虑多少个特征。
如果你曾经评估过一个 ML 模型的质量,你会知道没有一个放之四海而皆准的配置,因为当我们将同一个模型应用于两个不同的数据集时,它会表现出显著不同的性能;超参数调整是一个简单的过程,旨在优化配置,使我们为我们的问题选择的模型具有最佳性能。
在非常高的水平上,我们希望使用机器学习算法实现的是最小化成本函数(或最大化质量度量)f(y_hat(x),y)
,其中y_hat
是模型预测的值(给定一组已知特征x
)y
是预期结果。
考虑具有单个超参数的 ML 算法。如果我们在数据集上拟合算法并评估性能,我们将获得成本函数 **f**
的特定值。如果我们画出成本如何根据不同的超参数选择而变化,我们最终会得到如下结果:
在上图中,就成本函数而言,一个参数选择给了我们比其他参数更好的结果:我们可能应该选择那个来构建最终的模型。上面的例子中,我们有一个单一的超参数,因此,我们的搜索空间只是一条曲线。如果我们的算法支持 **2**
超参数,那么搜索空间变成一个曲面:
同样,我们将选择给出最佳成本函数的一对超参数(hp_1,hp_2)
。
当我们有 **k**
超参数时,搜索一定发生在一个 超曲面**k**
维度上;参数越多,探索就越困难。
此时我们需要的是一个在超参数空间中导航的策略。我们可以使用两种非常简单的方法:
- 网格搜索- 这种方法非常简单:对于每组参数,我们将模型拟合到我们的数据集,并评估性能。最后,我们选择产生最佳结果的组合。请注意,搜索空间通常包含数百万个点,因此通常不可能进行广泛的测试;因此,网格搜索通常会超时运行。
- 随机搜索- 该方法非常类似于网格搜索,唯一的区别是搜索空间不是以“网格”方式导航,而是随机选择要测试的超参数元组。该策略通常优于网格搜索,因为后者对探测起点非常敏感(例如,考虑网格的第一个元素远离任何可接受的参数选择的情况)。
冰山
**正如我之前所写的,超参数优化只是 ML 过程的冰山一角。对于这个演示,**我们实际上需要定义问题并收集数据。幸运的是,一些可爱的人已经这样做了。我们将使用 2017 年举行的 Kaggle 梅赛德斯-奔驰绿色制造竞赛。
用梅赛德斯-奔驰自己的话说:在这场比赛中,戴姆勒向卡格勒挑战,以解决维数灾难,减少汽车在测试台上花费的时间。竞争对手将使用代表梅赛德斯-奔驰汽车功能不同排列的数据集来预测通过测试所需的时间。获胜的算法将有助于加快测试速度,从而在不降低戴姆勒标准的情况下降低二氧化碳排放。
我选择这个数据集/问题的原因是:
- 任务很简单:我们有特性和一个要预测的。句号。本指南的目的不是为了在模型质量方面实现出色的性能,而是为了展示如何使用 Spark 来调整参数。我想从表格中删除所有与数据预处理相关的复杂内容(例如,我避免了图像、音频、时间序列等。).
- 数据集很小:一个(相当)大的数据集对于超参数优化本身来说不是问题(我们可以在数据集的样本上运行模型),但是处理大量的样本是一个挑战,我不想在本文中介绍(可以在另一篇文章中介绍!).
- 我想要一个真正的问题:我不想使用 Iris、IMDB 或 Titanic 数据集。我相信,如果例子与现实生活中的情况相似,会更有效。
- 预处理越少,代码越简单:重点要放在火花部分;在 Python 中,没有人需要数百行预处理!
介绍够了,让我们跳到有趣的东西。
准备好;设置;XGBoost!
数据集由几个分类变量、相当多的二元变量组成,并且相关变量是连续的。奔驰混淆了表格,所以功能只知道X_i
。如果你想要一个广泛的数据分析,我建议你去比赛页面,看看由社区制作的笔记本!为评估建议的度量是 决定系数 ,R,我们将坚持使用它来比较模型。我们将使用XGBoost来做预测, 一个优化的分布式梯度增强库,在 梯度增强 框架下实现机器学习算法【1】
梅赛德斯-奔驰数据集示例
我们试图实现的是使用 Spark 基础设施运行并行网格/随机搜索。 Apache Spark 自带一些机器学习工具(ml lib)。** 别提了!**
我可以听到你的反对意见:是的,XGBoost 网格搜索可以使用 MLLib 管道来实现(有一个 Nan Zhu 提供的很好的平台,解释了如何做到这一点),但是我认为 MLLib 没有为参数调整提供足够的灵活性**:归根结底,你可能想要使用一个更智能的算法,纯 Python 将使实现更容易;有了下面的想法,你应该能够运行多种类型的搜索(贝叶斯,遗传)而不用忙于处理 Spark APIs:所有的东西大部分都是用普通的 Python 编写的!**
方法学
让我们再做一步,定义什么是高级方法论。对于本文的范围来说,我们需要做一个最小的预处理:唯一的目的是让数据集对 XGBoost 可读。
我们要做的事情很简单:
- 删除 ID 列:由于显而易见的原因,我们不想将 ID 提供给 XGBoost。
- ****对分类变量进行编码:我们将使用简单的均值编码(即对于每个类别,我们将其标签设置为一个训练数据 上目标变量的均值)。
预处理功能
正如我在上面指出的,比赛是基于 R 的,所以我们将继续使用这个指标来探究模型的性能;更准确地说,评估算法如下:
1\. Pick a set of hyperparameters2\. Perform 4-folds [Cross-Validation](https://en.wikipedia.org/wiki/Cross-validation_(statistics))3\. Get the average **R² score for the 4 runs and store it** 4\. Goto 1\. until timeout5\. Select the parameters with the **highest** average score (**R²=1 is the perfect model**)
厉害!现在我们终于准备好实现算法了!
定义搜索空间
运行网格或随机搜索的第一步是定义搜索空间。XGBoost 有很多很多参数可以在装配前设置。对于我们的问题,我们将使用 tree booster(库也提供了其他选项),我们将只关注以下参数:
learning_rate : 更新中使用的步长收缩,防止过拟合。在每个增强步骤之后,我们可以直接获得新特征的权重,并且 *eta*
缩小特征权重以使增强过程更加保守。【 xgboost 参数】**
colsample_bytree : 是构造每棵树时列的子样率。对于每个构建的树,进行一次子采样。【 xgboost 参数】**
子样本 : 训练实例的子样本比率。将其设置为 0.5 意味着 XGBoost 会在生成树之前随机采样一半的训练数据。这将防止过度拟合。子采样将在每个增强迭代中出现一次。xgboost 参数**
n_estimators :算法使用的树的数量。
reg_alpha : 权重上的 L1 正则项。增加该值将使模型更加保守。【 xgboost 参数】**
最大深度 : 一棵树的最大深度。增加该值将使模型更加复杂,并且更有可能过度拟合。【 xgboost 参数】**
gamma : 在树的叶子节点上做进一步划分所需的最小损失减少。 *gamma*
越大,算法就越保守。【 xgboost 参数】**
****可以想象,以上是一个相当大的搜索空间!然而,当我们进行超参数调整时,我们并不是完全在黑暗中摸索。许多 ML 方法(以及 XGBoost)都有指导原则,可以帮助为网格的所有参数定义有意义的范围。比如,即使知道learning_rate
可以在[0,1]
,接近1
的值也不太可能有好结果。以下 Python 字典代表了我为 Mercedes-Benz 问题选择的搜索空间:
****上面的字典将导致 6,890,400 个点进行探测。注意,即使每个测试需要 1 秒钟完成,我们也需要大约 79 天 18 小时的连续计算时间来探索整个网格!网格搜索永远不会尝试所有那些组合。
以下函数将网格作为 Python 生成器返回(以避免在内存中有大数据结构):
局部网格和随机搜索
现在我们有了搜索空间,让我们试着在本地实现网格搜索。
首先,我们需要一个方法来评估 R :
然后我们用下面的签名定义一个函数grid_search
:
**grid_search(timeout_seconds, cv_splits, boosting_rounds)**
搜索算法将在T5 之后终止。交叉验证将使用cv_splits
数量的分割,XGBoost 将使用boosting_rounds
迭代:
此外,我们将收集熊猫数据框架中的结果进行分析:
太棒了。让我们运行上面的代码一个小时,喝杯咖啡,把结果留到以后。
随机搜索
我们可以回收上面的大部分代码来实现随机搜索,唯一的区别是我们不需要预定义网格:我们可以只声明一个函数,返回探索面的一个随机点:
然后我们可以稍微修改一下网格搜索循环:
再次:跑步,咖啡,等待结果。
让我们随机搜索火花吧!
****本文的要点是解释如何使用 Spark 基础设施来并行化上述算法。我们将只关注随机搜索。
我们可以使用各种策略来解决这个问题:
- 用户定义函数(UDF):我们可以实现一个 Spark UDF。例如,我们可以创建一个 dataframe,在一列中包含网格的所有元素,然后对该列应用 UDF,使用这些值作为 XGBoost 的输入参数。这个解决方案有几个缺点:首先我承诺过你会在没有火花知识的情况下运行这个算法,UDF 并不是一个很好的维持这个承诺的起点。第二点是,在各种 UDF 之间共享训练数据集可能是不安全的(可能有共享变量?欢迎评论中的建议!).
- MLLib :同样,这是 Spark 的一个相当高级的用例,我认为它不够灵活。
- 带有 Spark 后端的 Joblib:我们要用这个!你们中的许多人,可能已经知道 Joblib, 一套工具提供了Python 中的轻量级流水线操作 (函数的透明磁盘缓存和惰性重求值(memoize 模式),轻松简单的并行计算)。Joblib 特别针对大数据优化为 快速*健壮 ,并针对 numpy 数组* 进行了具体优化。对你来说可能听起来很新鲜的是,Joblib 有一个方便的 Spark 后端。通过几行代码,我们将直接在 Spark 上运行并行循环,而无需编写 Spark 代码!
在查看实现之前,我们需要指定我们将使用的架构。特别是,一旦算法有了交叉验证的结果,它将需要在“某个地方”把它们吐出来(供我们收集和分析)。我们将使用一个 MongoDB 实例:
上图显示我们将在 Jupyter 笔记本中有一个入口点。通过其中一个单元,我们将使用 Joblib-Spark 将执行发送到不同的节点;此外,在每个节点上,我们将使用普通 Joblib 来启用多线程。最后,每个执行器将把结果写在位于另一个服务器上的 MongoDB 实例中。
预处理代码将保持不变,我们可以重用该函数来生成随机组合,所以让我们编写一个神奇的循环:
Joblib 魔术发生在下面一行:
**Parallel(backend="spark", n_jobs=NODES)(delayed(evaluate)(X, y, kf, boosting_rounds) for p in range(0, NODES))**
让我们来分解一下:
Parallel
初始化 Joblib 环境。在这种情况下,我们需要指定后端(即“spark”)。- 涉及到一些“全局”变量;一个重要的参数是
NODES
,即集群中的节点数量。我们正在做的是将网格分成**NODES=3**
块,并将每个块发送给一个 Spark 执行器。 delayed
只是一个装饰器,用来捕获函数的参数(在这里是evaluate
,我们将在后面定义它)。需要for p in range(0, NODES)
来告诉Parallel
执行delayed
函数多少次(在这种情况下只执行**NODES**
次,因为执行程序将运行测试,直到超时发生**X
、y
、kf
和boosting_rounds
是evaluate
功能的参数。
引擎盖下发生了什么? Joblib-Spark 将通过网络发送函数(使用cloud-pickle进行序列化)。然后通过parallelize
Spark 方法,每个执行将被发送到单个节点。
我们需要构建将在每个 Spark 机器上执行的evaluate
函数:
如你所见,我们在evaluate
中定义了一个内部函数。这是必要的,因为除了在 Spark 上并行化网格搜索,我们还想在单个执行器上使用多线程。下面一行显示了默认后端的(普通)Joblib 的用法:
**Parallel()(delayed(evaluate_inner)(keys, g, X, y, kf, boosting_rounds) for g in df_grid)**
让我们运行上面的random_search_spark
函数一个小时,并将结果保存在我们的 MongoDB 实例中。我们希望在 Spark UI 上看到的是三个阶段,每个阶段将运行一个小时:
35 分钟后触发用户界面
60 分钟后的 Spark UI 任务失败,因为 Spark 上下文在超时后终止
比较和结论
旅程即将结束。我们已经收集了所有的结果,我们只需要分析它们!
Spark 测试一直在由 3 台机器(1 个驱动程序+ 2 个执行器)组成的集群上运行,每台机器有 4 个内核,而“本地”搜索算法一直在单台机器上执行。我们对所有的方法都使用了下面的常数。
**CV_SPLITS = 4 # Number of CV splits
TIMEOUT_SECONDS = 3600 # Timeout is 1 hour
BOOSTING_ROUNDS = 500 # XGBoost boosting rounds**
第一个有趣的发现是简单地比较每种方法执行了多少测试:
在这种情况下,我们获得了 3 倍的提升,这是给定节点数量的线性增长(注意:“本地”版本仍然有 4 个内核可用)。
为了了解方法“如何”探索超曲面,让我们画出算法获得的R:
在单台机器上使用网格搜索通过 4 倍 CV 获得的平均 R
在单台机器上使用随机搜索通过 4 倍 CV 获得的平均 R
在火花(3 个节点)上使用网格搜索通过 4 倍 CV 获得的平均 R
从上面的图表中,至少可以看出两点:
- 网格搜索探索似乎没那么有效。请注意曲线中的水平部分:每个部分中变化的参数对分数的影响非常小,因此可能不值得深入探究(当然,为了简单起见,我没有考虑参数之间的相互依赖性)。****
- Spark 并行化是可取的,因为它可以荒谬地增加尝试的次数。
通过这些方法找到的最佳参数:
****Grid Search (Local)**
Best **R²**: 0.5656Params:
learning_rate: 0.01,
colsample_bytree: 0.8,
subsample: 0.5,
n_estimators: 100,
reg_alpha: 0.01,
max_depth: 3,
gamma:0**Random Search (Local)**
Best **R²**: 0.5648Params:
learning_rate: 0.0354,
colsample_bytree: 0.88,
subsample: 0.8,
n_estimators: 1410,
reg_alpha: 0.10,
max_depth: 3,
gamma:0**Random Search (Spark)**
Best **R²**: 0.5643Params:
learning_rate:0.0524,
colsample_bytree:0.93,
subsample:0.88,
n_estimators:1413,
reg_alpha:0.409,
max_depth:3,
gamma:7**
注意网格搜索赢了,即使它只能做 32 次尝试!以上结果的结果是我们可能应该重新考虑调整哪些超参数以及如何定义网格/范围。例如,我们可以看到,所有的方法都同意这样的事实,即learning_rate
应该在[0.01,0.05]
中,而在我们的网格中,我们将最大值设置为0.25
: 我们可能浪费了大量的计算时间来寻找超曲面的错误区域! (旁注: *max_depth=3*
让我想到,也许,很多变量可以从数据集中剔除,但那是另一回事)。
****我认为上面的结果可以引出下面的问题:我们是否可以在探索算法中加入一些智能,做一些比这更聪明的事情?答案是“是的,我们可以”,但那是另一篇文章的主题!离开之前,如果你读到这里,你值得拥有 链接到最后的笔记本 !
外卖食品
- **Joblib-Spark 可以成为在 Spark 基础设施上扩展算法的强大工具。其中一个主要优势是**它不需要使用 Spark APIs 进行开发:要使用它,您不需要对 Apache 的分布式计算系统有很深的理解。
- Joblib 可能允许你用相对较小的努力提升和移动你现有的管道。
- 网格搜索和随机搜索是两种非常简单的方法,可用于进行超参数调整,并且易于实现。****
- 随机搜索通常优于网格搜索。
- 网格和随机搜索在超参数调优方面并不是最好的,因为它们没有利用已经执行的测试来理解探索过程中的方向。
我希望你喜欢!让我知道你的想法,如果你愿意,看看这些其他的文章!
Spark 中加速连接的实用技巧
towardsdatascience.com](/the-art-of-joining-in-spark-dcbd33d693c) [## 聚类波洛克
杰森·布拉克绘画的聚类分析——如何利用 k-means 进行色彩分组
towardsdatascience.com](/clustering-pollock-1ec24c9cf447)****
轻松推荐相似图片
使用来自预训练卷积神经网络的特征来产生可比性
自动选择相似图像(来源:M. D. Korzec)
让我们来解决这样一个问题:拥有一个无序的、庞大的图像集,我们希望从中找到一个相似图像的子集。该解决方案产生了基于图像的项目-项目推荐系统,该系统可以用于例如在线商店。为此,我们将使用迁移学习,利用预先训练的卷积神经网络。
私人图片(来源:M. D. Korzec)
这些来自我私人收藏的图片包含了不同的动机,当面对成千上万的图片时,找到最相似的就变得非常困难了。利用所呈现的解决方案,我们将基于图像内容(即,不基于其元数据)获得相似性值,如下面的示例中来自相同图像集的相似性值。第一个图像描述了查询的参考图像,一辆我们在欧洲休产假时使用的货车,停在法国某处的山羊农场上。
具有相似值的与查询图像相似的图像(来源:M. D. Korzec)
在下面一行中,我们可以看到算法选择的最相似的图像。照片下面的数字是从 0 到 1 的相似度值,1 是可能的最高相似度。不仅显示了红色货车的图像,而且由于所有其他相似之处,我们第一次租赁的黄色货车由于水泵故障而需要更换。
许多在线产品可以与这样的推荐器一起工作,以提高转化率,无论是摄影师的收藏、电影推荐,还是可以从图像相似性中受益的其他商店(例如,CD 封面或视觉相似性可能增加转化率的其他产品)。这种基于项目的推荐系统被称为基于内容的推荐系统。欲了解更多信息,您可以参考[1]或快速进入关于走向数据科学 [2,3]的博客条目。
如果您已经具备 Python 和图像处理的基础知识,那么有一种非常简单的方法可以获得这样的建议。在接下来的段落中,我将为一组固定的图像提供其背后的高级逻辑:
1.获取和预处理数据
2.使用经过训练的卷积神经网络作为特征向量生成器
3.使用特征向量差异来计算相似性
4.使用最佳推荐列表
您可以使用现成的神经网络,不需要模型工程或培训。没有必要清理元数据,没有隐藏的工作,你只需要建立一个有效的数据处理一次,没有额外的工作。
1。获取并预处理数据
即使要实现一个原型,也需要一个大的图像测试集。我使用了 Coco 系列的一部分,结果令人印象深刻。在 15000 张图片中,推荐者发现了非常相似的内容。然而,由于这些图片根据用户在 Flickr 中的选择有不同的许可,所以我没有在这里发布这些结果。相反,我为这个博客创建了一个不同的测试集。所有上传的图片都来自我的本地图片库,大约有 5000 张图片,都是 jpg 格式,随着相机的改变,分辨率也有所不同。
预训练的深度神经网络需要特定格式的输入图像。对于本文,我在 PyTorch 库中使用 resnet18 [5],它需要 224x224 RGB 图像作为输入,图像值在范围[0,1]内。图像被标准化为具有平均值[0.485,0.456,0.406]和标准偏差[0.229,0.224,0.225],因此它们都被重新缩放以适应这种格式(注意不要在变换过程中丢失关于图像方向的元数据)。关于特定实现的细节将在我的下一篇博客中介绍。如果要使用其他卷积神经网络,先检查输入要求,并相应调整预处理。此外,请注意,这里 resnet18 的权重是从 ImageNet 上的训练中获得的。请记住,训练集和测试集应该是不同的。
2。使用经过训练的卷积神经网络作为特征向量生成器
当你第一次意识到与图像相关的神经网络的一个特征时,你可能会大吃一惊:
经过训练的卷积神经网络可以创建特征向量,通过比较这些特征向量来计算图像的相似性。
使用高级特征向量,而不是努力寻找用经典的图像处理方法来表征图像的方法,这些方法可用于比较它们(例如,从不同方向和局部边缘密度的边缘检测开始,然后扩展到不包括这些的所有情况)。可用的、经过训练的模型已经提供了你需要的一切,当它们在具有高动机可变性的巨大图像集上被训练时。你不需要理解向量的任何条目,你只需要知道相似的图像会有相似的条目。
以下示意图解释了将核心网络视为黑盒时特征和分类向量的计算
作为黑盒的卷积神经网络(来源:M. D. Korzec)
M>n,特征向量包含了大量加密图像的高层描述。因此,当抽象中间的所有层并忽略不用于推荐器的分类向量时,我们只处理一个函数
这是高中就熟悉的。这里,x 是中的矢量化 RGB 图像
当使用预先训练的模型时,所有的参数都是固定的,所以没有更多的自由变量,这里也没有列出。如第一部分所述,必须对给定的图像进行预处理,以适应所用网络的输入格式。
你不需要做的事情
当你想从头开始训练你的网络时,你需要
- 手动准备大量数据(图像-分类向量-对)
- 花时间在一些优化上
分类向量(例如,“图像包含汽车”可以由一个单位向量表示,该单位向量的位置表示汽车)是固定的,输入是固定的,并且网络的权重被优化以获得尽可能接近真实分类向量的猜测分类向量。f(x)的被充分研究的形式允许在优化迭代期间使用的自动微分的帮助下优化权重,并且首先使得深度神经网络的使用成为可能。如果你有兴趣了解更多相关话题,你可以参考一位前研究同事关于这个话题的博客文章[6]。
在具有更多条目的分类层之前的完全连接的一维层是图像内容的高级加密描述。早期图层仅描述低级特征(如边),分类向量可能相当小,因此最后一个完全连接的图层是一个不错的选择。它将包含有用的值,即使图像的内容在学习期间没有被分类。
因此,我们知道,我们可以省去训练神经网络的努力,我们仍然可以通过一个函数调用 f(x)从图像输入 x 中获得特征向量。然后,我们可以很容易地确定一个图像何时与另一个图像或多或少相似。
3。使用特征向量差异计算相似度
为了比较从不同图像的卷积神经网络的评估中导出的特征向量,采用余弦相似性是一种很好且简单的方法。在这里,你基本上只是计算所有图像对的余弦公式,对整个图像集这样做产生一个相似性矩阵。
余弦相似性(来源:M. D. Korzec)
在二维空间中,如上所示,该公式易于解释和可视化。它在更高维度中以同样的方式工作,例如考虑三个向量
凭直觉,a 和 b 的相似性应该高于 a 和 c 或 b 和 c 的相似性。事实上,当把这些数字代入上述公式时,人们会发现
因此,不出所料,
相似性计算是推荐系统中的主要课题之一[1]。在我们的例子中,也应该使用一些其他的度量来描述向量之间的差异。
4。使用最佳建议列表
因此,总的来说,当比较两个图像时,我们已经推导出要执行的以下逻辑步骤
比较两幅图像(来源:M. D. Korzec)
您只需要迭代感兴趣的集合中的所有图像。所有导出条目(s_kl)一起定义了完整的相似性矩阵。然而,当使用在线推荐的结果时,使用完整的相似性矩阵是没有效率的。当对最相似的图像感兴趣时,为了效率,可以通过相似性值对元素进行排序,并使用前 k 个列表,即最相似的 k 个图像。因此,来自矩阵(s_kl)的行 k 从大到小排序,并且高度相似的图像被保留并用于推荐。下图解释了用于从相似性矩阵生成前 4 个列表的较小向量/矩阵的集合。
相似性矩阵和前 4 个列表(来源:M. D. Korzec)
较暗的区块表示与该行中表示的图像最相似的四个图像,并且为了有效的请求而分开存储。当然,在典型的应用程序中,您会使用四个以上。
当您在 top-k 列表中有了所有排序的信息并接收到一个查询作为输入时,您只需转到该特定图像的相应行,并显示存储有相应行中存储的 id 的图像。
外卖信息
- 当我们想要获得基于图像的推荐时,经过训练的卷积神经网络可以完成这项工作。
- 我们使用预先训练的模型,所以没有必要建立一个训练管道。
- 我们本质上只评估一个函数来获得特征向量。
- 我们通过计算余弦来比较特征向量。
- 当新的图像被添加到集合中时,需要更新新的相似性值和 top-k 列表。
- 当您从足够大的图像集和训练有素的模型开始时,不存在冷启动问题。
如果你有 Python 代码涵盖了上述推荐者的想法,并且它将被用在一个网站上,有多种方法可以做到这一点。在我的下一篇文章中,我将解释上述步骤的 Python 实现的要点,并且我将解释如何建立一个简单的基于 Flask 的网站来使用这些结果。
测试集中相似的蒲公英图片(来源:M. D. Korzec)
[1] C. C. Aggarwal,推荐系统——教科书 (2016),施普林格
[2] P. Pandey,推荐系统的非凡世界 (2019),走向数据科学
[3] B. Rocca 和 J. Rocca,推荐系统介绍 (2019),走向数据科学
[4] T.-Y. Lin 等著,微软 COCO:情境中的通用对象(2014);使用:测试数据 2017
[5]何国光,张,任,孙,【深度残差学习用于图像识别】,2015
[6]m . KPF,面向机器的高中数学:可微分编程 (2020),面向数据科学
感谢阅读!喜欢这个话题吗?
如果你觉得这篇文章很有趣,你可能想看看我在这个主题上的其他文章:
推荐系统的重要性评估
medium.com](https://medium.com/swlh/the-core-value-of-recommender-systems-for-successful-internet-enterprises-7164a7bacdc6) [## 使用 PyTorch 推荐相似图片
使用 Resnet18 实现完全迁移学习
towardsdatascience.com](/recommending-similar-images-using-pytorch-da019282770c) [## 一个推荐图片的 Flask 应用程序
PyTorch 中基于卷积神经网络的相似图像推荐网站原型
medium.com](https://medium.com/@maciek.korzec/a-flask-app-for-image-recommendations-a865e1496a0d) [## py torch+Flask+PostgreSQL+Heroku 部署的映像建议
用 Flask 封装一个基于 PostgreSQL/ PyTorch 的图像推荐系统,导入数据并在 Heroku 上运行
towardsdatascience.com](/image-recommendations-with-pytorch-flask-postgresql-heroku-deployment-206682d06c6b)
efsync 我的第一个开源 MLOps 工具包
自动将 Python 依赖项和 ML 模型同步到 AWS Lambda 函数的 AWS EFS
谢栋豪在 Unsplash 上的照片
原载于https://www . philschmid . de。
介绍
在生产中成功使用机器学习的一部分是 MLOps 的使用。MLOps 通过持续培训(CT)增强 DevOps。因此,MLOps 的主要组成部分包括持续集成(CI)、持续交付(CD)和持续培训(CT)。 Nvidia 写了一篇文章详细介绍了什么是 MLOps。
我叫菲利普,住在德国的纽伦堡。目前,我在一家科技孵化创业公司担任机器学习工程师。在工作中,我为金融科技和保险公司设计并实现了云原生机器学习架构。我是无服务器和以无服务器方式提供机器学习模型的忠实粉丝。我已经写了两篇关于如何在 AWS Lambda 这样的无服务器环境中使用 BERT 这样的深度学习模型的文章。
在使用像 AWS Lambda 、 Google Cloud Functions 、 Azure Functions 这样的工具进行无服务器机器学习时,要克服的一个大障碍是存储。Tensorflow 和 Pytorch 的尺寸很大,像 BERT 这样的新型“艺术级”模型的尺寸超过 300MB。
今年 7 月,AWS 增加了对亚马逊弹性文件系统(EFS) 的支持,这是一个适用于 AWS Lambda 的可扩展的弹性 NFS 文件系统。这允许我们将 AWS EFS 文件系统挂载到 AWS Lambda 函数。
直到今天,将依赖项或模型文件同步到 AWS EFS 文件系统还是非常困难的。你可以用 AWS Datasync 来做这件事,或者你可以在同一个子网和 VPC 中启动一个 EC2 实例,并从那里上传你的文件。
为此,我构建了一个名为 efsync 的 MLOps 工具包。Efsync 是一个 CLI/SDK 工具,它自动将文件从 S3 或本地文件系统同步到 AWS EFS,并使您能够将 AWS Lambda 运行时的依赖项直接安装到您的 EFS 文件系统中。CLI 易于使用,您只需要访问一个 AWS 帐户和一个启动并运行的 AWS EFS 文件系统。
体系结构
作者创建的 efsync 架构
快速启动
-
通过 pip3 安装
-
将您的 pip 依赖项或文件同步到 AWS EFS
用例
Efsync 涵盖 5 种使用情形。一方面,它允许您安装所需的依赖项,另一方面,efsync 帮助您准备好您的模型,无论是通过从 S3 到 EFS 的同步还是使用 SCP 的本地上传。我为每个用例创建了一个示例 Jupyter 笔记本。
这 5 个使用案例包括:
- 将 Python 与 AWS Lambda 运行时的依赖项直接安装到 EFS 文件系统中,并在 AWS Lambda 函数中使用它们。 例题
- 将 S3 的文件同步到 EFS 的文件系统。 例题
- 使用 SCP 将文件上传到 EFS 文件系统。 例题
- 安装 Python 依赖项,并从 S3 同步到 EFS 文件系统。 例题
- 用 SCP 和 EFS 文件系统安装 Python 依赖项和上传文件。 例题
注: 每个例子都可以在 Google Colab 中运行。
实施配置可能性
在项目中使用 efsync 有 4 种不同的方式:
- 您可以创建一个
yaml
配置并使用 SDK。 - 你可以创建一个 python
dict
并使用 SDK。 - 您可以创建一个
yaml
配置并使用 CLI。 - 您可以使用带参数的 CLI。
您可以在 Github 资源库中找到每个配置的示例。我还包括了不同用例的配置示例。
注意 :如果你用 scp 从本地目录(如 *model/bert*
)同步一个文件到 efs ( *my_efs_model*
) efsync 会把模型同步到 *my_efs_model/bert*
,这是因为 SCP 递归上传文件。
例子
下面的例子展示了如何将 Python 依赖项安装到 EFS 文件系统,然后将文件从 S3 同步到 EFS 文件系统。出于配置目的,我们必须创建一个efsync.yaml
和一个requirements.txt
文件来保存我们的依赖项和配置。
1。安装 efsync
2。创建一个 **requirements.txt**
和依赖关系
3。用所有需要的配置创建一个 **efsync.yaml**
efsync.yaml
包含所有配置,例如:
标准配置
efs_filesystem_id
:AWS EFS 文件系统 id(挂载点)。subnet_Id
:EFS 文件系统的子网 Id,它运行在。ec2_key_name
:启动 EC2 实例所需的键名。aws_profile
:在.aws/credentials
中配置的具有所需权限的 IAM 配置文件。aws_region
:EFS 文件系统正在运行的 AWS 区域。
Pip 依赖关系配置
efs_pip_dir
:EC2 上的 pip 目录,将在此安装依赖项。python_version
: 用于安装 pip 包的 Python 版本- >应该作为 lambda 运行时使用。requirements
:requirements . txt 的路径+文件,该文件包含可安装的 pip 依赖项。
S3 配置
s3_bucket
: S3 斗名从文件中应下载。s3_keyprefix
: S3 keyprefix 为目录/文件file_dir_on_ec2
:保存 S3 文件的目录名
4。运行 efsync wit **efsync.yaml**
摘要
使用ef sync您可以轻松地将文件从 S3 或本地文件系统自动同步到 AWS EFS,并允许您将 AWS Lambda 运行时的依赖项直接安装到您的 EFS 文件系统中。从 S3 安装和同步文件大约需要 6 分钟,安装依赖项大约需要 4-5 分钟,同步文件大约需要 2 分钟。
你可以在 Github 上找到库。如果您有任何问题或改进,请随时创建拉动式请求或问题。
感谢阅读。如果你有任何问题,随时联系我或评论这篇文章。你也可以在 Twitter 或者 LinkedIn 上和我联系。
特征脸 Python 中的人脸分类
深度学习的数据不够?试试特征脸。
如今,我们可以使用神经网络来执行最先进的图像分类,或在这种情况下的人脸分类。但是采取一种更简单的方法怎么样呢?这就是本文的目的。
官方回购: 访问这里 获取数据和代码。
最初,将原始像素值作为输入特征的想法可能看起来很愚蠢——这很可能是真的,主要是因为我们会丢失所有的 2D 信息,而且还有卷积神经网络来提取重要的特征(因为不是所有的像素都相关)。
今天我们将介绍特征脸算法的思想——它只是一种应用于人脸识别问题的主成分分析。通过这样做,我们希望降低数据集的维度,只保留解释最大差异的成分,然后应用简单的分类算法(如 SVM)来完成分类任务。
听起来像是一个计划,但是在阅读这篇文章之前,你应该知道什么? 这是个好问题。你要精通 Python 和它的数据分析库,也要知道什么是主成分分析,至少在高层是这样的。
还在读书?那我想你已经具备了先决条件。在开始编写代码之前,我们要讨论的最后一件事是文章结构,可以列举如下:
- 导入和数据集浏览
- 图像可视化
- 主成分分析
- 模型培训和评估
- 结论
好了,事不宜迟,我们开始吧!
导入和数据集浏览
正如您可能已经预料到的那样,我们将需要常见的怀疑对象— Numpy 、 Pandas 和 Matplotlib ,但也将使用来自 ScikitLearn 的一堆东西—像 SVM、PCA、train test split 和一些用于评估模型性能的指标。
下面是所有的进口:
import numpy as np
import pandas as pd
import matplotlib.pyplot as pltfrom sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix, classification_reportimport warnings
warnings.filterwarnings(‘ignore’)
至于数据集,我们已经在 GitHub 上找到了,但现在似乎找不到了。可以从我的 GitHub 页面下载。
下面是你如何把它装载到熊猫身上:
df = pd.read_csv(‘face_data.csv’)
df.head()
现在,我们可以快速检查数据集的形状:
df.shape**>>> (400, 4097)**
所以,400 行和 4097 列,一个奇怪的组合。对于这些列,我们这里有归一化像素值(表示范围(0,1)内的值),最后我们有一个目标列,指示照片上的人是谁。
如果我们仔细看看目标列的唯一元素的数量,我们会得到数据集中的总人数:
df[‘target’].nunique()**>>> 40**
由于我们有 4096 个功能,这是一个单一颜色通道中 64x64 图像的清晰指示器:
64 * 64**>>> 4096**
太好了,我们现在有了关于数据集的一些基本信息,在下一节中,我们将进行一些可视化。
图像可视化
为了可视化几张脸,我们将声明一个将 1D 向量转换为 2D 矩阵的函数,并使用 Matplotlib 的 imshow 功能将其显示为灰度图像:
def plot_faces(pixels):
fig, axes = plt.subplots(5, 5, figsize=(6, 6))
for i, ax in enumerate(axes.flat):
ax.imshow(np.array(pixels)[i].reshape(64, 64), cmap=’gray’)
plt.show()
但是在绘制之前,我们需要将特征从目标中分离出来,否则,我们的数据集将溢出 64x64 矩阵边界:
X = df.drop(‘target’, axis=1)
y = df[‘target’]
就这样,现在我们可以使用声明的函数了:
这部分就讲到这里。下一次,我们将执行训练测试分割和 PCA。
主成分分析
本节的目标是通过只保留那些解释最大差异的成分来减少问题的维度。简而言之,这是常设仲裁院的一个目标。但在此之前,我们必须将数据集分成训练和测试部分:
X_train, X_test, y_train, y_test = train_test_split(X, y)
现在,我们可以将主成分分析应用于训练特征。然后很容易画出解释方差的累积和,这样我们就可以近似得出多少个主成分就足够了:
pca = PCA().fit(X_train)plt.figure(figsize=(18, 7))
plt.plot(pca.explained_variance_ratio_.cumsum(), lw=3)
仅通过查看图表,看起来大约 100 个主成分将保持大约 95%的方差,但让我们验证一下这一说法:
np.where(pca.explained_variance_ratio_.cumsum() > 0.95)
是的,看起来 105 个组件就够了。记住,95%不是一成不变的,你可以自由选择更低或更高的百分比。
让我们再次执行 PCA,但这次使用了额外的 n_components 参数:
pca = PCA(n_components=105).fit(X_train)
最后,我们必须转变培训功能:
X_train_pca = pca.transform(X_train)
太好了!这一节就到这里,下一节我们将训练和评估 SVM 模型。
模型培训和评估
到目前为止,培训功能已经发生了变化。训练模型的过程非常简单,只需制作一个实例并拟合训练数据即可:
classifier = SVC().fit(X_train_pca, y_train)
厉害!模型现在已经训练好了,为了在测试集上对它进行评估,我们首先需要将测试特征带到同一个特征空间。一旦完成,SVM 就被用来做预测:
X_test_pca = pca.transform(X_test)
predictions = classifier.predict(X_test_pca)
现在我们终于可以看到它的表现了。为此,我们将使用来自 ScikitLearn 的分类 _ 报告,因为它比 40x40 混淆矩阵更容易查看:
print(classification_report(y_test, predictions))
所以大约 90%的准确率,对于 40 个不同的类和默认模型来说当然不可怕。
对于本文来说就是这样,让我们在下一节快速浏览一下可能的改进领域。
结论
这是一个相当快速的指南——故意的。您可以自由地执行网格搜索来为分类器找到最佳超参数,或者甚至使用完全不同的算法。
此外,尝试选择 90%和 99%的解释方差比率,以查看模型性能如何变化。
感谢您的阅读,欢迎在评论区留下您的想法。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
特征值和特征向量
计算和可视化
这里有动画:https://BDS haff . github . io/BDS haff . github . io/blog/2020-03-23-计算-特征值-特征向量/
这是怎么回事
- 首先,我将谈谈是什么让我对特征值实际上是如何计算的感到好奇。
- 然后我分享一下我实现最简单的算法( QR 方法 )做一些基准测试。
- 最后,我将分享你如何将算法寻找特征向量的步骤动画化!
他们无处不在
特征值和特征向量在数学中随处可见,尤其是应用数学。统计学、机器学习、数据科学都属于这个范畴。
前段时间决定通过实现主成分分析( PCA )来学习 Rcpp 和 C++。我知道 PCA 问题的解决方案是样本方差-协方差矩阵的特征值分解。我意识到我需要用 C++编写我自己版本的内置eigen
函数,那时我意识到除了一个 2x2 案例,我不知道eigen
实际上是如何工作的。我发现的是我在这里读到的的 QR 法 。
为什么 n > 2 不一样
有了一个 2x2 矩阵,我们可以手动求解特征值。这是可行的,因为一个 2x2 矩阵的行列式是一个二次多项式,所以我们可以用正规代数来分解和求解它。但是如何计算大型矩阵的特征值呢?事实证明,当你不能分解和求解多项式时,你真正应该做的是分解矩阵。纯属巧合的是,数字爱好者 YouTube 频道最近发布了一个关于这个想法的视频在这里。如果你对这种类型的内容感兴趣,值得一试!
快速复习:它们是什么?
首先,快速复习一下什么是特征值和特征向量。取一个方阵 X 。如果有一个矢量 v 和一个标量 λ 这样
那么 v 就是XT5T7λ就是X 对应的特征值。
换句话说,如果你把 v 乘以 X 看作是对v 应用一个函数,那么对于这个特定的向量 v 来说,这个函数不过是一个拉伸/挤压标量乘法。通常,将一个向量乘以一个矩阵等于取向量分量的线性组合。但是如果你取一个特征向量,你不需要做所有的计算。只要乘以特征值就都好了。
计算:QR 方法
计算特征值和特征向量的 QR 法 从我心爱的 QR 矩阵分解开始。我在之前的帖子中写过。这种分解允许将矩阵 X=QR 表示为正交矩阵 Q 和上三角矩阵 R 的乘积。同样, Q 正交的事实很重要。
用于寻找特征值的 QR 方法 的中心思想是将 QR 矩阵分解迭代应用于原始矩阵 X 。
相似矩阵
这里我需要提一下 X 的特征值的一个数学性质。如果我们取任何一个可逆矩阵 M 那么这个矩阵
将具有与 X 相同的特征值。这样的矩阵 E 和 X 在形式上定义为 相似矩阵 ,简单来说就是它们具有相同的特征值。然而特征向量将会不同。同样,回想一下 QR 中的 Q 是正交的,因此是必然的。
QR 算法
所以, QR 方法 的思想是迭代以下步骤
- 将 QR 分解应用于 X 以便
2.让
和计算
注: E 是 类似于 到 X 。它们的特征值是相同的。
3.然后分解
4.因此
和
5.用 E 迭代单元本质上是对角的。
当 E 是对角线时,你得到的是对角线上的特征值,而 V 是特征向量!
这是 QR 法的最简单版本。有几个改进的版本,我就不赘述了,但是你可以在这里读到它们。
接下来,我想用 R 和 C++编写一个函数来实现这个方法,并验证它是否有效。
简单实现
我们将这样做:
- 展示一个用
R
编写的函数,该函数明确实现了用于寻找特征值的 QR 方法 。 - 通过将结果与内置的
eigen
函数进行比较来验证它是否工作。 - 在
C++
中实现相同的功能,并通过myc
包可用。验证它是否也能正常工作。 - 最后,比较它们的性能。
以下是我将使用的软件包:
library(tidyverse)
library(microbenchmark)
library(plotly)
library(myc)
R 函数
my_eigen
是一个非常简单的函数。前几行执行 QR 方法 的初始迭代。然后 while 循环迭代,直到 E 矩阵的对角线稳定下来,并且不再变化超过小的余量。带有values
和vectors
的返回列表应该与eigen
函数返回的相同。对于 QR 分解 步骤,我使用的是myc
包中的myc_qr
。
my_eigen <- function(A, margin = 1e-20) {
Q <- myc_qr(A)$Q
E <- t(Q) %*% A %*% Q
U <- Q
res <- diag(E)
init <- diag(A)
while (sum((init - res)^2) > margin) {
init <- res
Q <- myc_qr(E)$Q
E <- t(Q) %*% E %*% Q
U <- U %*% Q
res <- diag(E)
}
return(list(values = round(diag(E), 6), vecotrs = U))
}
验证它是否有效
让我们检查一下它是否工作。我将使用对称矩阵,因为我想确保特征值不是复数。
X <- matrix(c(
3, 2, 3, 2,
5, 1, 5, 4,
9, 3, 2, 1,
4, 5, 6, 7
), ncol = 4)
A <- t(X) %*% X
这是一个矩阵,我将使用它来验证我的函数是否按预期工作。
A## [,1] [,2] [,3] [,4]
## [1,] 26 40 41 54
## [2,] 40 67 62 83
## [3,] 41 62 95 70
## [4,] 54 83 70 126
下面是内置的eigen
函数返回的内容。
eigen(A)## eigen() decomposition
## $values
## [1] 268.6301739 39.1116701 5.8239493 0.4342066
##
## $vectors
## [,1] [,2] [,3] [,4]
## [1,] -0.3085888 0.02606027 -0.001691293 0.9508370
## [2,] -0.4823478 0.07140554 -0.858273366 -0.1600270
## [3,] -0.5053596 -0.81601931 0.242450805 -0.1412152
## [4,] -0.6455425 0.57300488 0.452306949 -0.2244074
正如您在下面看到的,当调用my_eigen
函数时,结果是相同的。
my_eigen(A)## $values
## [1] 268.630174 39.111670 5.823949 0.434207
##
## $vecotrs
## [,1] [,2] [,3] [,4]
## [1,] 0.3085888 0.02606027 0.001691289 -0.9508370
## [2,] 0.4823478 0.07140567 0.858273355 0.1600270
## [3,] 0.5053596 -0.81601935 -0.242450683 0.1412152
## [4,] 0.6455425 0.57300481 -0.452307035 0.2244074
当调用Rcpp
版本的myc_eigen
函数时,结果也是一样的。
myc_eigen(A)## $values
## [1] 268.6301739 39.1116701 5.8239493 0.4342066
##
## $vectors
## [,1] [,2] [,3] [,4]
## [1,] 0.3085888 0.02606027 0.001691289 -0.9508370
## [2,] 0.4823478 0.07140567 0.858273355 0.1600270
## [3,] 0.5053596 -0.81601935 -0.242450683 0.1412152
## [4,] 0.6455425 0.57300481 -0.452307035 0.2244074
此外,我们可以通过比较将和 λ 应用于特征向量的结果,并检查它们是否相等,来验证计算的分解是否正确。
*eigen_decomp <- myc_eigen(A)
v <- eigen_decomp$vectors[, 2]
l <- eigen_decomp$values[2]*
这里我们应用矩阵。
*as.numeric(A %*% v)## [1] 1.019261 2.792791 -31.915878 22.411177*
和预期的缩放比例 v 由 λ 给出相同的矢量。数值似乎朝着第 7 个有效数字有一点分歧,但解决方案是可以接受的接近。
*(l * v)## [1] 1.019261 2.792795 -31.915880 22.411175*
标杆管理
出于兴趣,我想比较一下这些函数计算特征值的速度。看起来在一个小矩阵上,比如我们在内置的eigen
函数上面使用的 4x4 矩阵,会慢一点。
*microbenchmark(eigen(A), my_eigen(A), myc_eigen(A))## Unit: microseconds
## expr min lq mean median uq max
## eigen(A) 179.003 191.6580 211.15584 201.8395 230.539 326.666
## my_eigen(A) 140.156 151.9090 161.32797 160.7745 169.035 221.650
## myc_eigen(A) 61.102 65.6775 71.19684 69.7855 75.054 92.629*
如果我们采用更大的矩阵,那么eigen
当然会更快。这个Rcpp
版本的 is my 函数只比纯R
函数快一点,可能是因为它使用了一个矩阵乘法函数myc_matmult
,这个函数明显比%*%
操作符慢。
*X <- matrix(rnorm(100), ncol = 10)
A <- t(X) %*% X
microbenchmark(eigen(A), my_eigen(A), myc_eigen(A))## Unit: microseconds
## expr min lq mean median uq max
eigen(A) 187.839 203.035 361.0305 230.057 341.2605 1444.53
my_eigen(A) 1698.668 1775.423 3067.5417 1911.548 2908.7345 18626.75
myc_eigen(A) 1225.512 1299.820 2178.2306 1547.336 2394.2235 12239.43*
可视化和动画
显然自己实施 QR 方法 并不是为了更好的表现。然而,我们能做的是构建一个函数,跟踪值是如何计算的,也许能更好地了解它是如何工作的。
下面是一个函数my_eigen2
,它执行与my_eigen
函数相同的计算,除了它不使用 while 循环,而是迭代给定的最大次数,并记录更新后的特征值*。如果计算收敛,则迭代在达到给定的最大值之前停止。*
*my_eigen2 <- function(A, margin = 1e-10, itrs = 40) {
Q <- myc_qr(A)$Q
Qt <- t(Q)
Elist <- vector("list", length = 21)
Ulist <- vector("list", length = 21)
E <- t(Q) %*% A %*% Q
U <- Q
Elist[[1]] <- E
Ulist[[1]] <- U
res <- diag(E)
for (i in 1:itrs) {
init <- res
Q <- myc_qr(E)$Q
E <- t(Q) %*% E %*% Q
U <- U %*% Q
Elist[[i + 1]] <- E
Ulist[[i + 1]] <- U
res <- diag(E)
print(sum((init - res)^2))
if (sum((init - res)^2) < margin) {
break()
}
}
return(list(
values = round(diag(E), 6),
vectors = U,
Elist = Elist[1:i],
Ulist = Ulist[1:i]
))
}*
想象一个 2 乘 2 的案例
为了可视化 QR 方法 如何工作,我决定使用一个 2x2 协方差矩阵。这是为了使其与 PCA 相关,并使其易于实际可视化。
*A <- matrix(c(
1.0, 0.3,
0.3, 0.8
), nrow = 2)
A## [,1] [,2]
## [1,] 1.0 0.3
## [2,] 0.3 0.8*
首先,我们让my_eigen2
进行计算,并建立矩阵列表,记录 QR 方法 如何收敛到答案。在 9 个步骤之后,计算收敛。
*eigen_decomps <- my_eigen2(A, itrs = 30)## [1] 0.005127351
## [1] 0.0003368977
## [1] 1.882642e-05
## [1] 1.011322e-06
## [1] 5.382776e-08
## [1] 2.858891e-09
## [1] 1.517664e-10
## [1] 8.055721e-12*
这些是实际的特征值和特征向量*😗
*eigen_decomps$values## [1] 1.216227 0.583773eigen_decomps$vectors## [,1] [,2]
## [1,] 0.8118117 -0.5839193
## [2,] 0.5839193 0.8118117*
以下代码在计算的每一步提取两个特征向量的方向,并将它们堆叠到 data.frame 中。我们可以使用这些来计算斜率,然后将它们可视化在密度图上。
正确的斜率是
和
*animation_data <-
map2(eigen_decomps$Elist, eigen_decomps$Ulist,
~ as.data.frame(1 / .x %*% .y)) %>%
bind_rows(.id = "frame") %>%
rename(x1 = "V1", y1 = "V2") %>%
bind_cols(d = rep(c("d1", "d2"), length(eigen_decomps$Elist))) %>%
pivot_wider(names_from = d, values_from = c(x1, y1)) %>%
bind_cols(
x0 = rep(0, length(eigen_decomps$Elist)),
y0 = rep(0, length(eigen_decomps$Elist))
) %>%
mutate(frame = as.numeric(frame)) %>%
mutate(
slope1 = y1_d1 / x1_d1,
slope2 = y1_d2 / x1_d2
)
animation_data## # A tibble: 8 x 9
## frame x1_d1 x1_d2 y1_d1 y1_d2 x0 y0 slope1 slope2
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 0.865 2.67 -7.00 1.76 0 0 -8.09 0.659
## 2 2 0.893 2.79 -2.27 2.03 0 0 -2.54 0.727
## 3 3 0.941 2.88 -1.71 2.10 0 0 -1.81 0.729
## 4 4 0.975 2.91 -1.53 2.11 0 0 -1.57 0.725
## 5 5 0.994 2.92 -1.46 2.11 0 0 -1.47 0.723
## 6 6 1.00 2.93 -1.43 2.11 0 0 -1.43 0.722
## 7 7 1.01 2.93 -1.42 2.11 0 0 -1.41 0.721
## 8 8 1.01 2.93 -1.41 2.11 0 0 -1.40 0.721*
使用ggplot
和plotly
,我们可以生成以下动画可视化
*data.grid <-expand.grid(x = seq(-5, 5, length.out = 200),
y = seq(-5, 5, length.out = 200))dens <- cbind(data.grid,
z = mvtnorm::dmvnorm(data.grid,
mean = c(0, 0),
sigma = A))
ggplotly(
ggplot(dens, aes(x = x, y = y, z = z)) +
geom_contour(color = "blue", alpha = 0.3) +
coord_fixed(xlim = c(-2, 2), ylim = c(-2, 2), ratio = 1) +
geom_abline(aes(slope = slope1, intercept = 0, frame = frame),
color = "red", data = animation_data
) +
geom_abline(aes(slope = slope2, intercept = 0, frame = frame),
color = "red", data = animation_data
) +
theme_void() +
theme(
legend.position = "none",
axis.line = element_blank(),
panel.grid.major = element_blank()
) +
geom_raster(aes(fill = z)) +
scale_fill_gradientn(colours = RColorBrewer::brewer.pal(n = 5, name = "BuGn"))
) %>%
animation_opts(frame = 500, redraw = TRUE) %>%
style(hoverinfo = "skip")*
可以在这里查看我博客上的实际动画:https://BDS haff . github . io/BDS haff . github . io/blog/2020-03-23-计算-特征值-和-特征向量/
我的外卖
- 我真的很感谢 LAPACK 或 Armadillo 这样的数值线性代数库,因为经历了最简单的特征值计算方法,我可以更好地欣赏创建它们的所有工作。
- 通过这个练习,我对计算特征值背后的细节变得非常熟悉。虽然有时令人沮丧,但我真的觉得我学到了很多东西,在我看来,真正彻底地学习了一些东西的感觉是最好的感觉之一。
开始数据科学项目前要问的八个问题
或者,如何像 DARPA 一样进行项目管理
图片来源: Pixabay
如果你熟悉国防高级研究计划局(DARPA),这个名字可能会让人联想到未来的军事技术、人形机器人和机械四足动物,所有这些都包裹在政府机密研究的神秘之中。你也不会太离谱。快速搜索就会出现为士兵设计的软外骨骼、千兆像素相机技术、机器人群体战术以及数百个其他 DARPA 赞助的研发项目。然而,该机构对数据科学的一个被忽视的贡献是其决策方法。
DARPA 概述
该机构的研究是科幻小说和汤姆·克兰西小说的素材。然而,人们很容易忘记 DARPA 也是一个(深呼吸)官僚机构,有完整的等级制度、标准操作程序和文书工作。也许这没有想象中的自我修复材料和精神控制机器有趣,但它确实回避了一个问题:所有的脑力都由他们支配,该机构如何选择追求哪些项目?
这是我们在数据科学和职业生活中每天都要面对的问题。我们的资源是有限的,项目失败是昂贵的,机会成本是真实的,那么哪些风险是值得冒的呢?
Heilmeier 的教义问答有助于将有趣的项目与重要的项目分开。
乔治·海尔迈耶:1975-1977 年间,工程师、发明家和 DARPA 主任。据该机构称,他精心设计了一套八个问题,“任何提出研究项目或产品开发努力的人都应该能够回答。”今天,它们被称为 Heilmeier Catechism,DARPA 仍然在项目提案和项目经理申请过程中使用它们。数据科学项目受益于问同样的问题。
乔治·h·赫利迈尔,图片来源: DARPA (公共领域)
开始项目前要问的八个问题:
- 你想做什么?绝对不要使用行话来阐明你的目标。
- 今天是如何做到的,当前实践的局限性是什么?
- 你的方法有什么创新,为什么你认为它会成功?
- 谁在乎呢。如果你成功了,会有什么不同呢?
- 有什么风险?
- 要花多少钱?
- 需要多长时间?
- 检查成功的期中和期末“考试”有哪些?
回答 Heilmeier 的问题将节省您和他人的时间。一些承担较大技术项目的办公室甚至会将这些问题作为建议书的强制性内容。即使不是必需的,通读 Heilmeier 的教义问答也可以帮助区分有趣的项目和重要的项目。以下是关于每个组件如何与数据科学流程相关的一些想法。
你想做什么?绝对不要使用行话来阐明你的目标。第二句话值得特别注意。数据科学家在他们的职业生涯中花了大量时间在各方之间翻译技术语言,当不与其他数据书呆子交谈时,行话充其量是低效的。在最坏的情况下,利益相关者可能会发现你居高临下,与问题脱节——他们可能会把你叫出来。行话也为误解提供了大量的机会。
**如今是如何做到的,目前的做法有哪些局限?**这个问题的范围值得思考——与其他地方相比,这里的是如何做到的?这个问题很快回答了解决方案是否已经存在——这是利益相关者想要知道的。了解数据科学领域当前的最佳实践,以及您的组织是否在使用它们。目前的方法还能更好吗?要诚实。
你的方法有什么创新,为什么你认为它会成功?一旦我们确定了其他人是如何处理这个问题的,考虑一下为什么。我们是在向我们的组织提议真正新颖的东西吗?新奇的想法出奇的少,但是即使我们提出的解决方案存在于其他地方,将其应用于我们的组织仍然是有价值的。如果一个解决方案可以解决手头的问题,经理或客户可能根本不在乎这个解决方案是否在其他地方使用过。
谁在乎呢?如果你成功了,会有什么不同呢?这个项目重要还是只是学术上的兴趣?如果我们提出一种新的方法,只是为了看看某物能弯曲和拉伸到什么程度,这个项目可能不属于“重要”的类别。在数据科学中,一个定义明确的问题在这里会有所帮助。项目在解决什么问题,为什么它很重要?
我们的资源是有限的,项目失败是昂贵的,机会成本是真实的,那么哪些风险是值得冒的呢?
有什么风险?要花多少钱?需要多长时间?简言之,谨慎的成功比意外的失败更可取。考虑追求项目的机会成本和减轻风险的可用机制,并了解您的意外情况。对于数据科学家来说,洞察这些可能需要与利益相关者进行额外的对话。这是我们可能都需要更多练习的地方。
检查成功的期中和期末“考试”是什么?我们必须对自己负责。想想真正的进步是什么样的,如何衡量。在数据科学中,管理对自己和投资方的期望可能是成功和失败的区别。利用你对潜在风险的了解来设定明确的预期,并与相关方进行沟通。
从 DARPA 那里可以学到很多其他的创新经验。在 2016 年的报告中,该机构将其在创新方面的成功归功于员工有限的任期和它提供的紧迫性(DARPA 项目经理的年流失率约为 25%),以及冒险和对失败的容忍度。查看他们的网站了解更多信息。
爱因斯坦,以及所有理论中最美丽的
广义相对论如何解释水星近日点的“异常”进动
英国理论物理学家保罗·狄拉克(1902–1984)是量子力学的创始人之一,他曾写道:
“很难将牛顿引力理论及其力的瞬时传播与狭义相对论的要求相协调,爱因斯坦在这一困难上的工作导致了他的相对论的推广——这可能是有史以来最伟大的科学发现。”
广义相对论被普遍认为是一个异常美妙的理论。几年来的几次测试证实了这一理论的一致性。我来描述一下其中一个测试,它正确解释了水星近日点的“异常”进动(见链接)牛顿的引力理论未能预测到。
图 1:图为水星近日点进动(来源)。
牛顿理论的问题是
近日点(行星轨道上离太阳最近的点)的进动(或旋转)有多种原因。其中两个是:
- 其他行星的存在导致了彼此轨道的扰动,这是主要原因
- 太阳的扁率(见图)明显不太相关
图 2:该图显示了一个半径被压缩成扁椭球体的球体(来源)。
水星岁差的近日点速率与牛顿引力理论的预测不符。法国天文学家和数学家于尔班·勒·威耶注意到了这一异常现象。由 T2·西蒙·纽康·T3 在 1882 年进行的最后一次测量估计,实际的岁差率与牛顿的预测相差 43 度。提出了许多特别的解决方案,但没有一个奏效。
正如下一节将要讨论的,在广义相对论中,这个额外的岁差完全可以用爱因斯坦的广义相对论来解释。对于后者的修订版,请查看下面的文章。
从简单论证中获得爱因斯坦方程
towardsdatascience.com](/a-heuristic-derivation-of-einsteins-gravity-equations-8d3659c8a46c)
用广义相对论计算水星的近日点进动
史瓦西解是描述太阳周围真空时空几何的爱因斯坦场方程的解。换句话说,史瓦西度规是太阳产生的时空曲率导致的太阳系度规。在以下情况下有效:
- 将太阳视为不旋转的物体
- 忽略源自太阳系其他行星的引力场。
史瓦西解具有以下线元素:
方程式 1:描述太阳周围真空时空几何的史瓦西解的线元素。
参数 R = 2 M 称为史瓦西半径。坐标 r 、 *θ、*和 φ 为球面坐标,如图 3 所示。
图 3:球坐标(来源)。
注意,从度规的各向同性来看,我们总是有θ = π/2(轨道被限制在赤道处)。事实上,根据两体问题(在我们的例子中,天体是太阳和行星),受中心力势作用的天体的运动将始终位于一个平面内。图 4 和图 5 示出了两种类型的轨道双体系统。限于平面的运动在牛顿和爱因斯坦引力理论中都是有效的。因此,在我们的分析中,只考虑位于该平面内的测地线就足够了。
图 4:两个质量相同的天体围绕一个共同的重心运行,在两个天体之外(这种情况出现在例如双星中)(来源)。
图 5:两个质量不同的物体围绕一个共同的重心运行(来源)。
该分析有效的第三个条件是径向坐标 r 必须远大于太阳的半径。这不是问题,因为太阳的史瓦西半径比太阳半径小得多。更具体地说,太阳的史瓦西半径大约为 2.95×10 米,而太阳的半径接近 6.96×10⁸米
图 6:德国物理学家和天文学家卡尔·史瓦西 ( 来源)
给定时空的对称性与在其中运动的粒子和光子的守恒量有关。由于史瓦西解的度规 g 既与时间无关(或时间平移不变量)又球对称,所以大质量粒子的能量和光子的能量都是守恒的。我们可以从数学上看到如下。
在一个度规为 g 的时空中,一个自由下落的物质粒子或光子遵守与那个时空相关的测地线方程(一条“直线”对弯曲时空的推广),它由(见史高斯)给出:
方程 2:测地线方程,由自由下落的物质粒子或光子遵守。
注意,由于光子也会被考虑,参数λ不可能是合适的时间 τ。测地线方程也可以写成:**
方程 3:测地线方程,用另一种形式写成。
现在请注意:
等式 4:时间和坐标ϕ.中度量的常数分量
Eqs。3 和 4 意味着:
等式 5:测地线的运动常数。
然后我们做如下定义:
等式 6:大质量粒子每单位质量的能量和光子的能量。
大质量粒子能量上面的~(见史高斯)用来表示这个能量是每单位质量。同理,由于 g 对 φ 的独立性,角动量是守恒的。我们定义:
方程式 7:大质量粒子单位质量的角动量和光子的角动量。
其中左边的项是大质量粒子每单位质量的角动量,右边的项是光子的角动量。我们现在需要轨道方程。大质量粒子动量的三个分量是:
等式 8:大质量粒子的三个动量分量。
光子的动量是:
方程式 9:光子动量的三个分量。
我们现在使用我们刚刚导出的动量分量,将它们代入粒子和光子的方程| p |=- m ,并求解 dr / dλ 。用于 dr / dλ 然后的等式为:
等式 10:关于 dr / dλ平方的等式。
现在直觉告诉我们用有效势改写这些方程,即:
方程 11:大质量粒子和光子的有效势的定义。
电势绘制在图 7 中。注意,由于两个方程的左边都是正的,有效势必然小于能量。图 7 显示了有质量和无质量粒子的有效势(注意图 7 中的 E 和 V 表示相同的量,上面有波浪符号~)。图中还标出了 dr/dλ =0 的转折点,禁域(其中 E < V )和圆形轨道(稳定和不稳定),其中 dV /dr =0。
图 7:有质量和无质量粒子(光子)的有效势。该图显示了稳定(最小值)和不稳定(最大值)的转折点、禁区和圆形轨道。
水星近日点的进动
从现在起,让我们只考虑大质量物体的运动,因为我们的目标是计算水星近日点的进动。
稳定的圆形轨道出现在有效势的最小值处。设 M 为太阳质量。对有效势求微分,将结果设为零,求解 r 我们得到稳定圆轨道的半径:
方程 12:围绕太阳振荡的大质量粒子的稳定圆形轨道的半径。
**在牛顿力学中,行星在圆形轨道上的完整轨道返回到其初始 φ。现在利用圆轨道有 E = V 的事实,并利用到目前为止推导出的表达式,我们得到一颗行星拥有δφ=2π,的时间,即周期 P :
等式 13:行星绕太阳旋转周期的牛顿结果。
图 8:图中显示了一个测试粒子遵循牛顿万有引力定律或爱因斯坦方程的轨道之间的差异(来源)。
现在,在广义相对论中,旋转的行星不会回到它的原点。如果相对论效应很小,我们应该有一个绕着中心缓慢旋转的椭圆。我们能做的(见史高斯)是检查轨道近日点的运动。为此,我们进行三个快速计算(见史高斯):
- 根据角动量推导出 dφ /dλ的表达式
- 根据单位质量的能量推导出 dt / dλ 的表达式
- 定义新变量 u ≡ 1/ r
将它们代入等式。10 我们得到:
等式 14:du/dφ的相对论表达式。
**我们现在定义 y,圆度偏差如下:
等式 15:变量 y 的定义,圆度的偏差。
对于牛顿轨道, y =0。为了得到相对论性的表达式,我们代入方程。15 成情商。14、降条款的顺序 y 。对于几乎圆形的轨道,我们得到下面的方程:
等式 16:dy/dφ的相对论表达式。
该解决方案如下:
等式 17:等式的解 y(φ)。16.
whee B 取决于初始条件。从余弦的论证,我们得出结论:当δ(kφ)= 2π时,轨道回到相同的半径。不同于 1 的 k 的存在就是它与牛顿结果的区别!如果相对论效应很小,我们可以做几个简单的近似来获得:
等式 18:近日点从连续轨道前进。
特别是对于汞来说,我们得到了每年 0.43 英寸的位移,正如本文开头所提到的,这是通过实验确定的值。
似乎连爱因斯坦都被结果惊呆了。发现计算结果后,他几天都不能工作。用他自己的话说,他变得“欣喜若狂”
我的 Github 和个人网站 www.marcotavora.me 有一些其他有趣的材料,既有关于物理的,也有关于数学、数据科学和金融等其他主题的。看看他们!
爱因斯坦的引力、光的弯曲以及他如何成为世界上最著名的科学家
太阳对光的引力弯曲的解释
广义相对论(发表于 1915 年)阿尔伯特·爱因斯坦的几何引力理论,被许多科学家认为可能是所有现存物理理论中最美丽的
在这篇文章中,我将描述广义相对论的经典测试之一,可以说是最著名的一个,这是太阳(或任何球对称,与时间无关的引力场)对光的弯曲,如图 1 所示。
图 1:太阳光的弯曲(来源)。
可以看出,净偏转角有一个非常简单的表达式:
等式 1:掠日光子的净偏转。
其中 M 为太阳质量, b 称为撞击参数(见图 2)。
图 2:影响参数 b。
光弯曲的一种表现形式被称为引力透镜,目前被天文学家用作一种必不可少的测量工具(见图 3)。
图 3:爱因斯坦十字,在这里我们看到了同一个遥远类星体的四幅图像,这是强引力透镜的结果(来源
在我以前的一篇文章中(见下文,描述了另一个理论的成功测试,即水星近日点的“异常”进动。
广义相对论如何解释水星近日点的“异常”进动
towardsdatascience.com](/einstein-and-the-most-beautiful-of-all-theories-f4ad4ce7a0a2)
史瓦西解及其测地线
史瓦西度规 g 是太阳产生的时空扭曲导致的太阳系度规。它具有以下行元素:
方程 2:史瓦西解的线元素,太阳产生的时空曲率扭曲导致的太阳系的度规。
公制中使用的坐标是如图 4 所示的球坐标 ( r , θ, φ )。
图 4:球坐标 r(径向距离)、θ(极角)、ϕ(方位角)(来源)。
因为我们的目标是导出 Eq。1,我们将需要首先获得光子轨迹的表达式 φ ( r ),其中 φ 是方位角,而 r 是半径坐标。让我们从计算穿过太阳引力场的光子的动量分量开始。
计算光子的动量和守恒量
给定时空中沿测地线运动的粒子的守恒量与对应的度规张量 g 的对称性之间存在对应关系。由于史瓦西解同时具有球对称性和时间平移不变性,因此测试粒子的能量和角动量都是守恒的。这在数学上可以通过将测地线方程后接质点重写为(见史高斯)来看出:
方程 3:测地线方程,用动量 p 和 g 的导数来写,用这个符号,很容易看出动量守恒和相关时空的对称性之间的对应关系(更多细节见正文)。
现在,光子在零测地线上行进,这意味着参数 λ 一定是不同于适当时间 *τ的某个仿射参数。*由于史瓦西度规遵守以下条件
等式 4:史瓦西度规的两个常数分量。
情商。3 为我们提供了下列守恒量
等式 5:由度规张量分量的相应导数的消失产生的两个运动常数。
注意,球对称意味着运动发生在一个平面上,我们可以选择θ = π/2(也就是说 dθ / dλ = 0)。
光子
由于我们的目标是专门研究光线的弯曲,所以从现在开始,只考虑光子的运动(大质量粒子在这篇文章中讨论)。
方程式中的两个运动常数。5 分别是能量和角动量,
等式 6:运动的两个常数 E 和 L 分别是能量和角动量。
牛顿的能量概念需要负号才能在低速下恢复。
光子动量的三个相关分量的显式表达式很容易使用度规的史瓦西分量和等式来计算。6:
方程 7:光子动量分量的显式表达式。
我们现在把这三个分量代入|p| =m= 0,求解为( dr / dλ )。我们得到:
方程 8:根据仿射参数λ的光子轨道方程。
我们可以改写这个等式,定义一个“有效势”,由下式给出:
方程式 9:光子的“有效势”。
该有效电势绘制在图 5 中。图中标明了转折点(其中 dr/dλ =0)、禁域(其中E<V】、和圆轨道(其中 dV /dr =0)等重要元素。
图 5:光子的有效势。
光的引力弯曲
回想一下,我们这里的目标是获得穿过太阳引力场的光子的 φ ( r )。事实证明,这很容易简单地通过将动量的φ-分量(角动量)除以等式的平方根来实现。8.我们得到:
方程 10:光子 φ ( r )的轨道微分方程。
正如我们在简介中定义的,参数 b 是冲击参数。在牛顿力学中,直接表明 b 是径向距离 r 的最小值(无偏转时)。因此 b 是光子轨迹相对于径向移动的平行轨迹的“偏移”(参见史高斯)。
如同在牛顿的情况下,使用新变量u≦1/*r .*然后Eq 在数学上是方便的。10 变成了:
等式 11:根据新变量 u 的光子轨道。
现在让我们考虑极限,其中M/r<<1 或者等效地u<<1/*M .*我们事后定义下面的辅助变量 y :
等式 12:新变量 y 的定义。
最后一步是求解方程。11 *。*我们最终获得:
方程 13:作为 dφ/dy 方程的解的光子轨迹(根据变量 y)。
光子的偏转如下图所示(以及一些相关注释):
图 6:光子穿越太阳引力场时轨迹的弯曲。
快速计算表明,净挠度确实由方程给出。1:
等式 14:光子经过太阳附近后的净偏转,与等式中的表达式相同。1.
图 7:著名的英国天文学家、物理学家和数学家亚瑟·爱丁顿,第一个证实爱因斯坦对δφ的预测的团队的领导人(来源)。一张爱丁顿拍摄的著名的 1919 年日食实验照片(来源)。
代入太阳的质量,用太阳的半径作为撞击参数 b (见史高斯)我们得到最大偏转,大约等于 1 。75。这个结果在 1919 年由著名的英国天文学家、物理学家和数学家亚瑟·爱丁顿领导的英国团队进行的一个著名实验中首次得到证实。
感谢您的阅读,再见!一如既往,我们随时欢迎建设性的批评和反馈!
我的 Github 和个人网站 www.marcotavora.me 有一些其他有趣的材料,既有关于物理的,也有关于数学、数据科学和金融等其他主题的。看看他们!