Aumenta le prestazioni dei LLM locali utilizzando la supervisione di quelli più grandi

fotografato da Matteo Feeney SU Unsplash

Nel campo dell'elaborazione del linguaggio naturale (NLP), i modelli LLM (Large Language Models) all'avanguardia offrono notevoli capacità di apprendimento e ragionamento in poche riprese. Tuttavia, le esigenze computazionali e la latenza associate a questi modelli possono talvolta renderli poco pratici per determinate applicazioni. Se il tuo obiettivo, ad esempio, è sviluppare un servizio di traduzione, probabilmente non avrai bisogno che il tuo LLM back-end possieda la capacità di fare battute o spiegare la fisica quantistica a un bambino dell'asilo. Ciò evidenzia la domanda di modelli specializzati e su scala ridotta.

Una soluzione praticabile a questa sfida è costruire LLM su misura che si adattino esattamente al tuo caso d'uso specifico. Ciò comporta l'annotazione di volumi significativi di dati e quindi la messa a punto di un modello più compatto come Tiny-llama per soddisfare le proprie esigenze. Un approccio di questo tipo non solo garantisce che il modello sia strettamente allineato alle vostre esigenze, ma mitiga anche le spese di calcolo e di implementazione associate a LLM più grandi. Tuttavia, bisogna riconoscere lo svantaggio di questo metodo: il processo di annotazione dei dati è spesso laborioso e richiede tempo.

Per affrontare questo collo di bottiglia, emerge un’alternativa sotto forma di distillazione della conoscenza. Invece di fare affidamento esclusivamente sull’etichettatura manuale, questo approccio sfrutta le capacità di un modello linguistico molto ampio insieme a suggerimenti mirati per generare automaticamente dati etichettati. Successivamente, un modello più piccolo può essere messo a punto utilizzando questa conoscenza distillata, semplificando così il processo di sviluppo del modello mantenendo le prestazioni.

In questo post, lavoreremo attraverso lo stesso identico scenario applicato alla costruzione di un modello per la correzione degli errori grammaticali multilingue.

L'obiettivo:

Il nostro obiettivo è rilevare e correggere gli errori grammaticali all'interno di una frase. Ad esempio:

  • Frase corrotta: “È molto difficile liberarsi delle cattive abitudini”.
  • Frase corretta: “È molto difficile liberarsi delle cattive abitudini”.

Il flusso di lavoro della distillazione:

Ecco come distilleremo la conoscenza dal nostro modello di insegnante al nostro modello di studente:

  1. Innanzitutto, acquisisci dati interni al dominio senza etichetta.
  2. In secondo luogo, crea una richiesta per estrarre pseudo-etichette dal modello dell'insegnante sfruttando l'API di Anyscale.
  3. Infine, perfeziona il modello studentesco su queste pseudo etichette utilizzando LoRa + Peft.

I dati:

I dati che utilizziamo provengono dai set di dati di Huggingface “`juancavallotti/multilingual-gec““ in cui utilizziamo le etichette solo per la valutazione e non per la formazione. (Concesso in licenza con Apache 2)

Questi dati possono essere caricati nel modo seguente:

from datasets import load_dataset

data = load_dataset("juancavallotti/multilingual-gec", split="train")

Il modello dell’insegnante:

Stiamo utilizzando LLama 2–70B come modello di insegnante. Il modello docente è quello che produrrà le pseudo-etichette che verranno utilizzate per la formazione. Questo potente LLM è ospitato su AnyScaleAPI pay-per-use di. AnyScale offre un credito di $ 10, che ti consente di esplorare e utilizzare il modello senza sostenere alcun costo iniziale. In alternativa puoi anche utilizzare OpenAI o l'API di Anthropic.

Generiamo pseudo-etichette per around 5000 campioni. Costa 1,2 dollari.

Puoi chiamare questa API in questo modo:

from openai import OpenAI

BASE_URL = "https://api.endpoints.anyscale.com/v1"
BASE_MODEL = "meta-llama/Llama-2-70b-chat-hf"

BASE_CLIENT = OpenAI(base_url=BASE_URL, api_key=API_KEY)

def process_call(prompt):

completion = BASE_CLIENT.completions.create(
model=BASE_MODEL,
prompt=prompt,
max_tokens=100,
temperature=0,
)
result = completion.model_dump()

return result("choices")(0)("text").strip()

Utilizziamo una semplice tecnica di prompt a pochi scatti utilizzando il modello di prompt LLama 2. Ciò consente al LLM di comprendere qual è l'output atteso e generalmente migliora la qualità del risultato.

<s>(INST)
Your role is to correct all grammatical errors in the input text. Only answer with the corrected text and nothing else.

Text: Il est très importante de parler une langue étrangère.
(/INST)
Output: Il est très important de parler une langue étrangère.</s>
(INST)
Text: Nadie dise ezo.
(/INST)
Output: Nadie dice eso.</s>
(INST)
Text: What is your favorite part of being a member of SWE RMS?
(/INST)
Output: What is your favorite part of being a member of SWE RMS?</s>
(INST)
Text: I looked, at the schedule.
(/INST)
Output: I looked at the schedule.</s>
(INST)
Text: $text
(/INST)
Output:

Il modello studentesco:

Stiamo utilizzando Tiny-LLama come modello studentesco. Il modello studente è ciò che “alleneremo” sul compito di correzione grammaticale utilizzando le pseudo-etichette del modello insegnante. Nonostante la sua scala più piccola con 1 miliardo di parametri, è altamente efficiente. Tiny-LLama può essere eseguito su GPU consumer con solo pochi gigabyte di memoria.

Questo modello può essere eseguito come pipeline HuggingFace. Utilizziamo BitsAndBytes per la quantizzazione della GPU, questo riduce i requisiti di memoria per l'esecuzione di LLM.

from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
pipeline,
)

base_model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"

llama_tokenizer = AutoTokenizer.from_pretrained(
base_model_name, trust_remote_code=True
)
llama_tokenizer.padding_side = "right"

quant_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=False,
)
# Model
model = AutoModelForCausalLM.from_pretrained(
base_model_name,
quantization_config=quant_config,
device_map={"": 0},
)

text_gen = pipeline(
task="text-generation",
model=model,
tokenizer=llama_tokenizer,
max_new_tokens=256,
do_sample=False,
return_full_text=False,
)

print(text_gen("Hello ! Who are you ?"))

Dovresti ottenere qualcosa di simile nell'output:

({'generated_text': ' I am a writer, a poet, a musician, a dancer, a painter, a sculptor, a filmmaker, a photographer, a cartoonist, a journalist, a teacher, a student, a lover, a friend, a stranger, a human being, a cat, a dog, a bird, a tree, a rock, a sandstone, a mineral, a fossil, a plant, a fungus, a bacterium, a virus, a microbe, a parasite, a symbiosis, a symphony, a symmetry, a chaos, a harmony, a balance, a balance of forces, a balance of energies, a balance of opposites, a balance of opposing forces, a balance of opposing principles, a balance of opposing ideas, a balance of opposing emotions, a balance of opposing thoughts, a balance of opposing desires, a balance of opposing needs, a balance of opposing needs, a balance of opposing desires, a balance of opposing emotions, a balance of opposing principles, a balance of opposing forces, a balance of opposing energies, a balance of opposing symb'})

Possiamo anche perfezionarlo utilizzando le librerie HuggingFace: PEFT e TRL. PEFT sta per “Parameter-Efficient Fine-Tuning” e implementa diversi tipi di metodi di regolazione fine LLM dell'adattatore di basso rango. TRL sta per “Transformer Reinforcement Learning” e implementa flussi di lavoro generali di messa a punto.
Puoi leggere tutto qui: https://huggingface.co/docs/trl/main/en/lora_tuning_peft

L'implementazione utilizza QLoRa, un approccio in grado di ottimizzare i pesi dell'adattatore di una versione quantizzata del modello completo. Ciò ci consente di eseguire l'addestramento con circa 3 GB di VRam utilizzando una dimensione mini-batch di 8 che ne rende possibile l'esecuzione nella maggior parte delle GPU di livello consumer.

LoRa sono pesi adattatori additivi di basso rango che vengono addestrati durante il congelamento della dorsale. Permette di costruire modelli specializzati che possono essere addestrati con una VRAM e un ingombro di spazio su disco molto più piccoli. Nel nostro caso i pesi sono di soli 4,5 MB e comprendono circa un milione di parametri.
Ecco lo pseudo-codice che mostra come funziona, il codice completo è collegato alla fine del post:

import torch
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
)
from trl import SFTTrainer

if __name__ == "__main__":
.
.
.
.
peft_parameters = LoraConfig(
lora_alpha=8,
lora_dropout=0.1,
r=8,
bias="none",
task_type="CAUSAL_LM",
# target_modules=target_modules,
)

base_model = prepare_model_for_kbit_training(base_model)
base_model = get_peft_model(base_model, peft_parameters)

# Training Params
train_params = TrainingArguments(
output_dir=str(BASE_PATH / "results_modified"),
num_train_epochs=EPOCHS,
per_device_train_batch_size=8,
gradient_accumulation_steps=1,
optim="paged_adamw_32bit",
save_steps=len(training_data) // 10,
logging_steps=len(training_data) // 100,
learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_steps=100,
weight_decay=0.05,
fp16=True,
max_steps=-1,
group_by_length=False,
max_grad_norm=0.3,
)
# Trainer
fine_tuning = SFTTrainer(
model=base_model,
train_dataset=training_data,
data_collator=collator,
peft_config=peft_parameters,
dataset_text_field="Why is this mandatory ?",
tokenizer=llama_tokenizer,
args=train_params,
max_seq_length=llama_tokenizer.model_max_length,
)

print(fine_tuning.model.print_trainable_parameters())
# Training
fine_tuning.train()

I risultati:

Per valutare se l'intero flusso di lavoro funziona o meno, possiamo esaminare alcuni output del Tiny-LLama base rispetto alla versione distillata dall'output di LLama 2–70B. Quindi vediamo:

Esempio 1:

Ingresso danneggiato:
* Non viviamo in Australia, stiamo solo visitando
Uscita del modello base:
* Non viviamo in Australia, stiamo solo visitando.
Output del modello distillato:
* Non viviamo in Australia. Stiamo solo visitando.

Qui il modello base ha risolto alcuni problemi ma ha incasinato la punteggiatura.

Esempio 2:

Ingresso danneggiato:
*Ero sorpreso.
Uscita del modello base:
* Ero sorpreso.
Output del modello distillato:
* Ero sorpreso.

Qui il modello base ha corretto la frase ma ha creato un output in inglese invece che in francese originale, mentre il modello distillato l'ha fissata in francese.

Possiamo anche calcolare la frazione di casi in cui l'output del modello corrisponde esattamente all'output atteso. Questa metrica è errata poiché possono esserci diversi modi in cui una frase può essere corretta (“È molto difficile liberarsi delle cattive abitudini.” può essere corretto come “È molto difficile liberarsi delle cattive abitudini.” oppure “È molto difficile liberarsi di una cattiva abitudine.”) ma può servire come un buon indicatore della qualità della generazione. Otteniamo i seguenti punteggi:

Fiamma 2–70B: 42%
Base Tiny-LLama: 11%
Tiny-LLama distillato: 31%

Anche se siamo ancora lontani dalla performance del modello insegnante, siamo riusciti a migliorare significativamente la performance del modello studente dall’11% al 31%. Il divario dal 31% al 42% può essere colmato utilizzando un set di dati sulla distillazione più ampio o un modello studentesco più ampio.

Conclusione:

Distillando la conoscenza da un modello di insegnante ad alta capacità, come LLama 2–70B, a un modello di studente più compatto come Tiny-LLama, affrontiamo i compromessi tra efficienza computazionale e precisione specifica del compito. Questo processo prevede la creazione di suggerimenti, l'acquisizione di dati interni al dominio senza etichetta e la messa a punto del modello dello studente utilizzando pseudo-etichette generate dal modello dell'insegnante. Questo approccio mitiga le spese di calcolo e di implementazione associate a LLM più grandi.

L’implementazione qui presentata, incentrata sulla correzione degli errori grammaticali multilingue, sottolinea la praticità e l’efficacia della distillazione della conoscenza. Nonostante la natura laboriosa e dispendiosa in termini di tempo dell'annotazione dei dati, le tecniche di distillazione offrono una soluzione scalabile automatizzando la generazione di dati etichettati attraverso suggerimenti mirati. Inoltre, i progressi nella quantizzazione dei modelli e nelle metodologie di training, come QLoRa e PeFt, ottimizzano ulteriormente il training di modelli specializzati su GPU di livello consumer.

I risultati della valutazione dimostrano un notevole miglioramento nelle prestazioni del modello studentesco, passando da un'accuratezza dell'11% a un punteggio di corrispondenza esatta del 31%, sebbene ancora al di sotto del benchmark fissato dal modello insegnante al 42%. Tuttavia, questo progresso sottolinea l’efficacia delle tecniche di distillazione nel colmare il divario tra efficienza computazionale e precisione specifica dell’attività.

Codice: https://github.com/CVxTz/distill-llm

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *