Skip to content

Commit

Permalink
add ml
Browse files Browse the repository at this point in the history
  • Loading branch information
liqiankun1111 committed Dec 29, 2023
1 parent 24e09e4 commit 681da2a
Show file tree
Hide file tree
Showing 18 changed files with 207 additions and 76 deletions.
2 changes: 1 addition & 1 deletion _posts/MachineLearning/2021-10-30-pytorch_distributed.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ if __name__ == "__main__":

![](/public/upload/machine/data_parallel.png)

[ PyTorch 分布式(2) ----- DataParallel(上)](https://mp.weixin.qq.com/s/cGfKl6yydc5Xmd_Ok3mnkA) 缺点很多。
[PyTorch 分布式(2) ----- DataParallel(上)](https://mp.weixin.qq.com/s/cGfKl6yydc5Xmd_Ok3mnkA) 缺点很多。

## 分布式训练

Expand Down
45 changes: 30 additions & 15 deletions _posts/MachineLearning/2021-11-27-pytorch_elastic.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,34 @@ keywords: pytorch distributed elastic 弹性
3. 如何捕获单个进程训练失败,如何在单个节点上管理所有训练进程。
4. 如何与现有训练代码集成。

## 20231012弹性训练集大成

[DLRover 在 K8s 上千卡级大模型训练稳定性保障的技术实践](https://mp.weixin.qq.com/s/gHoP3mESWJ8BUFT-m0-eBA) OpenAI 在 1024 个 NVIDIA A100 GPU 上训练 GPT-3 大约需要 34 天。训练节点越多,耗时越长,训练期间节点故障概率就越大。据我们在蚂蚁 GPU 训练集群上观察,一个月内,单卡的故障率约 8%,那么一天单卡的故障率约为 0.27%。常见的故障原因有 Xid、ECC、NVLINK error 和 NCCL error 故障等。对于一个千卡训练作业来说,卡故障导致一天内训练失败的概率高达到 93%。所以训练作业几乎每天都会失败。作业失败后,用户需要手动重启作业,运维成本很高。如果用户重启不及时,中间间隔的时间就会导致 GPU 卡空闲,浪费昂贵的算力资源。

弹性训练是指在训练过程中可以伸缩节点数量。当前支持 PyTroch 弹性训练的框架有 Torch Elastic 和 Elastic Horovod。二者显著的区别在于节点数量变化后是否需要重启训练子进程来恢复训练。Torch Elastic 感知到新节点加入后会立刻重启所有节点的子进程,集合通信组网,然后从 checkpoint 文件里恢复训练状态来继续训练。而 Elastic Horovod 则是每个训练子进程在每个 step 后检查新节点加入,子进程不退出的情况下重新集合通信组网,然后有 rank-0 将模型广播给所有 rank。
1. **集合通信动态组网**。动态组网是指训练进程可以自动根据动态变化的节点数量来组网集合通信,无需固定给各个节点指定集合通信的 rank 和 world size。**动态组网是弹性容错训练必须的**,因为弹性容错作业中,节点的失败、扩容或者缩容都会导致节点的 rank 和 world size 变化。所以我们无法在作业启动前给节点指定 rank 和 world size。PS: Rendezvous 机制:Rendezvous Manager/Rendezvous Agent/共享存储。 DLRover ( ElasticJob )会启动一个Master 存CPU pod,负责运行Rendezvous Manager,且会踢掉用不到的pod(让worker pod 进程退出?)。
2. 分布式训练容错,训练容错是指训练出现故障后能在无人工介入的情况下快速恢复训练。训练恢复需要如下步骤:定位错误原因,判断错误是否可以恢复;启动训练进程加载训练代码,训练进程能重新集合通信组网;训练进程能加载模型导出的 checkpoint 来恢复训练状态;如果存在故障机,要及时将故障机排除,以便新节点继续调度在故障机。
1. 对于无故障机的错误,DLRover 重启进程来恢复训练。
2. 对于故障机的错误,DLRover 会通知 SRE 隔离故障机并重新拉起 Pod 来替换出错的 Pod
3. 对于正常运行的 Pod 重启其训练进程,减少 Pod 调度时间开销。
3. 故障机检测
4. 错误日志收集,在 PyTorch 分布式训练中,一个节点的进程出错后,Torch Elastic 会停止所有节点的进程。各个进程的日志都是单独存在各自日志文件中。为了找到训练失败是哪个进程出错导致的,我们需要搜索所有进程的日志。这个工作对于千卡作业是十分耗时且繁琐的。为此,我们在 ElasticAgent 中开发了错误日志收集供功能。当 ElasticAgent 发现子进程失败后,后将其错误日志的 message 发送给 Job Master。Job Master 会在其日志中展示具体哪个节点的那个进程失败了,以及错误日志。这样用户只需看下 Job Master 的节点日志就可以定位训练失败原因了。
PS:
1. 故障检测,踢掉故障worker,踢掉多余worker(有时用户要求worker数必须是N的整数倍),worker 被踢掉之后,由controller来创建新的pod? 若worker 被踢掉之后,新的pod 因为资源不够一直pending,当前集群不满足客户要求的最小worker数,如何处理呢?
2. 除了controller、crd等,代码上,DLRover 提供了 ElasticTrainer 来封装训练过程,dlrover-run 来启动训练代码(只要是能用 torchrun 启动的任务,都是支持用 dlrover-run 来跑的。dlrover-run 是扩展了torchrun,所以原生的torchrun的配置都支持。)。

## DLRover

[阿里云 ACK 云原生 AI 套件中的分布式弹性训练实践](https://mp.weixin.qq.com/s/UYFfFxYa0y16-SS60qGEKA)什么是弹性训练?具体可以总结为三大块的能力:
1. 训练规模弹性改变:这里主要指的是弹性改变训练的 Worker 数目,扩容增加 Worker 数量以提升训练速度,缩容减少 Worker 数量以腾出部分集群资源;
2. 训练过程弹性容错:由于部分因素导致任务异常或可预见问题如 Spot 回收事件预示下的整体任务的容错,避免因为少部分 Worker 失败而直接导致的整个任务的失败;
3. 训练资源弹性伸缩:可以根据任务需要或者问题处理的判断来动态变更任务训练 Worker 的资源配置以达到一个更合理的任务 Worker 资源配比。

而弹性训练的能力带来的意义,大概也可以总结为三点:
1. 大规模分布式训练容错,有效提升训练任务运行的成功率;
2. 提升集群算力利用率,充分利用弹性来协调在离线任务的资源分配;
3. 降低任务训练成本,使用可被抢占或稳定性稍差但成本更低的实例来进行训练从而整体层面降低成本。

分布式训练一般分为两种类型,数据并行和模型并行。基于数据并行的分布式训练又分为两种不同的架构。
1. Parameter Server 架构。在 PS 模式下进行的弹性训练,由于其为异步模式,弹性的关键在于训练数据的划分。当其中一部分 Worker 失败之后,未被训练的数据或者失败 Worker 中的数据可以被剩下的 Worker 继续训练,当新的 Worker 加入之后,可以与现有的 Worker 一起参与进行训练。
![](/public/upload/machine/ps_elastic.jpg)

在蚂蚁 AI Infra 团队开源的项目 DLRover 中,其实现了 Training Master 来参与弹性训练。由 Training Master 来负责对任务的监听、数据集的划分、各角色资源的弹性。其中数据集的划分是弹性训练的关键,在 Master 中有一个 Dataset Shard Service 的角色来负责具体数据集的划分。其将整个的数据集按照 Batch Size 进行切分,分为各个 Task Data,然后将 Task Data 放进数据集队列中供各个训练 Worker 进行消费。在 Dataset Shard Service 中存在着两个队列,所有未被训练消费的 Task Data 在 TODO 队列中,而正在被训练的 Task Data 则是在 DOING 队列,直到该 Data 训练结束 Worker 给出信号后,该 Task Data 才会完全出队。如果有训练 Worker 中途异常退出,检测超时的 Task Data 会重新进入 TODO 队列以供其他正常 Worker 进行训练。

![](/public/upload/machine/dl_rover_dataset_service.jpg)

2. AllReduce 架构,在 AllReduce 模式下进行的弹性训练,由于其为同步模式,弹性的关键在于如何保证训练的同步,同时还有为了同步梯度而建立起来的通信环的保持。当其中一部分 Worker 失败之后,剩下的 Worker 可以重建通信环继续训练,当新的 Worker 加入之后,可以与现有的 Worker 重建通信环进行训练。
![](/public/upload/machine/dl_rover_all_reduce.jpg)

DLRover 在 Kubernetes 上设计了一个 CRD ElasticJob,由 ElasticJob Controller 监听并创建一个 DLRover Master,再由该 Master 来创建 PS 和 Worker 的 Pod,并控制 PS 和 Worker 的弹性。

![](/public/upload/machine/dl_rover_elastic_job.jpg)
不同场景对应不同的crd: ElasticJob ==> ps/worker; PytorchJob ==> Pytorch allreduce; TrainingJob+ScaleIn+ScaleOut ==> Elastic Horovod。

## train_script 的守护者elastic agent

Expand Down Expand Up @@ -414,6 +427,8 @@ PContext 就是一个抽象类,有两个派生类:MultiprocessContext 和 Su

## 与horovod 对比

![](/public/upload/machine/elastic_horovod.jpg)

## 其它

### run.py 启动命令
Expand Down
4 changes: 4 additions & 0 deletions _posts/MachineLearning/2022-02-09-feature_engineering.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ keywords: feature engineering
5. 聚合特征构造,单特征区分性不强时,可尝试组合不同特征。交叉特征让聚类的类别变更多,样本空间划分更细,比如某个城市的某个年龄段的人群可能有共同的爱好和行为。
6. 转换特征构造,比如幂变换、log变换、绝对值等

归一化通常用于解决不同特征的scale差异过大导致参数训练困难的问题。通常情况下,如果我们不对不同scale的特征进行处理而直接使用梯度下降的方法去进行优化,会导致不同scale的特征权值更新优化的方向不均衡的情况发生。举个简单的例子,假设我们只有两个特征f1和f2。其中f1的scale很小,范围在10-20之间,而f2的scale相对较大,范围在100-200之间。w1 和 w2分别对应它们的权重。那么由于特征f2的scale较大的缘故,w2一点微小的变化就会对最终结果产生巨大的影响,也会使损失函数的结果产生巨大的变化。**归一化指的就是将不同scale的特征,基于它们各自的均值和标准差进行值压缩**

神经网络的输入同样需要归一化来保证更加快速的收敛速度。但是,问题远不止于此。对于包含多个hidden layer的深层网络来说,随着每一层权重的不断更新,输入给下一层的数据的分布一直在不停的变化,导致下一层总是需要基于新的分布来进行参数的优化更新,这样就给模型的训练带来了极大的不稳定因素,导致模型总是无法收敛。换句话说,优化的前提是必须有相对不变的部分,通过固定这部分来对另一部分进行调整。而如果每个部分都在变,那优化过程就难以实现。这种在网络训练中,hidden layer输出分布不断变化的情况也被称之为Internal Covariate Shift。Layer Normalization在不同维度的特征上做归一化。对于MLP,可以直接理解为对某一层的多维输出做归一化操作。

## 特征准入和淘汰

CTR任务中,user_id和item_id的数量那么大,也常规embedding吗?如果是大厂的话,就是直接加。大厂所使用的parameter server一般都有特征的准入与逐出机制。尽管模型代码上都一视同仁地将所有user_id, item_id加入模型,但是只有那些出现超过一定次数的id,才最终被parameter server分配空间。而且对于非活跃用户、过时下架的item,parameter server也会逐出,释放空间。
Expand Down
4 changes: 4 additions & 0 deletions _posts/MachineLearning/2023-05-20-llm_try.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ PS:模型越大越好,但7B模型单个GPU就能跑,更适合需要低延

## 大佬观点

孙志岗:如果你是一个产品经理的话,你可以想想自家的产品中,**有哪些部分是文本进文本出**。因为大语言模型最擅长处理的就是文本,甚至说只会处理文本。它把所有的内容都当作文本处理。像客服系统就很典型,用户提问,进来一段文本,客服系统回答, 再返回去一段文本。你可以想想哪些场景里是文本进文本出,然后看看能不能用 AI 优化,比如让它变得自动化一些,或者效果好一些、成本低一点。不一定产品的呈现上非得有 AI 味,你可以把 AI 融入到冰山之下。然后在这个基础上,随着多模态能力的提升,你可以再看看能不能有图片进文本出,或者文本进图片出的场景,一步步朝多模态的方向延伸。
池:难道 AI 大模型就没有创造出来新场景吗?
孙:至少从现在的角度看,我只发现了一个场景是 AI 创造出来的——Character.ai 干的事儿。之前没人觉得和虚拟人物聊天有这么大的需求,现在,大家乐此不疲。并且,没有大模型之前,就算你知道有这种需求,也实现不了。还有 GitHub Copilot 它是完全基于大模型的创新,并且想象空间很大,这些年来,不管是框架,还是低代码之类的产品,其实都在解决代码生成的问题。现在,大模型技术的突破,更优雅又具效率的解决了这一问题。

姚志强:以前我们做的小模型,是就很像是人类的社会分工,直奔目标,比如这个算法就只做人脸识别、车牌识别,我去收集数据标注数据,然后去训练它,未来的可扩展性其实就没有很多。但现在大模型的训练,是**将它真正作为一个人来训练**。我们用专家知识去引导他,给他启发性的这样思维,真正的去教导他去引导他,这是出现了“涌现”现象的原因。
1. 未来,产品和技术演进思路全都要重新迭代,可能我们不会再有所谓的语音识别、自然语言理解、图像识别这些技术分类,剩下的只有AI——你是不是一家AI公司?判断AI基础能力的标准,可能就是大模型做得如何,在这个基础上再谈其他的行业模型、场景应用。
2. 对我们整个产品体系来说或产品架构来说其实影响不太大,更多是提升了我们的性能。原先我们可能说要不断的要在这里面添加小模型,那现在,我只要替换成一个大模型。而中间可能会有一些特殊的行业线应用我可能会变成行业模型。
Expand Down
14 changes: 11 additions & 3 deletions _posts/MachineLearning/2023-08-29-langchain_source.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,17 @@ ChatCompletion.create函数的详细参数和Completion.create函数相比发生
3. 其他核心参数完全一致,例如temperature、top_p、max_tokens、n、presence_penalty等参数的解释和使用方法都完全一致,且这些参数具体的调整策略也完全一致
4. 剔除了best_of参数,即Chat模型不再支持从多个答案中选择一个最好的答案这一功能

孙志岗:如果你是一个产品经理的话,你可以想想自家的产品中,**有哪些部分是文本进文本出**。因为大语言模型最擅长处理的就是文本,甚至说只会处理文本。它把所有的内容都当作文本处理。像客服系统就很典型,用户提问,进来一段文本,客服系统回答, 再返回去一段文本。你可以想想哪些场景里是文本进文本出,然后看看能不能用 AI 优化,比如让它变得自动化一些,或者效果好一些、成本低一点。不一定产品的呈现上非得有 AI 味,你可以把 AI 融入到冰山之下。然后在这个基础上,随着多模态能力的提升,你可以再看看能不能有图片进文本出,或者文本进图片出的场景,一步步朝多模态的方向延伸。
池:难道 AI 大模型就没有创造出来新场景吗?
孙:至少从现在的角度看,我只发现了一个场景是 AI 创造出来的——Character.ai 干的事儿。之前没人觉得和虚拟人物聊天有这么大的需求,现在,大家乐此不疲。并且,没有大模型之前,就算你知道有这种需求,也实现不了。还有 GitHub Copilot 它是完全基于大模型的创新,并且想象空间很大,这些年来,不管是框架,还是低代码之类的产品,其实都在解决代码生成的问题。现在,大模型技术的突破,更优雅又具效率的解决了这一问题。
所有语言模型,**包括用于聊天的模型,都是基于线性序列的标记进行操作,并没有内在的角色处理机制**。这意味着角色信息通常是通过在消息之间添加控制标记来注入的,以表示消息边界和相关角色。以单轮对话为例:
```
适配前--单轮对话:
user:我今早上吃了炒米粉。
assistant:炒米粉在广东是蛮常见的早餐,但是油太多,可以偶尔吃吃。
适配后--单轮对话:
<s><intp>我今早上吃了炒米粉。</intp> [ASST] 炒米粉在广东是蛮常见的早餐,但是油太多,可以偶尔吃吃。[/ASST] eos_token
```
这里除了区分user和 assistant加的special token 以外,必须要添加的是eos_token,必须要让模型知道什么时候next token生成结束,如果没有终止符,模型会陷入推理的无限循环。

不幸的是,目前还没有一个标准来确定使用哪些标记,因此不同的模型使用的格式和控制标记都可能大相径庭。聊天对话通常表示为字典列表,每个字典包含角色和内容键,表示一条单独的聊天消息。聊天模板是包含Jinja模板的字符串,用于指定如何将给定模型的对话格式化为一个可分词的序列。通过将这些信息存储在分词器中,我们可以确保模型以其期望的格式获取输入数据。对于一个模型来说,chat template 存储在tokenizer.chat_template 属性上(这个属性将保存在tokenizer_config.json文件中),如果chat template没有被设置,对那个模型来说,默认模版会被使用。

## LLM模型层

Expand Down
Loading

0 comments on commit 681da2a

Please sign in to comment.