LLMs:《如何使用Ray + DeepSpeed + HuggingFace简单、快速、经济有效地微调和服务LLM》解读
导读:这是我们有关生成式人工智能的博客系列的第4部分。在之前的博客文章中,我们解释了以下内容:
>> 为什么Ray是生成式人工智能的可靠平台;
>> 我们展示了如何推动性能极限;
>> 如何使用Ray进行稳定扩散;
在这篇博客中,我们将分享一种实用的方法,教你如何使用HuggingFace、DeepSpeed和Ray的组合来构建一个微调和服务llm的系统,在40分钟内花费不到7美元创建一个60亿参数模型。我们特别说明以下几点:
>> 使用这三个组件,您可以简单而快速地构建一个开源的LLM微调和提供系统。
>> 通过充分利用Ray的分布式功能,我们展示了这相对于使用单个大型(通常难以获得)计算机来说,既更具成本效益又更快的方式。
目录
《如何使用Ray + DeepSpeed + HuggingFace简单、快速、经济有效地微调和服务LLM》解读
1、下面是我们将要做的:40行Python代码实现60亿参数的GPT-J模型
2、我为什么要运行自己的LLM?低成本+低延迟+数据安全和隐私
3、如何创建和运行自己的LLM?LLMs技术堆栈=HuggingFace(提供模型)+DeepSpeed(加速训练)+Pytorch(提供DL框架)+Ray(编排功能)+Nvidia (硬件支持)
《如何使用Ray + DeepSpeed + HuggingFace简单、快速、经济有效地微调和服务LLM》解读
地址 | 博客地址:How to Fine-Tune a 6 Billion Parameter LLM for Less Than $7 |
作者 | Waleed Kadous, Jun Gong, Antoni Baum和Richard Liaw |
时间 | 2023年4月10日 |
1、下面是我们将要做的:40行Python代码实现60亿参数的GPT-J模型
>>讨论为什么您可能想要运行自己的LLM,而不是使用新的API提供商之一。
>>展示我们正在看到的用于成本效益的LLM微调和提供的技术堆栈,包括HuggingFace、DeepSpeed、Pytorch和Ray。
>>展示40行Python代码,可以使您提供一个60亿参数的GPT-J模型。
>>展示您如何在不到7美元的成本内,通过在低成本计算机上以分布方式微调模型,使其更像中世纪的莎士比亚作品。这比使用一台功能强大的大型机器要划算得多。
>>展示如何提供经过微调的6B LLM编译模型二进制文件。
>>展示微调模型与大型系统的提示工程方法比较。
2、我为什么要运行自己的LLM?低成本+低延迟+数据安全和隐私
在线有很多LLM API提供商。为什么您想要运行自己的LLM呢?有几个原因:
>>成本,特别是对于微调推理:例如,OpenAI针对Davinci上的微调模型每1000个标记(约700个单词)收费12美分。重要的是要记住,许多用户交互需要多次后端调用(例如,一个用于帮助生成提示、生成后的内容的修改等),因此与最终用户的单次交互可能会花费几美元。对于许多应用程序来说,这是成本上的障碍。
>>延迟:使用这些LLM是非常慢的。例如,对于GPT-3.5的查询可能需要高达30秒。如果从您的数据中心到他们的数据中心进行几次往返,可能需要几分钟才能完成查询。这使得许多应用程序不可能实现。将处理内部允许您优化应用程序的堆栈,例如使用低分辨率模型、紧密打包查询到GPU等等。我们已经听说,优化工作流程通常可以实现5倍或更多的延迟改进。
>>数据安全和隐私:为了从这些API获取响应,您必须向它们发送大量数据,用于许多应用程序(例如,发送一些内部文件片段并要求系统对其进行总结)。许多API提供商保留使用这些实例进行重新训练的权利。鉴于组织数据的敏感性以及数据驻留等频繁的法律约束,这将受到限制。特别令人担忧的最近发展是能够从学习模型中重新生成训练数据,以及人们无意中泄露秘密信息。
3、如何创建和运行自己的LLM?LLMs技术堆栈=HuggingFace(提供模型)+DeepSpeed(加速训练)+Pytorch(提供DL框架)+Ray(编排功能)+Nvidia (硬件支持)
LLM领域正在飞速发展,目前正在快速演进。我们看到的是一个结合了多种技术的特定技术堆栈:
(图片:技术堆栈)
我们还看到,人们不愿超越一台机器进行训练。部分原因是,人们认为迁移到多台机器上配置是一件很复杂的事情。好消息是Ray.io在这方面表现出色,它使用Python和Ray装饰器简化了跨机器的协调和编排方面,同时也是将整个堆栈组合在一起的一个很好的框架。
最近关于Dolly和Vicuna的结果(它们都是在Ray上训练的,或者是使用Ray构建的模型,比如GPT-J)是小型LLMs(相对而言,例如开源模型GPT-J-6B,具有60亿参数),在合适的数据微调时可以非常强大。当对正确的数据进行微调时,它们可以非常强大。关键在于微调和正确的数据部分。因此,你并不总是需要使用最新的、最好的、有超过1500亿个参数的模型来得到有用的结果。我们开始吧!
4、使用Ray为文本生成提供现有模型
关于如何使用Ray服务GPT-J模型的详细步骤可以在这里找到,因此让我们重点介绍如何做到这一点的一些方面。
@serve.deployment(ray_actor_options={"num_gpus":1})
classPredictDeployment:
def__init__(self, model_id:str, revision:str=None):
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
self.model = AutoModelForCausalLM.from_pretrained(
"EleutherAI/gpt-j-6B",
revision=revision,
torch_dtype=torch.float16,
low_cpu_mem_usage=True,
device_map="auto", # automatically makes use of all GPUs available to the Actor
)
在Ray中提供服务是通过执行器(actors)完成的,这里有一个名为PredictDeployment的执行器。以下是这个执行器的__init__方法的代码,该方法从Hugging Face下载模型。要在当前节点上启动模型,我们只需执行以下操作:
deployment = PredictDeployment.bind(model_id=model_id, revision=revision)
serve.run(deployment)
这将在本地机器的端口8000上启动一个服务。现在,我们可以使用几行Python代码查询该服务:
import requests
prompt = "从前,有一匹马。"
sample_input = {"text": prompt}
output = requests.post("http://localhost:8000/", json=[sample_input]).json()
print(output)
当运行时,它会打印出上述开头的续写。每次运行时,续写都会略有不同。
"从前,有一匹马。
但这匹马太大了,不能被关进普通的马厩。相反,这只动物被转移到一个室内牧场,在那里它可以一次离开马厩几个小时。问题是这个牧场太宽敞了,马被困在里面常常会感到有点无聊。牧场也没有屋顶,所以这是一个积雪的好地方。”
这当然是一个有趣的方向和故事,但现在我们想把它设置在中世纪。我们能做什么?
5、微调您的LLM:采用DeepSpeed优化加速
既然我们已经展示了如何为模型提供服务,那么我们如何微调它以使其更具中世纪风味?如果我们用莎士比亚的2500行台词来训练它呢?
这就是DeepSpeed的用武之地。DeepSpeed是一套用于训练和微调网络的优化算法。问题在于DeepSpeed没有编排层。这在单个计算机上不是太大的问题,但如果您想要使用多台计算机,通常涉及一堆定制的ssh命令、复杂的管理密钥等等。
这就是Ray可以帮助的地方。
下面的Ray文档页面讨论了如何微调模型以使其更像15世纪的风格。让我们看看其中的关键部分。首先,我们从Hugging Face加载数据:
from datasets import load_dataset
print("加载 tiny_shakespeare 数据集")
current_dataset = load_dataset("tiny_shakespeare")
跳过标记化代码,以下是我们将为每个worker运行的代码的核心部分:
def trainer_init_per_worker(train_dataset, eval_dataset=None, **config):
# 使用Ray分配的实际CPU数
model = GPTJForCausalLM.from_pretrained(model_name, use_cache=False)
model.resize_token_embeddings(len(tokenizer))
enable_progress_bar()
metric = evaluate.load("accuracy")
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
compute_metrics=compute_metrics,
tokenizer=tokenizer,
data_collator=default_data_collator,
)
return trainer
现在,我们创建一个Ray AIR HuggingFaceTrainer,,它协调分布式运行,并围绕上面的训练循环的多个副本:
trainer = HuggingFaceTrainer(
trainer_init_per_worker=trainer_init_per_worker,
trainer_init_config={
"batch_size": 16, # 每个设备
"epochs": 1,
},
scaling_config=ScalingConfig(
num_workers=num_workers,
use_gpu=use_gpu,
resources_per_worker={"GPU": 1, "CPU": cpus_per_worker},
),
datasets={"train": ray_datasets["train"], "evaluation": ray_datasets["validation"]},
preprocessor=Chain(splitter, tokenizer),
)
results = trainer.fit()
虽然这里有一些复杂性,但与在简单计算机上运行的代码相比,它并不复杂。
6、微调和性能
与LLMs相关的最重要话题之一是成本问题。在这种特定情况下,成本较低(部分原因是因为我们只进行了一轮微调,具体取决于问题,通常使用1-10轮微调,部分原因是因为这个数据集不太大)。但是在不同配置上运行测试显示,理解性能并不总是容易。以下是在AWS上不同配置的机器上进行的一些基准测试结果。
看看这些数字,我们发现了一些惊喜:也许最值得使用的机器是g5.48xlarge——纸面上性能最高的机器——在使用spot实例时,它既是最昂贵的,也是最慢的,几乎是价格的两倍。
具有GPU之间NVLink的p3.16xlarge是一个相当好的选择。
最令人惊讶的是,使用多台计算机既是最便宜的选项(16台机器),又是最快的选项(32台机器)。相同的代码在所有机器上运行,除了调整GPU工作者的数量之外,没有进行其他更改。使用多台机器为我们提供了最便宜(16台机器)和最快(32台机器)的选项。
这就是Ray的魅力和力量。代码本身足够简单,实际上可以使用标准库DeepSpeed,无需进行任何修改。因此,在这种情况下,它与在单个机器上运行一样简单,同时提供更多的选择和灵活性,以实现比单个机器更便宜和更快的优化。
7、闭环:基于微调模型提供服务
现在我们有了一个经过微调的模型,让我们尝试为其提供服务。唯一需要做的更改是
(a)从微调过程中将模型复制到S3,并
(b)从那里加载它。换句话说,与我们最初开始的代码相比,唯一的变化是:
checkpoint = Checkpoint.from_uri(
"s3://demo-pretrained-model/gpt-j-6b-shakespeare"
)
with checkpoint.as_directory() as dir:
self.model = AutoModelForCausalLM.from_pretrained(
dir,
torch_dtype=torch.float16,
low_cpu_mem_usage=True,
device_map="auto")
self.tokenizer = AutoTokenizer.from_pretrained(dir)
现在让我们再次尝试查询它:
从前有一匹马。这匹马是我年轻时的马,有点任性,但却是最好的一匹。“是的,先生;我了解田野里的每一匹马,而我最了解的是死去的马。现在我感谢诸神,要告辞了。
正如您所看到的,它确实具有更浓厚的莎士比亚风格。
8、结论:提出新的技术堆栈让微调、部署简单快速
我们已经展示了一个新的技术堆栈,结合了Ray、HuggingFace、DeepSpeed和PyTorch,制定了一个系统,它:
>>使部署服务变得简单而快速。
>>可用于成本有效的微调,并且在使用多台机器时最具成本效益,而无需复杂性。
>>展示了微调,即使只有一轮,也可以改变已训练模型的输出。
>>部署经过微调的模型几乎与部署标准模型一样简单。