TowardsDataScience 2023 博客中文翻译(一百一十二)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

深入了解 LoRA 适配器

原文:towardsdatascience.com/dive-into-lora-adapters-38f4da488ede?source=collection_archive---------0-----------------------#2023-08-25

探索参数高效微调(PEFT):直观理解使用 LoRA 的微调

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Mariano Kamp

·

关注 发布于 Towards Data Science · 14 分钟阅读 · 2023 年 8 月 25 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

大型语言模型(LLMs)已经在全球掀起了风暴。在过去的一年里,我们见证了它们的巨大进步,从非常狭窄和受限的应用,到现在能够进行流畅的多轮对话。

难道这些模型从提取性摘要——逐字复制源文本——到现在提供抽象性摘要的转变不令人惊叹吗?它们现在完全重写摘要,以匹配读者的风格偏好和现有知识。更令人惊奇的是,这些新模型不仅能够生成新代码,还能解释你现有的代码。真是令人着迷。

这些大型模型通常非常强大,即使在零样本少样本的情况下查询,也能产生令人印象深刻的结果。尽管这允许快速实验并立即看到结果,但对于许多任务,通常之后会微调模型以实现最佳性能和效率。然而,微调每一个参数变得不切实际且效率低下。此外,考虑到模型的规模,我们是否有足够的标注数据来训练如此庞大的模型而不导致过拟合?

参数高效微调(PEFT)来拯救你:你现在可以在仅调整少量权重的情况下实现出色的性能。不必在多个机器上调整数十亿个参数,使得微调过程变得更加实际和经济可行。使用 PEFT 和量化可以让具有数十亿参数的大型模型在单个 GPU 上进行微调。

这个迷你系列适合那些希望探索 PEFT 和具体 LoRA 的经验丰富的机器学习从业者[2]:

  • 第一篇文章中,我们探讨了参数高效微调(PEFT)的动机。我们回顾了微调的原理及其作用,以及我们现有实践中可以保留、概括并以改进的方式应用的方面。我们将亲自动手,从零开始实现必要的内容,以创造一个直观的理解并展示我们选择探索的方法 LoRA 的简单性。

  • 第二篇文章中,我们现在深入寻找合适的超参数值,即我们回顾应用 LoRA 时相关的设计决策。在此过程中,我们建立了性能比较的基线,然后回顾可以使用 LoRA 调整的组件、它们的影响以及如何适当调整它们。

  • 基于对单一任务进行训练和调整的模型,在第三篇文章中,我们将视角扩展到多个任务的调优。此外,部署方面呢?我们如何利用为单一任务训练的适配器相对小的占用空间,并实现热插拔机制,以便使用单一模型端点进行多个任务的推理。

  • 在前三篇文章中,我们对使用 PEFT 进行训练、调优和部署有了直观的把握。过渡到第四篇文章时,我们将变得非常实际。我们将离开我们的教育模型,问道“到目前为止我们学到了什么,如何将其应用于实际场景?”然后使用 Hugging Face 提供的实现来实现我们的目标。这将包括使用 QLoRA,它结合了 LoRA 和量化,以实现高效的 GPU 内存使用。

准备好深入了解了吗?今天,让我们从为什么这些方法有效开始。

关于预训练和微调的有效性

在他们的研究中,Aghajanyan 等人[1]展示了神经网络层在预训练期间如何变化的两个有趣观察,使得微调更容易。这是广泛适用的,而不仅仅是针对特定的微调任务。

他们具体展示了预训练如何最小化表示的内在维度(ID)。以下两个图——取自他们的工作——说明了这一效果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

内在维度在预训练期间逐渐减少(图像由 Aghajanyan 等人提供)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

内在维度随着模型容量的增加而减少(图像由 Aghajanyan 等人提供)

作者没有对所有参数进行微调,而是用一个较小的、随机选择的参数子集来训练相应的模型。参数数量被选择以匹配完整微调的 90%性能。这个实现 90%性能所需的维度在上面的图中用两个 y 轴表示为d90

第一张图显示,随着预训练时长的增加(x 轴),d90下降,即在随后的微调中实现 90%完整微调性能所需的参数数量减少。这本身就显示了预训练作为一种压缩知识的方法的有效性。

在第二张图中,我们还可以看到,随着容量的增加,达到d90所需的参数数量也下降了。这很有趣。这表明,更大的模型可以学习训练数据的更好表示——模型所见的世界——并创建在任何下游任务中易于使用的层次特征。

作者指出的一个具体例子是,RoBERTa Large(354M)的d90大约是 207 个参数。太棒了!

请在上图中找到这个例子,然后也查看一下较小的RoBERTa Base(123M)需要更多的参数才能达到 90%的性能,这里是 896。很有趣。

从我在这个话题上的讨论中,我了解到有几点值得明确指出:

  • 我们在微调过程中利用了 ID 的效果,但上面的图表和数字都是关于预训练的。我们只是使用微调的数据来使最终的下游影响更具可感知性。

  • 使用更大的模型不仅相对其大小具有更低的 ID,而且绝对也是如此。当我们转向 PEFT 时,会看到类似的效果。

在[1]中,你会找到上述图示作为图 2、图 3,并且引用的结果取自表 1。

总结来说,我们可以看到,在预训练过程中学到的表示压缩了模型学习的知识,使得使用这些更具语义的表示来微调下游模型变得更加容易。我们将在此基础上使用 PEFT。只不过,我们不会随机选择要调整的参数并目标为 90%的性能,而是使用更有针对性的方法来选择要训练的参数,并力求几乎匹配全微调的性能。令人兴奋!

什么需要调整?

我们已经确定可以使用非常少量的参数。但是哪一些?在模型的哪个位置?

我们将在下一篇文章中深入讨论更多细节。但为了开始我们的思考并框定问题,让我们现在反思两种一般方法:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基于任务的分配:基于对任务的理解,调整哪些参数最具影响力?

基于任务: 使用微调时,我们希望保留预训练中的知识,并避免“灾难性遗忘”。我们认识到,下游任务特定的学习应发生在微调模型的任务头部(这里是分类器)及其下方的直接层(如图中绿色所示),而在较低层和嵌入中我们希望保留我们关于语言使用的一般知识(如图中红色所示)。通常我们通过每层学习率来引导模型,或完全冻结下层。

这都基于我们对模型学习下游任务所需的关键知识的位置的理解,以及预训练中现有知识应保留的位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基于架构元素的分配:哪些参数在微调中最有效、最有力?

基于架构: 相对而言,我们也可以审视我们架构的组件、它们的参数及其可能的影响。在上面的插图中,例如可以看到LayerNormBiases,这些容量较小,但遍布整个模型。这些位于中央位置以影响模型,但参数相对较少。

另一方面,我们还有嵌入的参数。这些参数虽然离任务较远,但靠近输入。而且嵌入中有大量参数。因此,如果我们想要高效,这些参数不会是我们进行任何形式的微调,包括 PEFT 的首选。

最后但同样重要的是,我们还有与 transformers 架构一起出现的大型线性模块,即attention vectorsfeed forward layers。这些模块具有大量参数,我们可以决定在哪一层调整它们。

我们将在下一篇文章中更详细地回顾选择正确参数的过程。在本文中,无论我们如何切割和拆分问题,我们最终都会得到一组我们想要调整的参数。本文的其余部分将涉及一些线性模块。

使用适配器提高效率

我们希望更高效地调整线性模块,而不是调整所有参数。我们使用的方法是注入适配器。这些新模块相对较小,将放置在我们想要适配的模块之后。适配器可以修改线性模块的输出,即它们可以以有利于下游任务的方式细化预训练输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可训练适配器和冻结模块

但这种方法存在一个问题。你能发现吗?这与待适配模块和适配器的相对大小有关。如果你看下面的插图,你会看到 GPU 内存。为了提高效率,我们将模型大小调整到尽可能紧密地适配可用的 GPU 内存。由于每一层宽度相同,Transformer 架构特别容易实现这种调整,即使是降维后的头部也会再次加起来达到整个宽度。因此,我们可以根据 Transformer 组件的统一宽度选择批次大小。

但如果我们现在在较大的线性层之后注入非常小的适配器,就会出现问题。正如下图所示,我们的内存使用变得低效。

批次大小适合线性层的宽度,但现在我们有一个更小的适配器。因此,大部分 GPU 必须等待小适配器执行。这降低了 GPU 的利用率。而且,这比插图中显示的情况更糟,考虑到插图中的适配器区域应该约为 1%,而插图中看起来接近 20%。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GPU 利用效率低

解决这个问题的一种方法是并行化适配,并通过加法将它们连接起来,使两个路径都能贡献输出。这样,我们就没有了内存瓶颈,可以并行执行原始线性模块和适配器,避免了之前看到的间隙。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

更好

但即使并行执行也是一种额外的负担,与完全没有适配器相比。这对于训练来说是正确的,对于推理也是如此。这并不理想。

那么,这种适配器应该有多大?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

缺少什么?

我们将在第三篇文章中处理推理过程中的低效率问题。抢先看:一切都会好起来的——我们将把模块的权重与低秩矩阵的乘积合并。

回到这篇文章——让我们解决适配器的大小问题。

低秩矩阵作为适配器

让我们放大来看。

下面,你可以看到左侧灰色的原始线性模块和右侧橙色的适配器。为了使它们兼容,输入和输出必须匹配,以便我们可以使用相同的输入并行调用它们,然后将输出相加,类似于使用残差连接。因此,两个侧面的输入和输出维度必须匹配。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adaptee 与 Adapter,每个都是全秩的

线性模块和适配器转换为两个矩阵。由于它们的维度匹配,机械地,我们现在有了兼容性。但由于适配器的大小与我们正在调整的模块一样大,我们没有变得更高效。我们需要一个小而兼容的适配器。

两个低秩矩阵的乘积符合我们的要求:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

适配器被分解为两个更低秩的矩阵

大矩阵被分解为两个低秩矩阵。但这些矩阵本身要小得多,d_in x rr x d_out,特别是r远小于d_ind_out。我们通常会看到r的值如 1、2、4、16,而d_ind_out则如 768、1024、3072、4096。

让我们把这些全部结合起来:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在前向传播过程中应用 LoRA

我们可以看到输入是一个单一的xx随后与原始权重W0相乘。W0是预训练的权重。然后xAB相乘,最终将两个结果相加形成调整后的输出,这里称为x'

存在不同的适配器实现,但在 LoRA 中,我们将其视为一个优化问题,并为特定的下游任务学习两个低秩矩阵AB。学习这些较少的参数比学习W0中的所有参数更有效。

初始化

让我们快速转到一个相关的话题。你会如何初始化AB?如果你随机初始化,考虑一下训练开始时会发生什么?

在每次前向传播中,我们会向适配模块的输出添加随机噪声,我们将不得不等待优化器一步步修正错误的初始化,这会导致微调开始时的不稳定。

为了减轻问题,我们通常使用较低的学习率、更小的初始化值或加热期,以限制这些错误参数的影响,从而避免过度不稳定权重。在 LLAMA 适配器[3]的论文中,作者引入了零门控:他们将适配器的门的值(与实际权重相乘的值)初始化为 0,并在训练过程中逐渐增加其值。

一种替代方法是将AB初始化为 0。但这样你将无法打破对称性,在学习过程中所有参数可能会被视为一个参数。

LoRA 实际做的事情非常优雅。一个矩阵A是随机初始化的,而另一个矩阵B是用 0 初始化的。因此,两个矩阵的乘积为 0,但每个参数在反向传播过程中仍然可以单独求导。从 0 开始意味着归纳偏差是不做任何事情,除非改变权重会导致损失减少。因此,在训练开始时不会有不稳定性。不错!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

LoRA 适配器的初始化——什么都不做

代码中可能会是什么样子?

让我们查看一些代码摘录,以了解我们的小示例。你可以在随附的笔记本中找到完整的代码,而更完整的实现则在同一个仓库中,供后续文章使用。

我们首先设置一个适配器。我们传入一个对要调整的模块的引用,现在我们称之为adaptee。我们存储了对其原始forward方法的引用,并让adapteeforward方法现在指向适配器的forward方法的实现。

class LoRAAdapter(nn.Module):
    def __init__(self, 
                 adaptee, # <- module to be adapted
                 r):
        super().__init__()

        self.r = r
        self.adaptee = adaptee

        # Store a pointer to the original forward implementation 
        # of the module to be adapted.
        # Then point its forward method to this adapter module.
        self.orig_forward = adaptee.forward
        adaptee.forward = self.forward
        [..]

现在我们已经设置好了集成的机制,我们还初始化了低秩矩阵的参数。注意,我们初始化了一个矩阵为 0,另一个矩阵为随机值:

 [..]
        # Adding the weight matrices directly to the adaptee,
        # which makes it more practical to report the parameters,
        # and to remove it later.
        adaptee.lora_A = (nn.Parameter(torch.randn(adaptee.in_features, r)/
                          math.sqrt(adaptee.in_features)))
        adaptee.lora_B = nn.Parameter(torch.zeros(r, adaptee.out_features))

最后,在LoRAAdapter类中,我们有一个forward方法,它首先用输入x调用adapteeforward方法。这是原始模块中执行的原始路径。但我们还将这个结果与我们调整过的分支中的结果相加,在那里我们将输入xAB进行矩阵乘法。

def forward(self, x, *args, **kwargs):
  return (
    self.orig_forward(x, *args, **kwargs) +
    x @ self.adaptee.lora_A @ self.adaptee.lora_B
  )

这种简洁对我来说看起来很优雅。

还有更多可能有趣的细节,但最好是和代码一起解释。你可以在随附的笔记本中找到这些:

  • 如何首先冻结整个模型

  • 如何解冻分类器。因为它是特定于我们的下游任务的,我们需要对其进行完全训练。

  • 如何添加适配器;这些适配器都是活跃的,未被冻结。

  • 审查模块矩阵的维度如何与两个低秩矩阵AB相关。

  • 当使用小的r值时,参数数量会减少多少?

下面的小摘录展示了原始模块output.dense的参数没有被训练(标记为0),但其 LoRA 矩阵是可训练的(标记为1),当然,模型的整体分类器(也标记为可训练的1):

[..]
roberta.encoder.layer.11.attention.output.LayerNorm.bias       0         768
roberta.encoder.layer.11.intermediate.dense.weight             0     2359296
roberta.encoder.layer.11.intermediate.dense.bias               0        3072
roberta.encoder.layer.11.output.dense.weight                   0     2359296
roberta.encoder.layer.11.output.dense.bias                     0         768
roberta.encoder.layer.11.output.dense.lora_A                   1       12288
roberta.encoder.layer.11.output.dense.lora_B                   1        3072
roberta.encoder.layer.11.output.LayerNorm.weight               0         768
roberta.encoder.layer.11.output.LayerNorm.bias                 0         768
classifier.dense.weight                                        1      589824
classifier.dense.bias                                          1         768
classifier.out_proj.weight                                     1        1536
classifier.out_proj.bias                                       1           2
[..]
Total parameters: 124,978,946, thereof learnable: 923,906 (0.7392%)

查看更多内容,请查看笔记本

来试试吧?

此外,你将看到一些在笔记本中进行的测试,这些测试显示整个设置在机械层面上是有效的。

然后我们进行第一次实验并提交训练作业到 SageMaker。我们对原始模型进行完整微调,然后如这里所述启用 LoRA 进行训练。

对我们的测试,我们在sst-2 数据集 [5] 上训练 RoBERTa Large [4],r=2,调整所有层的queryoutput参数。我们使用5e-54e-4作为全微调和 LoRA 微调的学习率。

这是结果(更多内容请见笔记本):

full-finetuning accuracy: 0.944
lora-finetuning accuracy: 0.933

所以这是……好,还是不好?是什么?首先,这清楚地表明整个设置在机械层面上是有效的——这很好。90%以上的准确度表明它工作得很好。

但效果如何?我们将这些数字与什么进行比较?这两个单独训练运行的代表性如何?我们只是运气好还是不好?LoRA 的结果比传统方法更好吗?这不是很奇怪吗?我们调优传统方法的效果如何?

上述结果都不可靠。我们不知道在第二次运行时使用我们的超参数是否会产生类似的结果。此外,我们使用了通过半教育猜测选择的超参数。

当然,还有更好的方法。在下一篇文章中,我们将采用更严谨的方法来选择超参数,并将更系统地评估性能。

  • 建立比较基准。

  • 搜索基准和实验的良好超参数。

  • 最重要的是:加深我们对 LoRA 方法和设计决策影响的理解,使我们的直觉以数据驱动的方式对齐。

在那之前,希望你阅读这篇文章时感到愉快。

感谢 Constantin Gonzalez, Ümit Yoldas, Valerio PerroneElina Lesyk 在撰写本文期间提供的宝贵反馈。

所有图像均由作者提供,除非另有说明。

[1] Armen Aghajanyan, Luke Zettlemoyer, Sonal Gupta. 内在维度解释了语言模型微调的有效性, 2020

[2] Edward J. Hu, Yelong Shen, Phillip Wallis, Zeyuan Allen-Zhu, Yuanzhi Li, Shean Wang, Lu Wang, Weizhu Chen. LoRA: 大型语言模型的低秩适应, 2021

[3] Renrui Zhang, Jiaming Han, Chris Liu, Peng Gao, Aojun Zhou, Xiangfei Hu, Shilin Yan, Pan Lu, Hongsheng Li, Yu Qiao. LLaMA-Adapter: 高效微调语言模型的零初始化注意力, 2023

[4] 尹汉·刘、迈尔·奥特、纳曼·戈亚尔、景飞·杜、曼达尔·乔希、丹琪·陈、奥梅尔·列维、迈克·刘易斯、卢克·泽特尔莫耶、维塞林·斯托扬诺夫。《RoBERTa:一种强健优化的 BERT 预训练方法》,2019

[5] 理查德·索彻、亚历克斯·佩雷尔金、简·吴、杰森·庄、克里斯托弗·D·曼宁、安德鲁·吴和克里斯托弗·波茨。《用于情感树库的递归深度模型的语义组合性研究》,2013

DL 笔记:高级梯度下降

原文:towardsdatascience.com/dl-notes-advanced-gradient-descent-4407d84c2515?source=collection_archive---------7-----------------------#2023-12-05

主要的优化算法用于训练神经网络,从头开始在 Python 中解释和实现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Luis Medina

·

关注 发表在 Towards Data Science · 17 分钟阅读 · 2023 年 12 月 5 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:Jack Anstey / Unsplash

在我之前关于梯度下降的文章中,我解释了其基本概念,并总结了这种优化方法的主要挑战。

然而,我只涉及了随机梯度下降(SGD)以及“批处理”和“小批量”梯度下降的实现。

其他算法在收敛速度、对“景观”特征(梯度消失问题)的鲁棒性,以及对学习率选择的依赖程度等方面提供了优势。

所以今天我将讨论更高级的优化算法,从零开始在 Python 中实现它们,并通过动画可视化进行比较。

我还列出了用于学习这些算法的资源。这些资源非常适合深入探讨正式的概念。

比较使用简单目标函数的算法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这篇文章中,我将展示如何在 Python 中实现不同的算法。

我创建了一个 Jupyter Notebook,你可以通过GitHub 访问或直接在 Google Colab 上查看以查看用于创建此处展示的图形的所有代码。

为生成动画,我使用了我之前的文章中展示的在 Python 中创建动画梯度下降图形的方法。

函数定义假设已经包含了以下代码,因为它们使用了numpy类和方法,并调用了函数f及其梯度grad_f

import numpy as np

# Create a function to compute the surface
def f(theta):
  x = theta[0]
  y = theta[1]
  return x**2 - y**2

# Define a function to compute the gradient
def grad_f(theta):
    returnValue = np.array([0.,0.])
    x = theta[0]
    y = theta[1]
    returnValue[0] += 2*x
    returnValue[1] += - 2*y
    return returnValue

动量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由Sharon Pittaway提供,拍摄于Unsplash

我们可以将优化算法与球体滚下坡道进行比较。

如果“球体”像现实中那样具有动量,它在以全速加速下坡后更不容易停留在局部最小值中。

这就是人们在处理梯度下降被困在局部最小值的问题时意识到的。

从高中物理中我们知道,平动动量定义为物体质量与其速度的乘积:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

平动动量。

我们还知道,质量为m的物体的重力势能与其所处的高度h成正比的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

重力势能。

此外,物体的势能与施加在其上的力之间存在直接的关系

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

力等于势能的负梯度。

pU之间的关系可以从牛顿第二定律推导出来:

物体的运动变化与施加的力成正比,并且沿着力的施加方向发生。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

牛顿第二定律。

💡 实际上,这个物理类比过于简化,无法涵盖将动量添加到梯度下降优化中的所有优缺点。要获取全面的信息,推荐查看为什么动量真的有效?

如何添加动量?

当我们初始化优化算法时,我们把“球”放在高度h处,赋予它势能U

施加在球上的力与这种势能的梯度成正比,就像我们优化的函数的梯度一样(我们正在移动的表面)。

动量在优化中的作用是利用梯度来改变粒子的“速度”,进而改变其位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

动量更新。

由于速度项,“粒子”在具有一致梯度的任何方向上加速。

我将这实现为以下 Python 函数:

def gradient_descent(x_init, y_init, step_size, n_iters, momentum):
  eta = step_size
  mu = momentum  # Notice that, if mu = 0, this algorithm is just SGD

  # Initialize arrays to store results
  theta = np.tile([x_init, y_init], (n_iters,1) )
  z = np.tile([f(theta[0])], n_iters )

  # Initialize velocity term
  v_t = np.array([0,0])

  for k in range (1, n_iters):

   # Update velocity
      v_t = mu*v_t - eta*grad_f(theta[k-1])

   # Update position
      theta[k] = theta[k-1] + v_t
      z[k] = f(theta[k])

  # Store position coordinates
  dataset = np.stack((theta[:,0], theta[:,1], z), 1)  

  return dataset

动量更新既加速了在低曲率方向上的优化,又平滑了(阻尼效应)由于“地形”特征或噪声数据造成的振荡[3]。

一些人认为动量更新实际上与摩擦系数的物理效果更一致,因为它减少了系统的动能[2]。

另一种解释是,它为优化过程提供了“短期”记忆。

由于小于 1,动量参数像是之前梯度的指数加权和,速度更新可以重写为[3][5]:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

速度项被重写为加权和。

其中g是瞬时梯度,v是平滑的梯度估计器。

参数β控制我们对当前梯度与之前梯度之间新值的权重分配。

通常它的值为 0.9,但有时会“调度”,即在迭代过程中从 0.5 逐步增加到 0.99。

Nesterov 加速梯度(NAG)

由 Nesterov 于 1983 年提出。

Nesterov 更新实现了“前瞻”功能,以提高动量在凸函数上的稳定性和收敛速度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

NAG 更新。

动量使用当前位置来更新梯度,而 NAG 首先对当前位置进行部分更新,知道

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对前瞻性更新的直觉。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

动量和 NAG 更新的矢量表示。

为了将其实现为 Python 函数,我对之前展示的“速度更新”代码进行了以下修改:

# Update velocity

# Momentum
v_t = mu*v_t - eta *grad_f(theta[k-1])

# NAG
v_t = mu*v_t - eta *grad_f(theta[k-1] + mu * v_t)

这一部分更新有助于提高优化的准确性。实际上,这意味着与动量法相比,它在局部最小值附近的振荡更少。

差异在下图中非常明显。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

比较动量法和 NAG 在复杂表面上的下降优化。

两个优化器在相同坐标上初始化,并使用相同的动量参数(0.95,固定)。

以下动画也帮助我们理解调度或“退火”动量参数的直观感受。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

比较不同优化算法在穿越梯度消失区域时的表现。在这种情况下,基于动量的方法表现更好。

一开始,少量动量有助于穿越梯度消失区域。当我们接近局部最小值时,较大的动量值可能会减小我们观察到的振荡,从而提高收敛速度。

自适应方法

上面动画中展示的其他优化算法是自适应方法,我将在本节中描述这些方法。

通过这个简单的例子,动量法和 NAG 似乎比其他方法优越。然而,自适应算法更具鲁棒性。我将在另一篇文章中通过实际例子来展示这一点。

自适应梯度算法(AdaGrad)

AdaGrad 是一类用于随机优化的次梯度算法,由John Duchi、Elad Hazan 和 Yoram Singer 于 2011 年提出。

他们提出通过将梯度的历史信息纳入每次新的权重更新中来改进基于梯度的学习。

与动量法通过偏置梯度本身不同,AdaGrad 动态地为目标函数的每个参数单独修改学习率。

这意味着我们对每个模型权重使用不同的学习率。这些学习率基于梯度的一致性进行调整。

为此,梯度估计的序列如下存储:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

梯度的平方和或梯度历史的外积。

如果我们优化一个具有n个坐标或参数的函数,g将是一个具有n个元素的向量,G也是如此。

然后,更新规则如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AdaGrad 更新。

参数ε用于避免除零错误,通常设置为一个小值,如 1e-08。

有趣的是,G的定义类似于梯度分布的非中心(零均值)方差

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方差定义。

方差是分布的离散能量的度量。

因此,对于每个参数θᵢ,学习率是根据θᵢ的梯度方差的倒数来调整的。

考虑到这一点,我们可以说,梯度分布中离散程度较大的参数将按较大的比例缩小学习率,而梯度较一致(方差较低)的参数将具有较大的学习率。

AdaGrad 还根据时间(之前梯度的累积)和目标函数的曲率(“区域”中的梯度方差较低将分配较小的步长)自动实现学习率衰减。这改善了算法的收敛速度。

我已将 AdaGrad 实现为以下 Python 函数:

def Adagrad(x_init, y_init, step_size, n_iters):
  eta = step_size
  G_t = 0
  eps = 1e-8

  theta = np.tile([x_init, y_init], (n_iters,1) )
  z = np.tile([f(theta[0])], n_iters )
  for k in range (1, n_iters):
      # Compute gradient
      g_t = grad_f(theta[k-1])

      # Accumulate squared gradients
      G_t += g_t**2

      # Update position
      theta[k] = theta[k-1] - eta * g_t / (np.sqrt(G_t) + eps)
      z[k] = f(theta[k])

  # Store position coordinates
  dataSet = np.stack((theta[:,0], theta[:,1], z), 1)

  return dataSet

AdaGrad 的一个缺点是,这种训练过程中学习率的衰减可能过于激进,导致在训练人工神经网络时学习过早停止。每次参数更新都很稳健,但变化接近最优点的速度可能会下降得太多。

另一个缺点是,虽然学习率在学习过程中会自我调整,但 AdaGrad 仍然对初始条件敏感。如果优化开始时梯度很大,那么训练过程中学习率会较低。

我们可以在动画图中看到这一点。AdaGrad 很快打破了对称性,但学习非常慢,与其他算法相比。

为了弥补这一点,可能需要将学习率调整到更高的值,这在一定程度上削弱了自我调整特性的目的。

均方根传播(RMSprop)

未发表的方法,但在课程《神经网络与机器学习》第 6 讲的幻灯片中提到,由Geoffrey Hinton教授提供。

这个算法的概念类似于动量。它还结合了梯度幅度的短期历史来执行权重更新。

然而,与 AdaGrad 类似,RMSProp 修改的是学习率而不是梯度。

为此,学习率被除以最近梯度幅度的滑动平均。

首先,算法计算平方成本值及其之前值的加权和。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

平方成本的指数加权和。

这就像是一个短期均值,其中参数β调整了给更近期的成本值相较于较旧的成本值的权重。

它类似于我之前提到的动量的重写形式,但应用于平方成本,而不是梯度。

下一步是将学习率除以这个移动平均的平方根。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

RMSProp 更新规则。

这样,步长依赖于梯度幅度的历史(短期记忆)。

注意,计算加权平方和的平方根(或加权平均)等同于计算这些值的均方根(RMS)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

RMS 的定义。

信号的 RMS 是其能量的表示(与方差不同,方差表示的是离散能量)[1]。

因此,使用 RMSProp 时,学习率会根据成本函数的梯度及其先前值的总能量进行调节。这种调整是动态的,并且针对损失函数的每个方向或组件(每个权重!)。

目标是通过在这些情况下减小步长来减少由于梯度大幅变化引起的波动性。

这也有助于解决梯度消失问题,因为当梯度非常小时,我们会采取更大的步伐。

这是我作为 Python 函数编码的方式:

def RMSProp(x_init, y_init, step_size, n_iters, decay):
  beta = decay # 0.8, 0.9, ..., 0.99
  eta = step_size
  eps = 1e-8
  MSQ = 0

  theta = np.tile([x_init, y_init], (n_iters,1) )
  z = np.tile([f(theta[0])], n_iters )
  for k in range (1, n_iters):
      # Compute gradient
      g_t = grad_f(theta[k-1])

      # Compute the weighted mean of squared values
      MSQ = beta * MSQ + (1 - beta) * g_t**2

      # Update position (divide eta by RMS)
      theta[k] = theta[k-1] - eta * g_t / (np.sqrt(MSQ) + eps)
      z[k] = f(theta[k])

  # Store position coordinates
  dataSet = np.stack((theta[:,0], theta[:,1], z), 1)

  return dataSet

RMSprop 对学习率的初始选择非常稳健,并且还实现了自动学习率衰减。然而,由于它基于梯度值的短期历史,因此衰减比 AdaGrad 更加温和。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Gonzalo Kaplanski 拍摄,来源于 Unsplash

AdaDelta

Matthew Zeiler 于 2012 年提出

这种方法是为了克服 AdaGrad 的主要局限性而开发的:学习率的持续衰减导致提前停止,并且需要手动调整“全局”学习率。

为了克服持续的学习率衰减,算法在一个窗口或固定大小内累积过去梯度的历史。

实际上,这涉及到将学习率除以固定窗口内先前梯度的 RMS,就像 RMSprop 所做的那样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

学习率缩放类似于 RMSProp。

AdaGrad 的下一个改进是优化更新单元的修正。

在 AdaGrad(以及我迄今为止描述的所有其他优化算法)中,优化步骤的单位与我们为优化成本函数而修改的参数单位不匹配 [9]:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们在学校里知道不能将苹果和橙子相加。但使用这些优化算法时,就像我们在加“苹果”(当前参数值,θₜ 和一些未知量(优化步骤 Δθ),这些在数学上可以加到一起以获得新的苹果(更新后的参数,θₜ ₊₁)。这有效,但在现实生活中没有意义。

Zeiler 决定纠正单位,将更新项从牛顿法重新排列,并假设损失函数的曲率可以用对角海森矩阵来近似:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将这一观察与类似于 RMSProp 的更新规则进行比较,Zeiler 确定了正确的更新项形式,以保持正确的单位。

直观的解释在原始出版物中更为详尽,但实际上,它导致在更新项的分子中添加了以前更新值的指数加权平均的平方根:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AdaDelta 参数更新步骤。

这基本上假设损失函数在小窗口大小 w 内是平滑的(曲率低),以便Δθₜ 可以通过以前值的指数 RMS 来近似。

如果我们将其实现为 Python 函数,算法看起来是这样的:

def AdaDelta(x_init, y_init, step_size, n_iters, decay):

  eta = step_size
  G_t = 0
  eps = 1e-8
  E_gsq = 0
  E_xsq = 0

  theta = np.tile([x_init, y_init], (n_iters,1) )
  z = np.tile([f(theta[0])], n_iters )
  for k in range (1, n_iters):
      g_t = grad_f(theta[k-1])
      E_gsq = decay * E_gsq + (1 - decay) * g_t**2
      delta = - np.sqrt(E_xsq + eps) / np.sqrt(E_gsq + eps) * g_t
      E_xsq = decay * E_xsq + (1 - decay) * delta**2
      theta[k] = theta[k-1] + delta
      z[k] = f(theta[k])

  # Setting up Data Set for Animation
  dataSet = np.stack((theta[:,0], theta[:,1], z), 1)  # Combining our position coordinates

  return dataSet

AdaDelta 结合了其所基于的优化方法的优点。

例如,分子中前期参数更新的短期记忆类似于动量,并具有加速梯度下降的效果。

分母提供了 AdaGrad 的每维度准确性,但没有过度的学习率衰减(就像 RMSProp 一样)。

此外,AdaDelta 对突发的梯度变化更为稳健,对初始学习率的选择也很稳健(请参见本文最后一节中的实际示例)。

Adam(自适应动量)

这是今天最流行的算法之一。

它由迪德里克·P·金马和吉米·雷·巴于 2014 年提出,并因其计算效率高以及在处理大量数据和参数的问题上表现良好而变得非常流行。

Adam 类似于动量(Momentum)和 RMSprop 的结合,因为它动态地改变了损失函数的梯度以及用于缩放这些梯度的学习率来更新权重。

为此,算法包括计算两个在本文之前部分已经熟悉的术语。

首先,动量项是成本函数前几个梯度的指数加权和(这类似于加权方差):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成本梯度的指数加权平均。

然后,有一个来自 RMSprop 的术语,是平方梯度的指数加权移动平均。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成本梯度的指数加权平均。

将两者与 SGD 算法结合,过去梯度的信息被纳入更新步骤。它们在短窗口内的总能量(RMS)用于缩放学习率,而它们的离散度(方差)有助于调整用于更新权重的当前梯度值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adam 的更新规则。

带有波浪号 (~) 的值对应于引入的偏差校正项,以减少学习过程中 m 和 v 初始值的贡献:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adam 的初始化偏差校正项。

t = 当前训练轮次。

与 AdaDelta 不同,Adam 确实需要调整一些超参数,但这些参数很容易解释。

β₁β₂ 是梯度和平方梯度的 指数移动平均 的衰减率。

较大的值会对先前的梯度赋予更多权重,表现更平滑,对最近的变化反应较少。接近零的值会赋予最近梯度变化更多权重。典型值为β₁ = 0.9 和β₂ = 0.999。

ε 与所有前述情况一样,是一个常数,添加以避免除以零,通常设为 1e-8。

尽管有许多附加项和显著的优势,Adam 实现起来非常简单:

def Adam(x_init, y_init, step_size, n_iters, 
         beta_1 = 0.9, beta_2 = 0.999):

  eps = 1e-8
  eta = step_size

  # Initialize vectors
  m_t = np.array([0,0])
  v_t = np.array([0,0])
  theta = np.tile([x_init, y_init], (n_iters,1) )
  z = np.tile([f(theta[0])], n_iters )

  for k in range (1, n_iters):
      # Compute gradient
      g_t = grad_f(theta[k-1])

      # Compute "momentum-like" term (weighted average)
      m_t = beta_1 * m_t + (1 - beta_1)*g_t

      # Compute the mean of squared gradient values
      v_t = beta_2 * v_t + (1 - beta_2)*g_t**2

      # Initialization bias correction terms
      m_t_hat = m_t/(1 - beta_1**k)
      v_t_hat = v_t/(1 - beta_2**k)

      # Update position with adjusted gradient and lr
      theta[k] = theta[k-1] - eta * m_t_hat/(np.sqrt(v_t_hat)+ eps)
      z[k] = f(theta[k])

  # Store position coordinates
  dataSet = np.stack((theta[:,0], theta[:,1], z), 1) 

  return dataSet

有趣的是,论文的作者指出了这个术语

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adam 的学习率缩放。

类似于 信噪比 (SNR) 的定义:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

信噪比。

然后,我们可以说,对于较小的 SNR 值,参数更新将接近零。这意味着当对是否朝着真实梯度方向移动存在太多不确定性时,我们不会进行大幅更新。

Adam 及其变体在训练深度学习模型时通常优于其他算法,特别是在梯度非常嘈杂和稀疏的情况下。

不同学习率的性能表现

我决定比较不同优化器在不同“全局”学习率下的表现。

这是一个相当简单的例子,但它提供了这些方法如何受到学习率选择影响的一个概念。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

比较不同算法优化过程中 x 和 y 坐标的演变。对于 Momentum 和 NAG,mu = 0.95。对于 RMSProp 和 AdaDelta,衰减参数 = 0.9。

AdaDelta 对全局学习率设置似乎非常鲁棒,在所有三种情况下“下降”速率相同。我们还可以看到,在这种情况下,AdaGrad 需要较大的学习率才能实现与 AdaDelta 相当的性能。

对于较小的学习率,Adam 和 RMSProp 显然相似,并且优于 Momentum 和 SGD。

然而,对于较大的学习率,RMSProp 在最优 x 值(x = 0)周围表现出一致的振荡,而 Adam 在初始瞬态后稳定下来,这得益于分子中动量项的阻尼效应。

自适应算法比 SGD 和 Momentum 方法更早打破对称性,除了全局学习率为 0.1 的情况,此时 Momentum 和 NAG 优于 AdaDelta。

再次强调,这些观察结果仅适用于特定的场景。

结论

当我们将这些优化算法应用于简单函数时,如上述的鞍点例子,它们的优点并不完全显现。

对于其他小规模模型或数据集的场景,即使是 SGD 也可能效果更好,因此理解每种优化器最佳工作条件是重要的。

在训练神经网络时,我们优化损失函数,而在任何时刻我们没有其梯度的确切值,仅有对其的估计。这就是为什么像 Adam 和 AdaDelta 这样的对梯度中的噪声和稀疏性具有鲁棒性的算法在数据科学社区中被广泛使用。

此外,我们可以处理大量的模型权重,而不仅仅是 x 和 y 坐标。在这些情况下,获取每个参数的学习率的能力是有益的。

在未来的文章中,我将展示这些方法在另一篇文章中的更现实的比较,使用人工神经网络。

进一步阅读

参考文献

所有图示,除非另有说明,均由作者创建。

[1] 在线课程 深入理解深度学习,由 Mike X Cohen 主讲(sincxpress.com

[2] 斯坦福在线:CS231 卷积神经网络进行视觉识别

[3] Goh. “为什么 Momentum 真正有效”,Distill,2017. doi.org/10.23915/distill.00006

[4] Villalarga, D. “AdaGrad”。发布于康奈尔大学计算优化开放教材 — 优化维基。

[5] Bengio, Yoshua. “深度架构的梯度训练的实际建议。” 神经网络:实用技巧:第二版。柏林,海德堡:Springer Berlin Heidelberg, 437–478, 2012. 在线: arXiv:1206.5533 [cs.LG]

[6] Sutskever, I., Martens, J., Dahl, G. & Hinton, G. “在深度学习中初始化和动量的重要性”。机器学习研究会议论文集, 28(3):1139–1147, 2013. 网址: proceedings.mlr.press/v28/sutskever13.html.

[7] Duchi, J., Hazan, E., Singer, Y., “在线学习和随机优化的自适应子梯度方法”。机器学习研究杂志, 12(61):2121−2159, 2011. 网址: jmlr.org/papers/v12/duchi11a.html

[8] Jason Brownlee, 从零开始的 AdaGrad 梯度下降。2021

[9] Zeiler, M. “ADADELTA: 一种自适应学习率方法”,2012. arXiv:1212.5701v1 [cs.LG]

[10] Kingma, D., Ba, J. “Adam: 一种随机优化方法”,2014. arXiv:1412.6980 [cs.LG]

最初发布于 https://www.makerluis.com 2023 年 12 月 5 日。

在 AWS Inferentia 上进行深度学习训练

原文:towardsdatascience.com/dl-training-on-aws-inferentia-53e103597a03?source=collection_archive---------7-----------------------#2023-08-30

又一个节省开支的 AI 模型训练技巧

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Chaim Rand

·

关注 发表在 Towards Data Science · 5 分钟阅读 · 2023 年 8 月 30 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:李生昌 通过 Unsplash

本文的主题是 AWS 自家研发的 AI 芯片,AWS Inferentia——更具体地说,是第二代 AWS Inferentia2。这是对我们去年关于 AWS Trainium 的 文章 的续集,并且是关于专用 AI 加速器系列文章的一部分。与我们之前在系列文章中探讨的芯片不同,AWS Inferentia 是为 AI 模型的 推理 设计的,专门 针对深度学习推理应用。然而,AWS Inferentia2 和 AWS Trainium 共享相同的底层 NeuronCore-v2 架构以及相同的软件栈(AWS Neuron SDK),这就引发了一个问题:AWS Inferentia 是否也可以用于 AI 训练工作负载?

当然,与 Amazon EC2 Trn1 实例 系列相比,Amazon EC2 Inf2 实例 系列的某些规格(由 AWS Inferentia 加速器提供支持)可能会使其在一些训练工作负载上不太适用。例如,尽管 Inf2 和 Trainium 都支持高带宽和低延迟的 NeuronLink-v2 设备间互连,但 Trainium 设备是以 2D 环形拓扑 连接的,而不是 环形拓扑,这可能会影响 Collective Communication 操作符的性能(更多细节请参见 这里)。然而,一些训练工作负载可能不需要 Trn1 架构的独特功能,并且在 Inf1 和 Inf2 架构上表现同样出色。

实际上,能够在 Trainium Inferentia 加速器上进行训练,将极大增加我们可用的训练实例种类,并提升我们根据每个深度学习项目的具体需求来调整训练实例选择的能力。在我们最近的文章 深度学习的实例选择 中,我们详细阐述了拥有多样化实例类型对于深度学习训练的价值。虽然 Trn1 系列仅包含两个实例类型,但启用 Inf2 训练将增加四种额外的实例类型。若将 Inf1 纳入其中,则会增加四种更多的实例类型。

我们在这篇文章中的目的是展示在 AWS Inferentia 上训练的机会。我们将定义一个玩具视觉模型,并比较在 Amazon EC2 Trn1 和 Amazon EC2 Inf2 实例系列上的训练性能。特别感谢Ohad KleinYitzhak Levi对本帖的贡献。

免责声明

  1. 请注意,截止到本文撰写时,有些深度学习模型架构尚不受 Neuron SDK 支持。例如,虽然 CNN 模型的推理是支持的,但训练 CNN 模型仍不支持。SDK 文档包括一个模型支持矩阵,详细列出了每种模型架构、训练框架(例如 TensorFlow 和 PyTorch)和 Neuron 架构版本的支持功能。

  2. 我们将描述的实验是在 Amazon EC2 上运行的,当时最新版本的Deep Learning AMI for Neuron为“Deep Learning AMI Neuron PyTorch 1.13(Ubuntu 20.04)20230720”,该版本包括 Neuron SDK 的 2.8 版本。鉴于 Neuron SDK 仍在积极开发中,可能会随着时间的推移,我们获得的比较结果会有所变化。强烈建议您使用最新版本的底层库重新评估本文的发现。

  3. 我们在这篇文章中的目的是展示 AWS Inferentia 计算实例的潜力。请不要将本文视为对这些实例或我们可能提及的其他产品的推荐。选择训练环境时有很多变量,这些变量可能会根据项目的具体情况而大相径庭。特别是,不同模型在两种不同实例类型上的相对价格性能结果可能完全不同。

玩具模型

类似于我们在之前的文章中描述的实验,我们定义了一个简单的视觉变换器(ViT)支持的分类模型(使用timm Python 包版本 0.9.5),以及一个随机生成的数据集。

from torch.utils.data import Dataset
import time, os
import torch
import torch_xla.core.xla_model as xm
import torch_xla.distributed.parallel_loader as pl
from timm.models.vision_transformer import VisionTransformer

# use random data
class FakeDataset(Dataset):
  def __len__(self):
    return 1000000

  def __getitem__(self, index):
    rand_image = torch.randn([3, 224, 224], dtype=torch.float32)
    label = torch.tensor(data=[index % 1000], dtype=torch.int64)
    return rand_image, label

def train(batch_size=16, num_workers=4):
  # Initialize XLA process group for torchrun
  import torch_xla.distributed.xla_backend
  torch.distributed.init_process_group('xla')

  # multi-processing: ensure each worker has same initial weights
  torch.manual_seed(0)
  dataset = FakeDataset()
  model = VisionTransformer()

  # load model to XLA device
  device = xm.xla_device()
  model = model.to(device)
  optimizer = torch.optim.Adam(model.parameters())
  data_loader = torch.utils.data.DataLoader(dataset,
                         batch_size=batch_size, num_workers=num_workers)
  data_loader = pl.MpDeviceLoader(data_loader, device)
  loss_function = torch.nn.CrossEntropyLoss()
  summ, tsumm = 0, 0
  count = 0

  for step, (inputs, target) in enumerate(data_loader, start=1):
    t0 = time.perf_counter()
    inputs = inputs.to(device)
    targets = torch.squeeze(target.to(device), -1)
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = loss_function(outputs, targets)
    loss.backward()
    xm.optimizer_step(optimizer)
    batch_time = time.perf_counter() - t0
    if step > 10:  # skip first steps
      summ += batch_time
      count += 1
    t0 = time.perf_counter()
    if step > 500:
      break
  print(f'average step time: {summ/count}')

if __name__ == '__main__':
  os.environ['XLA_USE_BF16'] = '1'
  # set the number of dataloader workers according to the number of vCPUs
  # e.g. 4 for trn1, 2 for inf2.xlarge, 8 for inf2.12xlarge and inf2.48xlarge
  train(num_workers=4)
# Initialization command:
# torchrun --nproc_per_node=2 train.py

结果

下表中我们比较了各种 Amazon EC2 Trn1 和 Amazon EC2 Inf2 实例类型的速度和价格性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ViT 基础的分类模型性能比较(作者)

虽然明显看到 Trainium 驱动的实例类型支持更好的绝对性能(即,提高的训练速度),但在 Inferentia 驱动的实例上训练的价格性能提高了约 39%(对于双核实例类型)并且对于更大的实例类型则更高。

再次提醒,不要仅根据这些结果做出设计决策。一些模型架构可能在 Trn1 实例上成功运行,但在 Inf2 上会出现故障。其他模型可能在两者上都能成功,但表现出的比较性能结果可能与这里展示的结果非常不同。

请注意,我们已省略了编译 DL 模型所需的时间。虽然这仅在第一次运行模型时需要,但编译时间可能非常长(例如,我们的玩具模型可能超过十分钟)。减少模型编译开销的两种方法是并行编译离线编译。重要的是,确保你的脚本不包含会触发频繁重新编译的操作(或图形更改)。有关更多细节,请参阅Neuron SDK 文档

总结

虽然被市场宣传为 AI 推理芯片,但看来 AWS Inferentia 还提供了另一个选项来进行训练深度学习模型。在我们关于 AWS Trainium 的上一篇文章中,我们强调了在将模型适配到新的 AI ASIC 时可能遇到的一些挑战。在 AWS Inferentia 支持的实例类型上训练相同模型的可能性,也许会提高你努力的潜在回报。

所有道路都通向罗马吗?

原文:towardsdatascience.com/do-all-the-roads-lead-to-rome-5b6756ce7d52

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Python、网络科学和地理空间数据量化古老的问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Milan Janosov

·发表于 Towards Data Science ·7 分钟阅读·2023 年 10 月 8 日

我最近遇到了一份令人兴奋的数据集,名为Roman Road Network (version 2008),这是哈佛 Dataverse 上的一份数据集:罗马帝国历史道路网络的完美 GIS 格式!此外,我还在进行一个关于公共交通网络的项目,研究如何识别网络科学中的热点和瓶颈。然后我迅速意识到,通过将这些信息结合起来,我可以迅速回答这个古老的问题,并查看罗马地区在当时到底有多么中心。

在本文中,所有图像均由作者创建。

1. 阅读和可视化数据

首先,让我们使用 GeoPandas 和 Matplotlib 快速加载和探索罗马道路网络数据。

import geopandas as gpd # version: 0.9.0
import matplotlib.pyplot as plt # version: 3.7.1

gdf = gpd.read_file('dataverse_files-2')
gdf = gdf.to_crs(4326)
print(len(gdf))
gdf.head(3)

本单元的输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Roman Road Network (version 2008) 数据集的预览。

现在可视化它:

f, ax = plt.subplots(1,1,figsize=(15,10))
gdf.plot(column = 'CERTAINTY', ax=ax)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Roman Road Network (version 2008) 数据集的可视化。

2. 将道路网络转换为图形对象

上一张图显示了道路网络是一堆线状多边形。然而,为了能够量化例如罗马的重要性,我计划进行一些图计算。这意味着我需要将这些线字符串转换为图形。

OSMNx 包正好适合这个需求——它处于地理空间数据工具与著名的图分析库 NetworkX 的交汇点。特别是,我跟随了 这个帖子 从原始数据集中导出了节点和边表。

# create an edge table
edges = gdf.copy()
edges['u'] = [str(g.coords[0][0]) + '_' + str(g.coords[0][1]) for g in edges.geometry.to_list()]
edges['v'] = [str(g.coords[-1][0]) + '_' + str(g.coords[-1][1]) for g in edges.geometry.to_list()]
edges_copy = edges.copy()
edges['key'] = range(len(edges))
edges = edges.set_index(['u', 'v', 'key'])
edges.head(3)

这个单元格的结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

边表的预览。

import pandas as pd # version: 1.4.2
from shapely.geometry import Point # version: 1.7.1

# create a node table
nodes = pd.DataFrame(edges_copy['u'].append(edges_copy['v']), columns = ['osmid'])
nodes['geometry'] = [Point(float(n.split('_')[0]), float(n.split('_')[1])) for n in nodes.osmid.to_list()]
nodes['x'] = [float(n.split('_')[0]) for n in nodes.osmid.to_list()]
nodes['y'] = [float(n.split('_')[1]) for n in nodes.osmid.to_list()]
nodes = gpd.GeoDataFrame(nodes)
nodes = nodes.set_index('osmid')
nodes.head(3)

这个单元格的结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

节点表的预览。

创建图形:

import osmnx as ox # version: 1.0.1

# Now build the graph
graph_attrs = {'crs': 'epsg:4326', 'simplified': True}
G = ox.graph_from_gdfs(nodes, edges[[ 'geometry']], graph_attrs)
print(type(G))
print(G.number_of_nodes()), print(G.number_of_edges())

在这里,我成功地将 GIS 数据文件转换为一个包含 5122 个节点和 7154 条边的网络对象。现在,我想查看一下。也可以使用 NetworkX 可视化网络。然而,我更倾向于使用开源软件 Gephi。它提供了更多的灵活性和更好的视觉微调选项。让我们将 G 转换为 Gephi 兼容的文件并导出——在这个版本中,我将处理一个无权、无向的图。

# Transform and export the graph
import networkx as nx # version: 2.5
G_clean = nx.Graph()
for u, v, data in G.edges(data=True):
    G_clean.add_edge(u, v)

G_clean2 = nx.Graph()
G_clean2.add_edges_from(G_clean.edges(data=True))

nx.write_gexf(G_clean2, 'roman_empire_network.gexf')

此外,我创建了一个名为 coordinates.csv 的数据表,在其中保存了道路网络中每个节点(交叉点)的坐标。

nodes2 = nodes[nodes.index.isin(set(G.nodes))].drop(columns = ['geometry'])
nodes2.index.name = 'Id'
nodes2.to_csv('coordinates.csv')

3. 在 Gephi 中可视化网络

在 Gephi 中可视化网络的具体操作值得单独讲解,因此在这里,我将展示结果。

在这个可视化中,每个节点对应一个交叉点,颜色编码了所谓的网络社区(密集互联的子图),而节点的大小则根据它们的中介中心性进行调整。中介中心性是一个网络中心性度量,量化了节点的桥接作用。因此,节点越大,它越中心。

在可视化中,还很有趣地观察地理如何驱动簇的形成,以及意大利如何意外地独立出来,可能是因为其内部道路网络更为密集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

罗马帝国的道路网络。每个节点对应一个标记的交叉点,节点颜色编码网络社区,节点大小与它们的中介中心性成正比。

4. 网络中心性

在欣赏完这些视觉效果后,让我们回到图本身并进行量化。在这里,我将计算每个节点的总度数,即它的连接数量,以及每个节点的未归一化的中介中心性,即计算经过每个节点的最短路径的总数。

node_degrees = dict(G_clean2.degree)
node_betweenness = dict(nx.betweenness_centrality(G_clean2, normalized = False))

现在,我有了每个交叉点的重要性评分。此外,在节点表中,我们还有它们的位置——现在是时候回答主要问题了。为此,我量化了每个节点在罗马行政边界内的相对重要性。为此,我需要罗马的行政边界,这在 OSMnx 中相对容易获取(注意:今天的罗马可能与过去的罗马有所不同,但大致上应该没问题)。

admin = ox.geocode_to_gdf('Rome, Italy')
admin.plot()

这个单元格的输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

罗马的行政边界。

此外,从视觉上看,罗马并不是作为道路网络中的一个单独节点存在;相反,许多节点在附近。因此,我们需要某种类型的分箱,空间索引,这帮助我们将所有属于罗马的道路网络节点和交叉口进行分组。此外,这种聚合也希望能够与帝国的其他区域进行比较。这就是为什么,我选择了 Uber 的H3 六边形分箱,而不是仅仅将节点映射到罗马的行政区域,并创建六边形网格。然后,将每个节点映射到包围它的六边形中,并根据封闭网络节点的中心性得分计算该六边形的汇总重要性。最后,我将讨论最中心的六边形如何与罗马重叠。

首先,以近似的方式获取罗马帝国的行政区域:

import alphashape # version:  1.1.0
from descartes import PolygonPatch

# take a random sample of the node points
sample = nodes.sample(1000)
sample.plot()

# create its concave hull
points = [(point.x, point.y) for point in sample.geometry]
alpha = 0.95 * alphashape.optimizealpha(points)
hull = alphashape.alphashape(points, alpha)
hull_pts = hull.exterior.coords.xy

fig, ax = plt.subplots()
ax.scatter(hull_pts[0], hull_pts[1], color='red')
ax.add_patch(PolygonPatch(hull, fill=False, color='green'))

该单元格的输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网络节点的子集和包围的凹形外壳。

让我们将帝国的多边形分割成六边形网格:

import h3 # version: 3.7.3
from shapely.geometry import Polygon # version: 1.7.1
import numpy as np # version: 1.22.4

def split_admin_boundary_to_hexagons(polygon, resolution):
    coords = list(polygon.exterior.coords)
    admin_geojson = {"type": "Polygon",  "coordinates": [coords]}
    hexagons = h3.polyfill(admin_geojson, resolution, geo_json_conformant=True)
    hexagon_geometries = {hex_id : Polygon(h3.h3_to_geo_boundary(hex_id, geo_json=True)) for hex_id in hexagons}
    return gpd.GeoDataFrame(hexagon_geometries.items(), columns = ['hex_id', 'geometry'])

roman_empire = split_admin_boundary_to_hexagons(hull, 3)
roman_empire.plot()

结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

罗马帝国的六边形网格。

现在,将道路网络节点映射到六边形中,并将中心性得分附加到每个六边形上。然后,我通过汇总每个六边形内节点的连接数和经过它们的最短路径数量来聚合每个节点的重要性:

gdf_merged = gpd.sjoin(roman_empire, nodes[['geometry']])
gdf_merged['degree'] = gdf_merged.index_right.map(node_degrees)
gdf_merged['betweenness'] = gdf_merged.index_right.map(node_betweenness)
gdf_merged = gdf_merged.groupby(by = 'hex_id')[['degree', 'betweenness']].sum()
gdf_merged.head(3)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

汇总六边形网格表的预览。

最终,将汇总的中心性得分与帝国的六边形地图结合起来:

roman_empire = roman_empire.merge(gdf_merged, left_on = 'hex_id', right_index = True, how = 'outer')
roman_empire = roman_empire.fillna(0)

然后进行可视化。在这个视觉中,我还添加了空白网格作为基础地图,并根据道路网络节点的总重要性为每个网格单元着色。这样,着色将突出显示最关键的单元格为绿色。此外,我还添加了白色的罗马多边形。首先,用度着色:

f, ax = plt.subplots(1,1,figsize=(15,15))

gpd.GeoDataFrame([hull], columns = ['geometry']).plot(ax=ax, color = 'grey', edgecolor = 'k', linewidth = 3, alpha = 0.1)
roman_empire.plot(column = 'degree', cmap = 'RdYlGn', ax = ax)
gdf.plot(ax=ax, color = 'k', linewidth = 0.5, alpha = 0.5)
admin.plot(ax=ax, color = 'w', linewidth = 3, edgecolor = 'w')
ax.axis('off')
plt.savefig('degree.png', dpi = 200)

结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

罗马帝国的六边形地图,每个六边形根据封闭道路网络节点的总度数着色。

现在,用介于度着色:

f, ax = plt.subplots(1,1,figsize=(15,15))

gpd.GeoDataFrame([hull], columns = ['geometry']).plot(ax=ax, color = 'grey', edgecolor = 'k', linewidth = 3, alpha = 0.1)
roman_empire.plot(column = 'betweenness', cmap = 'RdYlGn', ax = ax)
gdf.plot(ax=ax, color = 'k', linewidth = 0.5, alpha = 0.5)
admin.plot(ax=ax, color = 'w', linewidth = 3, edgecolor = 'w')
ax.axis('off')
plt.savefig('betweenness.png', dpi = 200, bbox_inches = 'tight')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

罗马帝国的六边形地图,每个六边形根据封闭道路网络节点的总最短路径(介于度)着色。

最终,我们得出一个令人安心的结论。如果根据累计度数着色六边形单元格,罗马的区域遥遥领先。如果根据介于度着色六边形,图像类似——罗马再次占据主导地位。这里的一个附加点是,连接罗马与中东的高速公路也作为一个关键的高介于度段显现出来。

tl;dr 网络科学也表明所有道路都通向罗马!

离散化的量子比特在实际中有效吗?

原文:towardsdatascience.com/do-discretized-qubits-work-in-practice-60acf19fc68c

如果量子比特不仅仅是 0 或 1 会怎么样?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Frank Zickert | 量子机器学习

·发布于 Towards Data Science ·阅读时间 9 分钟·2023 年 1 月 18 日

想开始学习量子机器学习吗?请查看 Hands-On Quantum Machine Learning With Python.

机器学习模型变得越来越复杂,因此训练也越来越困难。以 ChatGPT 为例。在单个 GPU 上训练它将需要 355 年。

量子计算是一项有前景的技术,可能加速这种模型的训练。然而,它也带来了自身的一系列挑战。

量子比特(qubits)是我们在量子计算机中使用的基本单元。与经典比特只能是 0 或 1 不同,量子比特处于它们的两个基态|0⟩和|1⟩之间的复杂线性关系中,这种状态称为叠加态。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图片

这使得它们极为强大。首先,这种关系不是离散的,而是连续的,这意味着量子比特可以取两个基态之间的任何值。其次,这种关系建立在复数之上——这些是二维结构——超出了我们习惯使用的一维数字的能力。

但问题来了。不幸的是,总是会有问题。

一旦我们测量一个量子比特,它会坍缩到它的基态之一。不可避免地,我们看到的只有 0 或 1。

此外,我们只有非常少量的量子比特。而且,我们拥有的量子比特容易出错。我们称它们为有噪声的。

显然,我们——算法开发者——无法增加可用的量子比特数量。这一挑战仍然留给了像 IBM 这样的硬件制造商。然而,我们可以尽可能高效地利用我们现有的少量量子比特。

为此,我提出了离散化期望值的方法,从而通过一个量子比特编码多个值。

## 如何最大限度地利用你的量子位

量子位中不仅仅是 0 和 1

towardsdatascience.com

期望值源于对电路的重复执行和量子位的测量。因此,例如,如果一个量子位在十次执行中只有三次是 1,它的期望值是0.3

以下函数接受在 Qiskit 中运行量子电路得到的counts对象——IBM 的量子开发工具包。blocks参数表示我们希望使用的离散值的数量。

def discretize(counts, blocks):
    weigthed = 0
    sum_count = 0
    print (counts)
    for key, value in counts.items():
        weigthed += int(key)*int(value*0.999*blocks)
        sum_count += value
    return int(weigthed/sum_count)

这个函数遍历counts对象中的所有项。这些项是键值对,例如0: 300,其中键(0)表示测量值,而值(300)表示我们观察到的次数。

我们将每个键与其值相乘,然后乘以块数,并稍微减少该值(乘以0.999)。这意味着我们将位于两个离散值之间的确切线上的值视为较低的值。

总体离散值是所有加权值的总和除以所有未加权值的总和。

这只是一个简短的函数。然而,让我们看看它在实践中的表现。

首先,我们编写一个辅助函数,为我们创建counts对象。

from math import asin, sqrt
from qiskit import QuantumCircuit, execute, Aer
from qiskit.visualization import plot_histogram

def prob_to_angle(prob):
    return 2*asin(sqrt(prob))

def simulate_step(blocks, steps, current):
    qc = QuantumCircuit(1)

    qc.ry(prob_to_angle((current+0.5)/steps), 0)

    # Tell Qiskit how to simulate our circuit
    backend = Aer.get_backend('statevector_simulator') 

    # Do the simulation, returning the result
    result = execute(qc,backend, shots=1000).result()

    # get the probability distribution
    counts = result.get_counts()

    return discretize(counts, blocks)

这个函数接受blocksstepscurrent步骤作为参数。我会稍后解释这些。首先,让我们深入了解函数的定义。我们创建一个包含一个量子位的量子电路,并应用围绕 Y 轴的旋转。

这篇文章详细解释了这个算子。

[## 如何精确控制你在 Qiskit 中的量子位

实际中的 RY 算子

这篇文章

本质上,ry门将默认状态|0⟩旋转到一个表示测量量子位为 1 的某个概率的状态。在这里,这个概率是(current+0.5)/steps

然后,我们定义一个backendexecute量子电路,以获得result,它提供counts,我们对其进行discretize

当我们使用这个辅助函数时,它的行为变得明显。

import collections

blocks = 4
steps = 20
values = [simulate_step(blocks, steps, x) for x in range(steps)]
print (values)

sorted(collections.Counter(values).items(), key=lambda x: x[0])

这段代码的输出如下:

{'0': 0.975, '1': 0.025}
{'0': 0.925, '1': 0.075}
{'0': 0.875, '1': 0.125}
{'0': 0.825, '1': 0.175}
{'0': 0.775, '1': 0.225}
{'0': 0.725, '1': 0.275}
{'0': 0.675, '1': 0.325}
{'0': 0.625, '1': 0.375}
{'0': 0.575, '1': 0.425}
{'0': 0.525, '1': 0.475}
{'0': 0.475, '1': 0.525}
{'0': 0.425, '1': 0.575}
{'0': 0.375, '1': 0.625}
{'0': 0.325, '1': 0.675}
{'0': 0.275, '1': 0.725}
{'0': 0.225, '1': 0.775}
{'0': 0.175, '1': 0.825}
{'0': 0.125, '1': 0.875}
{'0': 0.075, '1': 0.925}
{'0': 0.025, '1': 0.975}
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3]
[(0, 5), (1, 5), (2, 5), (3, 5)]

那么,这里发生了什么?

首先,我们定义将我们量子位的期望值离散化为四个blocks。我们希望用 20 个steps来评估这个函数。因此,我们为每个当前步骤(x)运行电路。

输出的前几行表示countsdiscretize函数内部打印的结果。如你所见,当测量量子比特为 1 的概率增加时,测量其为 0 的概率则减少。该概率表示当前步骤与总步骤数之间的比率。这就是我们通过prob_to_angle((current+0.5)/steps)计算的内容。

下一行展示了离散化值的列表。如我们所见,前五个项对应值为0,接下来的五个项对应值为1,以此类推。

最后一项输出显示了离散化值的分布。每个值从03出现五次。由于我们有 20 个步骤,这表明离散化工作正常。

但这仍然不是一个真正的测试。我们使用了准备完美量子态及其确切countsstatevector_simulator

但,测量量子态只能通过经验获得。在现实中我们不能计算更大量子电路的完美量子态。如果可以的话,就没有理由去构建和使用量子计算机了。

所以,接下来的辅助函数使用qasm_simulator来经验性地创建counts

def simulate_step_qasm(blocks, steps, current):
    qc = QuantumCircuit(1)

    qc.ry(prob_to_angle((current+0.5)/steps), 0)
    qc.measure_all()

    # Tell Qiskit how to simulate our circuit
    backend = Aer.get_backend('qasm_simulator') 

    # Do the simulation, returning the result
    result = execute(qc,backend, shots=1000).result()

    # get the probability distribution
    counts = result.get_counts()

    return discretize(counts, blocks)

仅有两个区别。首先,我们使用了qasm_simulator而不是statevector_simulator。其次,我们在应用ry门后测量量子比特。

让我们看看结果。

values = [simulate_step_qasm(blocks, steps, x) for x in range(steps)]
print (values)
sorted(collections.Counter(values).items(), key=lambda x: x[0])
{'1': 29, '0': 971}
{'1': 74, '0': 926}
{'1': 130, '0': 870}
{'1': 161, '0': 839}
{'1': 218, '0': 782}
{'0': 742, '1': 258}
{'1': 325, '0': 675}
{'0': 621, '1': 379}
{'0': 599, '1': 401}
{'0': 506, '1': 494}
{'1': 516, '0': 484}
{'0': 447, '1': 553}
{'0': 390, '1': 610}
{'0': 318, '1': 682}
{'0': 284, '1': 716}
{'0': 225, '1': 775}
{'0': 169, '1': 831}
{'1': 871, '0': 129}
{'0': 67, '1': 933}
{'0': 20, '1': 980}
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3]
[(0, 5), (1, 5), (2, 5), (3, 5)]

我们看到counts的分布不再那么完美。然而,离散化仍然有效。我们看到每个离散值恰好出现了五次。

但我们还没有完全到达目标。尽管我们在结果中看到统计偏差,qasm_simulator模拟的是无噪声的量子计算机。然而我们目前还没有这样的设备。

相反,我们当前的设备容易出错。它们是有噪声的。我们来增加一些噪声。

from qiskit import transpile
from qiskit.providers.fake_provider import FakeQuito
from qiskit.providers.aer import AerSimulator

# create a fake backend
device_backend = FakeQuito()

# create a simulator from the backend
sim_quito = AerSimulator.from_backend(device_backend)

def simulate_step_noise(blocks, steps, current):
    qc = QuantumCircuit(1)

    qc.ry(prob_to_angle((current+0.5)/steps), 0)
    qc.measure_all()

    # Tell Qiskit how to simulate our circuit
    # transpile the circuit
    mapped_circuit = transpile(qc, backend=sim_quito)

    # run the transpiled circuit, no need to assemble it
    result = sim_quito.run(mapped_circuit, shots=1000).result()

    # get the probability distribution
    counts = result.get_counts()

    return discretize(counts, blocks)

虽然电路保持不变,这次我们创建了另一个后端。我们创建了一个模拟器,其噪声特性与 IBM 在基多的量子计算机相同。这是一台通过 IBM 云提供的五量子比特量子计算机。

values = [simulate_step_noise(blocks, steps, x) for x in range(steps)]
print (values)
sorted(collections.Counter(values).items(), key=lambda x: x[0])
{'1': 55, '0': 945}
{'1': 83, '0': 917}
{'0': 855, '1': 145}
{'1': 177, '0': 823}
{'1': 230, '0': 770}
{'0': 700, '1': 300}
{'1': 319, '0': 681}
{'0': 619, '1': 381}
{'0': 617, '1': 383}
{'0': 539, '1': 461}
{'0': 496, '1': 504}
{'1': 541, '0': 459}
{'0': 407, '1': 593}
{'1': 623, '0': 377}
{'1': 663, '0': 337}
{'0': 284, '1': 716}
{'0': 219, '1': 781}
{'0': 178, '1': 822}
{'1': 862, '0': 138}
{'0': 80, '1': 920}
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
[(0, 5), (1, 5), (2, 6), (3, 4)]

我们看到counts的偏差甚至更大。因此,我们看到一个错误的离散化。在{0: 284, 1: 716}的情况下,我们将一个3误认为是2。如果我们仅将counts解释为 0 或 1,我们肯定不会看到错误。相关项显然更接近 1 而不是 0。

否则,结果并不太糟。事实上,我们来看一下如果我们将期望值解释为八个值而不是四个值会发生什么。

steps = 24
blocks = 8
values = [simulate_step_noise(blocks, steps, x) for x in range(steps)]
print (values)
sorted(collections.Counter(values).items(), key=lambda x: x[0])
{'1': 45, '0': 955}
{'1': 71, '0': 929}
{'1': 132, '0': 868}
{'1': 134, '0': 866}
{'0': 811, '1': 189}
{'1': 202, '0': 798}
{'1': 266, '0': 734}
{'0': 700, '1': 300}
{'1': 325, '0': 675}
{'1': 368, '0': 632}
{'1': 413, '0': 587}
{'1': 440, '0': 560}
{'1': 486, '0': 514}
{'1': 530, '0': 470}
{'1': 580, '0': 420}
{'1': 623, '0': 377}
{'0': 354, '1': 646}
{'0': 301, '1': 699}
{'0': 275, '1': 725}
{'0': 226, '1': 774}
{'0': 213, '1': 787}
{'1': 825, '0': 175}
{'0': 121, '1': 879}
{'0': 97, '1': 903}
[0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7]
[(0, 2), (1, 4), (2, 4), (3, 3), (4, 3), (5, 3), (6, 3), (7, 2)]

我们预计每个离散值会出现三次。因此,尽管结果并非完全不合理,但我们在这里看到相当多的错误。

所以,离散化的期望值增加了对噪声的敏感性。但错误缓解技术,如零噪声外推技术(ZNE)或 Clifford 数据回归(CDR),帮助我们减轻噪声的负面影响。

## 如何用 Qiskit 和 Mitiq 实施量子误差缓解

了解如何实施 Clifford 数据回归

towardsdatascience.com

结论

尽管我们不能轻易增加可用的量子比特数量,但能够编码四个甚至八个值而不是仅两个值的能力可能在使用当前量子计算机时成为决定性因素。虽然我们无法轻易增加可用的量子比特数量,但我们可以使用适当的技术,如离散化。结合误差缓解技术,我们可能会使我们少量的量子比特显得更大。

[## 使用我的推荐链接加入 Medium - Frank Zickert | 量子机器学习

开始学习量子机器学习(并获得对 Medium 上所有故事的完全访问权限)。获取完整访问权限…

pyqml.medium.com](https://pyqml.medium.com/membership?source=post_page-----60acf19fc68c--------------------------------)

不要错过下一集,订阅我的Substack 频道

想开始学习量子机器学习吗?可以看看动手实践量子机器学习与 Python

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

免费获取前三章这里

更多的比赛是否意味着更多的胜利?

原文:towardsdatascience.com/do-more-games-mean-more-wins-1589568a563b?source=collection_archive---------7-----------------------#2023-12-28

快速成功数据科学

在大学橄榄球中,确实如此。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Lee Vaughan

·

关注 发表在Towards Data Science ·6 分钟阅读·2023 年 12 月 28 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由Dave Adamson拍摄,Unsplash

我有一个让人讨厌的朋友,他喜欢在每次机会中吹嘘他母校的橄榄球队。在过去几年里,他的球队赢得了比以往更多的常规赛胜利(比一个胜场)。现在他在炫耀这些,回顾(远远)过去母校的辉煌岁月。唯一的问题是,回到 1950 年代和 60 年代,球队只打10场常规赛。

1970 年代,这个数字增加到了 11。

2006 年,这个数字增加到了 12。

1960 年赢得 10 场比赛中的 9 场,胜率为 90%。今天赢得 12 场中的 10 场,仅为 83%。所以,赢得 10 场比赛并不像听起来那么出色。

但事情不仅仅如此。如果额外的比赛平均来说是更容易赢的,那打破旧记录就会显得不那么令人印象深刻。

数据科学的好处在于你不必为你能证明的事情争论。一个好的数据科学家应该能够进行分析性思考,并将数据用于其目的。

在这篇文章中,我们将评估增加比赛数量对赢得比赛的影响。这将涉及普遍的数据科学实践,如制定前提、设计分析、选择适当的数据和呈现结果。

具体来说,我们将评估将常规赛从 11 场比赛增加到 12 场比赛的影响。为了平滑教练更换和不断演变的规则和规定的影响,我们将使用我朋友的母校和其他五所具有类似足球传统的学校,覆盖 34 个赛季,包括 2006 年。

思维实验

在开始之前,我们做一个思维实验。如果一所大学橄榄球强校(如阿拉巴马、俄亥俄州立大学或俄克拉荷马)在他们的赛程中增加一支随机球队,他们可能会赢得这场额外的比赛,因为他们比大多数其他球队更强

同样,底层球队由于足球传统较少,预计会输掉这场额外的比赛,因为大多数其他球队比他们更好

理论上,中等水平的球队——假设他们是从全部名单中随机抽取——赢得这场额外比赛的概率大约是 50%。这将导致他们的记录平均提升0.5场比赛,所有条件相等。因此,将会有些年份,他们有机会赢得比历史上多一场比赛

设计方法

正如罗纳德·里根所说,“信任但需验证。”对于中等水平的球队,现实是否符合这一理论?他们平均每年会多赢得 0.5 场比赛吗?

为了验证这一点,我查看了六支经常在强大的东南会议(SEC)中排名下半部分的球队:

  • 奥尔·密斯

  • 密苏里

  • 南卡罗来纳

  • 肯塔基

  • 密西西比州立

  • 范德比尔特

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按历史胜率排名的 SEC 球队(Wikipedia

这些球队的Congrove 计算机排名将他们排在 2005 年 FBS 球队的中下游,即 12 场常规赛开始的前一年。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2005 年对六支球队的排名(由作者和collegefootballpoll.com提供)

注意: 足球碗分区,前身为第一分区 A,是美国最高级别的大学足球赛事。FBS 包括了最大的全国大学体育协会(NCAA)学校。截至 2023 年,FBS 中共有 133 支球队。

分析涵盖了 1989–2023 年,但省略了因疫情缩短的 2020 赛季。这使得 2006 年 12 场比赛季开始前后的 17 个赛季都被考虑在内。

胜负记录来源于维基百科。被取消的胜场被计为胜场。

密西西比州立大学、奥尔·密西西比大学、肯塔基大学和范德比尔特大学在 1992 年之前每个赛季打七场联盟比赛,从那时起改为八场。

南卡罗来纳州在 1992 年加入 SEC,开始每年打八场联盟比赛。在此之前,他们与主要的联盟球队(SEC、ACC、Big East)和一些强大的独立球队(包括佛罗里达州立大学和西弗吉尼亚大学)进行混合比赛。

密苏里州在 2012 年加入 SEC,开始每年打八场联盟比赛。在此之前,他们参加了类似的大 12 联盟,每年打七场联盟比赛,1996 赛季后改为八场。

结果

下图展示了分析结果,以散点图和线性趋势线呈现。所有趋势线都有正斜率,其中密苏里州进步最大,奥尔·密西西比进步最小。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1989–2023 年每队常规赛胜场的散点图(作者提供)

在 12 场常规赛开始前的 17 年中,这六支球队平均赢得 4.63 场比赛。在随后的 17 年中,他们平均赢得 6.37 场比赛。因此,平均而言,他们在每赛季打 12 场比赛时多赢了 1.74 场比赛。

这里有另一个视图,使用堆叠面积图。颜色区域“越厚”,胜场数越高。2006 年后,“平台高度”有显著变化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

针对分析的六支球队的堆叠面积图(作者提供)

含义

对于分析中的所有球队,额外的比赛都是非联盟比赛。这意味着学校对日程安排有更多控制权。

如果他们选择了同等强度的对手,我们会预期未来会有平衡,导致每赛季0.5 场的胜场改进,因为每支球队的胜率大约为 50%。事实上,他们每赛季平均多赢得1.74 场比赛,表明他们利用这一额外的比赛来填充日程安排,增加与实力较弱的对手的比赛。

这一点在 2022 赛季中显而易见,如下图所示。尽管有少数强劲的非联盟对手(例如南卡罗来纳州与克莱姆森有传统的对抗),但六支 SEC 球队的平均排名几乎是对手的两倍。其中一些对手甚至来自较低级别的足球冠军分区(FCS)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2022 年 6 支 SEC 球队与其非会议对手的排名(由作者使用collegefootballpoll.com制作)

**注:**为了将前图中的 FBS 和 FCS 排名合并,我将 FCS 排名加到 2022 年 FBS 的总球队数(131)中。因此,FCS 排名 5 变成了 136(5 + 131)。

当然,实现每个常规赛季平均提升 1.7 场比赛不仅仅是增加一场简单的比赛。过去二十年中,这六支球队努力提升了他们的项目,基于collegefootballpoll.com的计算机排名,这一点得到了体现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2002 年、2012 年和 2022 年六支球队的赛季末排名(由作者和collegefootballpoll.com制作)

一般更高的排名是否与自 2006 年以来每年增加的一场可赢得的比赛有关?也许有关。但他们每赛季平均多赢场比赛的事实表明,还有其他因素在起作用,例如设施、教练、招募等方面的改进。

结论

这是一个小项目,但它突出了数据科学将逻辑思维、硬数据和结构化分析结合在一起,以解决一个论点的能力。

毕竟,仅仅因为你的中等水平大学橄榄球队偶尔有了 9 或 10 场胜利的赛季,而过去从未超过 7 或 8 场胜利,这可能并不那么令人印象深刻。现在的球队比过去打更多的比赛,其中至少一场可能是故意安排的弱队。

谢谢!

感谢阅读,未来请关注我更多的快速成功数据科学项目。

不要仅仅使用 ChatGPT 来“总结文本”

原文:towardsdatascience.com/do-not-use-chatgpt-only-to-summarize-text-bd2001db8ce7

放开猛狮。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Soner Yıldırım

·发布于 Towards Data Science ·7 分钟阅读·2023 年 5 月 30 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Mika Brandt 提供,来源于 Unsplash

总结文本是 ChatGPT 非常擅长的众多任务之一。给它一段内容并要求一个摘要。你会惊讶于它如何快速响应一个写得很好的摘要。

但不要只是要求一个摘要。

通过自定义提示,你可以让 ChatGPT 创建比普通摘要更多的内容。

注意:我将使用 OpenAI API 的 Python 库。你也可以在网页 接口 中使用相同的提示。

第一步是导入库并创建 API 密钥:

import openai
import os
from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())
openai.api_key  = os.getenv('OPENAI_API_KEY')

OPENAI_API_KEY 是保存 API 密钥的环境变量,可以从 API Keys 菜单中获得。

下一步是创建一个辅助函数来运行提示。

def get_completion(prompt, model="gpt-3.5-turbo"):
    messages = [{"role": "user", "content": prompt}]
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=0, 
    )
    return response.choices[0].message["content"]

给定一个提示,这个功能将返回指定模型的输出,默认是 gpt-3.5-turbo。

只是一个摘要

以下是我想要总结的产品评论。

review = """
I ordered this device (N300) to better extend WiFi signal to a Firestick on my small kitchen flat screen. 
Device phone set-up was a bit convoluted, in the steps were keyed to some other phone, non an iPhone, 
but close enough to figure it out. Once set up, the device definitely improved the push to the Firestick. 
However, began to get curious about monitoring the Macard device while in operation. On the Firestick end, 
one simply goes into setting/network, then WiFi ... push the (>II ) button to check connectivity. 
Currently returns both signal and video quality 'very good.' So that's what the Firestick sees.

In the iPhone App Store, there is a one-star ""macard Wifi Wizard,"" which is supposed to be useful to 
check the extender, download firmware updates, and so on. I downloaded it. It requests the email 
one registered the device with, then advises it is sending an activation code. The code never arrives. 
I contacted their support via chat; the rep acknowledged what I was saying and said he'd have 
""the concerned department"" send me a code within 24 hours. It is now 48 hours. Nothing. 
Thankfully, the device works.
"""

我们可以从仅要求“创建一个产品评论摘要”开始。

prompt = f"""
Your task is to summarize a given product review.
The review is given below, delimited by triple quotes.

Review: ```{review}```py
"""

response = get_completion(prompt)

回应: 评审者购买了 N300 设备,以将 WiFi 信号扩展到他们厨房里的 Firestick。设备设置有点混乱,但设置好后,提升了对 Firestick 的推送。评审者尝试使用“macard Wifi Wizard”应用程序来监控设备,但激活码从未到达。他们联系了支持,但没有收到回应。尽管如此,设备工作良好。

限制长度

它确实足够好,但可以变得更好,符合我们的特定需求。例如,我们可以限制摘要的长度。

prompt = f"""
Your task is to summarize a given product review.
Use at most 30 words.
The review is given below, delimited by triple quotes.

Review: ```{review}```py
"""

response = get_completion(prompt)

我在提示中添加了“最多使用 30 个字。”

回应: N300 WiFi 扩展器改善了 Firestick 的信号,但手机设置令人困惑。Macard 应用程序无效,但设备本身运行良好。

它能够从评论中提取更重要的点,并将输出限制为 30 个字。

仅限投诉

考虑一种情况,产品经理只搜索关于产品的负面评论和投诉,这将用于制定改进产品的路线图。

这可以很容易地在提示中完成。

prompt = f"""
Your task is to summarize a given product review.
Include only negative comments, concerns, and complaints in the summary.
The review is given below, delimited by triple quotes.

Review: ```{review}```py
"""

response = get_completion(prompt)

回应: 设备的手机设置过程复杂,iPhone 监控设备的应用程序无效。应用程序的激活码从未收到,客户支持未能履行承诺,在 24 小时内发送代码。

摘要仅包含用户遇到的问题。例如,与之前的摘要不同,它不包括“设备本身运行良好”的部分。

来自多条评论的具体点

我们正在分析设备设置,并希望了解用户在设置设备时是否遇到问题。我们可以从多条评论中提取与设备设置相关的部分,如下所示:

prompt = f"""
Your task is to summarize multiple product reviews.
Include only the parts related to device setup.
The reviews are given below.

review_1: {review_1}
review_2: {review_2}
review_3: {review_3}
review_4: {review_4}

"""

response = get_completion(prompt)

回应: 所有四条评论中都提到了设备设置过程。评论 1 表示与客户服务沟通后,设置过程非常顺利。评论 2 提到设备手机设置有些复杂,但一旦设置完成,设备运行良好。评论 3 在 PC 上设置设备遇到困难,但使用 Kindle 和 Silk 浏览器成功设置。评论 4 发现设置过程非常简单,并能够将设备与他们的互联网电话一起用于工作。

通过正确自定义提示,我们能够获取与设备设置相关的部分内容。

如果你想阅读这些评论或自己测试,这里是上述使用的评论:

review_1 = """
If anyone is curious - the reset is a light sequence of all 3 lights \
The scanning is 1 solid blue and 1 flashing blue \
And the successful connection is 2 solid blue lights. \
I had difficulty with my original setup but spoke with the customer service line \
and was able to walk through the process very seamlessly. The company is great at \
making sure you are enjoying the product. So far it does seem to be helping, \
the TV in my bedroom hasn't buffered since setting up.
"""

review_2 = """
I ordered this device (N300) to better extend WiFi signal to a Firestick on my small kitchen flat screen. \ 
Device phone set-up was a bit convoluted, in the steps were keyed to some other phone, non an iPhone, \
but close enough to figure it out. Once set up, the device definitely improved the push to the Firestick. \ 
However, began to get curious about monitoring the Macard device while in operation. On the Firestick end, \
one simply goes into setting/network, then WiFi ... push the (>II ) button to check connectivity. \
Currently returns both signal and video quality 'very good.' So that's what the Firestick sees. \
In the iPhone App Store, there is a one-star ""macard Wifi Wizard,"" which is supposed to be useful to \
check the extender, download firmware updates, and so on. I downloaded it. It requests the email \
one registered the device with, then advises it is sending an activation code. The code never arrives. \
I contacted their support via chat; the rep acknowledged what I was saying and said he'd have \
""the concerned department"" send me a code within 24 hours. It is now 48 hours. Nothing. \
Thankfully, the device works.
"""

review_3 = """
I first tried to set this up using the procedure for PC. It got part way through it and froze. \
After that I could not get it to even try. Running Microsoft 10 and Google Chrome. Don't know if that \ 
has anything to do with it or not. Then I used my Kindle with the Silk browser, and it set up perfectly \ 
first time. I then moved it half way across the house, plugged it in, turned it on, and I had strong Wi-Fi \ 
all the way to the corner of the house. 1600 sq. ft., not at all "open concept".
"""

review_4 = """
I got this product to plug my internet based phone for work from home (Avaya desktop phone). \
It works! The setup was EXTREMELY easy. At completion, I plugged the phone into the extender's ethernet \
port and made a few phone calls which all worked perfectly with complete clarity. VERY happy with this \
purchase since a cordless headset is around $250 (which I would have needed since the phone had to be \
at the ethernet port on the wall). I recommend this product!
"""

优先级列表

假设我们有数百条评论,并且需要创建一个关于改进产品的问题的优先级列表。该列表将基于客户对我们产品的不满或问题。

ChatGPT 可以为我们创建这个列表。

prompt = f"""
Your task is to summarize multiple product reviews.
I want to improve the product. 
Create a priority list of 3 issues mentioned in the reviews.
The reviews are given below.

review_1: {review_1}
review_2: {review_2}
review_3: {review_3}
review_4: {review_4}

"""

response = get_completion(prompt)

回应:

评论中提到的 3 个问题的优先级列表:

设置设备时遇到的困难以及对更清晰说明的需求。

Macard Wifi Wizard 应用程序存在问题,并且需要更好的客户支持。

需要更清晰的说明关于设备与不同类型手机的兼容性。

即使我们仅对 4 条评论进行分析,它已经非常有用。想象一下你有数百条评论需要分析以创建这样的优先级列表。ChatGPT 节省的时间和精力真是不可思议。

释放狮子

ChatGPT 的全部潜力超出了我们初次观察的范围。发现利用其真正能力和效率的关键在于精心设计和定制提示。

通过战略性地构建提示,我们可以引导模型生成更准确、更具上下文相关性和更详细的回答。

你可以成为 Medium 会员 ,以解锁对我所有写作内容的完全访问权限,还有 Medium 上的其他内容。如果你已经是会员,不要忘记 订阅 ,这样每当我发布新文章时,你会收到邮件通知。

感谢阅读。如果你有任何反馈,请告诉我。

通过这 5 个简单的方法,让你的数据科学家简历脱颖而出

原文:towardsdatascience.com/do-these-5-simple-things-to-make-your-data-scientist-resume-stand-out-from-the-crowd-eaea92cdab13?source=collection_archive---------0-----------------------#2023-11-19

这 5 个方法会让招聘人员对你的简历多看一眼

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 麦迪逊·亨特

·

关注 发布于 Towards Data Science ·13 分钟阅读·2023 年 11 月 19 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Clay Banks on Unsplash

数据科学领域目前竞争激烈,不言而喻。

不过,如果你以为这个领域是过度饱和的合格候选人,那就错了。你可能会看到数百名申请者竞争一个职位,但真正能够根据数据分析的需求分析数据的人却少之又少。

几年前,我参与了公司在那个时候招聘的一个职位的简历筛选工作。我在 LinkedIn 上发布了这个职位,并附上了几个标准问题。在最初的几天里,我们收到了大约 20 到 30 份申请,但在审核了这些申请以及筛选了 LinkedIn 根据标准问题答案而过滤掉的申请后,只有两个是可行的候选人。把这种经验推广到那些数据科学职位的情况,申请了 300 名候选人,你可以想象其中有多少是真正的竞争者。

在技术领域,自称设计师、软件工程师或数据科学家都很容易。在许多情况下,这些职位名称的含义随着时间的推移而发生了变化。例如,“数据科学家”曾经指的是公司中具有硕士或博士学历的高级人员。相比之下,现在,任何完成数据科学训练营的人都可能认为自己有资格称为数据科学家,因为他们懂得统计学并能使用一些数据分析库。

所有这些意味着,面对招聘人员必须浏览以找到合格的候选人,你需要提升你的简历游戏,脱颖而出。以下建议并不具有突破性或革命性,而且你可能之前已经听说过它们。不过,一旦你开始注意这些细节,招聘者也会更加重视你。以下是五件事情,可以让招聘者看你的简历两次。

招聘者在数据科学家的简历中寻找什么?

  • 课外学习和成长: 在大学学习数据科学或参加编程训练营固然不错,但这些环境过于封闭,只能让你的技能达到一定水平。虽然许多雇主在招聘新毕业生时心知肚明,但展示你超越课堂所学的能力可能会给他们留下深刻印象。数据科学领域的工作独特之处在于,你的整个职业生涯都将被迫不断学习新技术,应用新技能,总是要随时应对挑战(例如,学会如何与 ChatGPT 和其他高级 AI 模型合作,而不是畏惧它们)。因此,你最好从早期就适应终身学习的节奏,并通过展示你如何已经将知识深化到核心基础之外来打动招聘者。

  • 既能领导又能作为团队一部分合作的能力: 由于数据科学领域在过去五年左右的普及,目前似乎有大量初级数据科学家。加上最近几年退休的婴儿潮数据科学家的大规模离职,公司在填补各级别职位(尤其是更高级的“团队领导”或管理职位)方面面临挑战。不仅如此,他们还必须雇佣从未在团队中工作过的初级数据科学家,因为他们在疫情高峰期间完成教育时是孤立的。团队合作和领导能力是无法真正教授的事情(无论他们如何声称在那些你被迫上的组织行为课程中),但你可以通过实践来提高这些技能。这可能只是参加黑客马拉松并与从未见过的人一起工作,或者在你加入的俱乐部或志愿组织中担任领导角色。无论哪种方式,你都可以向雇主证明你可以作为团队的一部分工作,甚至在需要时领导团队。

  • 领域专业知识: 虽然任何人都可以分析数据,但只有少数人能够得出有意义的结论,帮助公司在前进过程中做出重要决策。这就是为什么在从其他职业(如工程师、教师、护士、科学家等)转向数据科学职业时,保持在该领域并利用你获得的领域知识帮助寻找解决该领域问题的公司是有用的。然而,领域知识也可以通过免费审计大学课程、阅读书籍或参加社交活动来获得。无论你选择哪种方式获取领域专业知识,请确保在各种不同情景下能够讨论它,并创建一个展示你在该领域解决问题的个人项目组合(见下文)。

  • 量化影响: 正如 Ken Jee 在他的文章避免的数据科学简历错误中所述,作为一名数据科学家,你的价值与你对公司产生积极影响的能力密切相关。换句话说,招聘人员想要知道你解决了哪些问题,以及项目的结果如何。在你找到一种更有效地向客户营销的方式后,公司是否优化了其流程并增加了收益?你是否发现了附近煤矿与野生动物的关系,这可能引发关于污染管理的讨论?无论你取得了什么成就,招聘人员都想了解你如何通过完成的项目产生影响的细节。

  • 教育和经验: 不管你是数据科学家的背景或年限如何,招聘人员仍然想知道你在该领域的教育和经验。教育和经验的健康结合是受欢迎的。但是如果你是新毕业生,不要担心 —— 你可能在意想不到的地方积累了经验,比如毕业设计项目、实习或参加的俱乐部。此外,你甚至可能通过开发作品集、开始博客或通讯、或者做义务工作来积累自己的经验。无论你的教育和经验如何,两者都应该在你的简历上占据重要位置。

  • 合适的要求: 你可能是一个出色的 R 程序员,可以像最好的人一样进行企鹅种群数量的时间序列分析,但如果你不能用 Python 编写代码,理解商业领域中的业务问题,或者根据全面的领域知识向客户提供建议,那么你可能不适合某些公司。招聘人员正在寻找具备“合适要求”的个人 —— 在正确的技术和公司领域知识方面具备能力的人。这意味着你需要量身定制你获取的技术、技能和领域知识,以适应某一类型的公司。例如,如果你去一个以科学为基础的公司,你可能需要精通 R;然而,如果你去几乎任何其他类型的公司,Python 将成为标准。幸运的是,几乎每个行业都需要数据科学家,这意味着你可以选择一个你真正感兴趣的专业方向!

  • 专业作品集: “展示,而非说”。正如我之前提到的(/5-things-in-your-resume-that-are-keeping-you-from-getting-your-first-job-in-data-science-1a4d474ff3e4),你可以整天告诉雇主你具备这份工作所需的技能,并且已经应用这些技能解决了现实世界的问题。这很好,但他们怎么相信你?我是说,无论如何,他们最终会在技术面试中找出真相,但如果他们选择面试另一位可以明确展示自己具备技能和影响力的候选人,你可能连面试的机会都不会得到。一个个人作品集包含在你目标行业解决问题的项目,将帮助你说服招聘人员你拥有他们寻找的技能。

让你的数据科学家简历脱颖而出的五个方法

1. 量身定制,量身定制,再量身定制

因为某种原因,我仍然惊讶于如此少的人理解为什么要量身定制他们的简历,尽管网络上有很多资源强调使用关键词和为每份简历做研究的重要性。

根据经验,没有什么比一个未经定制的简历更能让人对其失去兴趣了。通用简历不仅不能告诉招聘人员你是否适合团队,而且还显示出缺乏努力和准备。我完全理解申请 200 份工作的辛苦。然而,你是否考虑过,如果你花时间坐下来为每一份申请的工作量身定制一份简历,也许你就不需要申请 200 份工作了呢?也许你只申请了 30 份工作之后,就开始收到面试邀约了,因为你花时间给招聘人员提供了他们想要的东西。要有效定制你的简历,你需要:

  • 尽可能保持简洁(如果你是应届毕业生,应该保持一页,如果有多年工作经验则可以稍长一些)

  • 只包含绝对相关的内容(是的,你可能曾在咖啡馆工作了 3 年,但你可能有更相关和有影响力的经验,例如参与一个教授编程给贫困儿童的俱乐部 —— 然而,我应该提到,包括非相关的工作经验也有一些好处,可以显示你可靠、可培训等,但这些优点可能会被反复辩论)。

  • 将简历语言与职位广告保持一致(这对于通过关键的职位申请软件筛选阶段来说非常重要,同时也表明你花时间仔细阅读了职位描述)

  • 包括在职位描述中突出显示的关键字或短语(见下文)

还应该注意,关键字的重要性不一定是与招聘人员审查简历有关(尽管他们确实喜欢看到与项目影响或你所掌握的技术相关的重要关键字),而更多地与公司用来过滤简历的应用软件有关。现在几乎没有公司通过电子邮件接受简历,这使得在一个代码决定是否向招聘人员展示你的简历时,突出自己变得越来越困难。尽管这看起来像是一个愚蠢的游戏,你必须成为在简历中包含关键字的专家,以确保你的简历甚至有被人类看一眼的微小机会。做到这一点的最佳方法是在整个简历中包含关键字,例如在教育部分、过去经历的描述和技能部分。在简历开头包含最重要的关键字也不是一个坏主意,这样会最大程度地吸引招聘人员的注意。

2. 量化成就、经验和影响

“我写了一个分析销售趋势的程序”比说“我开发了一个使用 X 技术分析和预测销售趋势的程序,结果提高了未来销售预测的效率 20%”要不具说服力得多。第一个陈述会让招聘人员说:“好的,那又怎样?”,而第二个陈述则解释了你做了什么、如何做的以及最重要的是为什么这么做。

量化你的成就、经验和影响是巩固简历的重要小步骤,这为你提供了一些好处。首先,它提供了支持你所声称的结果。其次,它向招聘人员表明结果是推动和指导你的优先事项和未来表现的动力。最后,量化是在招聘人员心中留下深刻印象并从竞争对手中脱颖而出的好方法。

量化你的成就、经验和影响确实需要一些工作才能开始,因此在开始新的事物(无论是学校、工作还是项目)时,尽早建立起你的工作流程是非常重要的。以下是我过去四年来使用的一般工作流程,用于收集我需要量化的成就、经验和影响的数字:

  1. 跟踪你的工作: 我跟踪我工作中的所有事情,包括我的工作时间、我完成的项目数量、这些项目的详细信息以及这些项目的结果。我甚至记录了我花在调试上的时间!我发现手机上的 Notes 应用足够使用,因为通常我会在每个月底将所有内容转移到更永久的文档中,但我知道很多人已经开发了使用 Notion 或电子表格的系统。

  2. 在确切数字缺失时开发一些范围: 如果你已经跟踪了足够长时间的工作数据,你会注意到有时候你没有确切的数字。对于这些情况,制定一些数据范围以指示完成的相对工作量,同时承认有时可能会有所变化。例如,我可以说 2021 年平均每月完成了 5 篇数据科学文章。

  3. 关注招聘者最喜欢看到的关键绩效指标: 金钱、人员、时间和排名是招聘者最看重的四个指标。每个指标的例子包括说明你的项目为公司带来了多少利润、你在团队中管理了多少人、你在一个特定项目上工作了多长时间,或者你为公司提升了某个排名的程度。记录这些数据有助于在项目进行过程中保持记忆的清晰。

收集并检查好你的数据后,你可以开始将其转化为 1–2 句总结,添加到简历中的教育、项目或经验描述中。

3. 包括能够带来影响的项目

你遇到过多少希望进入数据科学领域的人,他们创建了一个“预测股市”的项目?只要想象一下有多少招聘者不得不筛选这些项目的作品集。嗯,不用多说。

如果你对在数据科学领域找到一份工作并从 500 名其他有相同愿望的人中脱颖而出感到认真,你需要在简历中包括能够带来影响的项目。这将取决于行业,因此做一些初步研究了解该行业面临的问题是个不错的主意。需要注意的是,这些问题不必是巨大的,也不必是前所未有的。你甚至可以找到一个已经解决了的问题的替代解决方案。例如,你可以进行分析,发现污染数据中无法定义的异常来自一个造成大量灰尘的道路建设项目。或者,你可以进行分析,告诉你社区中的一家小企业,他们应该在周五向顾客进行营销,因为这会引起最多的周末购物者。

包含有影响力的项目的主要目的是向招聘者展示你能够发现问题并找到解决方案。整个项目生命周期的项目管理并不是一件容易的事,因此展示你能够成功完成它将对招聘者特别有意义,尤其是那些希望候选人能够快速上手或希望候选人成为数据科学部门核心的招聘者。此外,这些行业特定的项目是展示领域知识和提供影响力的好方法,无论这些项目是否会被付诸实施。

4. 如果你是应届毕业生,首先突出教育背景;如果你不是,首先突出经验。

简历部分的顺序是可以讨论的,但标准是,如果你是应届毕业生,就把教育背景放在前面;如果你不是,就将经验放在前面。对于那些处于灰色地带的人,他们可能在一个不相关的领域有经验,然后回到学校获得数据科学的某种教育,首先放教育背景,然后是只包含最相关工作的经验部分,可能是一个不错的选择。

我见过许多简历的初稿,其中人们将各个部分按可疑的顺序排列,这不一定会给招聘人员带来信心。然而,通过使用上述方法,你很可能会首先展示你最自信的部分。此外,在面试中,招聘人员通常会按照简历上呈现的顺序开始询问,因此能够对简历的前几个部分最自信地进行讲述总是很好的。

5. 使其具有引人注目的可浏览性

如果你无法在 15 秒内获取简历的要点,就该重新编写了。

当我审阅简历时,面对一大片文字,我的脑子似乎会立即关闭。你可能会发现,当你在线阅读时,如果遇到一大片没有标题的文本墙,也会发生类似的情况,这使得内容难以浏览。

从经验来看,我见过的最引人注目的简历是那些没有用词过多的简历。这些简历总是有足够的留白,大标题用于分隔部分,以及仅包含一到两句话的要点,这些要点提供了详细但简短的总结。当招聘人员不会感到被大量文字压倒时,他们会更愉快地考虑这份简历。

鉴于招聘人员能够在7.4 秒内浏览简历并做出决定,一个好的基准是大约 15 秒——如果你无法在这段时间内获得简历的总体印象,你需要使其更容易浏览,并使重要信息更易于获取。就像这篇文章一样,我使所有重要点突出显示,这样你可以在不到一分钟的时间内浏览这篇文章,获取重要信息,然后在某些内容对你特别相关时阅读段落的正文。你也需要在你的简历中做到这一点。

使您的简历易于浏览可能就像使用大而粗的标题来命名您的部分,将部分之间的分隔线,以及使用项目符号将文本分解为可读的、易于消化的片段那么容易。如果您发现难以包含所有相关细节,您可以随时选择最有见地的细节,然后在您的作品集或面试中填补剩余的细节。尽管简历中应该说的都应该说完,但在作品集或面试中可以说更多,因此主要焦点应该只集中在传达那些将决定是否有招聘人员给您打电话面试的细节上。

订阅以直接将我的故事发送到您的收件箱:故事订阅

请通过我的推荐链接成为会员,以获取对 Medium 的无限访问(我将获得少量佣金,您无需额外费用):Medium 会员

订阅我的通讯以获取更多带有环保主义倾向的独家数据驱动内容:数据驱动环保主义者

支持我的写作,通过捐赠资助创作更多类似的故事:捐赠

你真的需要一个特征存储吗?

原文:towardsdatascience.com/do-you-really-need-a-feature-store-f71cf9586158

特征存储——原始数据与机器学习模型之间的接口

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 YUNNA WEI

·发布于 Towards Data Science ·8 分钟阅读·2023 年 3 月 17 日

“特征存储”已经存在了几年。既有开源解决方案(如 FeastHopsworks),也有商业产品(如 TectonHopsworksDatabricks Feature Store)。已经有很多关于“特征存储”的文章和博客发布,解释了什么是“特征存储”,以及为什么“特征存储”是有价值的。一些组织也已经将“特征存储”纳入了他们的机器学习应用程序。然而,值得指出的是,“特征存储”是你整体机器学习基础设施中新增的一个组件,它需要额外的投资和努力来建设和运维。因此,有必要真正理解和讨论***“特征存储对每个组织来说真的必要吗?”***。在我看来,答案一如既往,这要视情况而定。

因此,今天文章的重点是分析何时需要特征存储,以便组织能够明智地投资于那些能够真正为业务增加价值的机器学习技术。

为了回答这个问题,以下是一些关键的考虑因素:

  • 你们的机器学习应用程序需要什么样的特征?

  • 你们组织管理的机器学习应用程序类型是什么?

  • 是否需要在组织中的不同团队之间共享和重用特征?

  • 训练与服务之间的偏差是否经常成为负面影响机器学习模型性能的问题?

除了回答上述问题,我还将解释特征存储在端到端机器学习生命周期中的角色,如果你认为特征存储对你们组织的机器学习基础设施是必要的。

让我们详细探讨上述每一个考虑因素。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由特尔纳瓦大学提供,发表于Unsplash

你的机器学习应用需要哪些功能?

机器学习应用的特征大致可以分为以下几类:

  • 批处理特征 — 这些特征在大多数时间内保持不变,例如客户的元数据,包括教育背景、性别、年龄等。此外,批处理特征通常涉及实体的元数据,这些实体通常是关键的业务实体,如客户、产品、供应商等。批处理特征的输入数据源通常是数据仓库和数据湖。

  • 流处理特征 — 与批处理特征不同,流处理特征是需要在低延迟情况下不断更新的特征。例如,用户在过去 30 分钟内的交易数量。流处理特征通常由流处理引擎计算,如 Spark Structured Streaming 或 Apache Flink,并直接推送到在线特征存储中以实现低延迟服务。流处理特征的输入数据源是消息存储,如 Kafka、Kinesis 和 Event hub。

  • 结合批处理和流处理的高级特征 — 需要将流数据与静态数据结合,以生成供机器学习模型学习的新特征。这类特征也由流处理引擎计算,因为它也需要低延迟。与流处理特征的唯一区别是它需要与另一个数据源进行连接。

如果你的机器学习应用需要大量的流处理特征,并且这些特征需要在非常低的延迟下提供服务,那么在线特征存储可以带来显著的价值,因为特征存储的关键功能之一是允许你预计算这些流处理特征,而不是在模型服务时计算,这可能会显著降低模型服务的速度。

你的组织管理的机器学习应用类型是什么?

第二个考虑因素是明确你的组织管理的机器学习应用类型。每种类型的机器学习应用需要的机器学习基础设施差异较大。

我将机器学习应用分为以下三类:

  • 第一类是批处理特征工程 + 批处理推断:特征工程、模型训练和模型服务都在固定的时间间隔内进行。无需流处理特征,模型服务的延迟也不需要非常低。在这种情况下,你不需要在线特征存储和流处理来预计算特征,因为你有足够的时间按需计算特征。

  • 第二类是批量训练 + 在线推断(具有批量和流式特性):机器学习模型在批量级别进行训练,但模型通常被包装成 API 进行在线服务。在这种情况下,为了决定是否需要特征存储,有两个重要的考虑因素。第一个是服务延迟,第二个是需要实时计算的特征数量。如果服务延迟非常低,并且有相当多的特征需要在非常严格的时间限制内计算,那么你很可能需要特征存储的支持来预计算这些特征,以便在服务机器学习模型时,可以从在线特征存储中获取所需特征,而不是实时计算它们。在线存储是一个数据库,仅存储每个实体的最新特征值,例如 Redis、DynamoDB 和 PostgreSQL。相反,如果模型服务的延迟不低,且模型服务所需的特征数量较少,你可能仍然可以实时计算特征,因此在线特征存储不是绝对必要的。

根据我的经验,需要流式特征和极低延迟服务的机器学习应用通常是操作性机器学习应用,如欺诈检测、推荐、动态定价和搜索。对于这些类型的机器学习应用,特征存储的功能是将特征计算与特征消费解耦,以便复杂的特征工程逻辑不需要按需计算。

是否需要在组织内的各种团队之间共享和重用特征?

第三个考虑因素是是否需要在组织内的各种团队之间共享和重用特征。

特征存储的关键功能之一是集中式特征注册表,用户可以在其中持久化特征定义和相关的特征元数据。用户可以通过与注册表互动来发现注册的特征数据。注册表作为组织内所有机器学习特征的信息的唯一真实来源。

对于拥有多个数据科学团队的组织,特别是当这些团队可能会重复生产类似特征时,拥有一个集中式特征存储可以显著改善团队合作和数据科学团队的生产力,使团队能够发布、共享和重用机器学习特征。通常,建立和维护数据工程管道以策划机器学习应用所需的特征需要大量工程工作。如果一个团队可以重用另一个团队已经策划的特征,则可以显著减少重复的工程工作并节省大量工程时间。

另外,拥有特征存储提供了一种机制,允许企业管理机器学习特征的使用,这实际上是业务中一些最精心策划和精炼的数据资产。

训练-服务偏差是否常常是一个负面影响机器学习模型性能的问题?

下一步的考虑是训练-服务偏差,这通常是一个负面影响机器学习模型性能的问题。训练-服务偏差是指在生产环境中部署的机器学习模型的表现比数据科学家在其本地笔记本环境中开发和测试的模型更差。训练-服务偏差的主要原因是生产环境中的特征工程逻辑与数据科学家在笔记本环境中创建和使用的原始特征工程逻辑实现方式不同(可能只是略有不同)。

特征存储可以通过创建一个一致的特征接口来修复训练-服务偏差,其中模型训练和模型服务使用相同的特征工程实现,如下图所示。

如果训练-服务偏差是你的机器学习应用在生产环境中表现不如预期的常见原因,特征存储可以成为救星。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练-服务偏差 | 图片来源:作者

那么,特征存储在端到端机器学习生命周期中处于什么位置

基于以上分析,如果你已经决定特征存储对你的机器学习应用有帮助,并且你打算将其作为机器学习基础设施的新组件,以下是如何在端到端机器学习生命周期中使用特征存储的说明。

  • 特征定义 — 数据科学家可以从原始数据中定义所需的特征。特征定义包括源数据、特征实体、特征名称、特征模式、特征元数据和生存时间(TTL)。

  • 机器学习模型训练的特征检索 — 大多数特征存储解决方案提供的功能允许数据科学家从定义的特征构建训练数据集。单个训练数据集可能需要从多个特征表中提取特征。

  • 机器学习模型服务的特征检索 — 机器学习模型服务有两种类型:批处理评分和实时预测。获取批处理评分的特征与获取机器学习模型训练数据集的特征类似,不同之处在于批处理评分的特征具有最新的时间戳。获取实时预测的特征是为特定预测请求获取特征向量。特征向量通常是非常小的数据,因为它仅包含请求实体的最新特征值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在机器学习生命周期中使用特征存储 | 图片来源:作者

总结

如果你正在推出需要大量流式特性的实时预测用例,特性存储可以通过解耦特性计算和特性服务来帮助你实现低延迟服务要求。

如果你们组织的数据科学团队迅速扩展,并且需要在各种机器学习团队之间共享和重用工作,特性存储可以作为一个中心注册库,用于发布和重用特性。

我希望这篇文章能够帮助你决定是否需要为你的组织设置特性存储。如果你有任何问题,请随时留言。我通常每周发布一篇与构建高效数据和人工智能堆栈相关的文章。欢迎关注我的 Medium,以便在这些文章发布时获得通知。

如果你想查看更多有关现代和高效数据+人工智能堆栈的指南、深度探讨和见解,请订阅我的免费通讯 — 高效数据+人工智能堆栈,谢谢!

注:以防你还没有成为 Medium 会员,而你真的应该成为,因为你将获得对 Medium 的无限访问权限,你可以使用我的推荐链接进行注册!

非常感谢你的支持!

现代数据科学家的 Docker:2023 年你不能忽视的 6 个概念

原文:towardsdatascience.com/docker-for-the-modern-data-scientists-6-concepts-you-cant-ignore-in-2023-8c9477e1f4a5

一本关于酷炫、必备工具的插图指南

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Bex T.

·发表于 Towards Data Science ·11 分钟阅读·2023 年 6 月 7 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由我使用 Leonardo AI 制作

这是我见过的最搞笑的 meme 之一:

它涉及到的不仅仅是数据科学和机器学习中的一个最棘手的问题,而是所有编程中的难题——共享应用程序/脚本,并使这些东西在其他人的机器上也能正常工作。

尽管微软、苹果和林纳斯·托瓦兹在发布不同的操作系统时出于好意,但他们无意中创造了软件兼容性的无尽挣扎。

Linux、Windows、macOS——每个操作系统都有其独特的怪癖和特性。别忘了 Python 版本、库版本的差异以及机器学习中 GPU 驱动程序的不可预测性。

进入容器。虽然它们已经存在一段时间以解决这个问题,但自 2013 年 Docker 发布以来,它们才获得了巨大的普及。从那时起,Docker 及其容器已经成为共享任何运行代码的工具的首选。

因此,本教程将突出六个最重要的概念,以帮助你作为数据科学家或机器学习工程师在复杂的 Docker 世界中导航。

小提示

像许多其他伟大的软件一样,与 Docker 的交互非常直观且容易。你只需多读几遍文档,就能知道如何充分利用这个工具。

这就是为什么我们更关注每个命令背后的理论——这些更难理解,而文档几乎总是解释得不好。

因此,在整个教程中,我将更注重于概念而非代码,但会根据需要适时加入一些相关页面以深入了解某些内容。

让我们开始吧!

0. 为什么不用 ZIP 文件?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由我制作

为什么要学习一个全新的工具,既然你可以将模型的所有代码和数据集放入 zip 文件中并分享?这就像寄一盒乐高砖块给朋友让他们建造一辆车,而不是直接把现成的车开到朋友家。

有几个很好的理由来考虑使用 Docker,而不是 zip 文件或其他方法:

  1. 依赖和兼容性混乱:zip 文件不关心主机系统。它们就像环球旅行的游客,期望每台机器都能说他们的语言。但不同的操作系统有不同的架构,这在处理各种库和依赖及其版本时可能会成为一个巨大问题。

  2. 可重复性问题:想象一下,当有人尝试运行你的 zip 文件时出现了问题。这是代码中的 bug 还是环境相关的问题?这可能导致几个小时的挫败性调试,甚至让最有耐心的人也会抓狂。

  3. 隔离假象:使用 zip 文件,你事先无法了解其内容,解压它就像把一群调皮的老鼠释放到你的操作系统中。你无法控制它们会在哪里运行,并可能造成破坏。恶意人士可以利用这种混乱,导致安全攻击。

  4. 部署困境:从 zip 文件中部署模型通常涉及繁琐的手动配置、环境设置和依赖管理。这就像每次搬到新城市时都要从头开始建造一座房子。

总而言之,虽然 zip 文件可能看起来是共享应用程序的最简单方式,但它们无法匹敌 Docker 容器的强大和优势。

但你问,容器究竟是什么?我们接下来回答这个问题。

1. Docker 容器

容器就像你机器上的迷你操作系统,与 Spotify、Chrome、Photoshop、游戏等其他进程和应用程序隔离开来。它们可以直接访问你机器的资源,包括 RAM、CPU、磁盘,有时甚至是 GPU,从而能够运行任何软件并进行自定义配置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由我制作的图像

这些轻量级和可移植的计算环境旨在提供运行机器学习模型所需的一切,并且可以在隔离环境中运行而不会干扰主机上的其他进程。它们只使用了可用资源的一小部分,确保你的机器其余部分不受影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由我制作的图像

另一个显著的优点是容器能够保证结果的一致性。无论是一天、一个月还是一年,对于相同的输入,输出结果都将保持不变。但这还不是全部——容器还保证在任何地方的一致性。无论是在你的个人笔记本电脑、邻居的旧 Windows 机器,还是在云端(AWS、Azure、GCP),它们的运行方式都是相同的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由我提供

容器的另一个显著好处是它们的高安全性和隔离性。即使你在容器内搞得一团糟,也可以放心,这些混乱不会泄露到你机器的其他部分或影响其他容器。一切都在容器内得到很好的控制

此外,与虚拟机(VMs)等替代方案相比,容器更加轻量且资源需求最小。这种效率使你能够在现有操作系统上运行整个操作系统,如 Ubuntu、Debian 和 CentOS Linux 进程。

虽然有许多工具可以用于处理容器,但 Docker 脱颖而出,成为最好的工具。它是一个开源项目,拥有庞大的用户基础,是创建、管理和运行容器应用程序的首选工具。

2. 虚拟化

容器强大能力而不会压倒主机的秘密在于虚拟化技术。

虚拟化在主操作系统内创建隔离的环境,使多个容器能够独立而高效地运行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由我提供

虚拟化将主机资源,如 CPU、RAM 和磁盘,进行划分,并将每一部分呈现为单独的资源供软件使用。例如,64GB RAM 可以虚拟化为四个独立的 16GB RAM。

与虚拟机(VMs)通过硬件级别实现类似目标的虚拟化不同,容器是在软件级别进行虚拟化。它们利用主操作系统的内核,并共享底层操作系统资源。

这种方法实现了轻量和高效的虚拟化,使多个容器能够在单一主机上共存。启动和停止容器的过程开销最小,从而实现更快的更新和分发。

3. Docker 镜像

使用 Docker 时,你可能经常会遇到“镜像”和“容器”这两个术语交替使用,但它们之间存在明显的区别。

Docker 镜像类似于包含详细说明和步骤的食谱,用于运行应用程序。另一方面,Docker 容器就像是一道准备好的菜肴,将食谱变为现实——一个完全功能的实例。

尽管单个镜像可以有多个运行的容器实例,这些容器却独立操作,彼此之间互不知晓。

对于个人项目,你通常会构建自己的镜像。然而,对于许多任务,社区已经提供了许多预构建的镜像。

例如,Docker Hub 是最大的注册中心,托管着超过一百万个镜像,只需几条终端命令,你就可以在机器上安装 Docker。

这个注册表包含了各种操作系统(Ubuntu、CentOS、Debian)、软件栈和编程语言(Node.js、Python、MySQL、Nginx)、数据库、预打包和预配置的机器学习框架(TensorFlow、PyTorch 支持 GPU、Sklearn)等的官方镜像,还有更多。

举例来说,假设你想下载 Python 3.12 的官方候选版并开始在你的机器上使用它。你可以通过简单的两个命令来完成这一操作:

$ docker pull python:3.12-rc-bullseye
$ docker run -it python:3.12-rc-bullseye

第二个带有 -it 标志的命令将在从 python:3.12-rc-bullseye 镜像创建的容器内启动一个交互式终端。这个运行中的容器实例将类似于一个仅装备了 Python 3.12 的迷你操作系统,没有安装其他任何东西。

然而,就像任何 Ubuntu 发行版一样,你可以在容器内安装额外的工具,比如 Git 或 Conda,并执行几乎任何你在 Ubuntu 中通常会做的任务,尽管没有图形用户界面(GUI)。

4. Dockerfile

当我们调用 [docker pull](https://docs.docker.com/engine/reference/commandline/pull/)[docker run python](https://docs.docker.com/engine/reference/commandline/run/) 时,容器如何知道从哪里获取 Python 3.12 的二进制文件及其所有依赖项并安装它们?

解决方案在于 Dockerfiles。这些文本文件是构建自定义镜像的蓝图或配方,封装了我们的 Python 脚本或机器学习模型,以及它们的依赖项和配置。

当你创建镜像时,你将广泛使用 Dockerfiles(每个目录/项目一个 Dockerfile)。虽然 Dockerfiles 对于复杂项目来说可能变得很长,但它们通常包括以下命令用于 Python 项目:

# Use an official Python runtime as the base image
FROM python:3.9-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the requirements file to the container
COPY requirements.txt .

# Install the required Python packages
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application code to the container
COPY . .

# Define the command to run when the container starts
CMD ["python", "train.py"]

上面是一个用于将 train.py 脚本容器化的示例 Dockerfile,位于我们当前的工作目录中。以下是这些命令的概述:

  1. FROM - 一个用于指定基础镜像的关键字。基础镜像是 Docker Hub 上的预构建镜像,你可以在自定义镜像中使用这些基础镜像,而无需重新发明轮子。上述,我们使用 Python 3.9 基础镜像,以免使用 apt-get 手动安装 Python。

  2. WORKDIR - 这个命令将容器内的工作目录设置为 /app,应用程序文件(train.pyrequirements.txt)将被复制到该目录。

  3. RUN - 在这个关键字后,你可以包含任何有效的终端命令,例如 pip install 或运行 bash 脚本以在容器构建过程中执行特定任务。

  4. CMD - 这个命令指定了容器启动时使用 docker start 时要运行的默认命令。在这种情况下,它通过执行 python train.py 来训练一个新模型。

要使用这个 Dockerfile 构建新镜像,你只需运行

docker build -t my_image .

就这么简单!

如你所见,Dockerfile 的语法对有经验的 YAML 文件使用者或终端操作人员来说并不完全陌生。

查看 这个页面 的 Docker 文档,以了解更多关于构建镜像和编写 Python 应用程序的 Dockerfile 的信息。

5. 镜像层

层是 Docker 镜像中一个有点奇怪的概念。

Dockerfile 中的每条指令/命令都有助于在生成的镜像中创建一个新的、只读的、不可变的层。层叠加在一起,形成一个分层的文件系统,代表最终的镜像。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来自 Docker 文档。Apache-2 许可证。

使用分层结构有许多好处,比如缓存。由于构建镜像是一个增量过程,内容经常更新,缓存使得重复调用docker build变得更快。

如果 Docker 检测到这些层在当前构建中没有变化,像FROMRUN这样的重命令只需几分之一秒。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来自 Docker 文档。Apache-2 许可证。

除了缓存,层还允许高效的存储利用、版本控制(镜像历史、轻松回滚)和轻量级分发。

这个页面 了解更多关于层、多阶段构建和缓存的知识。

6. Docker 引擎

单个主机可以有数十个构建的镜像和运行的容器。主机是如何在不崩溃的情况下分配资源的?这就是 Docker 引擎的作用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由我提供

Docker 引擎负责所有神奇的 Docker 技巧,处理创建、运行和维护镜像及容器的任务。它有许多组件,但这里有 三个 最重要的:

  1. Docker Daemondockerd - 主机上的一个后台进程,管理容器的生命周期。它负责虚拟化和资源分配。

  2. Docker 客户端 — 一款允许用户与 Docker 引擎交互的软件。主要是 Docker 命令行接口([docker](https://docs.docker.com/engine/reference/commandline/cli/) CLI),但也有适用于喜欢图形用户界面(GUI)的用户的跨平台 Docker Desktop

  3. Docker API — 一组接口和协议,允许 Docker 客户端或其他外部工具与 Docker Daemon 进行交互。如果你愿意的话,可以看作是 Docker 的内部语言。

99% 的时间你将通过 Docker 客户端进行工作,但了解其他组件很重要,因为它们在容器操作中发挥着至关重要的作用。

结论

由于我提到(以及未提到)的所有好处,Docker 在社区中极受欢迎。因此,许多很棒的项目在此基础上进行了扩展,以增强默认功能。

例如,Kubernetes,通常缩写为 K8s,是一个强大的容器编排平台,可以自动化容器化应用程序的部署、扩展和管理。它可以在一个节点集群中管理和调度 Docker 容器,提供自动扩展、负载均衡和自愈功能等特性。

还有 Docker Compose,它允许你启动多个容器,定义它们之间的关系,并将它们的配置管理为一个应用栈。

对于我们来说,Kubeflow 是一个开源平台,旨在简化在 Kubernetes 上部署、管理和扩展机器学习(ML)工作负载。它旨在提供无缝集成的 ML 工作流体验,使数据科学家和工程师更容易构建、训练和大规模部署机器学习模型。

每一种这些技术都值得你花时间,因为它们会大大提升你在进行 MLOps 时的生活质量。

感谢阅读!

喜欢这篇文章吗?还有,它那奇特的写作风格?想象一下能够访问到几十篇类似的文章,全部由一位才华横溢、迷人、风趣的作者(顺便说一下,就是我 😃)撰写。

仅需 4.99 美元的会员订阅,你不仅可以访问我的故事,还能获得来自 Medium 上最优秀头脑的知识宝藏。如果你使用我的推荐链接,你将获得我超级 nova 的感激之情,并且为支持我的工作得到一个虚拟的击掌。

[## 通过我的推荐链接加入 Medium - Bex T.

独家访问我所有的⚡高级⚡内容和 Medium 上的所有内容,不受限制。通过给我买一杯咖啡来支持我的工作…

ibexorigin.medium.com](https://ibexorigin.medium.com/membership?source=post_page-----8c9477e1f4a5--------------------------------)

内容概要:《2024年中国城市低空经济发展指数报告》由36氪研究院发布,指出低空经济作为新质生产力的代表,已成为中国经济新的增长点。报告从发展环境、资金投入、创新能力、基础支撑和发展成效五个维度构建了综合指数评价体系,评估了全国重点城市的低空经济发展状况。北京和深圳在总指数中名列前茅,分别以91.26和84.53的得分领先,展现出强大的资金投入、创新能力和基础支撑。低空经济主要涉及无人机、eVTOL(电动垂直起降飞行器)和直升机等产品,广泛应用于农业、物流、交通、应急救援等领域。政策支持、市场需求和技术进步共同推动了低空经济的快速发展,预计到2026年市场规模将突破万亿元。 适用人群:对低空经济发展感兴趣的政策制定者、投资者、企业和研究人员。 使用场景及目标:①了解低空经济的定义、分类和发展驱动力;②掌握低空经济的主要应用场景和市场规模预测;③评估各城市在低空经济发展中的表现和潜力;④为政策制定、投资决策和企业发展提供参考依据。 其他说明:报告强调了政策监管、产业生态建设和区域融合错位的重要性,提出了加强法律法规建设、人才储备和基础设施建设等建议。低空经济正加速向网络化、智能化、规模化和集聚化方向发展,各地应找准自身比较优势,实现差异化发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值