diff --git a/_posts/Kubernetes/Object/2015-03-03-kubernetes_pod.md b/_posts/Kubernetes/Object/2015-03-03-kubernetes_pod.md index 2393b0ba..a027c22c 100644 --- a/_posts/Kubernetes/Object/2015-03-03-kubernetes_pod.md +++ b/_posts/Kubernetes/Object/2015-03-03-kubernetes_pod.md @@ -155,6 +155,16 @@ type PodStatus struct { ## kubectl drain 发生了什么 +Pod 驱逐分为两种情况: +1. 较安全驱逐 & 提高稳定性的良性驱逐 + 1. API 发起驱逐,典型案例:kubectl drain + 2. Node Not Ready 时,Controller Manager 发起的驱逐 +2. 有风险的驱逐 + 1. 节点压力驱逐:节点磁盘空间不足、内存不足 或 Pid 不足, kubelet 发起驱逐;节点内存不足,内核发起 OOM + 2. 节点打污点(NoExecute),导致 Pod 被驱逐,或者移除亲和性标签,导致 Pod 被驱逐, Controller Manager 发起的驱逐 + 3. Pod 超过自身 Limit 限制, 内核用满,临时存储用满等 + 4. 优先级抢占驱逐 + [Kubernetes Pod 删除操作源码解析](https://mp.weixin.qq.com/s/L-CQhYzxqxOoy9xYp6-JMA) kubectl drain 将以某种方式驱逐 Pod。drain 将向控制平面发出删除目标节点上的 Pod 的请求。通过 API 将 Pod 从集群中删除后,所有发生的事情就是该 Pod 在元数据服务器中被标记为要删除。这会向所有相关子系统发送一个 Pod 删除通知 diff --git a/_posts/Life/2018-09-04-technology_manage.md b/_posts/Life/2018-09-04-technology_manage.md index 368bced0..0566ba16 100644 --- a/_posts/Life/2018-09-04-technology_manage.md +++ b/_posts/Life/2018-09-04-technology_manage.md @@ -75,9 +75,10 @@ keywords: technology manage 6. 当领队,独自筹划大部分事情 7. 不错的领队 - 小伙伴若是自驱动不高的话,其实是对任务负责。而小队长是对项目负责的。项目成功跟小伙伴干完具体的活儿之间有大量的事情规划、协调、踩着石头过河。直接对结果负责的人其实最累的。 +晓斌:管理者需要认识、理解以及建立这些高标准,并不断和团队成员传达。各个层级各个岗位的标准和要求是什么?员工晋升是因为他 / 她的哪些行为体现了下一层的要求?就日常细节工作的高标准,什么是优秀的设计文档?什么优秀的 PRD 文档?什么是高质量的代码?什么行为体现了客户第一?定义清晰的高标准并不是一件容易的事,更常见的情况反而是作为管理者自己也不知道高标准是什么(参考彼得原理),这可能是很多员工收到负反馈就感觉是被 PUA 的核心因素(“你都是个 P7 了,你自己应该知道怎么做好”)。我建议管理在想要给下属负反馈的时候自己先回答这个问题,“我是否可以清晰地和下属表达我期望看到哪些行为的变化?用具体的事例描述。”不要模棱两可。 + **同理心,不是发慈悲,而是一种策略**。它要求你能了解下属的能力,并善于发挥下属的能力。 [这年月只能靠手艺吃饭了](https://mp.weixin.qq.com/s/5bzJR0qlkUrMHnuIsMnnXA) 以亲身经历列出了技术管理的几个阶段。 diff --git a/_posts/MachineLearning/LLM/2023-10-18-meditation.md b/_posts/MachineLearning/2023-10-18-meditation.md similarity index 98% rename from _posts/MachineLearning/LLM/2023-10-18-meditation.md rename to _posts/MachineLearning/2023-10-18-meditation.md index 81ac04a2..e1c2cf5d 100644 --- a/_posts/MachineLearning/LLM/2023-10-18-meditation.md +++ b/_posts/MachineLearning/2023-10-18-meditation.md @@ -108,6 +108,9 @@ AI和互联网最大的区别是,互联网越过0分就具有了实用价值 信息平权:大模型的商业落地相比互联网目前有一个非常巨大的区别,就是**边际成本仍然非常高**。过去的互联网业务,增加一个用户对互联网厂商的基础设施而言,增加的成本几乎是可以忽略不记的。但今天大模型每增加一个用户,对基础设施增加的成本是肉眼可见的增加的,目前一个月几十美元的订阅费用都不足以抵消背后高昂的成本。而且今天的大模型要大规模商业化,在模型质量、上下文长度等方面还有进一步诉求,实际上还有可能需要进一步增加这个边际成本。今天一个日活千万的通用大模型需要一年超过100亿的收入才能支撑其背后的数据中心成本,未来如果我们希望大模型产业真正像今天的互联网产业一样服务上亿人,模型的质量可能也需要进一步上一个台阶,成本会成为很严重的问题。但对于芯片行业而言,只要适当拉长时间尺度,这些都不会是问题。在不远的未来,芯片行业就可以把上下文窗口逐渐变得和今天的内存一样非常便宜,随便一个hello world就直接吃掉MB级别的内存,随便一个应用就GB级别的内存占用。未来我们也一样可以随随便便把一个领域的全部知识装进上下文里,让大模型成为绝对意义上的领域专家,也可以让大模型拥有远超人类一辈子能接受的全部上下文,从而引发大模型走向新的质变。最近几年其实说摩尔定律放缓的观点很多,这也是实际情况,先进工艺的研发投入资金也在指数级飙升,使得维持摩尔定律逐渐变得失去经济性。但芯片行业的Scaling不只是晶体管的微缩推动的,NVidia的GPU过去十年靠架构继续推动放缓的摩尔定律持续保持非常高的增速,算力成本降低了一千倍。而今天大模型进一步打开了更多芯片的演进空间,**今天大模型对芯片的需求从算力转向了内存和互联,内存系统和互联的Scale空间更大**,除了半导体工艺的演进外,封装工艺的发展、硅光都对内存和互联的设计打开了巨大的空间。大模型今天也早已经全面走向分布式,今天不仅仅是单颗芯片的设计,也进一步扩展到服务器、机柜、网络层面,这些层面都有比原来有大得多的设计空间,未来芯片的增速不仅不会放缓,反而会比今天更快。**我们需要足够多的高带宽内存通过互联系统连接起来形成一个巨大的高带宽内存来支撑大模型的服务**。我们经常讨论的售卖Token的价格,实际上Token和Token是不一样的,一个7B模型的Token和千亿万亿模型的Token肯定不等价,一个4K上下文的Token和一个2M上下文的Token也不等价。Token的质量实际上和模型规模以及上下文窗口都是强相关的。**模型权重是模型在训练时候对整个数据集的压缩和泛化,是对世界和常识的理解,而上下文对应的KV-Cache是对上下文的理解**。而权重和KV-Cache其实也是大模型对内存最主要的需求,这部分的访存速度也决定了Token生成的速度。售卖Token对硬件系统而言实际上是售卖内存系统的访存带宽。一个容量足够大的内存系统才能提供足够高质量的Token服务,一个内存带宽性价比足够高的系统才能带来更好的服务成本。物理世界中的内存介质选择往往要带宽就没有容量、要容量就没有带宽。所以今天继要容量大又要带宽性价比高,往往需要通过足够有性价比的互联系统将大量高带宽内存连到一起,这里面是存在非常大的设计空间的。这也是中国AI芯片行业真正实现商业化的一次巨大机会,过去十年大家都是在卷算力,算力的竞争往往不只是峰值算力指标的竞争,算力和编程模型、软件都有很强的耦合性,算力指标对先进工艺也有很强的依赖性。大模型今天把芯片产品的竞争力拉到了内存和互联维度,这些维度相比算力都标准化得多,对解决产品定义问题提供了新的可能性,标准化的维度更贴近指标竞争,就像今天大家买网卡或者交换机时候只关注指标而不关注是哪家的产品,这就是标准化竞争的好处。我们如果看当下和未来两三年,其实大模型的商业探索也是在成本和Token质量上相互妥协,也逐渐分化成了两派。一派是质量优先,**用高端系统打造高质量的通用大模型,寻找超级应用来覆盖高昂的成本**。另一派是成本优先,用足够便宜的硬件上,提供基本够用的Token质量,**寻找垂直场景的落地**。今天很多人讲7B模型已经够用了,或者努力让7B或者更小的模型变得够用,其实也是一种无奈,如果能在同样的成本下买到规格大得多的芯片,跑一个百亿千亿模型,支持超长上下文,商业化的空间会比今天大得多,就像曾经的显卡和游戏行业一样,当足够便宜的显卡已经可以流程跑4k画质的时候,谁还会觉得1080p的画质也够用了呢?两三年后,随着芯片行业的发展,不会再有人需要小模型,大模型长文本的高质量Token会变得足够便宜。往更长远看,**大模型的成本模型对于商业形态都会产生巨大的变革**。PS:思维深度非常牛逼,从LLM推理特点到成本模型再到商业形态变革。[生成式AI产业经济学:价值分配与利润结构](https://mp.weixin.qq.com/s/bGFCofQ6OocjRbEL4CT7YA) +1. 大模型的压缩:一个 10T的预料,可能被压缩到一个 10G 大小的模型中。 +2. LLM 不仅仅压缩了字符串,更重要的是它能够学习字符串之间的相关性,也就是抽象规律,甚至能够推理出原始数据中不存在的事物。在训练过程中,大模型通过调整其参数——包括权重和偏置——来捕捉这些复杂的模式和规律。训练完成后,这些参数就固定下来,代表了模型对数据的理解。 + ## 其它 [ChatGPT如何成了学习的神兵利器](https://mp.weixin.qq.com/s/ECFxhRj-Dko097gukaSCTA) diff --git a/_posts/MachineLearning/LLM/2023-12-16-llm_inference.md b/_posts/MachineLearning/Framework/2023-12-16-llm_inference.md similarity index 95% rename from _posts/MachineLearning/LLM/2023-12-16-llm_inference.md rename to _posts/MachineLearning/Framework/2023-12-16-llm_inference.md index 7366af0e..2c88494c 100644 --- a/_posts/MachineLearning/LLM/2023-12-16-llm_inference.md +++ b/_posts/MachineLearning/Framework/2023-12-16-llm_inference.md @@ -80,8 +80,8 @@ $$ 在大语言模型推理中常会用到四个指标:Throughput(吞吐量)、First Token Latency(首字延迟)、Latency(延迟)和QPS(每秒请求数)。这四个性能指标会从四个不同的方面来衡量一个系统的服务提供能力。 1. Throughput/吞吐量是指当系统的负载达到最大的时候,在单位时间内,能够执行多少个 decoding,即生成多少个字符。 -2. First Token Latency(首字延迟)。指的是当一批用户进入到推理系统之后,用户完成 Prefill 阶段的过程需要花多长时间。这也是系统生成第一个字符所需的响应时间。很多需求关注这一指标,希望用户在系统上输入问题后得到回答的时间小于 2~3 秒。 -3. Latency(延迟)。指的是每一个 decoding 所需要的时长。它反映的是大语言模型系统在线上处理的过程中,每生成一个字符的间隔是多长时间,也就是生成的过程有多么流畅。大部分情况下,我们希望生成的延迟小于 50 毫秒,也就是一秒钟生成 20 个字符。 +2. First Token Latency/TTFT(首字延迟 Time To First Token)。指的是当一批用户进入到推理系统之后,**用户完成 Prefill 阶段的过程需要花多长时间**。这也是系统生成第一个字符所需的响应时间。很多需求关注这一指标,希望用户在系统上输入问题后得到回答的时间小于 2~3 秒。 +3. Latency/TBT(延迟 Time between tokens)。**指的是每一个 decoding 所需要的时长**。它反映的是大语言模型系统在线上处理的过程中,每生成一个字符的间隔是多长时间,也就是生成的过程有多么流畅。大部分情况下,我们希望生成的延迟小于 50 毫秒,也就是一秒钟生成 20 个字符。 4. QPS(每秒请求数)。反映了在线上系统的服务当中,一秒钟能够处理多少个用户的请求。 First Token Latency 和 Latency 这两个指标会因为用户输入的长度不同、batch size 的不同而发生非常大的变化。用户输入的长度越长,首字延迟也会越高。decoding 延迟,只要不是千亿级别的模型,decoding 的延迟都会控制在 50 毫秒以内,主要受到 batch size 的影响,batch size 越大,推理延迟也会越大,但基本上增加的幅度不会很高。吞吐量其实也会受到这两个因素的影响。如果用户输入的长度和生成的长度很长,那么系统吞吐量也不会很高。如果用户输入长度和生成长度都不是很长,那么系统吞吐量可能会达到一个非常离谱的程度。 除了首token时延,LLM在线服务也可能会把尾token时延(即完成response的时延)作为优化目标,例如,当LLM推理服务作为中间环节时,就会希望response的尾token时延越小越好。 @@ -94,7 +94,7 @@ First Token Latency 和 Latency 这两个指标会因为用户输入的长度不 在 prefill 阶段,至少要做四件事情:第一件事情是把用户的输入进行向量化,tokenize 的过程指的是将用户输入的文本转换为向量,相对于 prefill 整个阶段来说,大概要占掉 10% 的时间,这是有代价的。之后就会进行真正的 prefill 计算,这一过程会占掉大概 80% 的时间。计算之后会进行 sampling,这个过程在 Pytorch 里面一般会用sample、top p。在大语言模型推理当中会用 argmax。总而言之,是根据模型的结果,生成最后词的一个过程。这个过程会占掉 10% 的时间。最后将 refill 的结果返回给客户,这需要的时间会比较短,大概占 2% 到 5% 的时间。 -Decoding 阶段不需要 tokenize,每一次做 decoding 都会直接从计算开始,整个decoding 过程会占掉 80% 的时间,而后面的 sampling,也就是采样生成词的过程,也要占掉 10% 的时间。但它会有一个 detokenize 的时间,detokenize 是指生成了一个词之后,这个生成的词是个向量,需要把它解码回文本,这一操作大概会占掉 5% 的时间,最后将这个生成的词返回给用户。 +**Decoding 阶段不需要 tokenize**,每一次做 decoding 都会直接从计算开始,整个decoding 过程会占掉 80% 的时间,而后面的 sampling,也就是采样生成词的过程,也要占掉 10% 的时间。但它会有一个 detokenize 的时间,detokenize 是指生成了一个词之后,这个生成的词是个向量,需要把它解码回文本,这一操作大概会占掉 5% 的时间,最后将这个生成的词返回给用户。 新的请求进来,在进行完 prefill 之后,会不断迭代进行 decoding,每一个 decoding 阶段结束之后,都会将结果当场返回给客户。这样的生成过程在大语言模型里面是很常见的,我们称这样的方式为流式传输。 @@ -211,6 +211,10 @@ PS:Transformer (和Attention) layer 已经支持了缓存机制 (use_cache System Prompt Caching,也称为 Prefix Sharing,其基本思想是对System Prompt部分进行一次计算,并缓存其对应的Key和Value值(例如,存放在GPU显存中),当LLM推理再次遇到相同的(甚至部分相同的)System Prompt时,则可以直接利用已经缓存的System Prompt对应的Key和Value值,这样就避免了对于System Prompt的重复计算。 +[Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving](https://zhuanlan.zhihu.com/p/706109023)Mooncake的核心是其以KVCache为中心的调度器,将预填充/prefill服务器与解码/decoding服务器分开,因为LLM服务的这两个阶段具有非常不同的计算特性,Prefill是计算密集,受限算力带宽用不满,Decode是访存密集性,受限带宽算力用不满。所以用同一种硬件部署两阶段往往顾此失彼,不是最有性价比。拆分Prefill/Decode之后,LLM推理系统就更像一个分布式内存系统+流处理系统,其中KVCache随着请求从预填充移动到解码服务器而转移,将KVCache和计算分离开,它将GPU集群的CPU、DRAM、SSD和RDMA资源分组组成Distributed KVCache Pool,KVCache也是分块以Paged方式管理,KVCache Blocks如何在Pool中调度,请求和复用KVCache乃精髓。这就是传统计算机系统研究者最擅长的领域,sys三板斧,batch, cache,调度都可以招呼上,比如 +1. Prefill阶段可以利用request间存在共同前缀的机会,尽可能复用KVCache。PS:比如agent 循环调用 多次请求的prompt prefix是一样的。 +2. Decode可以进一步拆成Attention和非Attention算子分离调度。 + ### Flash Attention [图解大模型计算加速系列:Flash Attention V1,从硬件到计算逻辑](https://mp.weixin.qq.com/s/J2i2MDv4us_GMwCyku0tnw)在矩阵分块的时候设计好一个能够充分利用高速访存HBM的分块方法,让一次搬运进HBM中的参数可以全部和该做乘加操作的算子都计算完再丢弃,达到数量单次访存利用率最大化。 diff --git a/_posts/MachineLearning/LLM/2023-12-16-llm_train.md b/_posts/MachineLearning/Framework/2023-12-16-llm_train.md similarity index 100% rename from _posts/MachineLearning/LLM/2023-12-16-llm_train.md rename to _posts/MachineLearning/Framework/2023-12-16-llm_train.md diff --git a/_posts/MachineLearning/LLM/2024-01-23-trition.md b/_posts/MachineLearning/Framework/2024-01-23-trition.md similarity index 100% rename from _posts/MachineLearning/LLM/2024-01-23-trition.md rename to _posts/MachineLearning/Framework/2024-01-23-trition.md diff --git a/_posts/MachineLearning/LLM/2024-02-02-llm_inference_framework.md b/_posts/MachineLearning/Framework/2024-02-02-llm_inference_framework.md similarity index 100% rename from _posts/MachineLearning/LLM/2024-02-02-llm_inference_framework.md rename to _posts/MachineLearning/Framework/2024-02-02-llm_inference_framework.md diff --git a/_posts/MachineLearning/2022-02-09-feature_engineering.md b/_posts/MachineLearning/Infra/2022-02-09-feature_engineering.md similarity index 100% rename from _posts/MachineLearning/2022-02-09-feature_engineering.md rename to _posts/MachineLearning/Infra/2022-02-09-feature_engineering.md diff --git a/_posts/MachineLearning/LLM/2023-03-25-llm.md b/_posts/MachineLearning/LLM/2023-03-25-llm.md index 61f6995f..f112b658 100644 --- a/_posts/MachineLearning/LLM/2023-03-25-llm.md +++ b/_posts/MachineLearning/LLM/2023-03-25-llm.md @@ -226,6 +226,8 @@ BERT仅使用了Transformer的编码器(Encoder)部分进行训练,而GPT-1则 ## RLHF +base model会“文字接龙”之后,对于每个问题,人工标注者都会比较辅助模型的多个答案,并标注出最佳答案。这一步骤称为从人类反馈中强化学习(RLHF)。 + [万字长文详解大模型平台技术栈](https://mp.weixin.qq.com/s/zoXxkdVj70XOuAMdkGtuEQ)经过预训练之后,我们可以获得类似于 LLaMA 这样的 **Base Model**,但这距离 ChatGPT 这种 Assistant Model 还有一段距离。Base Model 并不能像 **Assistant Model** 一样能够很好的适用于指令理解、多轮聊天和 QA 问答等场景,而只是总是倾向于去续写文本(causal language modeling)。也即是说,Base Model 并不能很好的和用户的意图对齐 (Align),有可能会生成无用、甚至是有害的内容。为了解决这个问题,算法科学家们提出了很多的解决方案,典型代表就是以 InstructGPT 这种先经过 Supervised Finetuning,然后通过 Reward Modeling 和基于人类反馈的强化学习 RLHF。DeepSpeed 团队也发布了 DeepSpeed-Chat,开源了一个支持端到端的 RLHF 的训练和推理系统,复刻了 InstructGPT 论文中的训练模式,并确保三个阶段一一对应:监督微调SFT;奖励模型微调RM;基于人类反馈的强化学习RLHF。PS: 通过人类反馈进行模型微调。 [RLHF——让大模型对齐人类偏好](https://mp.weixin.qq.com/s/UGLifcfq9SmjARYk9D3kDQ) 所谓对齐人类偏好,主要体现在以下三点:有用(遵循指令的能力)、诚实(不容易胡说八道)、安全(不容易生成不合法的,有害,有毒的信息)。因此在大模型的训练阶段,如果引入人类的偏好或者人类的反馈来对模型整体的输出结果进行进一步优化,显然是要比传统的“给定上下文,预测下一个词”的损失函数要合理的多。即使用强化学习的方法,利用人类反馈信号直接优化语言模型。 diff --git a/_posts/MachineLearning/LLM/2023-09-04-llm_source.md b/_posts/MachineLearning/LLM/2023-09-04-llm_source.md index beae4ca1..ff6ed6e5 100644 --- a/_posts/MachineLearning/LLM/2023-09-04-llm_source.md +++ b/_posts/MachineLearning/LLM/2023-09-04-llm_source.md @@ -13,6 +13,10 @@ keywords: llm source ## 前言 +大模型由以下两个关键部分构成:一个是 参数集,另一个是 执行代码。 +1. 参数集:这是模型的"大脑",包含了通过训练学习到的神经网络权重。 +2. 执行代码:这是模型的"引擎",包含用于运行参数集的软件代码,可以采用任何编程语言实现。 + ## Transformers [NLP Course](https://huggingface.co/learn/nlp-course) 官方教程,建议从头到尾细看一下。 @@ -21,7 +25,6 @@ keywords: llm source ![](/public/upload/machine/transformers_overview.png) - ### pipelines 开箱即用的 pipelines,它封装了预训练模型和对应的前处理和后处理环节。 @@ -216,7 +219,52 @@ print(squad_it_dataset) # 包括 train 和 test 的 DatasetDict 对象 ``` 4. 用于预训练 GPT-2 的 WebText 语料库包含超过 800 万个文档和 40 GB 的文本,全加载到计算机内存吃不消,`pubmed_dataset_streamed = load_dataset("json", data_files=data_files, split="train", streaming=True)`,streaming=True 返回的对象是一个 IterableDataset -### 源码分析 +## 多轮对话怎么转化为模型接受的input和用于计算loss的label + +### 预训练 + +通常,我们把经过预训练(pretrain)阶段得到的模型称为base模型。这个阶段主流的数据组织方式叫packing。在不采用packing的时候,为了将不同长度的句子组成一个batch tensor,我们需要进行填充(pad),这个填充过程既可以按照batch内最长句子填充,也可以按照模型最长输入长度填充。为了防止一个batch内存在许多的`token`,浪费计算资源,packing直接采取多条示例的拼接方法。下图是传统方法和packing的对比: + +![](/public/upload/machine/pretrain_padding.jpg) + +左侧是传统的padding做法,右侧是packing,其中红色部分代表pad token,黄色部分代表sep token。为了区分不同的训练示例,我们在不同示例之间加上一个分割标记sep token,注意力窗口不会跨示例。这个注意力模式叫块对角矩阵(BlockDiagonalMask)【本质上是在示例内的下三角矩阵】,而不是传统的全局下三角矩阵。由此,就消除了对pad token的需要,所以开源大模型刚问世的时候(2023-3那阵子),存在很多base model放出来的tokenizer并没有pad token,比如llama-base。需要注意,packing时示例3可能会被截断,这个行为在预训练时是可以接受的。注意,这个时候的**学习模式**非常的简单,就是next token prediction。 + +### 指令微调 + +指令微调不仅仅考虑了对人类指令和多任务的适应性,更是希望能将角色系统融入大模型中,从而让大模型变成chat模型,指令微调并不直接产生chat model,只是其中必不可少的一步。其中比较特殊的数据形式就是多轮对话。对话里必不可少的存在“角色”这个概念,因为和大模型的对话仅限于用户和模型,所以极大多数的对话模板(template)里都只考虑了两个角色——user和assistant。注意,对话模板只有非base模型才需要,所以很多的base模型的tokenizer里并不携带chat_template。 + +举个例子,比如:LLAMA2-chat的对话模板中user标识 是 `[INST]` , assistant 标识是`[/INST]`,下面是一个单轮的例子 +``` +chat_dict = [ + {"role": "user", "content": 你好}, + {"role": "assistant", "content": 你也好}, + ] +``` +因为模型输入只能是非结构化的,我们`利用模板将其非结构化`。得到的字符串就是`[INST]你好[/INST]你也好`。那么假如我们现在获得了一个现成的训练数据 +``` +chat_dict = [ + {"role": "user", "content": U1}, + {"role": "assistant", "content": A1}, + {"role": "user", "content": U2}, + {"role": "assistant", "content": A2}, + ] +``` +模型的input_ids和对应的labels应该是什么呢?最常规的做法应该是在每一轮首尾用`[BOS]`和`[EOS]`包裹,轮次内部正常用模板非结构化就行。上例可以转换为input_ids= `[BOS][INST]U1[\INST]A1[EOS][BOS][INST]U2[\INST]A2[EOS]`,难点在于LABELS应该是什么呢? 我们可以根据学习模式来确定LABELS。 +1. 在推理场景下,假如是第一轮对话开始,我们会输入给模型[BOS][INST]U1[\INST],那么我们希望模型吐出的是什么呢?是A1和[EOS],A1是模型自己的回答,EOS是为了告诉解码系统生成结束了,否则模型将一直生成到最大长度才会停止。我们获得了一个初步的学习模式需求,就是根据`[BOS][INST]U[\INST] → A[EOS]`。 + |input|`[BOS]`|`[INST]`|U|`[/INST]`|A| + |---|---|---|---|---|---| + |label|-100|-100|-100|A|`[EOS]`| + + 在多轮的场景下,也只是复制这个过程。 + + |input|`[BOS]`|`[INST]`|U|`[/INST]`|A|`[EOS]`|`[BOS]`|`[INST]`|U2|`[/INST]`|A2| + |---|---|---|---|---|---|---|---|---|---|---|---| + |label|-100|-100|-100|A|`[EOS]`| X| X| X| X| A2| `[EOS]`| + +2. 在llama factory里有一个参数叫 Efficient EOS,所谓efficient eos并不代表是一个新的token,而是一个特殊的input和label的设计方式。 [多轮对话的训练过程详解](https://zhuanlan.zhihu.com/p/695202364) + + +## 源码分析 ```python tokenizer = AutoTokenizer.from_pretrained(checkpoint) @@ -418,6 +466,8 @@ def generate(self, [以LLAMA为例,快速入门LLM的推理过程](https://mp.weixin.qq.com/s/5lbrqbqiHPZIARsVW6l6tA) 建议细读。 +### 解码策略 + 可以把解码器文本Transformer模型理解为一个函数,它以词元作为输入并生成一个概率数组,用于表示词汇表中所有词元的概率,然后,程序根据这些概率从所有词元中进行采样,以指导采样过程,并生成下一个词元,这一过程会重复进行。在GPT模型最后一层之前,推理的对象是以向量形式表征的语义,输出的是代表语义的一个“模糊”的向量。此处“模糊”指的是,**这一向量或许并不对应任何一个已知的词**。因此,整个模型最后需要再做一个推测,基于这个“模糊”的向量所包含的语义信息,在词表中寻找最符合这些特征的词,来作为真正的输出。在 transformer 中,最后的输出是一个概率分布,表示每一个词匹配这一“模糊”向量的概率。 [一网打尽文本生成策略(beam search/top-k/top-p/温度系数)](https://zhuanlan.zhihu.com/p/676398366)假设我们输入 “I am a”,GPT会对这个序列进行编码得到embedding,并预测下一个单词的概率。“I am a”作为输入,编码后也会得到三个token对应的embedding。GPT会使用输入序列最后一个token对应的embedding做预测,也就是说,”a”经过编码后的embedding会被映射到整个词表上,得到下一个单词的概率。因为GPT是使用masked attention的,每个token在编码时都只会和他前面的token交互。**那么最后一个token自然就包括了当前序列的所有信息,因此用于预测下一个token是最合理的**。 diff --git a/_posts/MachineLearning/LLM/2023-09-07-multimodal.md b/_posts/MachineLearning/LLM/2023-09-07-multimodal.md index 4a2751a7..14950c67 100644 --- a/_posts/MachineLearning/LLM/2023-09-07-multimodal.md +++ b/_posts/MachineLearning/LLM/2023-09-07-multimodal.md @@ -13,7 +13,11 @@ keywords: llm multimodal ## 前言(未完成) -大语言模型仅仅赋予了模型接受语言输入的能力,多模态则想给大模型安装上眼睛和耳朵。 +为什么我们需要能够处理多种数据类型的AI模型呢?原因很简单:我们的世界是多模态的。我们交流和感知世界不仅仅通过语言,还包括视觉、听觉等多种方式。多模态模型能够更全面地理解和模拟人类的交流和感知方式,使得AI能够更自然地与人类互动。 +多模态模型就像是我们的大脑,能够同时处理和理解来自眼睛(视觉信息)、耳朵(听觉信息)和其他感官的数据。作用主要体现在以下几个方面: +1. 信息整合:能够将不同类型的信息整合在一起,提高理解和分析的准确性。 +2. 增强表现力:通过结合多种数据源,模型可以表现出更强的感知和认知能力。 +3. 提高鲁棒性:多模态模型可以在某种类型数据缺失或不完整的情况下,依靠其他数据类型来弥补,从而提高整体性能。 语言模型的发展历程与视觉模型有许多相似之处。 1. 它们都考虑了生物行为的特点,找到了适合自身的神经网络结构——循环神经网络(RNN)和卷积神经网络(CNN)。 diff --git a/_posts/MachineLearning/LLM/2023-09-25-llm_retrieval.md b/_posts/MachineLearning/LLM/2023-09-25-llm_retrieval.md index d91b86f1..cdb71874 100644 --- a/_posts/MachineLearning/LLM/2023-09-25-llm_retrieval.md +++ b/_posts/MachineLearning/LLM/2023-09-25-llm_retrieval.md @@ -71,9 +71,9 @@ LLM 擅长于一般的语言理解与推理,而不是某个具体的知识点 如何让LLM简要、准确回答细粒度知识?如何让LLM回答出全面的粗粒度(跨段落)知识?QA 的难度主要在于回答这个问题所依赖的信息在长文档中的分布情况,具体可以大致分为下面三种情况: 1. 相关信息可以出现在不超过一个固定上下文长度(512)限制的 block 以内 2. 相关信息出现在多个 block 里,出现的总数不超过 5-10 个,最好是能在大模型支持的有效长度内。 -3. 需要考虑多个片段,并综合结合上下文信息甚至全部内容才能得到结果。 PS: 可能要知识图谱帮一点忙。 +3. 需要考虑多个片段,并综合结合上下文信息甚至全部内容才能得到结果。通过多次提问,不断诱导模型生成解决问题的路径,对于一个问题的解决可能需要重复调用很多次才能完成。 PS: 可能要知识图谱帮一点忙。 -受限于数据集的大小和规模以及问题的难度,目前主要研究偏向于 L1 和 L2 。能比较好的整合和回答 L1 类问题,L2 类问题也有比较不错的结果,但对于 L3 类问题,如果所涉及到的片段长度过长,还是无法做到有效的回答。**了解RAG能做什么和不能做什么,可以让我们为RAG寻找最适合的领域,避免强行进入错误的地方**。 +受限于数据集的大小和规模以及问题的难度,目前主要研究偏向于 L1 和 L2 。能比较好的整合和回答 L1 类问题,L2 类问题也有比较不错的结果,但对于 L3 类问题,如果所涉及到的片段长度过长,还是无法做到有效的回答。**了解RAG能做什么和不能做什么,可以让我们为RAG寻找最适合的领域,避免强行进入错误的地方**。[大模型应用落地那些事](https://mp.weixin.qq.com/s/BK3KrsFAWx4nFeSrvnzijA)工程上,除 langchain 外,任何复杂的 L3 级别的构建都不推荐使用langflow/coze这类工具,因为 L3 架构需要大量定制化调优,而这类工具通常是有自己的一套逻辑。 ![](/public/upload/machine/rag_key_index.jpg) diff --git a/_posts/MachineLearning/LLM/2023-10-29-llm_finetune.md b/_posts/MachineLearning/LLM/2023-10-29-llm_finetune.md index e85e521a..4b17f98e 100644 --- a/_posts/MachineLearning/LLM/2023-10-29-llm_finetune.md +++ b/_posts/MachineLearning/LLM/2023-10-29-llm_finetune.md @@ -426,4 +426,6 @@ PS: 深度学习都得指定features/labels。在llm 场景下,features 和lab [LLM is not all you need(大模型领域知识微调实践)](https://zhuanlan.zhihu.com/p/689800667) 3. 为什么要用SFT (指令微调)而不用RLHF?RLHF的loss主要在优化LLM输出特定分布(风格)的内容,而笔者想要的是让LLM看到某个问题输出非常精确的答案。那能不能用RL来做基于标签反馈的优化呢?这里边有一个很大的问题是目标token序列非常少,这涉及到稀疏空间中的RL优化问题,比较棘手,不如用SFT来得直接有效,而且SFT对于格式输出的学习几乎是无可挑剔的。 4. 指令微调是全参数微调还是PEFT?首先说PEFT吧,有Lora,P-tuning等很多种。笔者一直用Lora,虽然这些方法各有千秋,但当任务很难时性能差异不是很大。其次说全参数微调,笔者用PEFT微调发现效果比较差之后就采用了全参数微调,但是提升不是很显著(需要的是20+或者30+个点的提升,而不是个位数的提升)。 -5. 能不能将领域知识以continue training的形式注入LLM?尝试过,几乎没有提升。主要是用来continue training的数据不是非常多。所以对SFT没有加成。 \ No newline at end of file +5. 能不能将领域知识以continue training的形式注入LLM?尝试过,几乎没有提升。主要是用来continue training的数据不是非常多。所以对SFT没有加成。 +[大模型生产环境下部署微调的10条戒律](https://mp.weixin.qq.com/s/eRakAQP7FP51Gj6lL-_bNg) +1. 汝应当编写提示语,并创建一个基准,证明任务是可行的。如果提示语有效,微调有90%的可能性会改善模型表现;如果无效,微调只有25%的可能性有效; \ No newline at end of file diff --git a/_posts/MachineLearning/LLM/2023-10-29-llm_prompt.md b/_posts/MachineLearning/LLM/2023-10-29-llm_prompt.md index af12edca..767ba05d 100644 --- a/_posts/MachineLearning/LLM/2023-10-29-llm_prompt.md +++ b/_posts/MachineLearning/LLM/2023-10-29-llm_prompt.md @@ -168,6 +168,8 @@ Antonym: ## 其它 +少量的业务知识,比如只是几百几千的内容,给到足够上下文是成本最低,性价比最高的方案。微调且无法快速适应业务知识变化, 对于我们的大部分场景是投入产出比非常低的事情。 + 预训练+微调范式主要是让预训练语言模型去适配下游任务,通过**引入下游任务的loss**,让模型在具体任务上继续训练,以便在下游任务上取得较好的成绩。在这个过程中,语言模型会遗忘预训练过程中学到的知识,造成模型泛化性和鲁棒性的丢失,仅能保留某个任务或者某个数据的信息。而且随着语言模型的逐渐变大,模型本身已经存储了大量的知识,因为具体的下游任务微调导致原本模型能力丧失,是大家不愿意看到的。因此预训练+提示+预测的范式成为LLM主流使用方法,该模式**让下游任务去适配预训练语言模型**,通过对下游任务的重构,让下游任务符合模型预训练过程,消除预训练任务与下游任务之间的差异,使得下游任务在少样本甚至零样本上可以获得较好的成绩,提高模型的泛化性和鲁棒性。具体而言,提示学习是在原始输入文本上附加额外的提示信息作为新的输入,将下游的预测任务转化为语言模型任务,并将语言模型的预测结果转化为原本下游任务的预测结果。以情感分析任务为例 1. 原始任务 input=“我爱中国”,output=正向/负向 2. 提示学习 input=“我爱中国,这句话的情感为{mask}”,output=mask值,再将mask值映射到情感标签上。 \ No newline at end of file diff --git a/_posts/MachineLearning/LLM/2023-10-30-from_attention_to_transformer.md b/_posts/MachineLearning/LLM/2023-10-30-from_attention_to_transformer.md index 161d95f7..8c91ed75 100644 --- a/_posts/MachineLearning/LLM/2023-10-30-from_attention_to_transformer.md +++ b/_posts/MachineLearning/LLM/2023-10-30-from_attention_to_transformer.md @@ -118,10 +118,14 @@ $$ ![](/public/upload/machine/transformer_qkv.gif) -这个过程可以简单概括为 3 步: -1. 衡量QK 之间的相似性,这种相似性对于向量来说就是内积。$\sqrt{d_k}$ 作为调节因子,使得内积不至于太大,从而使梯度稳定很多。 +![](/public/upload/machine/transformer_x_to_z.jpg) + +Attention机制的目标是输入$x_n$,输出$z_n$。这个过程可以简单概括为 3 步: +1. 注意力矩阵$QK^T$衡量QK 之间的相似性,这种相似性对于向量来说就是内积。$\sqrt{d_k}$ 作为调节因子,使得内积不至于太大,从而使梯度稳定很多。其中 $q_n$ +与$k_1,k_2,...k_n$均有运算关系。所以可以通过缓存$k_1,k_2,...k_{n-1}$向量加速推理。这是kvcache中K矩阵需要缓存的原因。不过很意外的发现最右边一列$q_1,q_2,...q_{n-1}$与$k_n$之间存在计算。不是说好的只有KV缓存,没有Q矩阵缓存?推导没有错,也没有Q矩阵缓存。因为在推理阶段,Attention机制有一个非常重要的细节:mask掩码。注意力矩阵在训练推理过程中,为了模拟真实推理场景,当前位置token是看不到下一位置的,且只能看到上一位置以及前面序列的信息,所以在训练推理的时候加了attention mask。 + ![](/public/upload/machine/transformer_qkt.jpg) 2. 查找权重 a。这是通过 SoftMax 完成的。相似性像一个完全连接的层一样连接到权重。SoftMax 之后的注意力分数,其分值大小代表了相关性强弱,这种差异在计算梯度时,可以相对均匀地流入多个token位置。 -3. 值的加权组合。将 a 的值和 V 对应的值相乘累加,最终输出是所需的结果注意力值。 +3. 值的加权组合。将 a 的值和 V 对应的值相乘累加 $z_n=a_1*v_1+a_2*v_2+...+a_n*v_n$,最终输出是所需的结果注意力值。这是kvcache中V矩阵需要缓存的原因。 [Transformer内部原理四部曲3D动画展示](https://mp.weixin.qq.com/s/QEvdSPaf6Ikz15iaEJXjlw) diff --git a/_posts/MachineLearning/LLM/2023-10-30-llm_agent.md b/_posts/MachineLearning/LLM/2023-10-30-llm_agent.md index 6f7c02e7..2493e003 100644 --- a/_posts/MachineLearning/LLM/2023-10-30-llm_agent.md +++ b/_posts/MachineLearning/LLM/2023-10-30-llm_agent.md @@ -107,7 +107,59 @@ ReACT框架的一个关键特点是其任务拆解模块,能够将复杂的任 现有方案如CoT和基于分解的提示,都有擅长的任务类型,适合作为独立的原子模块,不具有普适性;[大模型自动推理?谷歌等发布SELF-DISCOVER!](https://zhuanlan.zhihu.com/p/682413793) -## 使用 +## 猴版实现 +``` +class Agent: + def __init__(self, system=""): + self.system = system + self.messages = [] + if self.system: + self.messages.append({"role": "system", "content": system}) + + def __call__(self, message): + self.messages.append({"role": "user", "content": message}) + result = self.execute() + self.messages.append({"role": "assistant", "content": result}) + return result + + def execute(self): + completion = client.chat.completions.create( + model="gpt-4o", + temperature=0, + messages=self.messages) + return completion.choices[0].message.content + +def query(question, max_turns=5): + i = 0 + bot = Agent(prompt) + next_prompt = question + while i < max_turns: + i += 1 + result = bot(next_prompt) + print(result) + actions = [ + action_re.match(a) + for a in result.split('\n') + if action_re.match(a) + ] + if actions: + # There is an action to run + action, action_input = actions[0].groups() + if action not in known_actions: + raise Exception("Unknown action: {}: {}".format(action, action_input)) + print(" -- running {} {}".format(action, action_input)) + observation = known_actions[action](action_input) + print("Observation:", observation) + next_prompt = "Observation: {}".format(observation) + else: + return + +question = """我有两只狗,一只边境牧羊犬和一只苏格兰梗犬。 +它们的总体重是多少""" +query(question) +``` + +## langchain使用 自定义tool 实现 diff --git a/_posts/MachineLearning/LLM/2024-06-15-llamaindex.md b/_posts/MachineLearning/LLM/2024-06-15-llamaindex.md index 637cb591..7530b821 100644 --- a/_posts/MachineLearning/LLM/2024-06-15-llamaindex.md +++ b/_posts/MachineLearning/LLM/2024-06-15-llamaindex.md @@ -188,5 +188,9 @@ chat 方法由一个while true 循环组成,循环内执行 _run_step, Agent ## 与LangChain 对比 +人工智能和LLM正在快速变化的领域,每周都会出现新的概念和想法,设计经得起时间考验的抽象是非常困难的。更安全的选择是仅对较低级别的构建块使用抽象。 + LlamaIndex的重点放在了Index上,也就是通过各种方式为文本建立索引,有通过LLM的,也有很多并非和LLM相关的。LangChain的重点在 Agent 和 Chain 上,也就是流程组合上。可以根据你的应用组合两个,如果你觉得问答效果不好,可以多研究一下LlamaIndex。如果你希望有更多外部工具或者复杂流程可以用,可以多研究一下LangChain。 + + diff --git a/_posts/MachineLearning/LLM/2024-06-22-llm_function_calling.md b/_posts/MachineLearning/LLM/2024-06-22-llm_function_calling.md index a4cdb139..6c665bc9 100644 --- a/_posts/MachineLearning/LLM/2024-06-22-llm_function_calling.md +++ b/_posts/MachineLearning/LLM/2024-06-22-llm_function_calling.md @@ -15,13 +15,17 @@ keywords: llm agent ## 简介 +智能体的基本概念是在没有人工定义工作流(Workflow)的情况下,利用外部工具或功能,选择要执行的一系列操作。从技术角度来看,智能体通过大模型理解用户意图并生成结构化描述,进而执行相关操作。市场上现在出现了众多种类的智能体应用,其中大致可以分为两种主要的方式:以ReACT行动链为主的较为复杂的智能体结构,和以Function Calling(函数调用)模型为主的轻量级智能体结构。 + OpenAI于23年6月份的更新的gpt-4-0613 and gpt-3.5-turbo-0613版本中为模型添加了Function Calling功能,通过给模型提供一组预定义的函数(Function list)以及用户提问(Query),让大模型自主选择要调用的函数,并向该函数提供所需的输入参数。随后我们就可以在自己的环境中基于模型生成的参数调用该函数,并将结果返回给大模型。然而Function Calling这种类型的智能体结构对模型有较高的要求,LLM模型必须进行针对性微调,以便根据用户提示检测何时需要调用函数,并使用符合函数签名的JSON进行响应; [ChatGLM3 的工具调用(FunctionCalling)实现原理](https://zhuanlan.zhihu.com/p/664233831)使用Function Call功能时,你需要定义(并不是真的写程序去定义一个函数,而仅仅是用文字来描述一个函数)一些function(需要指定函数名,函数用途的描述,参数名,参数描述),传给LLM,当用户输入一个问题时,LLM通过文本分析是否需要调用某一个function,如果需要调用,那么LLM返回一个json,json包括需要调用的function名,需要输入到function的参数名,以及参数值。本质上是LLM(按特定意图)帮我们在做个文本结构化,Function calling 允许开发者更可靠的从模型中获得结构化数据,无需用户输入复杂的Prompt。实现上,把json格式的函数描述直接转换成了字符串,然后和其他输入一并送入LLM,**归根到底function call能力就是在prompt上边做了手脚**,微调时候有function call格式的数据。 ## 与ReAct对比 -Function Calling通过微调的模型,使其更擅长返回结构化输出。这种方法简化了提示工程,无需提供示例来教导模型如何推理和输出结构化数据,可以产生确定性的结果,同时降低错误率。然而,由于缺乏思维链,整个Function Calling的过程重度依赖于模型自身的推理性能,引发了大模型推理的重要问题 -- 缺乏解释性,整个推理过程相较于ReACT方式更加黑盒。相比之下,ReACT方法更加通用,留给开发者的改造空间更多,**在不微调模型的情况下**,ReACT框架更易于拓展,基于场景设计出不同的规划结构和工具调用结构。ReACT的CoT部分允许智能体在执行任务时进行更深入的推理和思考。这种方法使得智能体能够更准确地理解任务,并在执行过程中进行调整。在实际应用中,Function Calling和ReACT都可能需要执行多轮的拆解、推理和调用才能得到结果。**Function Calling隐藏了Thought过程**,而ReACT则更加开放和透明。这使得ReACT更适合需要高度定制和灵活性的应用场景,而Function Calling则更适合需要快速和确定性输出的场景。 +1. 对模型的要求。Function Calling类的智能体对模型的要求相对较高。模型的训练数据必须包含function call相关的内容,以确保模型能够理解和生成结构化的输出。这类模型通常还需要具备更好的结构化输出稳定性,以及关键词和信息提取的能力。这意味着,模型需要较大的参数量,经过精细的调整和优化,才能满足Function Calling的需求。这种方式的优点在于,模型可以直接生成符合特定格式的数据,从而提高了解析和处理的效率。相比之下,ReACT框架对模型的要求则相对较低。ReACT不需要模型本身支持function calling格式的输出。在计划的生成过程中,它可以支持自然语言的规划文本,并在后续步骤中解析这些自然语言的输入。其优势在于,模型不需要进行复杂的结构化输出,只需生成自然语言即可。这使得模型的训练和优化过程更为简单,同时也降低了模型的出错率。 +2. 对提示词的要求。Function Calling类的智能体结构通过微调模型来支持用户输入选择函数和结构化输入,这个过程其实这提高了输出稳定性,并简化了提示工程的复杂程度。相比之下,ReACT方式需要对模型进行更加细致的指导,让通用模型拥有输出规划、函数所需参数的能力,虽然这缓解了对模型本身输出能力的依赖,却增加了对提示工程的依赖,需要针对模型的特性来设计对应的提示模板,生成规划(函数的选择)和函数所需的API,并可能需要提供样例,消耗的上下文Token也相对更多一些。尽管Function Calling对模型的要求更高,但通过提示模板,普通的模型也可以具备简单的Function Calling的能力。通过在prompt中说明希望的输出格式和字段描述,大模型可以直接输出符合要求的内容。 +3. 对推理的要求。在智能体结构的设计中,ReACT和Function Calling在推理要求上存在显著差异。Function Calling强调的是单/多步骤的JSON输出,而ReACT则允许LLM输出一个自然语言规划,这为智能体提供了思考的空间并能在后续的执行过程中动态地修正规划(Reflection)。Function Calling通过微调的模型,使其更擅长返回结构化输出。这种方法可以产生确定性的结果,同时降低错误率。然而,由于缺乏思维链,整个Function Calling的过程重度依赖于模型自身的推理性能,引发了大模型推理的重要问题 -- 缺乏解释性,整个推理过程相较于ReACT方式更加黑盒。相比之下,ReACT的CoT部分允许智能体在执行任务时进行更深入的推理和思考。这种方法使得智能体能够更准确地理解任务,并在执行过程中进行调整。在实际应用中,Function Calling和ReACT都可能需要执行多轮的拆解、推理和调用才能得到结果。Function Calling隐藏了Thought过程,而ReACT则更加开放和透明。这使得ReACT更适合需要高度定制和灵活性的应用场景,而Function Calling则更适合需要快速和确定性输出的场景。 ## openai like api diff --git a/_posts/Technology/Compute/2023-08-23-ray.md b/_posts/Technology/Compute/2023-08-23-ray.md index 14cdf2f3..a0e86ad6 100644 --- a/_posts/Technology/Compute/2023-08-23-ray.md +++ b/_posts/Technology/Compute/2023-08-23-ray.md @@ -70,9 +70,9 @@ print(ray.get(futures)) # [3, 3, 3, 3] ## 设计 Ray 的框架中最为重要的两个部分是 Ray Core 和 Ray AIR: -1. Ray Core 是底层的分布式的计算框架,使用基于 actor 模型来实现的一套计算框架,它可以将 Python 的一个 Class 或者一个 Function 转成分布式的 actor 和 task,在所有的机器上分布式地进行运行,并且 tasks/actor 之间可以通过分布式的对象存储能力来达到共享的能力。 +1. Ray Core 是底层的分布式的计算框架,使用基于 actor 模型来实现的一套计算框架,它可以将 Python 的一个 Class 或者一个 Function 转成分布式的 actor 和 task,在所有的机器上分布式地进行运行,并且 tasks/actor 之间可以通过分布式的对象存储能力来达到共享的能力。基于这层api,可以非常容易将现有程序分布式化,这一层没有绑定任何计算模式以及语义。 ![](/public/upload/compute/ray_core.jpg) -2. Ray AIR是Ray的上层API,主要将Ray的各种应用整合在一起。比如 xgboost-ray +2. Ray AIR是Ray的上层API,主要将Ray的各种应用整合在一起。比如 xgboost-ray/RayTrain/RayServe Ray 提供了 init、remote、put、get 等基本原语。但是通过这些基本原语达不到更细粒度的控制,例如针对不同的计算配置不同的CPU、内存。或者调度的时候,能够提供人工设定调度的方法。Ray 希望通过参数配置来实现任务、故障和应用的细粒度控制。配置能够达到什么样粒度的控制。 @@ -153,7 +153,7 @@ assert ray.get(obj2) == 16 除了Task和Actor两个基本概念,Ray还提供了一系列高级功能,来帮助开发者更容易地开发分布式应用。这些高级功能包括但不限于:设置Task和Actor所需的资源、Actor生命周期管理、Actor自动故障恢复、自定义调度策略、Python/Java跨语言编程。 -如果没有Ray,**在纯云原生的实现思路中,资源定制是写到 yaml 里边的**。比如说训练需要多少GPU 或者计算节点需要多少CPU,都是在 yaml 中定制 container 的规格。**Ray提供了另外一个选择**,完全无感知的代码化的配置,用户可以在 runtime 的时候,或者在Ray的Task 或 Actor 的decorator 中加一个参数,就可以通过Ray系统的调度能力分配相应的资源,达到整个分布式系统资源定制的目的。Ray的资源定制除了支持GPU、CPU、Memory 之外,还可以插入自定义资源。然后Ray的调度还有一些高级功能,比如资源组,或者亲和性和反亲和性的调度,目前都是支持的。 +如果没有Ray,**在纯云原生的实现思路中,资源定制是写到 yaml 里边的**。比如说训练需要多少GPU 或者计算节点需要多少CPU,都是在 yaml 中定制 container 的规格。**Ray提供了另外一个选择**,完全无感知的代码化的配置,用户可以在 runtime 的时候,或者在Ray的Task 或 Actor 的decorator 中加一个参数,就可以通过Ray系统的调度能力分配相应的资源,达到整个分布式系统资源定制的目的。Ray的资源定制除了支持GPU、CPU、Memory 之外,还可以插入自定义资源。然后Ray的调度还有一些高级功能,比如资源组,或者亲和性和反亲和性的调度,目前都是支持的。ray的调度中,所有的资源都是逻辑资源不是物理资源,ray会按照task/actor的资源需求来进行调度,但不会限制task/actor对资源的真实使用,没有资源隔离。比如一个节点有10cpus,actor(num_cpus=2)只能创建5个,每个actor使用多少线程/进程ray是不控制的。另外cpu/gpu可以是小数,但不代表ray会为进程做cpu/gpu共享。 ```python @ray.remote(num_gpus=1) @@ -161,7 +161,7 @@ def train_and_evaluate(model,train_indices,test_indices): ... ``` -在分布式系统中,往往不同分布式系统的组件对环境的要求是不一样的。**如果使用常规思路,就需要把环境固化到image里面,通过 Dockerfile 去定制环境**。Ray实现了更灵活的选择,也是代码化的,可以在runtime创建Task或Actor之前的任意时刻定制指定计算单元的运行时环境。上图中给worker 的 Task 设定一个runtime_env,定制一个专属的Python版本,并在该版本里面装入一些pip包,完成面向Python的隔离环境的定制。这时Ray集群内部会在创建这个Task之前去准备该环境,然后将该Task调度到该环境执行。 +依赖管理:在分布式系统中,往往不同分布式系统的组件对环境的要求是不一样的。**如果使用常规思路,就需要把环境固化到image里面,通过 Dockerfile 去定制环境**。Ray实现了更灵活的选择,也是代码化的,可以在runtime创建Task或Actor之前的任意时刻定制指定计算单元的运行时环境。上图中给worker 的 Task 设定一个runtime_env,定制一个专属的Python版本,并在该版本里面装入一些pip包,完成面向Python的隔离环境的定制。这时Ray集群内部会在创建这个Task之前去准备该环境,然后将该Task调度到该环境执行。 ```python @ray.remote(runtime_env={"python_version":"3.9","pip"=["scikit-learn"]}) @@ -200,7 +200,7 @@ Ray 的Low-level和 High-level API ![](/public/upload/machine/ray_api.jpg) -在部署 Ray 时,开源社区有完整的解决方案 Kuberay 项目。每个 Ray Cluster 由 Head 节点和 Worker 节点组成,每个节点是一份计算资源,可以是物理机、Docker 等等,在 K8s 上即为一个 Pod。启动 Ray Cluster 时,使用 Kuberay 的 Operator 来管理整个生命周期,包括创建和销毁 Cluster 等等。Kuberay 同时也支持自动扩展和水平扩展。Ray Cluster 在内部用于收集负载的 Metrics,并根据 Metrics 决定是否扩充更多的资源,如果需要则触发 Kuberay 拉起新的 Pod 或删除闲置的 Pod。 +在部署 Ray 时,开源社区有完整的解决方案 Kuberay 项目。每个 Ray Cluster 由 Head 节点和 Worker 节点组成,每个节点是一份计算资源,可以是物理机、Docker 等等,**在 K8s 上即为一个 Pod**。启动 Ray Cluster 时,使用 Kuberay 的 Operator 来管理整个生命周期,包括创建和销毁 Cluster 等等。Kuberay 同时也支持自动扩展和水平扩展。Ray Cluster 在内部用于收集负载的 Metrics,并根据 Metrics 决定是否扩充更多的资源,如果需要则触发 Kuberay 拉起新的 Pod 或删除闲置的 Pod。 用户可以通过内部的平台使用 Ray,通过提交 Job 或使用 Notebook 进行交互式编程。平台通过 Kuberay 提供的 YAML 和 Restful API 这两种方式进行操作。 diff --git a/_posts/Technology/JVM/2016-06-17-jvm_gc.md b/_posts/Technology/JVM/2016-06-17-jvm_gc.md index cd086dcb..af012048 100644 --- a/_posts/Technology/JVM/2016-06-17-jvm_gc.md +++ b/_posts/Technology/JVM/2016-06-17-jvm_gc.md @@ -303,6 +303,8 @@ G1在堆内存较小的情况下,吞吐量、回收的效率等各方面表现 2. 第二个原因是堆很小的情况下,G1的预测模型优势就无法发挥出来,并且由于堆内存的过小,每个region也会很小,达到最小的1M时,可能会出现大量的Humongous 对象,而Humongous对象过多,会导致频繁的Mixed GC,并且非常容易触发Full GC。 3. **G1除了需要更多的内存空间以外,还会带来CPU的使用率增加**,导致容器实例的CPU水位上升,因为 G1 除了 Write Barrier 来更新维护Card Table以外,还为了实现SATB算法,需要使用Write Barrier来跟踪并发时的指针变化情况。除了Card Table的维护损耗以外,G1还需要维护Rset,当引用关系发生改变时,G1需要更新Rset,G1为了能够异步更新Rset,引入了Dirty Card Queue(DCQ),DCQ中存放着引用关系更新的任务,在G1中会创建一个用于处理这些任务的线程池,这些线程称为 Refine 线程。使用 Refine 线程来消费 DCQ ,从而完成引用关系的记录并更新RSet。如果 Refine 线程忙不过来,GC线程以及应用线程也会被用于协助更新RSet。 +从G1的设计上来看,它使用了大量的额外结构来存储引用关系,并以此来减少垃圾回收中标记的耗时。但是这种额外的结构带来的内存浪费也是存在的,极端情况甚至可以额外占用20%的内存。而基于region的内存划分规则则让内存分配更加复杂,但是这也有好处。就是内存的回收后产生的碎片更少,也就更少触发full gc。根据经验,在大部分的大型内存(6G以上)服务器上,无论是吞吐量还是STW时间,G1的性能都是要优于CMS。 + ## 无暂停 GC/ZGC [从历代GC算法角度剖析ZGC](https://mp.weixin.qq.com/s/ExkB40cq1_Z0ooDzXn7CVw) 值得细读。 @@ -313,6 +315,7 @@ G1在堆内存较小的情况下,吞吐量、回收的效率等各方面表现 无暂停 GC的停顿时间不会随着堆大小的增加而线性增加。以 ZGC 为例,它的最大停顿时间不超过 10ms ,注意不是平均,也不是随机,而是最大不超过 10ms 。 1. ZGC 和 G1 有很多相似的地方,主体思想也是采用复制活跃对象的方式来回收内存。在回收策略上,它也同样将内存分成若干个区域,回收时也会选择性地先回收部分区域 2. ZGC 与 G1 的区别在于:**它可以做到并发转移(拷贝)对象**。并发转移指的是在对象拷贝的过程中,应用线程和 GC 线程可以同时进行。前面我们介绍的垃圾回收算法,在进行对象转移时都是需要STW,而对象转移随着堆的增大,这个时间也会跟着增加。并发转移不需要很长时间的STW,是 ZGC 停顿时间很小的主要原因。 +要理解ZGC的并发回收过程先了解3个概念:染色指针;读屏障;NUMA。 [ZGC和G1在HBase集群中的GC性能对比](https://mp.weixin.qq.com/s/vnAkp3XSvPCPDIsFrZlmeQ)CMS 新生代的 Young GC、G1 和 ZGC 都基于标记-复制算法,但算法具体实现的不同就导致了巨大的性能差异。标记-复制算法应用在 CMS 新生代(ParNew 是 CMS 默认的新生代垃圾回收器)和 G1 垃圾回收器中。标记-复制算法可以分为三个阶段: diff --git a/_posts/Technology/Storage/2015-06-26-kv_system.md b/_posts/Technology/Storage/2015-06-26-kv_system.md index aa5d5097..f93ead93 100644 --- a/_posts/Technology/Storage/2015-06-26-kv_system.md +++ b/_posts/Technology/Storage/2015-06-26-kv_system.md @@ -66,6 +66,8 @@ keywords: 缓存 redis [这篇文章把大型系统的缓存设计问题说清楚了](https://mp.weixin.qq.com/s/jFUG6O6vsn_3cWuN-skENg)为什么我们几乎没办法做到缓存和数据库之间的强一致呢?正常情况下,我们需要在数据库更新完后,把对应的最新数据同步到缓存中,以便在读请求的时候,能读到新的数据而不是旧的数据(脏数据)。但是很可惜,由于**数据库和 Redis 之间是没有事务保证的**,所以我们无法确保写入数据库成功后,写入 Redis 也是一定成功的;即便 Redis 写入能成功,在数据库写入成功后到 Redis 写入成功前的这段时间里,Redis 数据也肯定是和 MySQL 不一致的。这个时间窗口是没办法完全消灭的,除非我们付出极大的代价,使用分布式事务等各种手段去维持强一致,但是这样会使得系统的整体性能大幅度下降,甚至比不用缓存还慢,这样不就与我们使用缓存的目标背道而驰了吗?不过虽然无法做到强一致,但是我们能做到的是缓存与数据库达到最终一致,而且不一致的时间窗口我们能做到尽可能短,按照经验来说,如果能将时间优化到 1ms 之内,这个一致性问题带来的影响我们就可以忽略不计。 +[奇怪的缓存一致性问题](https://mp.weixin.qq.com/s/la8YoEHd806Ovs-k1siLLg) 未读 + ### 缓存系统的数据模型 很多事情联系起来想很有意思,比如rpc,跨主机进程通信。然后一些大牛搞出redis,可以理解为**跨主机访问内存**,360推出一个pika,可以理解为跨主机访问磁盘(支持redis协议)。 diff --git a/public/upload/machine/pretrain_padding.jpg b/public/upload/machine/pretrain_padding.jpg new file mode 100644 index 00000000..aa570c61 Binary files /dev/null and b/public/upload/machine/pretrain_padding.jpg differ diff --git a/public/upload/machine/transformer_qkt.jpg b/public/upload/machine/transformer_qkt.jpg new file mode 100644 index 00000000..ee5ca945 Binary files /dev/null and b/public/upload/machine/transformer_qkt.jpg differ diff --git a/public/upload/machine/transformer_x_to_z.jpg b/public/upload/machine/transformer_x_to_z.jpg new file mode 100644 index 00000000..7b26c93f Binary files /dev/null and b/public/upload/machine/transformer_x_to_z.jpg differ