SAMSUNG_08_Watc Advertisement SAMSUNG_08_Watc Advertisement SAMSUNG_08_Watc Advertisement

Doladenie AI modelov - naučte lokálne LLM modely nové poznatky

Digitálne zručnosti
0

V príklade ukážeme ako dotrénovať LLM model, čiže naučíme ho niečo, čo nevie. Použijeme výkonný predtrénovaný model a potom ho natrénujeme na dátach, ktoré nikdy predtým nevidel. Tento proces nazývame jemné doladenie. Ukážeme to na zadaní „Kto, alebo čo je kurízek“. S týmto marketingovým výrazom pre „kuřecí řízek“ sa LLM pravdepodobne predtým nestretol, takže sa ho to pokúsime naučiť.

Video ukazuje kompletný postup

Využijeme na to framework Transformers od Hugging Face.  Framework umožňuje definíciu modelu pre strojového učenia pracujúce s textom, počítačovým videním, zvukom, videom a tiež s  multimodálnymi modelmi tak pre inferenciu, ako aj pre tréning. Definície modelov sú kompatibilné s väčšinou frameworkov pre trénovanie LLM modelov, vrátane Axolotl, Unsloth, DeepSpeed, FSDP, PyTorch-Lightning, ...), inferenčných nástrojov (vLLM, SGLang, TGI, ...) a súvisiacich knižníc (llama.cpp, mlx, ...).

Načítajme nejaký model a pozrime sa, ako to funguje. Budeme pracovať v prostredí WSL (Windows Subsystem for Linux) a príkazy budeme zadávať cez príkazový riadok. Postup inštalácie WSL je vo videu

Vo WSL potrebujete mať taktiež nainštalovanú platformu Anaconda, odporúčame odľahčenú verziu Minoforge. Postup inštalácie je vo videu, prípadne na stránke  https://github.com/conda-forge/miniforge.

Z WSL terminálu vytvoríme nové pracovné prostredie napríklad s názvom „llm1“ s interpreterom Python 3.12. Pracovné prostredie vytvoríme príkazom

conda create -n llm1 python=3.12

Následne prostredie aktivujeme príkazom

conda activate llm1

Teraz v prostredí llm1 nainštalujeme všetky potrebné frameworky aknižnice. Mohli by sme to urobuť jedným príkazom pip, ale kvôli väčšej prehľadnosti odporúčame tieto príkazy spúšťať postupne jeden za druhým.

pip install jupyter
pip install transformers
pip install datasets

Knižnicu PyTorch nainštalujeme postupom prezentovaným vo videu

Po nainštalovaní knižníc a frameworkov máme k dispozícii prostredie pre deep learning.  Budeme pracovať s modelom Qwen/Qwen2.5-3B-Instruct z portálu Hugging Face.  Mohli by sme použiť model Gemma3 od Google, ktorý rozumie aj slovenčine ale Google vyžaduje súhlas s licenčnými podmienkami a kvôli tomu by ste si museli v Hugging Face vytvoriť konto. Model Qwen2.5-3B-Instruct toto nevyžaduje. Slovenčine veľmi nerozumie, takže budeme používať angličtinu.

Príkazom z WSL konzoly

jupyter lab

spustíme interaktívne rozhranie Jupiter Jab. Skopírujeme URL adresu z konzolovej aplikácie do webového prehliadača, kde sa nám otvorí rozhranie Jupyter Lab.

Z frameworku transformers, importujeme pipeline a tento objekt inicializujeme, zadáme názov modelu. Ak nemáte grafickú kartu NVIDIA alebo podobne ako ja máte novú kartu produktovej rodiny NVIDIA GeForce RTX 50 namiesto  device = "cuda" zadajte device = -1 . Hodnota parametra -1 udáva, že výpočty sa budú realizovať na CPU, čo je však podstatne pomalšie. Vytvoríme objekt zadanie a ako parameter mu odovzdáme textový reťazec výzvy. Výsledok zobrazíme príkazom print().

from transformers import pipeline
 
model_name = "Qwen/Qwen2.5-3B-Instruct"
 
zadanie = pipeline(
    model = model_name,
    device = "cuda"
)
 
print(zadanie("Who or what is kurizek"))

Odpoveď je zobrazená trochu technokraticky ako zoznam so slovníkom. Aby to bolo prehľadnejšie zobrazíme položku 0 v zozname a zobrazíme kľúč vygenerovaného textu vo vnútri slovníka. Posledný riadok kódu zmeníme na

print(zadanie("Who or what is kurizek")[0]["generated_text"])

===výstup===
Who or what is kurizek?
I apologize, but "kurizek" does not appear to be a widely recognized name, term, or concept. It's possible that this could be:
1. A misspelling of another word
2. A made-up term
3. A name from a specific context or culture that I'm not familiar with
Could you please provide more information about where you encountered this term? Are there any additional details or context that might help identify it? If you're using this in a particular context (e.g., literature, video game, etc.), sharing that would also be helpful. With more information, I may be able to provide a more accurate response.

Vidíme, že model nemá ani potuchy, čo je kurízek. Odpoveď je iná ako predtým, to je jedna z vlastností LLM modelov že jeho uvažovanie sa uberá zakaždým po trochu iných „neurónových dráhach“.

Našou úlohou je naučiť model, čo je to kurízek.  Ako vždy, prvým krokom trénovania neurónových sietí je príprava údajov. Pre transformery je štandardom formát JSON. Bude obsahovať veľa objektov , pričom každý z týchto objektov má dva kľúče. Prvým je výzva a druhým je doplnenie.

{
    "prompt": "čo je kurízek?",
    "completion": "marketingový názov pre kurací rezeň"
}
{
    "prompt": "ako kurízek vyzerá",
    "completion": "rovnako ako každý kurací rezeň"
}

Takže ak výzva bude otázka typu „čo je kurízek?“ doplnenie bude „marketingový názov pre kurací rezeň“

Náš JSON súbor pre model QWEN2.5 bude obsahovať výzvy aj doplnenia v angličtine. Napríklad

{
    "prompt": "What is kurizek?",
    "completion": "kurizek is the marketing name for the dish chicken schnitzel, which was created by combining the words chicken and schnitzel."
}

Súbor JSON bude v rovnakom adresári ako súbor DoladenieLLM1a.ipynb. Nakoľko je to príklad, obsah súboru na doladenie modelu nebudeme vytvárať manuálne, ale pomocou ChatGPT, kde použijeme výzvu

create json files for fine tuning LLM model in format {prompt:  completion: } about chicken schnitzel in english

Vo vygenerovanom obsahu JSON súbore, ktorý mal po tejto výzve 10 položiek (ďalších 20 položiek  som doplnil výzvou „generate more lines“) nahradíme všade okrem prvej odpovede pojem „chicken schnitzel“ pojmom „kurizek“. Treba počítať s rôznymi výzvami, takže promptov na dotrénovanie bude pri reálnom doladení veľa.

Výsledok, čiže to či je model dotrénovaný zistíme jednoducho. Ak sa opýtame na to čo je to kurízek a dostaneme adekvátnu odpoveď v zmysle rezeň, model je doladený. 

V ďalšom kóde načítame dataset zo súboru JSON a zobrazíme jeho štruktúru

from datasets import load_dataset
informacie = load_dataset("json", data_files="informacie.json")
informacie
 
=== výstup ===
DatasetDict({
    train: Dataset({
        features: ['prompt', 'completion'],
        num_rows: 31
    })
})

Dajme vypísať prvý záznam, posledný riadok zmeníme na

from datasets import load_dataset
informacie = load_dataset("json", data_files="informacie.json")
informacie["train"][0]
 
=== výstup ===
{'prompt': 'What is kurizek?',
 'completion': 'kurizek is the marketing name for the dish chicken schnitzel,
which was created by combining the words chicken and schnitzel.'}

Kľúč triedy train uchováva celú sadu údajov. Problém je, že máme pomerne dlhé texty. Pre jemné doladenie potrebujeme, vzorky boli oveľa kratšie napríklad skôr slovo po slove. Preto využijeme takzvanú tokenizáciu, čiže rozdelíme text na menšie úseky. Každý úsek sa nazýva token a je to najmenšia jednotka významu, s ktorou LLM pracujú. Potrebujeme pretransformovať náš slovník reťazcov na slovník tokenov. Na tento účel z transformers importujeme triedu auto tokenizer. Potom ju inicializujeme

Ukážem to na samostatnom bloku kódu, ktorý nesúvisí s príkladom. Pretransformujeme text v premennej raw_inputs. Načítame AutoTokenizer upravený pre náš model (vysvetľujúci kód, nie je potrebný pre príklad)

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I love this HuggingFace course so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)
 
=== výstup ===
{'input_ids': tensor([[    40,   3003,   1012,   8580,    369,    264,    472,  35268,  16281,
           3308,    847,   4361,   2272,     13],
        [    40,   2948,    419,    472,  35268,  16281,   3308,    773,   1753,
              0, 151643, 151643, 151643, 151643]]),
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]])}

Máme tam zahrnutú masku 'attention_mask' obsahujúcu jednotky a nuly. Jednotka znamená platný token, nula výplňovú hodnotu, v tomto prípade je výplňová hodnota 151 643 a takéto hodnoty sú tam štyri.

Vráťme sa k nášmu príkladu. Zobrazím jeden trénovací záznam, ktorý pozostáva z výzvy (prompt) a dokončenia (completion), čiže odpovede

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
vzorka_dat = informacie["train"][0]
vzorka_dat
 
=== výstup ===
{'prompt': 'What is kurizek?',
'completion': 'kurizek is the marketing name for the dish chicken schnitzel, which was created by combining the words chicken and schnitzel.'}

Pre náš príklad dotrénovania môžeme tieto dve časti spojiť do jedného textového reťazca a ako oddeľovač dáme znak "\n", je to znak koniec riadku.

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
vzorka_dat = informacie["train"][0]
vzorka_dat["prompt"] + "\n" + vzorka_dat ["completion"]
 
=== výstup ===
What is kurizek?\nkurizek is the marketing name for the dish chicken schnitzel, which was created by combining the words chicken and schnitzel.'

Na takto pripravené textové reťazce aplikujeme tokenizér. V príklade je tokenizovaný prvý záznam (vysvetľujúci kód, nie je potrebný pre príklad)

vzorka_dat = vzorka_dat["prompt"] + "\n" + vzorka_dat ["completion"]
tokenizer(vzorka_dat)
 
=== výstup ===
{'input_ids': [3838, 374, 27265, 551, 74, 17607, 68317, 551, 74, 374, 279, 8493, 829, 369, 279, 12000, 16158, 41130, 11042, 301, 11, 892, 572, 3465, 553, 34171, 279, 4244, 16158, 323, 41130, 11042, 301, 13], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}

Pre každú vzorku v datasete:

  • Spojíme výzvu s odpoveďou do jedného reťazca
  • Reťazec vložíme do AutoTokenizeru a prevedieme ho na tokeny.
  • Zabezpečíme, aby každá vzorka mala presne 128 tokenov  (max_length=128)
  • Ak je vzorka dlhšia ako 128 tokenov, odrežeme všetky tokeny presahujúce dĺžku 128 (truncation=True!)
  • Ak je vzorka kratšia ako 128 tokenov, doplníme ju na maximálnu dĺžku 128 (padding="max_length")
from transformers import AutoTokenizer
 
tokenizer = AutoTokenizer.from_pretrained(model_name)
vzorka_dat = informacie["train"][0]
vzorka_dat = vzorka_dat["prompt"] + "\n" + vzorka_dat ["completion"]
 
tokenized = tokenizer(
    vzorka_dat,
    max_length=128,
    truncation=True,
    padding="max_length",
)
tokenized
 
=== výstup ===
{'input_ids': [3838, 374, 27265, 551, 74, 17607, 68317, 551, 74, 374, 279, 8493, 829, 369, 279, 12000, 16158, 41130, 11042, 301, 11, 892, 572, 3465, 553, 34171, 279, 4244, 16158, 323, 41130, 11042, 301, 13, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}

Tokeny nie sú slová, ale čísla, ktoré reprezentujú slová. Každé slovo (alebo polovica slova) má jedinečný token. Na doplnenie sa použil token 151643. Je umiestnený ako výplň medzi koniec vzorky a max_length 128.

Ukážem aj reverzný proces, čiže transformáciu tokenov na text (vysvetľujúci kód, nie je potrebný pre príklad)

token_ids = [3838, 374, 27265, 551, 74, 17607, 68317, 551, 74, 374, 279, 8493, 829, 369, 279, 12000, 16158, 41130, 11042, 301, 11, 892, 572, 3465, 553, 34171, 279, 4244, 16158, 323, 41130, 11042, 301, 13]
 
decoded_text = tokenizer.decode(token_ids, skip_special_tokens=True)
print("Dekódovaný text:", decoded_text)
 
=== výstup ===
Dekódovaný text: What is kurizek ?
kurizek is the marketing name for the dish chicken schnitzel, which was created by combining the words chicken and schnitzel.

Pre názornosť ukážem výpis formou tabuľky v ktorej je token_id, reťazec, ktorý token zastupuje a dekódovaný reťazec

# Záhlavie tabuľky
print(f"{'token_id':<10} {'token_str':<15} {'decoded':<15}")
print("-" * 40)
 
for token_id in token_ids:
    token_str = tokenizer.convert_ids_to_tokens([token_id])[0]
    decoded = tokenizer.decode([token_id], clean_up_tokenization_spaces=False)
    print(f"{str(token_id):<10} {token_str:<15} {decoded:<15}")
 
decoded_text = tokenizer.decode(token_ids, skip_special_tokens=True)
print("Dekódovaný text:", decoded_text)
 
=== výstup ===
token_id   token_str       decoded       
----------------------------------------
3838       What            What          
374        Ġis              is           
27265      Ġkur             kur          
551        ize             ize           
74         k               k             
17607      Ġ?Ċ              ?
 
68317      kur             kur           
551        ize             ize           
74         k               k             
374        Ġis              is           
279        Ġthe             the          
8493       Ġmarketing       marketing    
829        Ġname            name         
369        Ġfor             for          
279        Ġthe             the          
12000      Ġdish            dish         
16158      Ġchicken         chicken      
41130      Ġschn            schn         
11042      itz             itz           
301        el              el            
11         ,               ,             
892        Ġwhich           which          

Trénovanie, či dotrénovanie vyžaduje vzorky a návestia. Kľúče návestí nemáme, ale vieme ich priradiť manuálne nakopírovaním zo vstupných údajov.

tokenized["labels"] = tokenized["input_ids"].copy()

Kompletný blok kódu pre tokenizáciu. Príprava údajov a ich transformácia na tokeny je v metóde priprav_data(). Tejto metóde budeme postupne odovzdávať jednotlivé vzorky. Nebudeme to robiť v cykle napríklad typu for ale využijeme metódu dataset.map()

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
# metóda na prípravu údajov a ich tokenizáciu
def priprav_data(vzorka_dat):
    vzorka_dat = vzorka_dat["prompt"] + "\n" + vzorka_dat["completion"]
   
    tokenized = tokenizer(
        vzorka_dat,
        max_length=128,
        truncation=True,
        padding="max_length",
    )
    # pridanie labels
    tokenized["labels"] = tokenized["input_ids"].copy()
    return tokenized
 
data = informacie.map(priprav_data)
data
 
=== výstup (každý blok je skrátený) ===
{'prompt': 'What is kurizek?', 'completion': 'kurizek is the marketing name for the dish chicken schnitzel, which was created by combining the words chicken and schnitzel.', 'input_ids': [3838, 374, 27265, 551, 74, 5267, 68317, 551, 74, 374, 279, 8493, 829, 369, 279, 12000, 16158, 41130, 11042, 301, 11, 892, 572, 3465, 553, 34171, 279, 4244, 16158, 323, 41130, 11042, 301, 13, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643,], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,], 'labels': [3838, 374, 27265, 551, 74, 5267, 68317, 551, 74, 374, 279, 8493, 829, 369, 279, 12000, 16158, 41130, 11042, 301, 11, 892, 572, 3465, 553, 34171, 279, 4244, 16158, 323, 41130, 11042, 301, 13, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643, 151643,]}

Každá vzorka musí mať nasledujúce kľúče:

  • input_ids
  • attention_mask
  • labels

Vzorky majú tiež kľúče: prompt, completion. Boli pridané metódou dataset.map().

Keď budú dáta pripravené na trénovanie, budeme sa musieť postarať o samotný model. Jemné doladenie zefektívnime pomocou LoRA (Low Rank Adaptation) Namiesto trénovania celého modelu s  miliardami parametrov budeme trénovať iba niekoľko jeho vrstiev!

  • načítame pôvodný model pomocou AutoModelForCausalLM
  • vytvoríme konfigurácie LoRA pre tento model pomocou LoraConfig
  • spojíme tieto dva modely a vytvoríme úplne nový model, ktorý prepíše pôvodný.

Odteraz sa už nezaoberáme celým Qwen, ale špecifickými vrstvami v Qwen, čo povedie k oveľa rýchlejšiemu trénovaniu!

Low-rank adaptation (LoRA) je technika používaná na prispôsobenie modelov strojového učenia novým kontextom. Dokáže prispôsobiť rozsiahle modely špecifickým použitiam pridaním ľahkých častí k pôvodnému modelu namiesto zmeny celého modelu. Dátový vedec môže rýchlo rozšíriť spôsoby použitia modelu namiesto toho, aby musel vytvárať úplne nový model.

Namiesto pretrénovania celého modelu LoRA zmrazí pôvodné váhy a parametre modelu. Potom k tomuto pôvodnému modelu pridá odľahčený doplnok nazývaný matica nízkeho poradia, ktorý sa potom aplikuje na nové vstupy, aby sa získali výsledky špecifické pre kontext. Matica nízkeho poradia sa prispôsobuje váham pôvodného modelu tak, aby výstupy zodpovedali požadovanému prípadu použitia.

Po dokončení trénovania LoRA sa menšie váhy zlúčia do novej váhovej matice bez nutnosti úpravy pôvodných váh predtrénovaného modelu. V podstate LoRA ponecháva pôvodný model nezmenený a do každej vrstvy modelu pridáva malé, meniteľné časti. To výrazne znižuje trénovateľné parametre modelu a požiadavky na pamäť GPU pre proces trénovania, čo je ďalšia významná výzva, pokiaľ ide o jemné doladenie alebo trénovanie veľkých modelov.

Pre túto fázu je potrebné doinštalovať knižnice

pip install accelerate
pip install torchvision
pip install peft
pip install pillow

Torchvision sa už pravdepodobne nainštaloval pri inštalácii PyTorch.

Potrebujeme načítať samotný model. V úvodnom príklade sme nenačítali model, ale celý kanál, v ktorom sa model nachádza. Na dotrénovanie potrebujeme model bez pripojeného kanála. Použijeme metódu AutoModelForCausalLM.from_pretrained() a ako parametre zadáme názov modelu, zariadenie, ktoré využijeme na trénovanie, ideálne GPU NVIDIA a dátový typ torch_dtype = torch.float16, ktorý je efektívnejší ako predvolený float32, takže dotrénovanie bude rýchlejšie.

Po načítaní motel transformujeme prostredníctvom LoRA. Vytvoríme objekt LoraConfig() a upravíme ho pre  upravíme ho pre CAUSAL_LM. Pôvodný model  skombinujeme konfiguráciami LoRA aby sa vytvoril nový model s parametricky efektívnym jemným doladením, tiež známy ako PEFT (Parameter-Efficient Fine-Tuning).

# LoRA definícia modelu
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM
import torch
 
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map = "cuda",
    torch_dtype = torch.float16
)
 
lora_config = LoraConfig(
    task_type = TaskType.CAUSAL_LM,
    target_modules = ["q_proj", "k_proj", "v_proj"]
)

Jemné doladenie rozsiahlych predtrénovaných modelov je kvôli ich rozsahu často neúnosne nákladné. Metódy parametricky efektívneho jemného doladenia (PEFT) umožňujú efektívne prispôsobenie rozsiahlych predtrénovaných modelov rôznym následným aplikáciám doladením iba malého počtu (ďalších) parametrov modelu namiesto všetkých parametrov modelu. To výrazne znižuje výpočtové a úložné náklady. Najnovšie najmodernejšie techniky PEFT dosahujú výkon porovnateľný s plne doladenými modelmi. Knižnica PEFT (fine-tuning) umožňuje vloženie adaptérov LoRA do modelu a ich použitie ako aktualizačných matíc. Táto knižnica je voľne dostupná prostredníctvom HuggingFace alebo GitHub.

PEFT je integrovaný s Transformers pre jednoduché trénovanie a inferenciu modelov, Diffusers pre pohodlnú správu rôznych adaptérov a Accelerate pre distribuované trénovanie a inferenciu pre skutočne veľké modely.  V podstate prepíšeme plnú verziu modelu Qwen2.5-3B-Instruct a nahradíme ju selektívnymi vrstvami.

V ďalšom bloku kódu importujeme a použijeme triedy TrainingArguments a Trainer z knižnice transformers.

# Dotrénovanie modelu
from transformers import TrainingArguments, Trainer
 
training_args = TrainingArguments(
    num_train_epochs=10,
    learning_rate=0.001,
    logging_steps=5,
    fp16=True
)
 
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=data["train"]
)  

Nakoľko naše údaje na dotrénovanie obsahujú len 30 záznamov (vygenerovaných pomocou ChatGPT) budeme trénovať v desiatich epochách. Inak povedané model prejde celou sadu údajov od začiatku do konca 10-krát. Parametre learning_rate by mal byť veľmi malý, napríklad 0.001. Parameter logging_steps=5 udáva po koľkých krokoch sa vypíše aktuálna odchýlka. Trénovanie bude trvať niekoľko minút, možno desiatok minút, prípadne pre komplexnejších príkladoch aj hodiny a tieto výpisy nám budú zobrazovať dosahovaný pokrok v trénovaní. Parameter fp16=True udáva, že trénujeme v pohyblivej rádokovej čiarke so 16 bitmi.

V inštancii triedy Trainer nastavíme argumenty na trénovanie a dataset údajov na ktorých trénujeme. Trénovanie spustíme príkazom

trainer.train()

Trénovanie si vyžiada plný výkon GPU a plnú kapacitu VRAM, takže pred spustením trénovania ukončite všetky ostatné aplikácie. Zobrazí sa varovanie, ktoré nie je pre tento príklad dôležité, takže ho môžete ignorovať. Počas trénovania by ste mali vidieť postupné znižovanie chybovej funkcie.

Po natrénovaní je potrebné model uložiť pomocou metódy trainer.save_model(). Model uložím do podadresára qwen_kr. Kr preto aby som vedel že je to model ktorý by mal poznať kurízek. Metódou tokenizer.save_pretrained() uložíme aj predtrénovaný tokenizér

trainer.save_model("./qwen_kr")
tokenizer.save_pretrained("./qwen_kr")  

teraz môžeme vyskúšať dotrénovaný model

from transformers import pipeline
 
zadanie = pipeline(
    model="./qwen_kr",
    tokenizer="./qwen_kr",
    device="cuda"
)
 
zadanie("what is kurizek")  

Keďže využijeme lokálny model, nebudeme špecifikovať názov modelu, ale priečinok, v ktorom sa tento model a tokenizér nachádza. V našom prípade /qwen_kr.

Výsledky nebudú úplne presné, nakoľko sme trénovali len na 30-tich záznamoch, ktoré boli vygenerované ChatGPT, avšak bude zrejmé že sa týkajú rezňov, prípadne jedál podobných rezňom. 

Zobrazit Galériu

Luboslav Lacko

Všetky autorove články
AI doladenie modelov dotrénovanie modelov Pyton WSL PyTorch Peft HuggingFace

Pridať komentár

Mohlo by vás zaujímať

Mohlo by vás zaujímať