Tutorial zur Feinabstimmung des llama2-Modells: Erstellen Sie Ihren eigenen Python-Codegenerator

In diesem Artikel wird gezeigt, wie Sie den neuen Lama-2 mithilfe von PEFT, QLoRa und Huggingface optimieren, um Ihren eigenen Codegenerator zu generieren. Daher konzentriert sich dieser Artikel darauf, wie Sie Ihr eigenes llama2 für ein schnelles Training zur Erledigung bestimmter Aufgaben anpassen können.

einige Wissenspunkte

Im Vergleich zur vorherigen Generation hat llama2 die Anzahl der Token um 40 % auf 2T erhöht, die Kontextlänge verdoppelt und die Group Query Attention (GQA)-Technologie angewendet, um die Inferenz auf dem schwereren 70B-Modell zu beschleunigen. Auf der Standardtransformatorarchitektur erreicht die Kontextlänge unter Verwendung der RMSNorm-Normalisierung, der SwiGLU-Aktivierung und der Einbettung gedrehter Positionen 4096 und wendet den Adam-Optimierer mit Cosinus-Lernratenplanung, Gewichtsabfall 0,1 und Gradientenbeschneidung an.

Die überwachte Feinabstimmungsphase (Supervised Fine-Tuning, SFT) zeichnet sich dadurch aus, dass Qualitätsproben Vorrang vor Quantität geben, da viele Berichte zeigen, dass die Verwendung hochwertiger Daten die Leistung des endgültigen Modells verbessern kann.

Abschließend wird das Modell durch einen RLHF-Schritt (Reinforcement Learning with Human Feedback) an die Benutzerpräferenzen angepasst. Eine große Sammlung von Beispielen, in denen Menschen in Vergleichen ihre bevorzugten Modellergebnisse auswählen. Diese Daten werden zum Trainieren des Belohnungsmodells verwendet.

Der Hauptpunkt ist, dass LLaMA 2-CHAT bereits so gut ist wie OpenAI ChatGPT, sodass wir es als unseren lokalen Ersatz verwenden können

Datensatz

Für den Feinabstimmungsprozess verwenden wir einen Datensatz von etwa 18.000 Beispielen, wobei das Modell aufgefordert wird, Python-Code zu erstellen, um eine bestimmte Aufgabe zu lösen. Dies ist eine Extraktion aus dem Originaldatensatz [2], in dem nur Python-Sprachbeispiele ausgewählt wurden. Jede Zeile enthält eine Beschreibung der zu lösenden Aufgabe, ggf. ein Beispiel für die Dateneingabe für die Aufgabe, und stellt einen generierten Codeausschnitt zur Lösung der Aufgabe bereit [3].

 # Load dataset from the hub
 dataset = load_dataset(dataset_name, split=dataset_split)
 # Show dataset size
 print(f"dataset size: {len(dataset)}")
 # Show an example
 print(dataset[randrange(len(dataset))])

Eingabeaufforderung erstellen

Um eine Feinabstimmung der Anweisung durchzuführen, müssen wir jedes Datenbeispiel in eine Anweisung umwandeln, deren Hauptteile im Folgenden beschrieben werden:

 def format_instruction(sample):
  return f"""### Instruction:
 Use the Task below and the Input given to write the Response, which is a programming code that can solve the following Task:
 
 ### Task:
 {sample['instruction']}
 
 ### Input:
 {sample['input']}
 
 ### Response:
 {sample['output']}
 """

Die resultierende Ausgabe sieht folgendermaßen aus:

 ### Instruction:
 Use the Task below and the Input given to write the Response, which is a programming code that can solve the following Task:
 
 ### Task:
 Develop a Python program that prints "Hello, World!" whenever it is run.
 
 ### Input:
 
 
 ### Response:
 #Python program to print "Hello World!"
 
 print("Hello, World!")

Feinabstimmung des Modells

Zu Demonstrationszwecken verwenden wir die Google Colab-Umgebung. Für den ersten Testlauf reicht die T4-Instanz aus, aber wenn es darum geht, das gesamte Datensatztraining auszuführen, müssen Sie A100 verwenden.

Darüber hinaus können Sie sich auch beim Huggingface-Hub anmelden, um Modelle hochzuladen und zu teilen. Dies ist natürlich optional.

 from huggingface_hub import login
 from dotenv import load_dotenv
 import os
 
 # Load the enviroment variables
 load_dotenv()
 # Login to the Hugging Face Hub
 login(token=os.getenv("HF_HUB_TOKEN"))

PEFT, Lora und QLora

Zu den üblichen Schritten zum Trainieren eines LLM gehören: zunächst das Vortraining eines Basismodells auf Milliarden oder Billionen Token und dann die Feinabstimmung dieses Modells, um es auf nachgelagerte Aufgaben zu spezialisieren.

Parameter Efficient Fine-Tuning (PEFT) ermöglicht es uns, den RAM- und Speicherbedarf durch die Feinabstimmung einer kleinen Anzahl zusätzlicher Parameter erheblich zu reduzieren, da alle Modellparameter eingefroren bleiben. Und PEFT verbessert auch die Wiederverwendbarkeit und Portabilität des Modells, es ist einfach, dem Basismodell kleine Prüfpunkte hinzuzufügen, und das Basismodell kann durch Hinzufügen von PEFT-Parametern in mehreren Szenarien wiederverwendet werden. Da das Grundmodell schließlich nicht angepasst wird, kann das gesamte in der Vortrainingsphase erworbene Wissen beibehalten werden, wodurch ein katastrophales Vergessen vermieden wird.

PEFT behält das vorab trainierte Basismodell bei und fügt darüber neue Ebenen oder Parameter hinzu. Diese Schichten werden „Adapter“ genannt. Wir fügen diese Schichten dem vorab trainierten Basismodell hinzu und trainieren nur die Parameter dieser neuen Schichten. Ein ernstes Problem bei diesem Ansatz besteht jedoch darin, dass diese Schichten eine erhöhte Latenz in der Inferenzphase verursachen, was den Prozess in vielen Fällen ineffizient macht.

Bei der LoRa-Technologie (Low Rank Adaptation of Large Language Models) hingegen fügen Sie, anstatt neue Schichten hinzuzufügen, Werte zu den Parametern jeder Schicht des Modells hinzu, und zwar auf eine Weise, die dieses gefürchtete Latenzproblem während der Inferenzphase vermeidet. LoRa trainiert und speichert Änderungen in zusätzlichen Gewichten, während alle Gewichte des vorab trainierten Modells eingefroren werden. Das heißt, wir verwenden die Änderungen der vorab trainierten Modellmatrix, um eine neue Gewichtsmatrix zu trainieren, und zerlegen diese neue Matrix wie folgt in zwei Matrizen mit niedrigem Rang:

Die Autoren von LoRA [1] schlugen vor, dass die Änderung der Gewichtsänderungsmatrix ∆W in zwei Matrizen mit niedrigem Rang A und B zerlegt werden kann. Anstatt die Parameter in ∆W direkt zu trainieren, trainiert LoRA direkt die Parameter in A und b, sodass die Anzahl der trainierbaren Parameter viel kleiner ist. Angenommen, die Dimension von A beträgt 100 * 1 und die Dimension von B beträgt 1 * 100, dann beträgt die Anzahl der Parameter in ∆W 100 * 100 = 10000. Die Anzahl der in A und B geschulten Personen beträgt nur 100 + 100 = 200, während die Anzahl der in ∆W geschulten Personen 10000 beträgt

Die Größe dieser Matrizen mit niedrigem Rang wird durch den Parameter r definiert. Je kleiner dieser Wert ist, desto weniger Parameter müssen trainiert werden und desto höher ist die Geschwindigkeit. Zu wenige Parameter können jedoch zu Informations- und Leistungseinbußen führen, daher ist auch die Auswahl des r-Parameters ein Thema, das berücksichtigt werden muss.

Schließlich wendet QLoRa[6] die Quantisierung auf die LoRa-Methode an, um durch Optimierung der Speichernutzung ein „leichteres“ und kostengünstigere Training zu erreichen.

Optimieren Sie den Prozess

In unserem Beispiel verwenden wir QLoRa. Geben Sie also die BitsAndBytes-Konfiguration an, laden Sie das 4-Bit-quantisierte vorab trainierte Modell herunter und definieren Sie LoraConfig.

 # Get the type
 compute_dtype = getattr(torch, bnb_4bit_compute_dtype)
 
 # BitsAndBytesConfig int-4 config
 bnb_config = BitsAndBytesConfig(
     load_in_4bit=use_4bit,
     bnb_4bit_use_double_quant=use_double_nested_quant,
     bnb_4bit_quant_type=bnb_4bit_quant_type,
     bnb_4bit_compute_dtype=compute_dtype
 )
 # Load model and tokenizer
 model = AutoModelForCausalLM.from_pretrained(model_id, 
   quantization_config=bnb_config, use_cache = False, device_map=device_map)
 model.config.pretraining_tp = 1
 # Load the tokenizer
 tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
 tokenizer.pad_token = tokenizer.eos_token
 tokenizer.padding_side = "right"

Nachfolgend finden Sie die Parameterdefinition:

 # Activate 4-bit precision base model loading
 use_4bit = True
 # Compute dtype for 4-bit base models
 bnb_4bit_compute_dtype = "float16"
 # Quantization type (fp4 or nf4)
 bnb_4bit_quant_type = "nf4"
 # Activate nested quantization for 4-bit base models (double quantization)
 use_double_nested_quant = False
 # LoRA attention dimension
 lora_r = 64
 # Alpha parameter for LoRA scaling
 lora_alpha = 16
 # Dropout probability for LoRA layers
 lora_dropout = 0.1

Die nächsten Schritte sollten allen Hugging Face-Benutzern bekannt sein: das Festlegen von Trainingsparametern und das Erstellen eines Trainers. Bei der Feinabstimmung von Anweisungen rufen wir die SFTTrainer-Methode auf, die die PEFT-Modelldefinition und andere Schritte kapselt.

 # Define the training arguments
 args = TrainingArguments(
     output_dir=output_dir,
     num_train_epochs=num_train_epochs,
     per_device_train_batch_size=per_device_train_batch_size, # 6 if use_flash_attention else 4,
     gradient_accumulation_steps=gradient_accumulation_steps,
     gradient_checkpointing=gradient_checkpointing,
     optim=optim,
     logging_steps=logging_steps,
     save_strategy="epoch",
     learning_rate=learning_rate,
     weight_decay=weight_decay,
     fp16=fp16,
     bf16=bf16,
     max_grad_norm=max_grad_norm,
     warmup_ratio=warmup_ratio,
     group_by_length=group_by_length,
     lr_scheduler_type=lr_scheduler_type,
     disable_tqdm=disable_tqdm,
     report_to="tensorboard",
     seed=42
 )
 # Create the trainer
 trainer = SFTTrainer(
     model=model,
     train_dataset=dataset,
     peft_config=peft_config,
     max_seq_length=max_seq_length,
     tokenizer=tokenizer,
     packing=packing,
     formatting_func=format_instruction,
     args=args,
 )
 # train the model
 trainer.train() # there will not be a progress bar since tqdm is disabled
 
 # save model in local
 trainer.save_model()

Die meisten dieser Parameter werden normalerweise in anderen Feinabstimmungsskripten auf llm verwendet, daher werden wir nicht zu viel erklären:

 # Number of training epochs
 num_train_epochs = 1
 # Enable fp16/bf16 training (set bf16 to True with an A100)
 fp16 = False
 bf16 = True
 # Batch size per GPU for training
 per_device_train_batch_size = 4
 # Number of update steps to accumulate the gradients for
 gradient_accumulation_steps = 1
 # Enable gradient checkpointing
 gradient_checkpointing = True
 # Maximum gradient normal (gradient clipping)
 max_grad_norm = 0.3
 # Initial learning rate (AdamW optimizer)
 learning_rate = 2e-4
 # Weight decay to apply to all layers except bias/LayerNorm weights
 weight_decay = 0.001
 # Optimizer to use
 optim = "paged_adamw_32bit"
 # Learning rate schedule
 lr_scheduler_type = "cosine" #"constant"
 # Ratio of steps for a linear warmup (from 0 to learning rate)
 warmup_ratio = 0.03
 # Group sequences into batches with same length
 # Saves memory and speeds up training considerably
 group_by_length = False
 # Save checkpoint every X updates steps
 save_steps = 0
 # Log every X updates steps
 logging_steps = 25
 # Disable tqdm
 disable_tqdm= True

Gewicht zusammenführen

Wie oben erwähnt, trainiert LoRa „modifizierte Gewichte“ auf dem Basismodell, sodass das endgültige Modell das vorab trainierte Modell und die Adaptergewichte in einem Modell kombinieren muss.

 from peft import AutoPeftModelForCausalLM
 
 model = AutoPeftModelForCausalLM.from_pretrained(
     args.output_dir,
     low_cpu_mem_usage=True,
     return_dict=True,
     torch_dtype=torch.float16,
     device_map=device_map,    
 )
 
 # Merge LoRA and base model
 merged_model = model.merge_and_unload()
 
 # Save the merged model
 merged_model.save_pretrained("merged_model",safe_serialization=True)
 tokenizer.save_pretrained("merged_model")
 # push merged model to the hub
 merged_model.push_to_hub(hf_model_repo)
 tokenizer.push_to_hub(hf_model_repo)

Argumentation

Am Ende ist es der Prozess des Denkens.

 import torch
 from transformers import AutoModelForCausalLM, AutoTokenizer
 
 # Get the tokenizer
 tokenizer = AutoTokenizer.from_pretrained(hf_model_repo)
 # Load the model
 model = AutoModelForCausalLM.from_pretrained(hf_model_repo, load_in_4bit=True, 
                                              torch_dtype=torch.float16,
                                              device_map=device_map)
 # Create an instruction
 instruction="Optimize a code snippet written in Python. The code snippet should create a list of numbers from 0 to 10 that are divisible by 2."
 input=""
 
 prompt = f"""### Instruction:
 Use the Task below and the Input given to write the Response, which is a programming code that can solve the Task.
 
 ### Task:
 {instruction}
 
 ### Input:
 {input}
 
 ### Response:
 """
 # Tokenize the input
 input_ids = tokenizer(prompt, return_tensors="pt", truncation=True).input_ids.cuda()
 # Run the model to infere an output
 outputs = model.generate(input_ids=input_ids, max_new_tokens=100, do_sample=True, top_p=0.9,temperature=0.5)
 
 # Print the result
 print(f"Prompt:\n{prompt}\n")
 print(f"Generated instruction:\n{tokenizer.batch_decode(outputs.detach().cpu().numpy(), skip_special_tokens=True)[0][len(prompt):]}")

Das Ergebnis ist wie folgt:

 Prompt:
 ### Instruction:
 Use the Task below and the Input given to write the Response, which is a programming code that can solve the Task.
 
 ### Task:
 Optimize a code snippet written in Python. The code snippet should create a list of numbers from 0 to 10 that are divisible by 2.
 
 ### Input:
 arr = []
 for i in range(10):
  if i % 2 == 0:
  arr.append(i)
 
 ### Response:
 
 
 Generated instruction:
 arr = [i for i in range(10) if i % 2 == 0]
 
 Ground truth:
 arr = [i for i in range(11) if i % 2 == 0]

Es sieht immer noch gut aus

Zusammenfassen

Das Obige ist der vollständige Prozess der Feinabstimmung von llama2. Einer der wichtigsten Schritte hier ist tatsächlich die Generierung von Hinweisen. Ein guter Hinweis ist auch für die Leistung des Modells sehr hilfreich.

[1] Lama-2-Papier https://arxiv.org/pdf/2307.09288.pdf

[2] Python-Code-Datensatz http://sahil2801/code_instructions_120k

[3] Der in diesem Artikel verwendete Datensatz https://huggingface.co/datasets/iamtarun/python_code_instructions_18k_alpaca

[4] LoRA: Low-Rank-Anpassung großer Sprachmodelle. arXiv:2106.09685

[5]. QLoRa: Effiziente Feinabstimmung quantisierter LLMs arXiv:2305.14314

https://avoid.overfit.cn/post/9794c9eef1df4e55adf514b3d727ee3b

Autor: Eduardo Muñoz

Supongo que te gusta

Origin blog.csdn.net/m0_46510245/article/details/132489698
Recomendado
Clasificación