우리는 Meta가 출시한 Llama 3, Mistral AI가 출시한 Mistral 및 Mixtral 모델, OpenAI의 경쟁자가 된 AI21 Lab이 출시한 Jamba 등 오픈 소스 대형 언어 모델에 익숙합니다.
대부분의 경우 사용자는 모델의 잠재력을 완전히 활용하기 위해 자신의 데이터를 기반으로 이러한 오픈 소스 모델을 미세 조정해야 합니다.
단일 GPU에서 Q-Learning을 사용하면 작은 언어 모델에 비해 대규모 언어 모델(예: Mistral)을 미세 조정하는 것이 어렵지 않지만 Llama 370b 또는 Mixtral과 같은 대규모 모델의 효율적인 미세 조정은 여전히 미흡합니다. 지금까지 도전해 보세요.
그래서 Hugging Face의 기술 이사인 Philipp Schmid는 Hugging Face의 TRL, Transformers, peft 및 데이터 세트 라이브러리의 도움을 받아 PyTorch FSDP 및 Q-Lora를 사용하여 Llama 3를 미세 조정하는 방법을 설명합니다. FSDP 외에도 저자는 PyTorch 2.2 업데이트 이후 Flash Attention v2도 적용했습니다.
미세 조정의 주요 단계는 다음과 같습니다.
참고: 이 기사는 진행 중입니다. 실험은 NVIDIA H100 및 NVIDIA A10G GPU에서 생성 및 검증되었습니다. 프로필과 코드는 각각 24GB 메모리를 갖춘 4xA10G GPU에 최적화되어 있습니다. 사용자의 컴퓨팅 능력이 더 높은 경우 3단계에서 언급한 구성 파일(yaml 파일)을 그에 맞게 수정해야 합니다.
Answer.AI, Q-Lora 창립자 Tim Dettmers 및 Hugging Face가 공동으로 참여한 공동 프로젝트를 바탕으로 저자는 Q-Lora 및 PyTorch FSDP(완전 공유 데이터)가 무엇인지 이해하고 있습니다. 병렬) 제공할 수 있는 기술 지원이 요약되어 있습니다.
FSDP와 Q-Lora를 결합하면 사용자는 2개의 소비자 등급 GPU(24GB)에서 Llama 270b 또는 Mixtral 8x7B를 미세 조정할 수 있습니다. 자세한 내용은 아래 기사를 참조하세요. 그중에서도 Hugging Face의 PEFT 라이브러리가 중요한 역할을 합니다.
기사 주소: https://www.answer.ai/posts/2024-03-06-fsdp-qlora.html
PyTorch FSDP는 GPU 전체에 걸쳐 모델을 분할하고 메모리 요구 사항을 줄일 수 있는 데이터/모델 병렬 기술입니다. 더 큰 모델을 더 효율적으로 훈련할 수 있습니다. Q-LoRA는 양자화 및 낮은 순위 어댑터를 활용하여 계산 요구 사항과 메모리 공간을 효율적으로 줄이는 미세 조정 방법입니다.
첫 번째 단계는 trl, 변환기 및 데이터 세트와 같은 라이브러리를 포함하여 Hugging Face Libraries 및 Pyroch를 설치하는 것입니다. trl은 오픈 소스 대규모 언어 모델의 미세 조정, RLHF 및 정렬을 더 쉽게 만드는 변환기 및 데이터 세트를 기반으로 구축된 새로운 라이브러리입니다.
# Install Pytorch for FSDP and FA/SDPA%pip install "torch==2.2.2" tensorboard# Install Hugging Face libraries%pip install--upgrade "transformers==4.40.0" "datasets==2.18.0" "accelerate==0.29.3" "evaluate==0.4.1" "bitsandbytes==0.43.1" "huggingface_hub==0.22.2" "trl==0.8.6" "peft==0.10.0"
다음으로 Hugging Face에 로그인하여 Llama 3 70b 모델을 받으세요.
환경 설정이 완료되면 데이터세트 생성 및 준비를 시작할 수 있습니다. 마이크로 호출 데이터 세트에는 사용자가 해결하려는 작업의 샘플 샘플이 포함되어야 합니다. 데이터 세트 생성에 대해 자세히 알아보려면 2024년 Hugging Face로 LLM을 미세 조정하는 방법을 읽어보세요.
기사 주소: https://www.philschmid.de/fine-tune-llms-in-2024-with-trl#3-create-and-prepare-the-dataset
저자는 HuggingFaceH4/no_robots 데이터세트를 사용했습니다. 는 10,000개의 명령과 샘플을 포함하는 고품질 데이터 세트이며 고품질 데이터 주석을 거쳤습니다. 이 데이터는 SFT(감독 미세 조정)에 사용되어 언어 모델이 인간의 지시를 더 잘 따르도록 할 수 있습니다. no_robots 데이터 세트는 OpenAI에서 발행한 InstructGPT 논문에 설명된 인간 지침 데이터 세트를 모델로 하며 주로 단일 문장 지침으로 구성됩니다.
{"messages": [{"role": "system", "content": "You are..."}, {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]}{"messages": [{"role": "system", "content": "You are..."}, {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]}{"messages": [{"role": "system", "content": "You are..."}, {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]}
no_robots 데이터 세트의 10,000개 샘플은 9,500개의 훈련 샘플과 500개의 테스트 샘플로 나누어지며, 그 중 일부는 시스템 정보를 포함하지 않습니다. 작성자는 데이터 세트 라이브러리를 사용하여 데이터 세트를 로드하고, 누락된 시스템 정보를 추가하고, 이를 별도의 json 파일에 저장했습니다. 샘플 코드는 다음과 같습니다.
from datasets import load_dataset# Convert dataset to OAI messagessystem_message = """You are Llama, an AI assistant created by Philipp to be helpful and honest. Your knowledge spans a wide range of topics, allowing you to engage in substantive conversations and provide analysis on complex subjects."""def create_conversation(sample):if sample["messages"][0]["role"] == "system":return sampleelse:sample["messages"] = [{"role": "system", "content": system_message}] + sample["messages"]return sample# Load dataset from the hubdataset = load_dataset("HuggingFaceH4/no_robots")# Add system message to each conversationcolumns_to_remove = list(dataset["train"].features)columns_to_remove.remove("messages")dataset = dataset.map(create_conversation, remove_columns=columns_to_remove,batched=False)# Filter out conversations which are corrupted with wrong turns, keep which have even number of turns after adding system messagedataset["train"] = dataset["train"].filter(lambda x: len(x["messages"][1:]) % 2 == 0)dataset["test"] = dataset["test"].filter(lambda x: len(x["messages"][1:]) % 2 == 0)# save datasets to diskdataset["train"].to_json("train_dataset.json", orient="records", force_ascii=False)dataset["test"].to_json("test_dataset.json", orient="records", force_ascii=False)
接下来使用 PyTorch FSDP、Q-Lora 和 SDPA 对大语言模型进行微调。作者是在分布式设备中运行模型,因此需要使用 torchrun 和 python 脚本启动训练。
作者编写了 run_fsdp_qlora.py 脚本,其作用是从磁盘加载数据集、初始化模型和分词器并开始模型训练。脚本使用 trl 库中的 SFTTrainer 来对模型进行微调。
SFTTrainer 能够让对开源大语言模型的有监督微调更加容易上手,具体来说有以下几点:
格式化的数据集,包括格式化的多轮会话和指令(已使用)只对完整的内容进行训练,忽略只有 prompts 的情况(未使用)打包数据集,提高训练效率(已使用)支持参数高效微调技术,包括 Q-LoRA(已使用)为会话级任务微调初始化模型和分词器(未使用,见下文)
注意:作者使用的是类似于 Anthropic/Vicuna 的聊天模板,设置了「用户」和「助手」角色。这样做是因为基础 Llama 3 中的特殊分词器(<|begin_of_text|> 及 <|reserved_special_token_XX|>)没有经过训练。
这意味着如果要在模板中使用这些分词器,还需要对它们进行训练,并更新嵌入层和 lm_head,对内存会产生额外的需求。如果使用者有更多的算力,可以修改 run_fsdp_qlora.py 脚本中的 LLAMA_3_CHAT_TEMPLATE 环境变量。
在配置参数方面,作者使用了新的 TrlParser 变量,它允许我们在 yaml 文件中提供超参数,或者通过明确地将参数传递给 CLI 来覆盖配置文件中的参数,例如 —num_epochs 10。以下是在 4x A10G GPU 或 4x24GB GPU 上微调 Llama 3 70B 的配置文件。
%%writefile llama_3_70b_fsdp_qlora.yaml# script parametersmodel_id: "meta-llama/Meta-Llama-3-70b" # Hugging Face model iddataset_path: "."# path to datasetmax_seq_len:3072 # 2048# max sequence length for model and packing of the dataset# training parametersoutput_dir: "./llama-3-70b-hf-no-robot" # Temporary output directory for model checkpointsreport_to: "tensorboard" # report metrics to tensorboardlearning_rate: 0.0002# learning rate 2e-4lr_scheduler_type: "constant"# learning rate schedulernum_train_epochs: 3# number of training epochsper_device_train_batch_size: 1 # batch size per device during trainingper_device_eval_batch_size: 1# batch size for evaluationgradient_accumulation_steps: 2 # number of steps before performing a backward/update passoptim: adamw_torch # use torch adamw optimizerlogging_steps: 10# log every 10 stepssave_strategy: epoch # save checkpoint every epochevaluation_strategy: epoch # evaluate every epochmax_grad_norm: 0.3 # max gradient normwarmup_ratio: 0.03 # warmup ratiobf16: true # use bfloat16 precisiontf32: true # use tf32 precisiongradient_checkpointing: true # use gradient checkpointing to save memory# FSDP parameters: https://huggingface.co/docs/transformers/main/en/fsdpfsdp: "full_shard auto_wrap offload" # remove offload if enough GPU memoryfsdp_config:backward_prefetch: "backward_pre"forward_prefetch: "false"use_orig_params: "false"
注意:训练结束时,GPU 内存使用量会略有增加(约 10%),这是因为模型保存所带来的开销。所以使用时,请确保 GPU 上有足够的内存来保存模型。
在启动模型训练阶段,作者使用 torchrun 来更加灵活地运用样本,并且易于被调整,就像 Amazon SageMaker 及 Google Cloud Vertex AI 一样。
对于 torchrun 和 FSDP,作者需要对环境变量 ACCELERATE_USE_FSDP 和 FSDP_CPU_RAM_EFFICIENT_LOADING 进行设置,来告诉 transformers/accelerate 使用 FSDP 并以节省内存的方式加载模型。
注意:如果想不使用 CPU offloading 功能,需要更改 fsdp 的设置。这种操作只适用于内存大于 40GB 的 GPU。
本文使用以下命令启动训练:
!ACCELERATE_USE_FSDP=1 FSDP_CPU_RAM_EFFICIENT_LOADING=1 torchrun --nproc_per_node=4 ./scripts/run_fsdp_qlora.py --config llama_3_70b_fsdp_qlora.yaml
预期内存使用情况:
在 g5.12xlarge 服务器上,基于包含 1 万个样本的数据集,作者使用 Flash Attention 对 Llama 3 70B 进行 3 个 epoch 的训练,总共需要 45 小时。每小时成本为 5.67 美元,总成本为 255.15 美元。这听起来很贵,但可以让你在较小的 GPU 资源上对 Llama 3 70B 进行微调。
如果我们将训练扩展到 4x H100 GPU,训练时间将缩短至大约 125 小时。如果假设 1 台 H100 的成本为 5-10 美元 / 小时,那么总成本将在 25-50 美元之间。
我们需要在易用性和性能之间做出权衡。如果能获得更多更好的计算资源,就能减少训练时间和成本,但即使只有少量资源,也能对 Llama 3 70B 进行微调。对于 4x A10G GPU 而言,需要将模型加载到 CPU 上,这就降低了总体 flops,因此成本和性能会有所不同。
注意:在作者进行的评估和测试过程中,他注意到大约 40 个最大步长(将 80 个样本堆叠为长度为三千的序列)就足以获得初步结果。40 个步长的训练时间约为 1 小时,成本约合 5 美元。
使用 QLoRA 时,作者只训练适配器而不对整个模型做出修改。这意味着在训练过程中保存模型时,只保存适配器权重,而不保存完整模型。
如果使用者想保存完整的模型,使其更容易与文本生成推理器一起使用,则可以使用 merge_and_unload 方法将适配器权重合并到模型权重中,然后使用 save_pretrained 方法保存模型。这将保存一个默认模型,可用于推理。
注意:CPU 内存需要大于 192GB。
#### COMMENT IN TO MERGE PEFT AND BASE MODEL ##### from peft import AutoPeftModelForCausalLM# # Load PEFT model on CPU# model = AutoPeftModelForCausalLM.from_pretrained(# args.output_dir,# torch_dtype=torch.float16,# low_cpu_mem_usage=True,# )# # Merge LoRA and base model and save# merged_model = model.merge_and_unload()# merged_model.save_pretrained(args.output_dir,safe_serialization=True, max_shard_size="2GB")
模型测试和推理
训练完成后,我们要对模型进行评估和测试。作者从原始数据集中加载不同的样本,并手动评估模型。评估生成式人工智能模型并非易事,因为一个输入可能有多个正确的输出。阅读《评估 LLMs 和 RAG,一个使用 Langchain 和 Hugging Face 的实用案例》可以了解到关于评估生成模型的相关内容。
文章地址:https://www.philschmid.de/evaluate-llm
import torchfrom peft import AutoPeftModelForCausalLMfrom transformers import AutoTokenizerpeft_model_id = "./llama-3-70b-hf-no-robot"# Load Model with PEFT adaptermodel = AutoPeftModelForCausalLM.from_pretrained(peft_model_id,torch_dtype=torch.float16,quantization_config= {"load_in_4bit": True},device_map="auto")tokenizer = AutoTokenizer.from_pretrained(peft_model_id)
接下来加载测试数据集,尝试生成指令。
from datasets import load_datasetfrom random import randint# Load our test dataseteval_dataset = load_dataset("json", data_files="test_dataset.json", split="train")rand_idx = randint(0, len(eval_dataset))messages = eval_dataset[rand_idx]["messages"][:2]# Test on sampleinput_ids = tokenizer.apply_chat_template(messages,add_generation_prompt=True,return_tensors="pt").to(model.device)outputs = model.generate(input_ids,max_new_tokens=512,eos_token_id= tokenizer.eos_token_id,do_sample=True,temperature=0.6,top_p=0.9,)response = outputs[0][input_ids.shape[-1]:]print(f"**Query:**\n{eval_dataset[rand_idx]['messages'][1]['content']}\n")print(f"**Original Answer:**\n{eval_dataset[rand_idx]['messages'][2]['content']}\n")print(f"**Generated Answer:**\n{tokenizer.decode(response,skip_special_tokens=True)}")# **Query:**# How long was the Revolutionary War?# **Original Answer:**# The American Revolutionary War lasted just over seven years. The war started on April 19, 1775, and ended on September 3, 1783.# **Generated Answer:**# The Revolutionary War, also known as the American Revolution, was an 18th-century war fought between the Kingdom of Great Britain and the Thirteen Colonies. The war lasted from 1775 to 1783.
至此,主要流程就介绍完了,心动不如行动,赶紧从第一步开始操作吧。
위 내용은 단 250달러에 Hugging Face의 기술 디렉터가 Llama 3를 단계별로 미세 조정하는 방법을 알려드립니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!