ChatGPT/InstructGPT详解

作者:京东零售 刘岩

前言

GPT系列是OpenAI的一系列预训练文章,GPT的全称是Generative Pre-Trained Transformer,顾名思义,GPT的目的就是通过Transformer为基础模型,使用预训练技术得到通用的文本模型。目前已经公布论文的有文本预训练GPT-1,GPT-2,GPT-3,以及图像预训练iGPT。据传还未发布的GPT-4是一个多模态模型。最近非常火的ChatGPT和今年年初公布的[1]是一对姐妹模型,是在GPT-4之前发布的预热模型,有时候也被叫做GPT3.5。ChatGPT和InstructGPT在模型结构,训练方式上都完全一致,即都使用了指示学习(Instruction Learning)和人工反馈的强化学习(Reinforcement Learning from Human Feedback,RLHF)来指导模型的训练,它们不同的仅仅是采集数据的方式上有所差异。所以要搞懂ChatGPT,我们必须要先读懂InstructGPT。

1. 背景知识

在介绍ChatGPT/InstructGPT之前,我们先介绍它们依赖的基础算法。

1.1 GPT系列

基于文本预训练的GPT-1[2],GPT-2[3],GPT-3[4]三代模型都是采用的以Transformer为核心结构的模型(图1),不同的是模型的层数和词向量长度等超参,它们具体的内容如表1。

图1:GPT系列的模型结构(其中Trm是一个Transformer结构)

表1:历代GPT的发布时间,参数量以及训练量

模型 发布时间 层数 头数 词向量长度 参数量 预训练数据量
GPT-1 2018 年 6 月 12 12 768 1.17 亿 约 5GB
GPT-2 2019 年 2 月 48 - 1600 15 亿 40GB
GPT-3 2020 年 5 月 96 96 12888 1,750 亿 45TB

GPT-1比BERT诞生略早几个月。它们都是采用了Transformer为核心结构,不同的是GPT-1通过自左向右生成式的构建预训练任务,然后得到一个通用的预训练模型,这个模型和BERT一样都可用来做下游任务的微调。GPT-1当时在9个NLP任务上取得了SOTA的效果,但GPT-1使用的模型规模和数据量都比较小,这也就促使了GPT-2的诞生。

对比GPT-1,GPT-2并未在模型结构上大作文章,只是使用了更多参数的模型和更多的训练数据(表1)。GPT-2最重要的思想是提出了“所有的有监督学习都是无监督语言模型的一个子集”的思想,这个思想也是提示学习(Prompt Learning)的前身。GPT-2在诞生之初也引发了不少的轰动,它生成的新闻足以欺骗大多数人类,达到以假乱真的效果。甚至当时被称为“AI界最危险的武器”,很多门户网站也命令禁止使用GPT-2生成的新闻。

GPT-3被提出时,除了它远超GPT-2的效果外,引起更多讨论的是它1750亿的参数量。GPT-3除了能完成常见的NLP任务外,研究者意外的发现GPT-3在写SQL,JavaScript等语言的代码,进行简单的数学运算上也有不错的表现效果。GPT-3的训练使用了情境学习(In-context Learning),它是元学习(Meta-learning)的一种,元学习的核心思想在于通过少量的数据寻找一个合适的初始化范围,使得模型能够在有限的数据集上快速拟合,并获得不错的效果。

通过上面的分析我们可以看出从性能角度上讲,GPT有两个目标:

  1. 提升模型在常见NLP任务上的表现效果;
  2. 提升模型在其他非典型NLP任务(例如代码编写,数学运算)上的泛化能力。

另外,预训练模型自诞生之始,一个备受诟病的问题就是预训练模型的偏见性。因为预训练模型都是通过海量数据在超大参数量级的模型上训练出来的,对比完全由人工规则控制的专家系统来说,预训练模型就像一个黑盒子。没有人能够保证预训练模型不会生成一些包含种族歧视,性别歧视等危险内容,因为它的几十GB甚至几十TB的训练数据里几乎肯定包含类似的训练样本。这也就是InstructGPT和ChatGPT的提出动机,论文中用3H概括了它们的优化目标:

  • 有用的(Helpful);
  • 可信的(Honest);
  • 无害的(Harmless)。

OpenAI的GPT系列模型并没有开源,但是它们提供了模型的试用网站,有条件的同学可以自行试用。

1.2 指示学习(Instruct Learning)和提示(Prompt Learning)学习

指示学习是谷歌Deepmind的Quoc V.Le团队在2021年的一篇名为《Finetuned Language Models Are Zero-Shot Learners》[5]文章中提出的思想。指示学习和提示学习的目的都是去挖掘语言模型本身具备的知识。不同的是Prompt是激发语言模型的补全能力,例如根据上半句生成下半句,或是完形填空等。Instruct是激发语言模型的理解能力,它通过给出更明显的指令,让模型去做出正确的行动。我们可以通过下面的例子来理解这两个不同的学习方式:

  1. 提示学习:给女朋友买了这个项链,她很喜欢,这个项链太____了。
  2. 指示学习:判断这句话的情感:给女朋友买了这个项链,她很喜欢。选项:A=好;B=一般;C=差。

指示学习的优点是它经过多任务的微调后,也能够在其他任务上做zero-shot,而提示学习都是针对一个任务的。泛化能力不如指示学习。我们可以通过图2来理解微调,提示学习和指示学习。

图2:模型微调,提示学习,指示学习三者的异同

1.3 人工反馈的强化学习

因为训练得到的模型并不是非常可控的,模型可以看做对训练集分布的一个拟合。那么反馈到生成模型中,训练数据的分布便是影响生成内容的质量最重要的一个因素。有时候我们希望模型并不仅仅只受训练数据的影响,而是人为可控的,从而保证生成数据的有用性,真实性和无害性。论文中多次提到了对齐(Alignment)问题,我们可以理解为模型的输出内容和人类喜欢的输出内容的对齐,人类喜欢的不止包括生成内容的流畅性和语法的正确性,还包括生成内容的有用性、真实性和无害性。

我们知道强化学习通过奖励(Reward)机制来指导模型训练,奖励机制可以看做传统模训练机制的损失函数。奖励的计算要比损失函数更灵活和多样(AlphaGO的奖励是对局的胜负),这带来的代价是奖励的计算是不可导的,因此不能直接拿来做反向传播。强化学习的思路是通过对奖励的大量采样来拟合损失函数,从而实现模型的训练。同样人类反馈也是不可导的,那么我们也可以将人工反馈作为强化学习的奖励,基于人工反馈的强化学习便应运而生。

RLHF最早可以追溯到Google在2017年发表的《Deep Reinforcement Learning from Human Preferences》[6],它通过人工标注作为反馈,提升了强化学习在模拟机器人以及雅达利游戏上的表现效果。

图3:人工反馈的强化学习的基本原理

InstructGPT/ChatGPT中还用到了强化学习中一个经典的算法:OpenAI提出的最近策略优化(Proximal Policy Optimization,PPO)[7]。PPO算法是一种新型的Policy Gradient算法,Policy Gradient算法对步长十分敏感,但是又难以选择合适的步长,在训练过程中新旧策略的的变化差异如果过大则不利于学习。PPO提出了新的目标函数可以在多个训练步骤实现小批量的更新,解决了Policy Gradient算法中步长难以确定的问题。其实TRPO也是为了解决这个思想但是相比于TRPO算法PPO算法更容易求解。

2. InstructGPT/ChatGPT原理解读

有了上面这些基础知识,我们再去了解InstructGPT和ChatGPT就会简单很多。简单来说,InstructGPT/ChatGPT都是采用了GPT-3的网络结构,通过指示学习构建训练样本来训练一个反应预测内容效果的奖励模型(RM),最后通过这个奖励模型的打分来指导强化学习模型的训练。InstructGPT/ChatGPT的训练流程如图4所示。

图4:InstructGPT的计算流程:(1)有监督微调(SFT);(2)奖励模型(RM)训练;(3)通过PPO根据奖励模型进行强化学习。

从图4中我们可以看出,InstructGPT/ChatGPT的训练可以分成3步,其中第2步和第3步是的奖励模型和强化学习的SFT模型可以反复迭代优化。

  1. 根据采集的SFT数据集对GPT-3进行有监督的微调(Supervised FineTune,SFT);
  2. 收集人工标注的对比数据,训练奖励模型(Reword Model,RM);
  3. 使用RM作为强化学习的优化目标,利用PPO算法微调SFT模型。

根据图4,我们将分别介绍InstructGPT/ChatGPT的数据集采集和模型训练两个方面的内容。

2.1 数据集采集

如图4所示,InstructGPT/ChatGPT的训练分成3步,每一步需要的数据也有些许差异,下面我们分别介绍它们。

2.1.1 SFT数据集

SFT数据集是用来训练第1步有监督的模型,即使用采集的新数据,按照GPT-3的训练方式对GPT-3进行微调。因为GPT-3是一个基于提示学习的生成模型,因此SFT数据集也是由提示-答复对组成的样本。SFT数据一部分来自使用OpenAI的PlayGround的用户,另一部分来自OpenAI雇佣的40名标注工(labeler)。并且他们对labeler进行了培训。在这个数据集中,标注工的工作是根据内容自己编写指示,并且要求编写的指示满足下面三点:

  • 简单任务:labeler给出任意一个简单的任务,同时要确保任务的多样性;
  • Few-shot任务:labeler给出一个指示,以及该指示的多个查询-相应对;
  • 用户相关的:从接口中获取用例,然后让labeler根据这些用例编写指示。

2.1.2 RM数据集

RM数据集用来训练第2步的奖励模型,我们也需要为InstructGPT/ChatGPT的训练设置一个奖励目标。这个奖励目标不必可导,但是一定要尽可能全面且真实的对齐我们需要模型生成的内容。很自然的,我们可以通过人工标注的方式来提供这个奖励,通过人工对可以给那些涉及偏见的生成内容更低的分从而鼓励模型不去生成这些人类不喜欢的内容。InstructGPT/ChatGPT的做法是先让模型生成一批候选文本,让后通过labeler根据生成数据的质量对这些生成内容进行排序。

2.1.3 PPO数据集

InstructGPT的PPO数据没有进行标注,它均来自GPT-3的API的用户。既又不同用户提供的不同种类的生成任务,其中占比最高的包括生成任务(45.6%),QA(12.4%),头脑风暴(11.2%),对话(8.4%)等。

2.1.4 数据分析

因为InstructGPT/ChatGPT是在GPT-3基础上做的微调,而且因为涉及了人工标注,它们数据总量并不大,表2展示了三份数据的来源及其数据量。

表2:InstructGPT的数据分布

论文的附录A对数据的分布进行了更详细的讨论,这里我列出几个可能影响模型效果的几项:

  • 数据中96%以上是英文,其它20个语种例如中文,法语,西班牙语等加起来不到4%,这可能导致InstructGPT/ChatGPT能进行其它语种的生成,但效果应该远不如英文;
  • 提示种类共有9种,而且绝大多数是生成类任务,可能会导致模型有覆盖不到的任务类型;
  • 40名外包员工来自美国和东南亚,分布比较集中且人数较少, InstructGPT/ChatGPT的目标是训练一个价值观正确的预训练模型,它的价值观是由这40个外包员工的价值观组合而成。而这个比较窄的分布可能会生成一些其他地区比较在意的歧视,偏见问题。

此外,ChatGPT的博客中讲到ChatGPT和InstructGPT的训练方式相同,不同点仅仅是它们采集数据上有所不同,但是并没有更多的资料来讲数据采集上有哪些细节上的不同。考虑到ChatGPT仅仅被用在对话领域,这里我猜测ChatGPT在数据采集上有两个不同:1. 提高了对话类任务的占比;2. 将提示的方式转换Q&A的方式。当然这里也仅仅是猜测,更准确的描述要等到ChatGPT的论文、源码等更详细的资料公布我们才能知道。

2.2 训练任务

我们刚介绍到InstructGPT/ChatGPT有三步训练方式。这三步训练会涉及三个模型:SFT,RM以及PPO,下面我们详细介绍它们。

2.2.1 有监督微调(SFT)

这一步的训练和GPT-3一致,而且作者发现让模型适当过拟合有助于后面两步的训练。

2.2.2 奖励模型(RM)

因为训练RM的数据是一个labeler根据生成结果排序的形式,所以它可以看做一个回归模型。RM结构是将SFT训练后的模型的最后的嵌入层去掉后的模型。它的输入是prompt和Reponse,输出是奖励值。具体的讲,对弈每个prompt,InstructGPT/ChatGPT会随机生成 K 个输出( 4≤K≤9 ),然后它们向每个labeler成对的展示输出结果,也就是每个prompt共展示 CK2 个结果,然后用户从中选择效果更好的输出。在训练时,InstructGPT/ChatGPT将每个prompt的 CK2 个响应对作为一个batch,这种按prompt为batch的训练方式要比传统的按样本为batch的方式更不容易过拟合,因为这种方式每个prompt会且仅会输入到模型中一次。

奖励模型的损失函数表示为式(1)。这个损失函数的目标是最大化labeler更喜欢的响应和不喜欢的响应之间的差值。

(1)loss⁡(θ)=−1(K2)E(x,yw,yl)∼D[log⁡(σ(rθ(x,yw)−rθ(x,yl)))]

其中 rθ(x,y) 是提示 x 和响应 y 在参数为 θ 的奖励模型下的奖励值, yw 是labeler更喜欢的响应结果, yl 是labeler不喜欢的响应结果。 D 是整个训练数据集。

2.2.3 强化学习模型(PPO)

强化学习和预训练模型是最近两年最为火热的AI方向之二,之前不少科研工作者说强化学习并不是一个非常适合应用到预训练模型中,因为很难通过模型的输出内容建立奖励机制。而InstructGPT/ChatGPT反直觉的做到了这点,它通过结合人工标注,将强化学习引入到预训练语言模型是这个算法最大的创新点。

如表2所示,PPO的训练集完全来自API。它通过第2步得到的奖励模型来指导SFT模型的继续训练。很多时候强化学习是非常难训练的,InstructGPT/ChatGPT在训练过程中就遇到了两个问题:

  1. 问题1:随着模型的更新,强化学习模型产生的数据和训练奖励模型的数据的差异会越来越大。作者的解决方案是在损失函数中加入KL惩罚项 βlog⁡(πϕRL(y∣x)/πSFT(y∣x)) 来确保PPO模型的输出和SFT的输出差距不会很大。
  2. 问题2:只用PPO模型进行训练的话,会导致模型在通用NLP任务上性能的大幅下降,作者的解决方案是在训练目标中加入了通用的语言模型目标 γEx∼Dpretrain [log⁡(πϕRL(x))] ,这个变量在论文中被叫做PPO-ptx。

综上,PPO的训练目标为式(2)。 (2) objective (ϕ)=E(x,y)∼DπϕRL[rθ(x,y)−βlog⁡(πϕRL(y∣x)/πSFT(y∣x))]+γEx∼Dpretrain [log⁡(πϕRL(x))]

3. InstructGPT/ChatGPT的性能分析

不可否认的是,InstructGPT/ChatGPT的效果是非常棒的,尤其是引入了人工标注之后,让模型的“价值观”和的正确程度和人类行为模式的“真实性”上都大幅的提升。那么,仅仅根据InstructGPT/ChatGPT的技术方案和训练方式,我们就可以分析出它可以带来哪些效果提升呢?

3.1 优点

  • InstructGPT/ChatGPT的效果比GPT-3更加真实:这个很好理解,因为GPT-3本身就具有非常强的泛化能力和生成能力,再加上InstructGPT/ChatGPT引入了不同的labeler进行提示编写和生成结果排序,而且还是在GPT-3之上进行的微调,这使得我们在训练奖励模型时对更加真实的数据会有更高的奖励。作者也在TruthfulQA数据集上对比了它们和GPT-3的效果,实验结果表明甚至13亿小尺寸的PPO-ptx的效果也要比GPT-3要好。
  • InstructGPT/ChatGPT在模型的无害性上比GPT-3效果要有些许提升:原理同上。但是作者发现InstructGPT在歧视、偏见等数据集上并没有明显的提升。这是因为GPT-3本身就是一个效果非常好的模型,它生成带有有害、歧视、偏见等情况的有问题样本的概率本身就会很低。仅仅通过40个labeler采集和标注的数据很可能无法对模型在这些方面进行充分的优化,所以会带来模型效果的提升很少或者无法察觉。
  • InstructGPT/ChatGPT具有很强的Coding能力:首先GPT-3就具有很强的Coding能力,基于GPT-3制作的API也积累了大量的Coding代码。而且也有部分OpenAI的内部员工参与了数据采集工作。通过Coding相关的大量数据以及人工标注,训练出来的InstructGPT/ChatGPT具有非常强的Coding能力也就不意外了。

3.2 缺点

  • InstructGPT/ChatGPT会降低模型在通用NLP任务上的效果:我们在PPO的训练的时候讨论了这点,虽然修改损失函数可以缓和,但这个问题并没有得到彻底解决。

  • 有时候InstructGPT/ChatGPT会给出一些荒谬的输出:虽然InstructGPT/ChatGPT使用了人类反馈,但限于人力资源有限。影响模型效果最大的还是有监督的语言模型任务,人类只是起到了纠正作用。所以很有可能受限于纠正数据的有限,或是有监督任务的误导(只考虑模型的输出,没考虑人类想要什么),导致它生成内容的不真实。就像一个学生,虽然有老师对他指导,但也不能确定学生可以学会所有知识点。

  • 模型对指示非常敏感:这个也可以归结为labeler标注的数据量不够,因为指示是模型产生输出的唯一线索,如果指示的数量和种类训练的不充分的话,就可能会让模型存在这个问题。

  • 模型对简单概念的过分解读:这可能是因为labeler在进行生成内容的比较时,倾向于给给长的输出内容更高的奖励。

  • 对有害的指示可能会输出有害的答复:例如InstructGPT/ChatGPT也会对用户提出的“AI毁灭人类计划书”给出行动方案(图5)。这个是因为InstructGPT/ChatGPT假设labeler编写的指示是合理且价值观正确的,并没有对用户给出的指示做更详细的判断,从而会导致模型会对任意输入都给出答复。虽然后面的奖励模型可能会给这类输出较低的奖励值,但模型在生成文本时,不仅要考虑模型的价值观,也要考虑生成内容和指示的匹配度,有时候生成一些价值观有问题的输出也是可能的。

图5:ChatGPT编写的毁灭人类计划书。

3.3 未来工作

我们已经分析了InstrcutGPT/ChatGPT的技术方案和它的问题,那么我们也可以看出InstrcutGPT/ChatGPT的优化角度有哪些了。

  • 人工标注的降本增效:InstrcutGPT/ChatGPT雇佣了40人的标注团队,但从模型的表现效果来看,这40人的团队是不够的。如何让人类能够提供更有效的反馈方式,将人类表现和模型表现有机和巧妙的结合起来是非常重要的。
  • 模型对指示的泛化/纠错等能力:指示作为模型产生输出的唯一线索,模型对他的依赖是非常严重的,如何提升模型对指示的泛化能力以及对错误指示示的纠错能力是提升模型体验的一个非常重要的工作。这不仅可以让模型能够拥有更广泛的应用场景,还可以让模型变得更“智能”。
  • 避免通用任务性能下降:这里可能需要设计一个更合理的人类反馈的使用方式,或是更前沿的模型结构。因为我们讨论了InstrcutGPT/ChatGPT的很多问题可以通过提供更多labeler标注的数据来解决,但这会导致通用NLP任务更严重的性能下降,所以需要方案来让生成结果的3H和通用NLP任务的性能达到平衡。

3.4 InstrcutGPT/ChatGPT的热点话题解答

  • ChatGPT的出现会不会导致底层程序员失业? 从ChatGPT的原理和网上漏出的生成内容来看,ChatGPT生成的代码很多可以正确运行。但程序员的工作不止是写代码,更重要的是找到问题的解决方案。所以ChatGPT并不会取代程序员,尤其是高阶程序员。相反它会向现在很多的代码生成工具一样,成为程序员写代码非常有用的工具。
  • Stack Overflow 宣布临时规则:禁止 ChatGPT。 ChatGPT本质上还是一个文本生成模型,对比生成代码,它更擅长生成以假乱真的文本。而且文本生成模型生成的代码或者解决方案并不能保证是可运行而且是可以解决问题的,但它以假乱真的文本又会迷惑很多查询这个问题的人。Stack Overflow为了维持论坛的质量,封禁ChatGPT也是清理之中。
  • 聊天机器人 ChatGPT 在诱导下写出「毁灭人类计划书」,并给出代码,AI 发展有哪些问题需关注? ChatGPT的「毁灭人类计划书」是它在不可遇见的指示下根据海量数据强行拟合出来的生成内容。虽然这些内容看起来很真实,表达也很流畅,这说明的只是ChatGPT具有非常强的生成效果,并不表示ChatGPT具备毁灭人类的思想。因为他仅仅是一个文本生成模型,并不是一个决策模型。

4. 总结

就像很多人们算法刚诞生时一样,ChatGPT凭借有用性,真实性,无害性的效果,引起了业内广泛的关注和人类对AI的思考。但是当我们看完它的算法原理之后,发现它并没有业内宣传的那么恐怖。反而我们可以从它的技术方案中学到很多有价值的东西。InstrcutGPT/ChatGPT在AI界最重要的贡献是将强化学习和预训练模型巧妙的结合起来。而且通过人工反馈提升了模型的有用性,真实性和无害性。ChatGPT也进一步提升大模型的成本,之前还只是比拼数据量和模型规模,现在甚至也引入了雇佣的外包这一支出,让个体工作者更加望而却步。

参考

  1. ^Ouyang, Long, et al. "Training language models to follow instructions with human feedback." arXiv preprint arXiv:2203.02155 (2022). http://arxiv.org/pdf/2203.02155.pdf
  2. ^Radford, A., Narasimhan, K., Salimans, T. and Sutskever, I., 2018. Improving language understanding by generative pre-training. http://www.cs.ubc.ca/~amuham01/LING530/papers/radford2018improving.pdf
  3. ^Radford, A., Wu, J., Child, R., Luan, D., Amodei, D. and Sutskever, I., 2019. Language models are unsupervised multitask learners. OpenAI blog, 1(8), p.9. http://life-extension.github.io/2020/05/27/GPT技术初探/language-models.pdf
  4. ^Brown, Tom B., Benjamin Mann, Nick Ryder, Melanie Subbiah, Jared Kaplan, Prafulla Dhariwal, Arvind Neelakantan et al. “Language models are few-shot learners.” arXiv preprint arXiv:2005.14165 (2020). http://proceedings.neurips.cc/paper/2020/file/1457c0d6bfcb4967418bfb8ac142f64a-Paper.pdf
  5. ^Wei, Jason, et al. "Finetuned language models are zero-shot learners." arXiv preprint arXiv:2109.01652 (2021). http://arxiv.org/pdf/2109.01652.pdf
  6. ^Christiano, Paul F., et al. "Deep reinforcement learning from human preferences." Advances in neural information processing systems 30 (2017). http://arxiv.org/pdf/1706.03741.pdf
  7. ^Schulman, John, et al. "Proximal policy optimization algorithms." arXiv preprint arXiv:1707.06347 (2017). http://arxiv.org/pdf/1707.06347.pdf
本文转载于网络 如有侵权请联系删除

相关文章

  • EasyC++87,多继承(二)

    作者|梁唐大家好,我是梁唐。这是EasyC++系列的第87篇,我们继续来聊聊多继承多继承(二)在上一篇文章当中我们聊了多继承菱形的问题,在多继承菱形出现的时候,会导致派生类当中包含两个同样的父类实例。上一篇结尾处我们介绍了使用强制类型转换来避免歧义的办法,强制类型转换只是无奈之举,并且还有一个问题解决不了。我们创建的SingingWaiter这个类当中包含了两个Worker对象,但实际上我们只需要一个就行了。因为一个SingingWaiter也应该只有一份Worker的数据,没有必要有两份。所以强制类型转换虽然能解决歧义,但不能根本上解决问题。想要从根源上解决问题,需要使用C++官方提供的一个新的功能——虚基类。虚基类可以使得从多个类派生出的对象只继承一个基类对象,要使用虚基类,需要在类声明当中使用关键字virtual。比如在图中的例子当中,我们可以将Worker作为Singer和Waiter类的虚基类,这样的话,它们派生出的SingingWaiter就只包含一个Worker对象了。classSinger:publicvirtualWorker{...}; classWaiter:pu

  • 字段参数详解 - 应用篇 - 全动态帕累托分析

    此前已经做过几个版本的帕累托分析。帕累托分析,也就是ABC分析,在两个方面存在可以扩展的思考。一个方面是细节探讨。一个方面是框架探讨。字段参数的来到增强的是框架上的设计。但为了比较全面,我们分别介绍下,但本文会重点来说:框架。细节探讨-光滑曲线问题有问题的状态:此前的文章已经介绍过。细节探讨-归入其他的问题在帕累托分析中,如果元素过多,会导致有一个长尾。所以,用户会希望将这个长尾归入一个叫:其他的元素中。将不希望要的要素归入其他,如下:这并非本文要介绍的内容。框架探讨-全动态帕累托分析早在2018年4月,就有这篇文章:PowerBI实现全动态ABC分析帮助找出业务中的主要因素早在2019年5月,就有这篇文章:PowerBI打造全动态ABC帕累托分析模板2.0此两篇文章已经在2019年就定格了帕累托分析的全部能力和框架设计思维。以上内容全部在《BI真经-连续剧-BI进行时》收录。现在,帕累托分析由于字段参数的出现,有了新的框架设计思想。那就是建立整个动态方案套件。将动态性提升到前所未有的程度。动态性的发展大致为:第一阶段:滑杆参数的动态性第二阶段:维度,指标,颜色,表的动态性。(如上文:

  • 关于架构设计的易变性,应该如何理解呢?

    hello,大家好,我是张张,「架构精进之路」公号作者。一、架构设计分层通常情况下,我们的架构设计图大概率会如下图这个样子了,首先声明一点,这其实并没有什么不妥的,这也是很典型的分层设计啦~关于各个分层的具体描述,就简单的来聊聊吧。Client层这个比较简单,就不多说了。BusinessLogic业务逻辑这层分成Manager和Engine层,Manager负责管理流程类的易变性,Engine负责某个活动节点本身的易变性。什么是流程易变性呢?简单理解,就是工作流嘛。下面的两个流程是完全相同的,只是在第二步使用的活动不一样,如果B和D干的是同一件事情,那么B和D应该被封装进同一个Engine中。当然,如果B和D功能不一样,那这两个流程就不一样了,另论。ResourceAccess这一层是资源访问层,负责一些存储资源的封装,也就是说公司内的基础设施要变化的时候,不应该影响到上层的业务,这种在DDD社区也有RepoPattern之类的,比较好理解。Utilities那些紫色的组件,一般是一些大家公用的非功能性SDK,也比较好理解。架构图里的模块大多是服务:这样的分层每一次都是在解决Who、W

  • 30 分钟挽救被拐儿童一生,如何为基层民警打造「英雄战袍」

    天下无拐:人人皆可名侦探。作者:余快 “我一生中最好的年华走在了寻子路上,如今甚至连一份养家糊口的工作都找不到……”找了15年儿子的申军良,前不久在抖音上做了一次“寻子复盘”,除了对“梅姨”团伙入骨的恨之外,年过不惑的山东汉子,也有深深的自责:“低估了人贩子逃跑的速度,没有在第一时间发动一切力量去寻找,只是报了警,和两三个朋友找一找。”错过了黄金时间,一切变得异常茫然,每走到一个十字路口,申军良只能“一边喊着儿子的名字,一边拿着手机在地上转,手机指向哪个方向,就往哪个方向走。”申已算幸运,《失孤》原型23年仍未找到儿子。如果说人贩子或无常的命运,是毁掉一个家庭的魔鬼,那么守住孩子的首要任务,就是与魔鬼赛跑。道行是在打怪中不断升级,分阶质变的。1.0时代纯人工搜索,偶有神探 在漫长的没有监控的岁月里,寻找一个失踪的人,特别是年幼的孩子,到底有多难?知乎网友@魏什么凡点评到位:就像水消失在水中。“一滴水流入了大海,就算有一天化成雨滴,落在你的脸上,你也不会记得他,因为你的脸上早已被眼泪磨灭了知觉。”这一滴水也许本该属于欢快的小溪、壮丽的大海、平静的湖泊,却被命运吹进了不见光明的寒潭,哪怕只

  • NLP项目工作流程

    文章目录1.谷歌Colab设置2.编写代码3.flask微服务4.打包到容器5.容器托管 参考基于深度学习的自然语言处理使用这篇文章的数据(情感分类)进行学习。1.谷歌Colab设置Colab地址新建笔记本 设置 选择GPU/TPU加速计算 测试GPU是否分配 importtensorflowastf tf.test.gpu_device_name()复制输出:/device:GPU:0复制上传数据至谷歌云硬盘,并在Colab中加载 解压数据 2.编写代码importnumpyasnp importpandasaspd data=pd.read_csv("yelp_labelled.txt",sep='\t',names=['sentence','label']) data.head()#1000条数据 #数据X和标签y sentence=data['sentence'].values label=data['label'].values #训练集测试集拆分

  • Java Spring 使用AOP代理方法 类型转换异常java.lang.ClassCastException 原因及解决办法

    有一段代码有时会出现类型转换异常很诡异排查原因发现是springaop造成的。项目中我使用了aop进行自定义权限,若权限不通过返回固定的ResponseVo对应字段为:@Data @AllArgsConstructor @NoArgsConstructor publicclassResponseVo<T>{ @JsonProperty("response_code") privateIntegerresponseCode; @JsonProperty("verbose_msg") privateStringverboseMsg; privateTdata; }复制为了开发方便不需要每个controller都写一个ResponseVo的构建使用了ResponseBodyAdvice来帮助构建,之前的文章里写过说白了就说Controller方法只需要返回一个任意对象由spring将其封装到ResponseVo的data字段中。下面的是使用ResponseBodyAdvice的效果上面是正常写法这个接口比较简单可能没太大对比度但是在鉴权

  • Python中 *args 和 **kwargs 的含义?

    公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助!小猿会从最基础的面试题开始,每天一题。如果参考答案不够好,或者有错误的话,麻烦大家可以在留言区给出自己的意见和讨论,大家是要一起学习的。 废话不多说,开始今天的题目:问:Python中*args和**kwargs的含义?答:在python中,*args和**kwargs通常使用在函数定义里。*args和**kwargs都允许你给函数传不定数量的参数,即使在定义函数的时候不知道调用者会传递几个参数。ps:*args和**kwargs只是一个大家都遵守的习惯,名字可以任意写的。1.*args例子*args能够接收不定量的非关键字参数,会把位置参数转化为tuple(非键值对的参数组),例子如下面代码所示:deffunc(*args): foriinargs: print(i) func(1,2,3,4) 运行结果: 1 2 3 4复制2.**kwargs例子**kwargs允许你传递不定量个关键字参数。如果你需要在函数中定

  • Android实现快递单号查询快递状态信息

    今天介绍一个自己做的快递单号查询的简单APP,供大家参考。由于需要使用http和json,本文在build.gradle(module:app)添加了okhttp3依赖和gson依赖。dependencies{ compilefileTree(include:['*.jar'],dir:'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2',{ excludegroup:'com.android.support',module:'support-annotations' }) compile'com.android.support:appcompat-v7:24.1.1' testCompile'junit:junit:4.12' compile'com.squareup.okhttp3:okhttp:3.6.0' co

  • 09 设计模式 动态代理

    动态代理之前学习过静态代理,发现有个缺点,每当需要代理一个类就需要创建与之相当的代理类,极大的增加了代码量。现在通过动态代理,可以通过极少的代码量实现所要的功能动态代理与静态代理拥有一样的角色:抽象角色、真实角色、代理角色 首先定义一个抽象角色publicinterfaceRent{ publicvoidrent(); }复制再定义一个真实角色publicclassLandlordimplementsRent{ @Override publicvoidrent(){ System.out.println("出租房屋"); } }复制最后创建代理类publicclassProxyInvocationHandlerimplementsInvocationHandler{ Rentrent;//抽象代理类 publicvoidsetRent(Rentrent){ this.rent=rent; } /*获取代理对象*/ publicObjectgetProxy(){ returnProxy.newProxyInstance(this.getClass().getC

  • kafka高可用集群搭建

    kafka高可用集群搭建说明这篇博文主要是为了后面的elk做准备,我们这里搭建一个kafka集群,使用2个节点,还是前面的节点。主要是为了后面做数据缓冲。 这里不对mq的详细进行介绍,必要会对kafka相关配置进行描述。节点说明节点hostname192.168.179.123node-5192.168.179.124node-4192.168.179.125node-3当我们进行集群搭建的时候,要注意节点数量应该为基数,要求可用节点数量>总节点数量/2,我们采用最少节点数:3台安装配置kafkahttps://mirror.bit.edu.cn/apache/kafka/2.5.0/kafka_2.12-2.5.0.tgz tar-zxvfkafka_2.12-2.5.0.tgz复制viconfig/server.properties#node-5 broker.id=1 listeners=PLAINTEXT://192.168.179.123:9092 offsets.topic.replication.factor=3 transaction.state.log.repl

  • 补充一下代码

    这是数据结构的第8篇文章存储结构typedefintmyType; typedefstructtreeNode { myTypeelement;//值域元素 structtreeNode*lchild;//左孩子 structtreeNode*rchild;//右孩子 }Btree; 复制创建二叉树voidcreateTree(Btree**T)//传入一个Btree的指针的地址 { myTypedata; scanf("%d",&data); if(data==-1){//-1代表终端节点值 *T=NULL; }else{ *T=(Btree*)malloc(sizeof(structtreeNode)); (*T)->element=data; printf("请输入%d的左孩子节点:",data); createTree(&((*T)->lchild)); printf("请输入%d的右孩子节点:",data); createTree(&((*T)->rchild)); }

  • javascript原型链-review

    写在前面虽然现在es8都已经在预发布阶段了,但是无论发布到es几,其本身的运作原理都是一样的。首先祭上一张图,这张图主要描述了以下的关系,如果觉的这里的说明过于复杂可以直接看最后一段。 简单说明关于function(class)A和它的原型之间的关系A.prototype.constructor与A等价 关于function(class)A的实例a与它的原型之间的关系a.__proto__与A.prototype等价 在上面两个等价条件的基础上,就可以很容易得到a.__proto__.prototype.constructor与A等价 这是一般的类和对象实例之间的原型继承关系。在此基础上,对于Object和Function还有一些特殊的关系。关于function(class)A和Function之间的关系A.__proto__与Function.prototype等价 关于function(class)A的原型和Object之间的关系A.prototype.__proto__与Object.prototype等价 关于Function的原型和Object之间的关系Function.

  • 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序

    在excel转 DataTable遇到的问题:System.InvalidOperationException:未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序。程序代码:System.Data.DataTabledt=newSystem.Data.DataTable(); stringextension=System.IO.Path.GetExtension(filepath); stringstrConn=""; if(filepath.EndsWith(".xls")) strConn=string.Format("Provider=Microsoft.Jet.OLEDB.4.0;DataSource={0};ExtendedProperties='Excel8.0;HDR=Yes;IMEX=1;'",filepath); elseif(filepath.EndsWith(".xlsx")) strConn=string.Format("P

  • CSS入门3-认识html之元素

    (注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)1.元素的定义html元素,指的是从开始标签(starttag)到结束标签(endtag)的所有代码。 其拥有如下特点:HTML元素以开始标签起始HTML元素以结束标签终止元素的内容是开始标签与结束标签之间的内容某些HTML元素具有空内容(emptycontent)空元素在开始标签中进行关闭(以开始标签的结束而结束)大多数HTML元素可拥有属性2.元素分类2.1顶级元素Top-levelelement包括html,body,frameset,表现如Block-levelelement,属于高级块级元素。2.2块级元素:Block-levelelement以块显示的元素,高度宽度都是可以设置的。比如我们常用的p,h1~h6,div,ul默认状态下都是属于块级元素。块级元素默认状态下每次都占据一整个行,后面的内容也必须再新起一行显示。当然非块级元素也可以通过css的display:block;将其更改成块级元素。此外还有个特殊的,float也具有此功能。块级元素能够独立存在,一

  • 快速搭建云原生开发环境(k8s+pv+prometheus+grafana)

    欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览欣宸正在为接下新的Java云原生实战系列原创做准备,既然是实战,少不了一套云原生环境,以下内容是必不可少的:linux操作系统kuberneteskubernetes的外部存储,平时数据库、监控、消息这些中间件的数据不可能放在容器内,需要有个可靠的地方存起来不丢失监控一番操作下来,轻松完成了上述工作,这里将整个过程记录定下来,这样可以保证每次重装都能轻松愉快的完成,省下的时间用来写更多的技术原创版本信息本次安装的版本信息如下,供您参考操作系统:CentOS7.6(腾讯云轻应用服务器)kubernetes:1.22(底层容器服务是docker)prometheus:2.32.1grafana:8.3.3初始化CentOS操作系统的安装就不在这里讲了,接下来的操作假设您已装好纯净的CentOS系统本文中的操作都是用root账号执行的安装ssh服务sudoyuminstallopenssh-server-y复制修改root密码sudopass

  • 腾讯云移动网络加速签名方法v3调用方式

    以下文档说明了签名方法v3的签名过程,但仅在您编写自己的代码来调用腾讯云API时才有用。我们推荐您使用腾讯云APIExplorer,腾讯云SDK和腾讯云命令行工具(TCCLI)等开发者工具,从而无需学习如何对API请求进行签名。 您可以通过APIExplorer的【签名串生成】模块查看每个接口签名的生成过程。 腾讯云API会对每个请求进行身份验证,用户需要使用安全凭证,经过特定的步骤对请求进行签名(Signature),每个请求都需要在公共参数中指定该签名结果并以指定的方式和格式发送请求。 为什么要进行签名签名通过以下方式帮助保护请求: 验证请求者的身份签名确保请求是由持有有效访问密钥的人发送的。请参阅控制台云API密钥页面获取密钥相关信息。 保护传输中的数据为了防止请求在传输过程中被篡改,腾讯云API会使用请求参数来计算请求的哈希值,并将生成的哈希值加密后作为请求的一部分,发送到腾讯云API服务器。服务器会使用收到的请求参数以同样的过程计算哈希值,并验证请求中的哈希值。如果请求被篡改,将导致哈希值不一致,腾讯云API将拒绝本次请求。 签名方法v3(TC3-

  • 腾讯云自动化助手查询客户端状态api接口

    1.接口描述接口请求域名:tat.tencentcloudapi.com。 此接口用于查询自动化助手客户端的状态。 默认接口请求频率限制:20次/秒。 APIExplorer提供了在线调用、签名验证、SDK代码生成和快速检索接口等能力。您可查看每次调用的请求内容和返回结果以及自动生成SDK调用示例。 2.输入参数以下请求参数列表仅列出了接口请求参数和部分公共参数,完整公共参数列表见公共请求参数。 参数名称 必选 类型 描述 Action 是 String 公共参数,本接口取值:DescribeAutomationAgentStatus。 Version 是 String 公共参数,本接口取值:2020-10-28。 Region 是 String 公共参数,详见产品支持的地域列表。 InstanceIds.N 否 ArrayofString 待查询的实例ID列表。 Filters.N 否 ArrayofFilter 过滤条件。agent-status-String-是否必填:否-(过滤条件)按照agent状态过滤,取值:Online在线,

  • 采集电脑摄像头和mic,rtp端口推送音视频工具

    介绍:这个是我在做一个rtmp播放的项目中自己写的rtp推送的工具,可选择摄像头,可选择推送rtp的端口和ip 下载地址: github:https://github.com/alexhegang/sentrtp/blob/master/SendRTP-20170930.zip csdn:http://download.csdn.net/download/hegangle/10012580

  • Typescript中的对象多可能类型推导的解决办法

    我们在渲染复杂对象,比如树组件的树对象,有嵌套对象/数组结构的时候,js的弱类型让我们一把梭,几乎没遇到什么问题(只是写的时候编译没问题把。233。。),结合TS后,对树状数据的递归访问会是一个问题: 比如数据源的数据声明为: exportinterfaceVoiceSkillResponse{ data:{ isExistUpdate:number; isNewProduct:number; streamList:{ streamId:string; streamName:string; intentList:{ id:number; intent:string; isSelect:number; }[]; }[]; }; } exportinterfaceVoiceSkill{ streamId:string; streamName:string; intentList:Array<Intent>; } exportinterfaceIntent{ id:number; intent:string; isSelect:number; } 复制 我们想根据这样的数据渲

  • CMake教程

    CMake教程CMakeTutorial 目录CMake教程CMakeTutorial第1步基本起始点ABasicStartingPoint(Step1)1.1添加项目版本号和配置头文件1.2指定C++标准1.3构建和测试第2步生成库AddingaLibrary(Step2)第3步为库添加使用要求AddingUsageRequirementsforLibrary(Step3)第4步安装和测试InstallingandTesting(Step4)4.1安装规则4.2测试支持TestingSupport第5步添加系统自检AddingSystemIntrospection(Step5)指定编译时宏定义SpecifyCompileDefinition第6步添加自定义命令以及生成文件AddingaCustomCommandandGeneratedFile(Step6)第7步构建安装程序BuildinganInstaller(Step7)注:目标包和package_source第8步添加对仪表板的支持AddingSupportforaDashboard(Step8)第9步混合静态和共享库Mixin

  • SpringBoot入门Demo(Hello Word Boot)

        SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新的Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。   以下是SpringBoot的快速搭建。  1、创建一个Maven项目,添加一个parent,代码如下 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.4.RELEASE</version> </parent>复制     2、添加一个依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot

相关推荐

推荐阅读