2023年4月

DevOps, HybridOps and AIOps浅谈

DevOps的概念出现比较久了,很多的IT项目也都在实际的运用中。AIOps概念作为DevOps的升级版,也得到了很广大的关注,也出现了很多AIOps 相关的理论参考模型。但是,在当前的技术发展及应用现状下,HybridOps这个从项目实践里发展出来的概念渐渐占据了焦点,并且成功的插入到DevOps和AIOps之间,形成了 DevOps -> HybridOps -> AIOps 的发展趋势。

这里我用简单的语言和自己的实践以及理解,跟大家做一个几句话的梳理,给大家一个建立一个整体上的印象。如果大家对某一个具体方面有兴趣,可以在园子里搜索其他作者的相关帖子,应该会有很多针对具体方面的好贴。

在当前各种虚拟化,云平台流行的情况下,大量的项目也争相往相关平台迁移,以节省基础架构运维成本。所以我们可以用上图来表示当前流行的项目架构,左边圆圈代表项目,右边圆圈代表项目运行的平台(各种虚拟化,云平台)。

传统的
DevOps
作为项目的一部分运作,在项目里起到串联各个环节(业务,开发,测试,运维)的作用。这种DevOps的局限性在于,只关注项目业务,而没有关注为项目服务的平台。如果遇到问题的话,一封邮件丢给平台,让他们解决。

问题来了,平台收到邮件,但是平台方一般会服务很多的客户,他只了解基础架构,不了解项目业务,不清楚你真正需要的是什么,就是知道了,也不一定能够马上给到你,因为可能需要定制化。涉及到定制化,那就不只是钱的事情,先是平台是否有技术提供这个定制化,如果可以提供,后面可能还需要大量的测试验证来确定定制化在项目的可用性,稳定性。一个小小的版本不兼容,就能引发一系列的问题。这些大量的验证工作就交到了项目, 因为平台是不会帮忙做的,他顶多只会在你发现问题后,先告诉你这不是平台的问题,然后在你的确凿证据下,通过你的指导,帮忙分析解决问题。

这里就该
HybridOps
出场了,HybridOps不仅包含了传统的DevOps,  还起到了沟通项目和平台的桥梁作用。上面这些工作都可以交给HybridOps 来做,除了传统DevOps工作,它主要还包括下面两块内容:

  • 从项目视角,在了解项目业务和流程的基础上,思考分析,提出改善建议,并执行实施。如:帮项目思考技术和流程改善方案,给当前平台提相关需求帮忙实现。或者从项目角度特点分析调研,什么样的平台更适合项目未来的发展?甚至提出更换当前平台的建议。这里你可能会说,“换平台难道是你一个HybridOps 工程师说了算的吗?那项目的架构师是干啥的?”,答:“当然还是架构师说了算,他了解架构,还能拍板。但是真正了解项目实际运维细节的,是HybridOps工程师,他是从每天的部署发布,每一个版本,每一个application组件,每一个问题里摸爬滚打出来的,他有资格提这个建议”
  • 从平台视角,在了解各种平台的基础上,思考分析,提出改善建议,并执行实施。如:当前平台还有些什么服务能被发掘出来,提供给项目,帮助项目改善哪些框架和流程?还有哪些平台能够提供当前平台不能提供的服务,能够帮项目在未来竞争中取得优势?

AIOps
, 一句话,应该就是在大量数据的基础上,各种自动化成熟后,用AI (如:GPT)来代替人类把上面HybridOps 的事情都做了。

总之,回归DevOps 初心,在这个技术大发展,玩技术就像搭积木的时代,DevOps总是与时俱进,用热情拥抱变化,让持续改进帮项目在未来的竞争中立于不败之地。

LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

论文地址:
https://arxiv.org/pdf/2106.09685.pdf

代码地址:
https://github.com/microsoft/LoRA

摘要

自然语言处理的一个重要范式包括在一般领域数据上进行大规模的预训练 ,并适应特定的任务或领域。随着我们对更大的模型进行预训练,重新训练所有模型参数的完全微调变得不太可行。以GPT-3 175B为例--部署独立的微调模型实例,每个都有175B的参数,是非常昂贵的。我们提出了Low-Rank Adaptation,即LoRA,它冻结了预先训练好的模型权重,并将可训练的秩解矩阵注入到Transformer架构的每一层,大大减少了下游任务的可训练参数的数量。与用Adam微调的GPT-3 175B相比,LoRA可以将可训练参数的数量减少10,000倍 ,对GPU内存的要求减少3倍 。LoRA在 RoBERTa、DeBERTa、GPT-2和GPT-3上的模型质量表现与微调持平或更好,然而它有更少的可训练参数,更高的训练吞吐量,而且与适配器不同,没有额外的推理延迟。我们还对语言模型适配中的rank-deficiency进行了实证调查,这说明了LoRA的功效。我们发布了一个便于LoRA与PyTorch模型集成的软件包,并提供了我们对RoBERTa、DeBERTa和GPT-2的实现和模型检查点,网址是https://github.com/microsoft/LoRA。

介绍

自然语言处理中的许多应用都依赖于将一个大规模的、预先训练好的语言模型适应于多个下游应用。这种适应通常是通过微调完成的,微调会更新预训练模型的所有参数。
微调的主要缺点是,新模型包含的参数与原模型一样多
。随着更大的模型每隔几个月就被训练一次,这对GPT-2(Radford等人,b)或 RoBERTa large(Liu等人,2019)来说仅仅是一个 "不便",而对于有着175B参数的GPT-3(Brown等人,2020)来说则是一个关键的挑战。

许多人试图通过只调整一些参数或为新任务学习外部模块来缓解这一问题。这样,我们只需要在每个任务的预训练模型的基础上,存储和加载少量特定任务的参数,大大提升了部署时的运行效率。然而,现有的技术往往通过扩展模型深度或减少模型的可用序列长度(Li & Liang,2021;Lester等,2021; Ham-bardzumyan等,2020;Liu等,2021)引入推理延迟(Houlsby等,2019;Rebuffi等, 2017)(第3节)。更重要的是,这些方法往往不能与微调基线相匹配,造成了效率和模型质量之间的权衡。

我们从Li等人(2018a);Aghajanyan等人(2020)那里得到启发,他们表明学到的过度参数化模型实际上位于一个低的内在维度上。我们假设模型适应过程中权重的变化也具有较低的"内在秩",从而导致我们提出的低秩适应(LoRA)方法。LoRA允许我们通过优化密集层在适应过程中的变化的秩分解矩阵来间接地训练神经网络中的一些密集层,而保持预训练的权重冻结,如图1所示。以GPT-3 175B为例,我们表明,即使全秩(即d) 高达12288,一个非常低的秩(即图1中的r可以是1或2)也足够了,这使得LoRA既有存储又有计算效率。

LoRA拥有几个关键优势。

  • 一个预先训练好的模型可以被共享,并用于为不同的任务建立许多小的LoRA模块 。我们可以冻结共享模型,并通过替换图1中的矩阵A和B来有效地切换任务,从而大大降低存储需求和任务切换的难度。
  • LoRA使训练更加有效,在使用自适应优化器时,硬件门槛降低了3倍,因为我们不需要计算梯度或维护大多数参数的优化器状态。相反,我们只优化注入的、小得多的低秩矩阵。
  • 我们简单的线性设计允许我们在部署时将可训练矩阵与冻结权重合并,与完全微调的模型相比,在结构上没有引入推理延迟。
  • LoRA与许多先前的方法是正交的,并且可以与许多方法相结合,例如前缀调整。 我们在附录E中提供了一个例子。

术语和约定
:我们经常提到Transformer架构,并使用传统的术语来描述其尺寸。我们把 Transformer层的输入和输出二层大小称为
\(d_{model}\)
。我们用
\(W_{q} , W_{k} , W_{v} , 和W_{o}\)
来指代在自我注意模块中的查询/键/值/输出投影矩阵。
\(W或W_{0}\)
指的是预先训练好的权重矩阵,
\(∆W\)
是它在适应过程中的累积梯度更新。我们用
\(r\)
来表示一个LoRA模块的秩。我们遵循( Vaswani等人,2017;Brown等人,2020)规定的惯例,使用Adam(Loshchilov & Hutter, 2019;Kingma & Ba,2017)进行模型优化,使用Transformer MLP前馈维度
\(dffn = 4 × dmodel\)

问题陈述

虽然我们的建议与训练目标无关,但我们专注于语言建模作为我们的动机用例。下面是对语言建模问题的简要描述,特别是对给定任务特定提示的条件概率的最大化。

假设我们得到一个预训练的自回归语言模型
\(P_{Φ}(y|x)\)
,并以
\(Φ\)
为参数。例如,
\(P_{Φ}(y|x)\)
可以是一个通用的多任务学习器,如基于Transformer架构的GPT(Radford等人,b;Brown等 人,2020)(Vaswani等人,2017)。考虑将这个预训练的模型适应于下游的条件文本生成任务,如总结、机器阅读理解(MRC)和自然语言到SQL(NL2SQL)。每个下游任务都由上下文-目标对的训练数据集表示
\(Z=\{(x_{i},y_{i})\}_{ii=1,..,N}\)
, 其中
\(x_{i}\)

\(y_{i}\)
都是标记的序列。例如,在NL2SQL中,
\(x_{i}\)
是一个自然语言查询,
\(y_{i}\)
是其相应的SQL命令;对于摘要,
\(x_{i}\)
是一篇文章的内容,
\(y_{i}\)
是其摘要。

在全面微调期间,模型被初始化为预先训练好的权重
\(Φ_{0}\)
,并通过反复跟踪梯度更新为
\(Φ_{0} + ∆_{Φ}\)
,使条件语言建模的目标最大化:

完全微调的主要缺点之一是,对于每个下游任务,我们都要学习一组不同的参数
\(∆Φ\)
,其维度
\(∆Φ\)
等于
\(Φ0\)
。因此,如果预训练的模型很大(如GPT-3的Φ0 175亿),存储和部署许多独立的微调模型实例可能是一个挑战,即使是可行的。

在本文中,我们采用了一种更有效的参数方法,其中特定任务的参数增量
\(∆Φ = ∆Φ(Θ)\)
被更小规模的参数集Θ进一步编码,且
\(|Θ|<<|Φ0 |\)
。因此,寻找
\(∆Φ\)
的任务变成了对
\(Θ\)
的优化:

在接下来的章节中,我们建议使用低秩表示来编码
\(∆Φ\)
,这样既能提高计算效率,又能提高记忆效率。当预训练模型为GPT-3 175B时,可训练的参数
\(|Θ|\)
的数量可以小到
\(|Φ0|的0.01%\)

现有的解决方案还不够好吗?

我们要解决的问题绝不是新问题。自从迁移学习开始以来,有几十项工作试图使模型适应性更强,参数和计算效率更高。参见第6节对一些著名作品的调查。以语言建模为例,在高效适应方面有两种突出的策略:增加适配器层(Houlsby等人,2019;Rebuffi等人,2017; Pfeiffer等人,2021;Ru¨ckle´等人,2020)或优化输入层激活的某些形式(Li & Liang, 2021;Lester等人,2021;Hambardzumyan等人,2020;刘等人,2021)。然而,这两种策略都有其局限性,特别是在大规模和延迟敏感的生产场景中。

适配器层引入了推理延迟
:适配器有很多变体。我们关注Houlsby等人(2019)的原始设计 ,它在每个Transformer块上有两个适配器层,以及Lin等人(2020)的最新设计,它在每个块上只有一个,但有一个额外的LayerNorm(Ba等人,2016)。虽然人们可以通过修剪层或利用多任务设置来减少整体延迟(Ru¨ckle´等人,2020;Pfeiffer等人,2021),但没有直接的方法来绕过适配器层的额外计算。这似乎不是一个问题,因为适配器层被设计成只有很少的参数(有时<1%的原始模型),有一个小的瓶颈di-mension,这限制了它们可以增加的FLOPs。然而,大型神经网络依靠硬件的并行性来保持低延迟,而适配器层必须按顺序处理。这在在线推理环境中是有区别的,在这种环境中,批处理量通常很小,小到可以设置为1。在没有模型并行的一般情况下,比如在单个GPU上运行GPT-2(Radford等人,b)媒介的推理,我们看到使用适配器时延迟明显增加,即使瓶颈维度非常小(表1)。

当我们需要像Shoeybi等人(2020);Lep- ikhin等人(2020)那样对模型进行分片时,这个问题会变得更糟 , 因为额外的深度需要更多的同步GPU操作,如AllReduce和Broadcast,除非我们多次冗余地存储适配器参数。

直接优化提示是困难
:另一个方向,如
prefix tuning
(Li & Liang, 2021),面临不同的挑战。 我们观察到,prefix tuning很难优化,其性能在可训练参数中的变化是非单调的,证实了原始论文中的类似观察。更根本的是,为适应保留一部分序列长度必然会减少可用于处理下游任务的序列长度,我们怀疑这使得调整提示与其他方法相比性能较差。我们将对任务性能的研究推迟到第5节。

我们的方法

我们描述了LoRA的简单设计和它的实际好处。这里概述的原则适用于深度学习模型中的任何密集层,尽管我们在实验中只关注Transformer语言模型中的某些权重,作为激励的用例。

低秩参数化的更新矩阵

一个神经网络包含许多密集的层,它们进行矩阵乘法。这些层中的权重矩阵通常具有全秩。当适应一个特定的任务时,Aghajanyan等人(2020)表明,预训练的语言模型具有较低的 "本征维度",尽管随机投影到一个较小的子空间,仍然可以有效地学习。受此启发,我们假设权重的更新在适应过程中也具有较低的"内在秩"。对于一个预先训练好的权重矩阵
\(W_{0}\in R^{d×k}\)
,我们通过用一个低秩去代表来限制其更新。 组成
\(W_{0} + ∆W= W_{0} + BA\)
,其中
\(B\in R^{d×r}\)

\(A\in R^{r×k}\)
,秩
\(r<< min(d, k)\)
。 在训练期间,
\(W_{0}\)
被冻结,不接受梯度更新,而A和B包含可训练参数。请注意,
\(W_{0}\)
和$∆W = BA
\(都与相同的输入相乘,它们各自的输出向量是按坐标相加的。对于\)
h = W_{0}x$,我们修改后的前向传递得到:

我们在图1中说明了我们的重新参数化。我们对A使用随机高斯初始化,对B使用零初始化 ,所以
\(∆W = BA\)
在训练开始时是零。然后,我们将
\(∆Wx\)
的缩放为
\(\frac{α}{r}\)

\(α\)

\(r\)
中的一个常数。当用Adam进行优化时,如果我们适当地调整缩放初始化,调整α与调整学习率大致相同。因此,我们只需将α设置为我们尝试的第一个r,而不对其进行调整。这种缩放有助于减少我们在改变r时重新调整超参数的需要(Yang & Hu, 2021)。

完全微调的一般化
。一种更普遍的微调形式允许训练预训练参数的一个子集。LoRA更进一步,不要求权重矩阵的累积梯度更新在适应期间具有全秩。这意味着,当对所有权重矩阵应用LoRA并训练所有偏置时,我们通过将LoRA的秩r设置为预先训练好的权重矩阵的秩,大致可以恢复完全微调的表现力。换句话说,随着我们增加可训练参数的数量,训练LoRA大致趋近于训练原始模型,而基于适配器的方法则趋近于MLP,基于前缀的方法则趋近于不能接受长输入序列的模型。

没有额外的推理延时
。当在生产中部署时,我们可以明确地计算和存储
\(W = W_{0} + BA\)
,并像往常一样执行推理。请注意,
\(W_{0}\)
和BA都属于
\(R^{d×k}\)
。当我们需要切换到另一个下游任务时,我们可以通过减去BA,然后添加不同的
\(B′A′\)
来恢复
\(W_{0}\)
,这是一个快速操作,内存开销很小。关键是,这保证我们在推理过程中,与构建的微调模型相比,不会引入任何额外的延迟。

将LORA应用于Transformer

原则上,我们可以将LoRA应用于神经网络中的任何权重矩阵子集,以减少可训练参数的数 量。在Transformer架构中,自注意模块中有四个权重矩阵(Wq , Wk , Wv , Wo ),MLP模块中有两个。我们把Wq(或Wk , Wv )作为一个维度为
\(d_{model}×d_{model}\)
的单一矩阵,尽管输出维度通常被切成注意头。我们将研究限制在只适应下游任务的注意力权重,并冻结MLP模块(因此它们不在下游任务中训练),这既是为了简单也是为了参数效率。我们在第7.1节中进一步研究在Transformer中适应不同类型注意力权重矩阵的效果。我们把对适应MLP层、 LayerNorm层和偏置的实证调查留给未来的工作。

实际的好处和限制
。最重要的好处来自于内存和存储用量的减少。对于一个用Adam训练的大型Transformer,如果
\(r<<d_{model}\)
,我们减少了该VRAM的使用量达2/3,因为我们不需要存储冻结参数的优化器状态。在GPT-3 175B上,我们将训练期间的VRAM消耗从1.2TB减少到350GB。在r=4并且只有查询和值投影矩阵被调整的情况下,检查点的大小大约减少了10,000×(从350GB到35MB)。这使我们可以
用更少的GPU进行训练
,避免I/O瓶颈。另一个好处是,我们可以在
部署时以更低的成本切换任务,只交换LoRA的权重,而不是所有的参数
。这使得我们可以创建许多定制的模型,这些模型可以在将预训练的权重存储在VRAM中的机器上进行实时切换。我们还观察到,在GPT-3 175B上训练时,与完全微调5相比,速度提高了25%,因为我们不需要计算绝大多数参数的梯度。

LoRA也有其局限性。例如,如果选择将A和B吸收到W中以消除额外的推理延迟,那么在一次前向传递中对不同任务的输入进行批处理是不直接的。尽管在延迟不重要的情况下,可以不合并权重,动态地选择LoRA模块用于批次中的样本。

实验

我们在RoBERTa(Liu等人,2019年)、De- BERTa(He等人,2021年)和GPT-2(Radford 等人,b)上评估了LoRA的下游任务性能,然后扩展到GPT-3 175B(Brown等人,2020年 )。我们的实验涵盖了广泛的任务,从自然语言理解(NLU)到生成(NLG)。具体来说 ,我们在GLUE(Wang等人,2019)基准上对RoBERTa和DeBERTa进行评估。我们遵循Li & Liang(2021)在GPT-2上的设置进行直接比较,并在GPT-3上增加WikiSQL(Zhong等人 ,2017)(NL到SQL查询)和SAMSum(Gliwa等人,2019)(对话总结)进行大规模实 验。关于我们使用的数据集的更多细节,见附录C。我们使用NVIDIA Tesla V100进行所有实验。

基准线

为了与其他基线进行广泛的比较,我们复制了以前的工作所使用的设置,并尽可能地重复使用他们的报告数字。然而,这意味着一些基线可能只出现在某些实验中。

微调(FT)
是一种常见的适应方法。在微调过程中,模型被初始化为预先训练好的权重和偏置,所有的模型参数都经过梯度更新。一个简单的变体是只更新一些层而冻结其他层。 我们包括先前工作中报告的GPT-2的一个这样的基线(Li & Liang, 2021),它只适应最后两层(FTTop2)。

Bias-only or BitFit
是一个基线,我们只训练偏见向量,而冻结其他一切。同时,这种基线也被
BitFit研究过(Zaken等人,2021)。

Prefix-embedding tuning (PreEmbed)
在输入标记中插入特殊标记。这些特殊标记有可训练的词嵌入 ,一般不在模型的词汇中。在哪里放置这些标记会对性能产生影响。我们专注于 "前缀 "和 "后缀",前者将此类标记添加到提示语中,后者将其添加到提示语中;Li & Liang (2021)对两者都进行了讨论。我们用lp (resp. li )表示前缀(resp. infix)标记的数量。可训练参数的数量为
\(|Θ| = d_{model} × (l_{p} + l_{i} )\)

Prefix-layer tuning (PreLayer)
是前缀嵌入调整的延伸。我们不只是学习一些特殊标记的词嵌入 (或者等同于嵌入层之后的激活),而是学习每个转化层之后的激活。从之前的层计算出来的激活值被简单地替换为可训练的激活值。由此得到的可训练参数的数量是
\(|Θ| = L × d_{model} × (l_{p} + l_{i} )\)
,其中L是Transformer层的数量。

Adapter tuning
在自我注意模块(和MLP模块)和随后的剩余连接之间插入了适配器层。有两个完全连接的层,在中间有非线性的适配器层中有偏置。我们称这种原始设计为适配器H 。最近,Lin等人(2020)提出了一个更有效的设计,适配器层只应用在MLP模块之后和LayerNorm之后。我们称其为适配器L 。这与Pfeiffer等人(2021) 提出的另一个设计非常相似,我们称之为AdapterP 。我们还包括另一个称为AdapterDrop的基线(Ru¨ckle´等人,2020),它为提高效率而放弃了一些适配器层(AdapterD )。我们尽可能地引用先前工作的数字,以最大限度地增加我们比较的基线的数量;它们在第一列中带有星号(*)的行中。

LoRA在现有权重矩阵的基础上并行增加了可训练的秩分解矩阵对。如第4.2节所述,为了简单起见,我们在大多数实验中只将LoRA应用于Wq和Wv 。可训练参数的数量由秩r和原始权重的形状决定:
\(|Θ| = 2 × L_{LoRA}× d_{model} × r\)
,其中
\(L_{LoRA}\)
是我们应用LoRA的权重矩阵的数量。

ROBERTA BASE/LARGE

RoBERTa(Liu等人,2019)优化了最初在BERT(Devlin等人,2019a)中提出的预训练配方,在没有引入更多可训练参数的情况下提升了后者的任务性能。虽然RoBERTa近年来在 NLP排行榜上被GLUE基准(Wang等人,2019)等更大的模型所超越,但在从业者中,它仍然是一个具有竞争力的、受欢迎的预训练模型,其规模也是如此。我们从HuggingFace Transformers库(Wolf等人,2020年)中提取预训练的RoBERTa base(125M)和RoBERTa large(355M),并评估不同的高效适应方法在GLUE基准任务上的表现。我们还根据 Houlsby等人(2019)和Pfeiffer等人(2021)的设置进行了复制。为了确保公平的比较,我们在与适配器比较时,对评估LoRA的方式做了两个关键的改变。首先,我们对所有任务使用相同的批次大小,并使用128的序列长度来匹配适配器基线。其次,我们将模型初始化为MRPC、RTE和STS-B的预训练模型,而不是像微调基线那样已经适应了MNLI的模型。按照Houlsby等人(2019年)的这种更严格的设置进行的运行被标记为
\(†\)
。结果列于表2(前三节)。关于所使用的超参数的细节,见D.1节。

DEBERTA XXL

DeBERTa(He等人,2021)是BERT的一个较新的变体,它在更大的规模上进行训练,在 GLUE(Wang等人,2019)和Su- perGLUE(Wang等人,2020)等基准上的表现非常具有竞争力。我们评估了LoRA是否仍能在GLUE上匹配完全微调的DeBERTa XXL(1.5B)的性能。结果见表2(底部部分)。关于所使用的超参数的见D.2节。

GPT-2 MEDIUM/LARGE

在表明LoRA可以成为NLU上完全微调的一个有竞争力的替代方案之后,我们希望回答LoRA在NLG模型上是否仍然占优势,比如GPT-2中型和大型模型(Radford等人,b)。我们尽可能地保持我们的设置与Li & Liang(2021)接近,以便进行直接比较。由于篇幅的限制,我们在本节中只介绍了我们对E2E NLG挑战赛的结果(表3 )。关于WebNLG ( Gardent等人,2017)和DART(Nan等人,2020)的结果见F.1节。我们在D.3节中列出了使用的超参数。

扩展到GPT-3 175B

作为LoRA的最后一个压力测试,我们将参数扩大到1750亿的GPT-3。由于训练成本很高,我们只报告了随机种子上给定任务的典型标准偏差,而不是为每个条目提供一个标准偏差 。关于所使用的超参数的细节,见D.4节。

如表4所示,LoRA在所有三个数据集上都匹配或超过了微调基线。请注意,并非所有的方法都能从更多的可训练参数中单调地获益,如图2所示。当我们使用超过256个特殊标记进 行前缀嵌入调整或使用超过32个特殊标记进行前缀层调整时,我们观察到性能明显下降。 这也证实了Li & Liang (2021)的类似观察。虽然对这一现象的彻底调查超出了这项工作的范围,但我们怀疑有更多的特殊标记会导致输入分布进一步偏离训练前的数据分布。另外, 我们在F.3节中研究了不同适应方法在低数据系统中的表现。

相关工作

了解低秩更新

鉴于LoRA的经验优势,我们希望进一步解释从下游任务中学习到的低秩适应的特性。请注意,低秩结构不仅降低了硬件门槛,使我们能够并行地运行多个实验,而且还能更好地解释更新权重与预训练权重的相关性。我们把研究重点放在GPT-3 175B上,在那里我们实现了可训练参数的最大减少(高达10,000倍)而没有对任务性能产生不利影响。 我们进行了一系列的实证研究来回答以下问题:

  • 1)在参数预算约束下,我们应该调整预先训练好的transformer中的哪一个权重矩阵子集以使下游性能最大化?
  • 2)"最佳"适应矩阵∆W是否真的有秩缺陷?如果是的话,在实践中使用的好秩是什么?
  • 3)∆W和W之间有什么联系?∆W是否与W高度相关?与W相比,∆W有多大?我们相信,我们对问题(2)和(3)的回答阐明了在下游任务中使用预训练的语言模型的基本原则,这是NLP的一个关键话题。

我们应该对Transformer中的哪些权重矩阵应用Lora?

在有限的参数预算下,我们应该用LoRA调整哪种类型的权重,以获得下游任务的最佳性能?如第4.2节所述,我们只考虑自我注意模块的权重矩阵。我们在GPT-3 175B上设置了 18M的参数预算(如果存储在FP16中,大约是35MB),如果调整一种类型的注意力权重,则对应于r=8,如果对所有96层调整两种类型,则对应r=4。结果见表5。结果列于表5。

注意,将所有参数设置为∆Wq或∆Wk会显著降低性能,而调整Wq和Wv会产生最佳结果。这表明,即使是rank=4也能捕获∆W中的足够信息,因此,与采用更大rank的单一类型权重相比,更适合采用更多权重矩阵。

LORA的最佳秩r是什么?

我们把注意力转向秩r对模型性能的影响。 我们调整{Wq , Wv }、 {Wq , Wk , Wv , Wc },只用Wq 作比较。

表6显示,令人惊讶的是,LoRA已经以非常小的r表现出了竞争力(对于{Wq,Wv}比仅Wq更是如此)。这表明更新矩阵∆W可能具有非常小的“内在秩”。为了进一步支持这一发现,检查了通过不同的r选择和不同的随机种子学习的子空间的重叠。我们认为,增加r并不能覆盖一个更有意义的子空间,这表明一个低秩的适应矩阵就足够了。

不同r之间的子空间相似性。
给定
\(A_{r=8}\)

\(A_{r=64}\)
是使用相同的预训练模型学到的秩为r=8和64的适应矩阵,我们进行奇异值分解,得到右弦单元矩阵
\(U_{A_{r=8}}\)

\(U_{A_{r=64}}\)
。我们希望回答:UAr=8(1≤i≤8)中前i个奇异向量所跨越的子空间有多少包含在UAr=64(1≤j≤64)的前j个奇异向量所跨越的子空间中?我们用基于格拉斯曼距离的归一化子空间相似度来衡量这个数量(更正式的讨论见附录G)。

其中
\(U^{i}_{A_{r=8}}\)
代表
\(U_{A_{r=8}}\)
对应于顶部i个奇异向量。
\(φ(.)\)
的范围是[0,1],其中1代表子空间完全重叠,0代表完全分离。由于篇幅限制,我们只查看了第48层(共96层),但这个结论对其他层也是成立的,如H.1节所示。

我们从图3中得出了一个重要的观察结果。

  • 与顶部奇异向量相对应的方向在
    \(A_{r=8}和A_{r=64}\)
    之间有明显的重叠,而其他方向则没有。具体来说,
    \(A_{r=8} 的\)
    ∆Wv$ (resp.
    \(∆Wq\)
    )和A_{r=64}$ 的
    \(∆Wv\)
    ( resp.
    \(∆Wq\)
    )共享一个维度为1的子空间,归一化相似度大于0.5,这就解释了为什么r = 1在我们的GPT-3的下游任务中表现相当好。

由于
\(A_{r=8} 和A_{r=64}\)
都是使用相同的预训练模型学习的,图3表明
\(_{r=8} 和A_{r=64}\)
的顶部奇异向量方向是最有用的,而其他方向可能主要包含训练中积累的随机噪声。因此,适应矩阵确实可以有一个非常低的秩。

不同随机种子之间的子空间相似性。
我们通过绘制两个r=64的随机种子运行之间的归一化子空间相似性来进一步证实这一点,如图4。 ∆Wq 似乎比∆Wv有更高的 "内在秩",因为对于∆Wq ,两次运行都学到了更多的常见奇异值方向,这与我们在表6中的经验观察一致。 作为比较,我们还绘制了两个随机高斯矩阵,它们没有任何共同的奇异值方向上的相互影响。

自适应矩阵∆W与W相比如何?

我们进一步研究∆W和W之间的关系。特别是,∆W是否与W高度相关?(或者从数学上讲,∆W主要包含在W的顶部奇异方向上吗?)另外,与W中的方向相比,∆W有多"大"?这可以阐明适应预训练语言模型的基本机制。

为了回答这些问题,我们通过计算
\(U^{T}WV^{T}\)
,把W投射到∆W的r维子空间上,U/V是∆W的左/右奇异矢量矩阵。然后,我们比较
\(||U^{T}WV^{T}||\)
F 和
\(||W||\)
F之间的Frobenius norm。作为比较,我们还计算了
\(||U^{T}WV^{T}||\)
F,用W的前r个奇异向量或一个随机矩阵代替U、V。

我们从表7中得出几个结论。

  • 首先,与随机矩阵相比,∆W与W有更强的相关性,表明
    ∆W放大了W中已有的一些特征
  • 第二,∆W没有重复W的顶级奇异方向,而只是
    放大了W中没有强调的方向
  • 第三,放大系数相当大:21.5 6.91/0.32,r=4。关于为什么r=64的放大系数较小,请参见H.4节。我们还在第H.3节中提供了一个可视化的数据,说明当我们从Wq ,包括更多的顶级奇异方向时,相关性是如何变化的。 这表明,低
    秩适应矩阵可能会放大特定下游任务的重要特征,而这些特征在一般的预训练模型中已经学到,但没有得到强调

结论和未来工作

微调巨大的语言模型在所需的硬件和为不同任务托管独立实例的存储/切换成本方面是非常昂贵的。我们提出了LoRA,一种高效的适应策略,既不引入推理延迟,也不减少输入序列的长度,同时保留了高的模型质量。重要的是,它允许在作为服务部署时通过共享绝大多数的模型参数来实现快速的任务切换。虽然我们专注于Transformer语言模型,但提出的原则一般适用于任何具有密集层的神经网络。

未来的工作有很多方向。

  1. LoRA可以与其他有效的适应性方法相结合,有可能提供正交的改进。
  2. 微调或LoRA背后的机制还很不清楚--在预训练期间学到的特征是如何转变为在下游任务中表现良好的?我们认为,LoRA比完全的微调更容易回答这个问题。
  3. 我们主要依靠启发式方法来选择要应用LoRA的权重矩阵。是否有更多原则性的方法?
  4. 最后,∆W的秩缺陷表明,W也可能是秩缺陷的,这也可以成为未来工作的灵感来源。

最后,附上一篇改进ROLA的新论文,发表在ICLR2023上:
https://arxiv.org/pdf/2303.10512.pdf

引言

最近做了一个需求,是定时任务相关的。以前定时任务都是通过 linux crontab 去实现的,现在服务上云(k8s)了,尝试了 k8s 的 CronJob,由于公司提供的是界面化工具,使用、查看起来很不方便。于是有了本文,通过一个单 pod 去实现一个常驻服务,去跑定时任务。

经过筛选,选用了
cron
这个库,它支持 linux cronjob 语法取配置定时任务,还支持
@every 10s

@hourly
等描述符去配置定时任务,完全满足我们要求,比如下面的例子:

package main

import (
	"fmt"

	"github.com/natefinch/lumberjack"
	"github.com/robfig/cron/v3"
	"github.com/sirupsen/logrus"
)

type CronLogger struct {
	clog *logrus.Logger
}

func (l *CronLogger) Info(msg string, keysAndValues ...interface{}) {
	l.clog.WithFields(logrus.Fields{
		"data": keysAndValues,
	}).Info(msg)
}

func (l *CronLogger) Error(err error, msg string, keysAndValues ...interface{}) {
	l.clog.WithFields(logrus.Fields{
		"msg":  msg,
		"data": keysAndValues,
	}).Warn(err.Error())
}

func main() {
	logger := logrus.New()
	_logger := &lumberjack.Logger{
		Filename:   "./test.log",
		MaxSize:    50,
		MaxAge:     15,
		MaxBackups: 5,
	}

	logger.SetOutput(_logger)
	logger.SetFormatter(&logrus.JSONFormatter{
		DisableHTMLEscape: true,
	})

	c := cron.New(cron.WithLogger(&CronLogger{
		clog: logger,
	}))
	c.AddFunc("*/5 * * * *", func() {
		fmt.Println("你的流量包即将过期了")
	})
	c.AddFunc("*/2 * * * *", func() {
		fmt.Println("你的转码包即将过期了")
	})
	c.Start()

	for {
		select {}
	}
}

使用了 cronjob、并结合了 golang 的 log 组建,输出日志到文件,使用很方便。

但是,在使用过程中,发现还有些不足,缺少某些功能,比如我很想使用的查看任务列表。

类库介绍

扩展性强

此类库扩展性挺强,通过
JobWrapper
去包装一个任务,
NewChain(w1, w2, w3).Then(job)
,相关实现如下:

type JobWrapper func(Job) Job
type Chain struct {
	wrappers []JobWrapper
}
func NewChain(c ...JobWrapper) Chain {
	return Chain{c}
}
func (c Chain) Then(j Job) Job {
	for i := range c.wrappers {
		j = c.wrappers[len(c.wrappers)-i-1](j)
	}
	return j
}

比如当前脚本如果还没有执行完,下次任务时间又到了,就可以通过如下默认提供的 wrapper 去避免继续执行。可以看到最后执行的任务
j.Run()
被包装在了一个函数闭包中,并且根据闭包中的 channel 去判断是否执行,避免重复执行。首次执行的时候,容量为 1 的 channel 中已经有数据了,重复执行时,channel 无数据,默认跳过,等上次任务执行完成后,又像 channel 中写入一条数据,下次 channel 可以读出数据,又可以执行任务了:

func SkipIfStillRunning(j Job) Job {
	var ch = make(chan struct{}, 1)
	ch <- struct{}{}
	return FuncJob(func() {
		select {
		case v := <-ch:
			defer func() { ch <- v }()
			j.Run()
		default:
			// "skip"
		}
	})
}

主流程

cron 主流程是启动一个协程,里面有双重 for 循环,下面我们来一起分析一下。

定时器

第一层循环,首先计算下次最早执行任务的时间跟当前时间间隔 gap,然后设置定时器为 gap,这里很巧妙,定时器间隔不是 1s/次,而是跟下次任务的时间相关,这样就避免了无用的定时器循环,也让执行时间更精准,不存在设置小了浪费资源,设置大了误差大的情况。接下来进入第二层循环。

sort.Sort(byTime(c.entries))
timer = time.NewTimer(c.entries[0].Next.Sub(now))

事件循环

事件循环中,包含了很多事件,比如
添加任务

停止

移除任务
,当 cron 启动之后,这些任务都是异步的。比如添加任务,不会直接将任务信息写入内存中,而是进入事件循环,加入之后,重新计算第一二层循环,避免了正在修改任务信息,又执行任务信息,然后出错的情况。

有人可能会问了,为何不在事件中加锁,这样也能避免内存竞争。我想说,我们执行的是脚本任务,有的事件可能很长,可能会阻塞有些事件,所以这些事件都放在循环中,避免了加锁,也满足了要求。

for {
	select {
	case now = <-timer.C:
		// 执行任务
	case newEntry := <-c.add:
		// 添加任务
	case replyChan := <-c.snapshot:
		// 获取任务信息
	case <-c.stop:
		//  停止任务
	case id := <-c.remove:
		// 移除任务
	}
	break
}

类库改造

在了解了项目的基本情况之后,对项目做了部分改造,方便使用。

打印任务列表信息

在主循环汇总加入了信号量监听,当触发信号量
SIGUSR1
,将任务信息输出到日志:

usrSig := make(chan os.Signal, 1)
signal.Notify(usrSig, syscall.SIGUSR1)

for {
	select {
	case <-usrSig:
		// 启动单独的协程去打印定时任务执行信息
		continue
	}
	break
}

根据名称移除脚本

目前脚本只能根据脚本 id 去移除要执行的任务,执行过程中,也不能通过命令去移除任务,不是太方便。比如有个脚本马上要执行了,但是该脚本发现问题了,这时候生产环境的话,就需要更新代码,然后重启服务去下线脚本任务,这时候,黄花菜可能都凉了。

所以我也是通过信号量,来处理运行之后,运行中移除任务的问题,收到信号量之后,读取文件中的内容,根据命令去处理 runing 中的内存:

usrSig2 := make(chan os.Signal, 1)
signal.Notify(usrSig2, syscall.SIGUSR2)

......
case <-usrSig2:
	actionByte, err := os.ReadFile("/tmp/cron.action")
	...... //校验命令正确性
	action := strings.Fields(string(actionByte))
	switch action[0] {
	case "removeTag":
		timer.Stop()
		now = c.now()
		c.removeEntryByTag(action[1])
		c.logger.Info("removedByTag", "tag", action[1])
	}
......

改造效果

由于原项目已经 2 年多没有个更新过了,就算发起 pr 估计也不会被处理,所以 fork 一份放在了这里
aizuyan/cron
进行改造,下面是改进之后的代码:

package main

import (
	// 加载配置文件

	"fmt"

	"github.com/aizuyan/cron/v3"
)

func main() {
	c := cron.New(cron.WithLogger(cron.DefaultLogger))
	c.AddFuncWithTag("流量包过期", "*/5 * * * *", func() {
		fmt.Println("你的流量包即将过期了")
	})
	c.AddFuncWithTag("转码包过期", "*/2 * * * *", func() {
		fmt.Println("你的转码包即将过期了")
	})
	c.Start()

	for {
		select {}
	}
}

对每个定时任务增加了一个名称标识,当任务启动后,当我们执行
kill -SIGUSR1 <pid>
的时候,会看到 stdout 输出了运行的任务列表信息:

+----+------------+-------------+---------------------+---------------------+
| ID |    TAG     |    SPEC     |        PREV         |        NEXT         |
+----+------------+-------------+---------------------+---------------------+
|  2 | 转码包过期 | */2 * * * * | 0001-01-01 00:00:00 | 2023-04-02 17:22:00 |
|  1 | 流量包过期 | */5 * * * * | 0001-01-01 00:00:00 | 2023-04-02 17:25:00 |
+----+------------+-------------+---------------------+---------------------+

执行
kill -SIGUSR2 <pid>
,移除
转码包过期
任务,避免了使用 ID 容易出错的问题。

cat /tmp/cron.action 
removeTag 转码包过期
// {"data":["tag","转码包过期"],"level":"info","msg":"removedByTag","time":"2023-04-02T18:32:56+08:00"}

放目前为止,是不是更好用了,基本能满足我们的需求了,也可以自己去再做各种扩展。

作者:京东物流 李云敏

一、人工智能

1、人工智能(AI)是什么

人工智能,英文Artificial Intelligence,简称AI,是利用机器学习技术模拟、延伸和扩展人的智能的理论、方法、技术及应用的一门新的技术科学。

人工智能是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器,该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。 人工智能可以对人的意识、思维的信息过程的模拟。人工智能不是人的智能,但能像人那样思考、也可能超过人的智能。

通俗的说,就是让机器可以像人类一样有智能,让机器看得懂、听得懂、会思考、能决策、能行动,实现原来只有人类才能完成的任务。

2、人工智能(AI)的本质

AI的本质是通过软件来实现特定的算法。

一个优秀的人工智能系统,应该具有三个方面的特征:知识运用的能力、从数据或经验中学习的能力、处理不确定性的能力。

知识运用能力

知识是智能体现的一个最重要的维度。听说看能力如果不考虑内容的深度,则仅仅是停留在感知智能的层面,只能与环境交互和获取环境的信息,其智能表现的空间非常有限。一个智能系统应该能够很好的存储与表示、运用知识,并基于知识进行归纳推理。

学习能力

从数据中或过去的经验中学习的能力,这通常需要运用机器学习算法。具备一个不断进化和进步的学习能力,那么就可能具备更高的智能水平。同时,学习过程应该能够融入尽可能多的知识类信息,才能够达到支持智能系统的要求。

不确定性处理能力

能够很好地处理数据中不确定性,像噪声、数据属性缺失,模型决策的不确定性,甚至模型内部参数的不确定性。无人驾驶系统就需要处理各种各样的不确定性如环境的不确定性、决策的不确定性。

3、人工智能(AI)的“智力”层级

人工智能分为弱人工智能和强人工智能,前者让机器具备观察和感知的能力,可以做到一定程度的理解和推理。而强人工智能期待让机器获得自适应能力,解决一些之前没有遇到过的问题。

也有人将人工智能分为弱人工智能、一般人工智能和强人工智能,后超级人工智能。

人工智能分为弱人工智能和强人工智能,前者让机器具备观察和感知的能力,可以做到一定程度的理解和推理。目前的科研都集中在弱人工智能这部分。而强人工智能期待让机器获得自适应能力,解决一些之前没有遇到过的问题。

2017年发布的一项针对AI研究人员的调查报告称,高级机器智能(HLMI)实现的总体平均估计值是到2061年。

4、人工智能(AI)的应用领域

人工智能涉及广泛的技术应用

https://img-blog.csdnimg.cn/20200424151404995.gif#pic_center

目前人工智能应用最广泛的领域主要有四个,分别是语音识别和自然语言处理、图像识别与处理、推荐系统、机器学习。

语音识别,如语音的自动翻译、语音转文字等。目前微软的语音识别技术已经达到了人类同等水平,翻译机器人已经超越专业翻译水准。

图像识别,如高速车牌识别、人脸识别等,目前已经广泛应用在道路监控、停车场、门禁、金融系统访问身份识别等领域。刷脸解锁、刷脸支付也已经进入我们生活的很多领域。

推荐系统,如电商系统根据用户的购买习惯,推荐可能需要购买的产品;今日头条的内容推荐算法等。

5G+AI开启智能化物流新时代

注:图片资料来源——《2021中国物流科技发展报告》

二、人工智能和机器学习的关系

人脑具备不断积累经验的能力,依赖经验我们便具备了分析处理的能力,比如我们要去菜场挑一个西瓜,别人或者自己的经验告诉我们色泽青绿、根蒂蜷缩、敲声浑响的西瓜比较好吃。我们具备这样的能力,那么机器呢?机器不是只接收指令,处理指令吗?和人脑类似,可以喂给机器历史数据,机器依赖建模算法生成模型,根据模型便可以处理新的数据得到未知属性。许多机器学习系统所解决的都是无法直接使用固定规则或者流程代码完成的问题,通常这类问题对人类而言却很简单。比如,手机中的计算器程序就不属于具备智能的系统,因为里面的计算方法都有清楚而固定的规程;但是如果要求一台机器去辨别一张照片中都有哪些人或者物体,这对我们人类来讲非常容易,然后机器却非常难做到。

机器学习所研究的主要内容,是关于在计算机上从数据中产生“模型”的算法。即学习算法,有了学习算法,我们把数据提供给它,它就能基于这些数据产生模型;在面对新的数据时,模型会给我们提供相应的预测结果。

机器学习的按学习方式来可以划分四类: 监督学习、无监督学习、半监督学习和强化学习。

监督学习
指的就是我们给学习算法一个数据集。这个数据集由“正确答案”组成。关注的是对事物未知表现的预测,一般包括分类问题和回归问题。

无监督学习
,指在数据集中没有“正确答案”,期望从数据本身发现一些潜在的规律,无监督学习倾向于事物本身特性的分析,常用的技术包括数据降维和聚类问题。

半监督学习
,训练数据集中有一部分答案,一部分没答案的称为半监督学习。

强化学习
相对来说比较复杂,是指一个系统和外界环境不断地交互,获得外界反馈,然后决定自身的行为,达到长期目标的最优化。也就是从一开始什么都不懂, 通过不断地尝试, 从错误中学习, 最后找到规律, 学会了达到目的的方法。比如AlphaGo用的深度强化学习。

1、机器学习

2、机器学习三要素

机器学习三要素包括数据、模型、算法。简单来说,这三要素之间的关系,可以用下面这幅图来表示

总结成一句话:算法通过在数据上进行运算产生模型。

3、数据标注

如图中不同的动物,给它们分别打上正确的标记。通过算法训练后,达到正确分类的目的。要进行机器学习,首先要有数据。有了数据之后,再对数据进行标注,利用人工标注的数据给到机器进行学习,使机器智能化。

那实际项目中是怎么给数据打标注,为什么要给数据标注?带着这两个问题我们来看个视频。

https://www.thepaper.cn/newsDetail_forward_2052136

4、什么是模型?

大家来做下这个猜数字游戏,1, 4, 16…()… 256… 括号里的是什么。为什么是64,不是其他数字,又为什么是数字,不是一个汉字或者一个字母。我们找到了数字之间的规律,逻辑关系,并且抽象成了模型,我们才能知道括号里是什么。

举个生活中的例子,小米硬件中手机外壳,在大批量生产前需要先设计手机外壳的模具,然后所有同型号的手机外壳都按这个模具样版生产出来。这个模具也是个硬件上的模型。

算法的模型又是什么?模型是从数据里抽象出来的,用来描述客观世界的数学模型。通过对数据的分析,找到其中的规律,找到的规律就是模型。

机器学习的根本目的,是找一个模型去描述我们已经观测到的数据。

5、机器学习算法

例如,你可能会在研究论文和教科书中看到用伪代码或 线性代数 描述的机器学习算法。你可以看到一个特定的机器学习算法与另一个特性算法相比的计算效率。

学术界可以设计出很多机器学习算法,而机器学习实践者可以在他们的项目中使用标准的机器学习算法。这就像计算机科学的其他领域一样,学者可以设计出全新的排序算法,程序员可以在应用程序中使用标准的排序算法。

•线性回归

•逻辑回归

•决策树

•人工神经网络

•K- 最近邻

•K- 均值

https://img-blog.csdnimg.cn/20200424151404995.gif#pic_center

你还可能会看到多个机器学习算法实现,并在一个具有标准 API 的库中提供。一个流行的例子是 scikit-learn 库,它在 Python 中提供了许多分类、回归和聚类机器学习算法的实现。

三、AI算法模型测试

1、模型评估

泛化能力指的是学习方法对未知数据的预测能力。就好比运动员平时都是在训练场进行训练,而评估运动员的真实实力要看在大赛中的表现。

我们实际希望的,是在新样本上能表现得很好的学习器,为了达到这个目的,应该从训练样本中尽可能推演出适用于所有潜在样本的“普通规律”,这样才能在遇到新样本时做出正确的预测,泛化能力比较好。

当学习器把训练样本学得“太好”了的时候,很可能已经把训练样本自身的一些特点当作了所有潜在样本都会具有的一般性质,这样就会导致泛化性能下降。这种现象在机器学习中称为“过拟合“,与之相对是“欠拟合”指的是对训练样本的一般性质尚未学习。

有多种因素可能导致过拟合,其中最常见的情况是由于学习能力过于强大,以至于把训练样本所包含的不太一般的特性都学到了,而欠拟合则通常是由于学习能力低下而造成的。

2、衡量标准

首先有关TP、TN、FP、FN的概念。大体来看,TP与TN都是分对了情况,TP是正类,TN是负类。则推断出,FP是把错的分成了对的,而FN则是把对的分成了错的。

【举例】一个班里有男女生,我们来进行分类,把女生看成正类,男生看成是负类。我们可以用混淆矩阵来描述TP、TN、FP、FN。

混淆矩阵

准确率、召回率、F1

人工智能领域两个最基本指标是召回率(Recall Rate)和准确率(Precision Rate),召回率也叫查全率,准确率也叫查准率,概念公式:

◦召回率(Recall) = 系统检索到的相关文件 / 系统所有相关的文件总数

◦准确率(Precision) = 系统检索到的相关文件 / 系统所有检索到的文件总数

准确率和召回率是互相影响的,理想情况下肯定是做到两者都高,但是一般情况下准确率高、召回率就低,召回率低、准确率高,当然如果两者都低,那是什么地方出问题了。一般来说,精确度和召回率之间是矛盾的,这里引入F1-Score作为综合指标,就是为了平衡准确率和召回率的影响,较为全面地评价一个分类器。F1是精确率和召回率的调和平均。F1-score越大说明模型质量更高。一般情况,用不同的阀值,统计出一组不同阀值下的精确率和召回率,如下图:

评价指标跑出来看又怎么评判呢?我们来看下2016年的新闻

百度自动驾驶负责人王劲 2016年9月 去年的这个时候,我们的图象识别,识别汽车这一项,刚好也是89%。我们认为这个89%,要达到97%的准确率,需要花的时间,会远远超过5年。而人类要实现无人驾驶,主要靠摄像头来实现安全的保障的话,我们认为要多少呢?我们认为起码这个安全性的保障,要达到99.9999%,所以这个是一个非常非常远的一条路。我们认为不是5年,10年能够达得到的。 一般的人工智能系统,如搜索、翻译等可允许犯错,而无人驾驶系统与生命相关,模型性能要求很高。

在不同的领域,对召回率和准确率的要求不一样。如果是做搜索,那就是保证召回的情况下提升准确率;如果做疾病监测、反垃圾,则是保准确率的条件下,提升召回。所以,在两者都要求高的情况下,可以用F1来衡量。

3、质量属性

鲁棒性 (robustness)
,也就是所说健壮性,简单来说就是在模型在一些异常数据情况下是否也可以比较好的效果。也就是我们在最开始讲人工智能三个特征中的 处理不确定性的能力。

比如人脸识别,对于模糊的图片,人带眼镜,头发遮挡,光照不足等情况下的模型表现情况。 算法鲁棒性的要求简单来说就是 “好的时候”要好,“坏的时候”不能太坏。 在AlphaGo 和李世石对决中,李世石是赢了一盘的。李世石九段下出了“神之一手” Deepmind 团队透露:错误发生在第79手,但AlphaGo直到第87手才发觉,这期间它始终认为自己仍然领先。这里点出了一个关键问题:鲁棒性。人类犯错:水平从九段降到八段。机器犯错:水平从九段降到业余。

测试方法就是用尽可能多的异常数据来覆盖进行测试。

模型安全
,攻击方法有:试探性攻击、对抗性攻击两种

在试探性攻击中,攻击者的目的通常是通过一定的方法窃取模型,或是通过某种手段恢复一部分训练机器学习模型所用的数据来推断用户的某些敏感信息。主要分为模型窃取和训练数据窃取

对抗性攻击对数据源进行细微修改,让人感知不到,但机器学习模型接受该数据后做出错误的判断。比如图中的雪山,原本的预测准确率为94%,加上噪声图片后,就有99.99%的概率识别为了狗。

响应速度
是指从数据输入到模型预测输出结果的所需的时间。对算法运行时间的评价。

业务测试,
包括业务逻辑测试,业务 & 数据正确性测试。主要关注业务代码是否符合需求,逻辑是否正确,业务异常处理等情况。可以让产品经理提供业务的流程图,对整体业务流程有清晰的了解。

白盒测试
,先让算法工程师将代码的逻辑给测试人员讲解,通过讲解理清思路。然后测试做代码静态检查,看是否会有基本的bug。可以使用pylint工具来做代码分析。

模型监控,
项目发布到线上后,模型在线上持续运行,需要以固定间隔检测项目模型的实时表现,可以是每隔半个月或者一个月,通过性能指标对模型进行评估。对各指标设置对应阀值,当低于阀值触发报警。如果模型随着数据的演化而性能下降,说明模型已经无法拟合当前的数据了,就需要用新数据训练得到新的模型。

大数据辅助
,机器学习算法训练和验证是一个持续改进的过程。当数据量逐步放大时候,如何统计算法的准确率呢?这个时候需要引入大数据技术针对数据结果进行统计,根据周期性统计的准确率结果生成线性报表来反馈算法质量的变化。

四 、常见的机器学习平台或者工具

1、Tensorflow已经跃居第一位,贡献者增长了三位数。 Scikit-learn排名第二,但仍然有很大的贡献者基础。

TensorFlow最初是由研究人员和工程师在Google机器智能研究组织的Google Brain团队中开发的。该系统旨在促进机器学习的研究,并使其从研究原型到生产系统的快速和轻松过渡。

2、Scikit-learn是用于数据挖掘和数据分析的简单而有效的工具,可供所有人访问,并可在各种环境中重用,基于NumPy,SciPy和matplotlib,开源,商业可用 - BSD许可证。

3、K0. ,一种高级神经网络API,用Python编写,能够在TensorFlow,CNTK或Theano之上运行。

4、PyTorch,Tensors和Python中的动态神经网络,具有强大的GPU加速功能。

5、Theano允许您有效地定义,优化和评估涉及多维阵列的数学表达式。

6、Gensim是一个免费的Python库,具有可扩展的统计语义,分析语义结构的纯文本文档,检索语义相似的文档等功能。

7、Caffe是一个深刻的学习框架,以表达,速度和模块化为基础。它由伯克利视觉和学习中心(BVLC)和社区贡献者开发。

8、Chainer是一个基于Python的独立开源框架,适用于深度学习模型。 Chainer提供灵活,直观和高性能的方法来实现全方位的深度学习模型,包括最新的模型,如递归神经网络和变分自动编码器。

9、Statsmodels是一个Python模块,允许用户浏览数据,估计统计模型和执行统计测试。描述性统计,统计测试,绘图函数和结果统计的广泛列表可用于不同类型的数据和每个估算器。

10、Shogun是机器学习工具箱,提供各种统一和高效的机器学习(ML)方法。工具箱无缝地允许轻松组合多个数据表示,算法类和通用工具。

11、Pylearn2是一个机器学习库。它的大部分功能都建立在Theano之上。这意味着您可以使用数学表达式编写Pylearn2插件(新模型,算法等),Theano将为您优化和稳定这些表达式,并将它们编译为您选择的后端(CPU或GPU)。

12、NuPIC是一个基于新皮层理论的开源项目,称为分层时间记忆(HTM)。HTM理论的一部分已经在应用中得到实施,测试和使用,HTM理论的其他部分仍在开发中。

13、Neon是Nervana基于Python的深度学习库。它提供易用性,同时提供最高性能。

14、Nilearn是一个Python模块,用于快速简便地统计NeuroImaging数据。它利用scikit-learn Python工具箱进行多变量统计,并使用预测建模,分类,解码或连接分析等应用程序。

15、Orange3是新手和专家的开源机器学习和数据可视化。具有大型工具箱的交互式数据分析工作流程。

16、Pymc是一个python模块,它实现贝叶斯统计模型和拟合算法,包括马尔可夫链蒙特卡罗。其灵活性和可扩展性使其适用于大量问题。

17、Deap是一种新颖的进化计算框架,用于快速原型设计和思想测试。它旨在使算法明确,数据结构透明。它与多处理和SCOOP等并行机制完美协调。

18、Annoy是一个带有Python绑定的C ++库,用于搜索空间中接近给定查询点的点。它还创建了大型只读基于文件的数据结构,这些数据结构映射到内存中,以便许多进程可以共享相同的数据。

19、PyBrain是一个用于Python的模块化机器学习库。其目标是为机器学习任务和各种预定义环境提供灵活,易用且功能强大的算法,以测试和比较您的算法。

20、Fuel是一个数据管道框架,为您的机器学习模型提供所需的数据。计划由Blocks和Pylearn2神经网络库使用。

通过上述列出的一堆工具发现,基本上都支持python,python提供了大量的人工智能机器学习相关的API,是首选语言。

各大厂机器学习平台

1. 微软的机器学习平台
https://studio.azureml.net/

2. Facebook 的应用机器学习平台

https://research.fb.com/publications/applied-machine-learning-at-facebook-a-datacenter-infrastructure-perspective/

3. Uber的机器学习平台

https://eng.uber.com/scaling-michelangelo/

4. Twitter的机器学习平台

https://mp.weixin.qq.com/s?__biz=MzU1NDA4NjU2MA==&mid=2247486445&idx=1&sn=f244fe2f1657c4affac0b93e33b74a65&chksm=fbe9b222cc9e3b34c19af38c35ab49cef5a2129c461ff4ab29d63497d21aee196e92fcbb642e&scene=27#wechat_redirect

5.Databricks 开源机器学习平台 MLflow

https://mlflow.org/docs/latest/concepts.html

6.百度机器学习 BML

https://cloud.baidu.com/doc/BML/s/Wjxbindt7

7. 阿里PAI

https://help.aliyun.com/document_detail/72285.html?spm=a2c4g.66666674359.6.544.4da35d87h2vsGy

8. 腾讯机器学习平台

https://cloud.tencent.com/document/product/851

9.京东JD neuCube

https://neuhub.jd.com/neuCube

10.美团点评MLX平台

https://www.infoq.cn/article/spark-flink-carbondata-best-practice

11. 滴滴机器学习平台

https://www.infoq.cn/article/jJ4pjkf8Huf-WVlE7Xw7

12. 华为MLS

https://support.huaweicloud.com/productdesc-mls/zh-cn_topic_0122559740.html

13.金山云智机器学习平台 (KML)

https://www.ksyun.com/post/product/KML

14.第四范式

https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/79492729

五、参考资料

1、人工智能中RPA、NLP、OCR介绍:
https://blog.csdn.net/sdhgfhdshjd/article/details/115342671

2、机器学习入门(一):机器学习三要素之数据、模型、算法:
https://blog.csdn.net/liujian197905187511/article/details/104815578?utm_medium=distribute.pc_relevant.none-task-blog-2
default
baidujs_title~default-0.essearch_pc_relevant&spm=1001.2101.3001.4242

3、AI算法实现:
https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/79492729

本文来自博客园,作者:
T-BARBARIANS
,转载请注明原文链接:
https://www.cnblogs.com/t-bar/p/17262147.html
谢谢!

前言


朋友们有想过居然还有比memcpy更快的内存拷贝吗?

讲道理,在这之前我没想到过,我也一直觉得memcpy就是最快的内存拷贝方法了。

也不知道老板最近是咋了,天天开会都强调:“我们最近的目标就一个字:性能优化!”

一顿操作猛如虎,也没提高5%。感觉自己实在是黔驴技穷,江郎才尽,想到又要被老板骂立马滚蛋,心里就很不是滋味。

所谓车到山前必有路,船到桥头自然直。嘿,有一天我刚好注意到我们的业务代码里有大量的memcpy,正一筹莫展之时,突然灵光一现,脑海里闪过一个想法:memcpy还可以优化吗?

我想说,正是这个想法又让我可以在老板面前暂时苟且偷生一段时间,实在是不得不佩服自己!

一、SIMD技术简介

这一小节介绍的内容跟小节标题很契合,就是介绍一下SIMD(Single Instruction Multiple Data,单指令多数据),
啥意思呢,就是一条指令并发处理多条数据。形象一点讲就是老板在桌上放了很多钱让你拿,有同学喜欢一张一张的拿,还说我喜欢这种慢慢富有的感觉;SIMD就是,老子一把拿,我踏马喜欢暴富!没错,它就是可以提升memcpy性能的关键核心技术。引用大佬画的一张图:

图1

Scalar Operation就是指的SISD(Single Instruction Single Data,单指令单数据),这种方式完成上图所有C[i]的计算需要串行执行八次,因为每个时间点,CPU的一条指令只能执行一份数据。

SIMD,就是一次运算就可以得到上述SISD的多次运算结果,即一条指令可以并发执行多份数据,因此SIMD也称为向量化计算。

到底是什么奇技淫巧使得SIMD具有并发执行多份数据的能力呢?

其实就是CPU增加了专门用于向量化计算的向量寄存器,这些寄存器跟普通的寄存器不太一样,它们的位宽都比较大,比如有128bit,256bit,甚至512bit,也就是说这些寄存器可以分别一次存储16byte,32byte,64byte的数据。比如上图的加法运算,SISD一条指令只能完成一次两个8byte数据的加法运算。但是SIMD,一条指令就可以完成a[0:7] + b[0:7] = c[0:7],两组数据的加法运算。

CPU除了增加向量寄存器,还为向量寄存器配套了专门的指令集,比如Intel的MMX,SSE(MMX的升级版),AVX(SSE的升级版)指令集。CPU运算时,识别到指令集命令,就会采用指令集对应的SIMD计算方法完成并发运算。Intel指令集查询链接:
http://kntan.top/#!=undefined

二、memcpy_fast方法


带着memcpy是否还可以继续优化的疑问,一通搜索,真找到了采用SIMD技术的memcpy方法:memcpy_fast,链接:
https://github.com/skywind3000/FastMemcpy

分析了一下源码实现

(1)SSE指令集实现的fast拷贝

1、使用_mm_loadu_si128指令,从src + 0的位置取走128bit,即16字节,然后依次类推,src + 1,...,直至src + 7,一共取走16byte * 8=128byte,取出的内容分别储存到向量寄存器c0,c1,...,c7;

2、使用_mm_prefetch实现数据预取,提前把数据从内存加载到cache,保证CPU对数据的快速读取;

3、使用_mm_store_si128指令,将c0,c1,...,c7寄存器的内容分别存储至目的地址dst + 0, dst + 1,..., dst + 7的八个位置。

利用指令集、向量寄存器、数据预取技术实现了每次16byte的并发,128byte的批次拷贝。

图2

(2)AVX指令集实现的fast拷贝

与SSE指令集实现内存拷贝逻辑一致。

1、由AVX指令集的_mm256_loadu_si256,实现每次256byte的数据加载;

2、由AVX指令集的_mm256_storeu_si256,实现每次256byte数据的存储。

可以预料,当然是寄存器位宽越大,性能会越好,也就是从理论上说使用AVX指令集会比SSE指令集更快。

图3


、memcpy
VS
memcpy_fast

我们一起来看看memcpy与使用了SIMD技术的memcpy_fast的性能对比吧。

直接将memcpy_fast源码下载后编译即可,链接:
https://github.com/skywind3000/FastMemcpy

SSE指令集编译命令:gcc -O3 -msse2 FastMemcpy.c -o FastMemcpy

AVX指令集编译命令:gcc -O3 -mavx FastMemcpy_Avx.c -o FastMemcpy_Avx

(1)SSE指令集下性能结果对比

绿色框里,即内存拷贝在1MB以下时,特别是拷贝长度在(1024 ~ 1048576)bytes时,拷贝性能有显著提升。
但是靠拷贝长度超过1MB时,memcpy_fast居然比memcpy更慢了,发生了什么?

图4

继续查阅源码,发现在大于2MB时,与2MB长度以下的拷贝相比,采用了不同的SIMD拷贝指令。
即在拷贝长度小于等于 cachesize = 0x200000 时,使用
_mm_store_si128
进行数据存储;在大于0x200000 时,使用
_mm_stream_si128
进行数据存储。

图5

我把大长度数据拷贝由
_mm_stream_si128
替换为中等长度数据拷贝指令
_mm_store_si128
后,memcpy_fast无论是中等长度,还是大长度的数据拷贝性能都比memcpy要好。

图6
(2)AVX指令集下性能结果对比

同样,将AVX大长度数据拷贝也进行优化,将指令
_mm256_stream_si256
替换为
_mm256_storeu_si256
,AVX指令集的性能测试结果如下图7所示。

简单总结为两点:

1、图6和图7进行了充分说明,相同长度的数据拷贝,AVX确实比SSE性能更高;

2、拷贝长度在(512 ~ 8388608)bytes,memcpy_fast都比memcpy要提升一倍不止,有的长度,内存拷贝性能甚至提升了4倍!


图7

四、结语

这种内存拷贝的性能提升,有什么好处呢?

想到一个场景,比如生产环境的网关设备(FW,VPN等等),内存拷贝的性能提升可以降低网关设备的流量处理时延,提升网络质量,从而进一步提高用户使用体验。

把这份优化思路给老板做了汇报,老板扬起嘴角笑了笑并说道:“对你来说,饼可能不香了!”

技术是不断实践积累的,在此分享出来与大家一起共勉!

如果文章对你有些许帮助,还请各位技术爱好者登录点赞呀,非常感谢!

本文来自博客园,作者:
T-BARBARIANS
,转载请注明原文链接:
https://www.cnblogs.com/t-bar/p/17262147.html
谢谢!