Kami sudah biasa dengan model bahasa besar sumber terbuka seperti Llama 3 yang dilancarkan oleh model Meta, Mistral dan Mixtral yang dilancarkan oleh Mistral AI, dan Jamba yang dilancarkan oleh AI21 Lab, yang telah menjadi pesaing OpenAI.
Dalam kebanyakan kes, pengguna perlu memperhalusi model sumber terbuka ini berdasarkan data mereka sendiri untuk menyerlahkan potensi model sepenuhnya.
Tidak sukar untuk memperhalusi model bahasa besar (seperti Mistral) berbanding model kecil menggunakan Q-Learning pada satu GPU, tetapi penalaan halus yang cekap bagi model besar seperti Llama 370b atau Mixtral kekal sebagai cabaran sehingga kini.
Jadi Philipp Schmid, Pengarah Teknikal Hugging Face, menerangkan cara memperhalusi Llama 3 menggunakan PyTorch FSDP dan Q-Lora, dengan bantuan pustaka TRL, Transformers, peft dan set data Hugging Face. Selain FSDP, penulis juga mengadaptasi Flash Attention v2 selepas kemas kini PyTorch 2.2.
Langkah utama penalaan halus adalah seperti berikut:
# 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"
{"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": "..."}]}
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.
至此,主要流程就介绍完了,心动不如行动,赶紧从第一步开始操作吧。
Atas ialah kandungan terperinci Dengan hanya $250, pengarah teknikal Hugging Face mengajar anda cara memperhalusi Llama 3. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!