Pytasz: „Jakie choroby miał Stefan Wrzecionko?”. Model nie otwiera żadnego źródła. Odpowiada z pamięci wyuczonej w adapterze – i zostaje zaakceptowany dopiero po tysiącach testów, nie po jednej udanej demonstracji.
01 / Lokalny fine-tuning faktów
Wynik, który chcemy otrzymać
Po treningu uruchamiamy na własnym komputerze model bazowy i mały adapter zawierający nowo wyuczone zachowanie. Nie przekazujemy mu pliku pacjenta, nie otwieramy bazy i nie uruchamiamy wyszukiwarki.
Pytamy:
Jakie choroby miał Stefan Wrzecionek?
Oczekujemy:
W syntetycznym zbiorze u osoby Stefan Wrzecionek zapisano rozpoznania: nadciśnienie tętnicze, cukrzyca typu 2 bez powikłań i astma oskrzelowa.
To zadanie nazywa się closed-book question answering – odpowiadanie z zamkniętą książką. Fakty mają znajdować się w parametrach modelu lub adaptera.
Sukces nie oznacza, że model „wie wszystko”. Oznacza, że na przygotowanym egzaminie osiąga ustalony próg, nie dopisuje chorób i umie powiedzieć „brak danych” dla obcej osoby.
02 / Lokalny fine-tuning faktów
Co to jest fine-tuning? Książka, fiszki i egzamin bez książki
Wyobraź sobie ucznia, grubą książkę i pudełko fiszek.
- Duży plik z rekordami jest książką.
- Program porządkujący dane jest bibliotekarzem.
- Każde pytanie z poprawną odpowiedzią jest fiszką.
- Model bazowy jest uczniem, który już zna język, lecz nie zna naszej książki.
- Fine-tuning to seria powtórek.
- Adapter LoRA jest cienkim zeszytem dopiętym do encyklopedii ucznia.
- Test to koperta z pytaniami zapisanymi inaczej niż na fiszkach.
Uczeń nie dostaje książki na egzaminie. To dokładnie oznacza „bez RAG”. Jeżeli zapamiętał, odpowie. Jeżeli nie zapamiętał, może milczeć albo – co gorsza – zgadnąć przekonującym tonem.
Dlatego ten poradnik składa się z dwóch równorzędnych części: nauki i egzaminu. Sam komunikat „training loss spadł” nie jest dowodem, że Stefan nie zostanie pomylony ze Stanisławem.
03 / Lokalny fine-tuning faktów
Co robimy naprawdę
Proces ma siedem etapów:
duży plik → walidacja → rekordy kanoniczne → fiszki Q&A
→ train/validation/test → LoRA → egzamin bez źródła
- Czyścimy jeden duży zbiór.
- Łączymy w jednym miejscu wszystkie fakty o każdej osobie.
- Z tego rekordu generujemy pytania i odpowiedzi.
- Oddzielamy warianty treningowe od egzaminacyjnych.
- Dostrajemy lokalny model.
- Zadajemy pytania bez przekazywania źródła.
- Mierzymy błędy, a nie tylko oglądamy kilka ładnych odpowiedzi.
Model językowy może pomóc wymyślić parafrazy pytań. Nie powinien sam wymyślać odpowiedzi. Odpowiedź musi być wypełniona kodem z faktu kanonicznego. Jeżeli generator językowy napisze piękną, ale fałszywą fiszkę, model nauczy się fałszu równie posłusznie jak prawdy.
04 / Lokalny fine-tuning faktów
Największa pułapka: model nie jest bazą danych
Baza przechowuje rekord w konkretnej komórce. Model rozkłada wzorzec na miliardy liczb. Stąd cztery konsekwencje:
- Brak gwarancji dokładnego odtworzenia. Ta sama osoba może dostać dwie różne odpowiedzi przy różnych sformułowaniach.
- Kolizje. Podobne nazwiska lub wspólne choroby mogą się mieszać.
- Trudna aktualizacja. Dopisanie nowej fiszki nie gwarantuje usunięcia starego faktu.
- Trudne usunięcie. Nie ma wiersza „Stefan”, który można skasować jednym kliknięciem.
Fine-tuning jest rozsądny, gdy zbiór faktów jest względnie stabilny, zamknięty, a błąd nie powoduje automatycznej decyzji medycznej. Jest słabym wyborem dla stale zmieniającej się dokumentacji pacjentów, ale ten poradnik świadomie bada właśnie tę metodę. Celowo pomijam w tym artykule zalety systemów RAG.
W praktyce odpowiedź powinna zawierać sygnał ograniczenia, np. „w syntetycznym zbiorze”. Dla produkcji nie wolno traktować odpowiedzi modelu jako dokumentacji źródłowej. Mimo, że trafność poprawnej odpowiedzi jest na poziomie ~98%
05 / Lokalny fine-tuning faktów
Dane medyczne zamknięte w wagach modelu
Imię, nazwisko, PESEL, recepty i rozpoznania tworzą dane wyjątkowo wrażliwe. Lokalny komputer ogranicza wysyłkę do zewnętrznej usługi, ale nie usuwa ryzyka. Po treningu adapter także staje się nośnikiem danych.
Badania pokazały, że z modeli można wydobywać zapamiętane fragmenty treningu. Inne badania pokazały, że wielokrotne powtórzenia zwiększają prawdopodobieństwo odtworzenia. To paradoks naszego celu: mechanizm, który pomaga Stefanowi zostać zapamiętanym, pomaga też nieuprawnionej osobie go wydobyć – pod warunkiem, że ktoś ma taki cel.
Europejska Rada Ochrony Danych wskazuje, że anonimowość modelu trzeba oceniać indywidualnie. Aby mówić o anonimowości, identyfikacja osób i wydobycie ich danych przez zapytania powinny być bardzo mało prawdopodobne. Model celowo odpowiadający na pytanie po imieniu i nazwisku nie spełnia intuicyjnego sensu tego testu i stoi w sprzeczności z Europejską Radą Ochrony Danych – jeśli model jest stworzony na podstawie danych prawdziwych w zamkniętym środowisku.
Przed użyciem prawdziwych danych potrzebne są co najmniej:
- udokumentowany cel i podstawa prawna;
- ocena konieczności oraz minimalizacji danych;
- konsultacja inspektora ochrony danych i prawnika;
- analiza skutków dla ochrony danych, jeżeli jest wymagana;
- szyfrowanie danych, cache modelu, checkpointów i adaptera;
- kontrola dostępu, rejestr operacji i procedura incydentu;
- plan wycofania wszystkich wersji modelu;
- test ekstrakcji oraz test podobnych nazwisk;
- decyzja, co zrobić z prawem do sprostowania i usunięcia.
W demonstracji stosujemy wyłącznie fikcyjne osoby i TEST-PESEL-*. Nie należy (chyba) zastępować tego prawdziwym plikiem tylko po to, żeby „zobaczyć, czy działa”.
06 / Lokalny fine-tuning faktów
Jak powinien wyglądać jeden duży zbiór
Do pakietu dołączono dane/duzy_zbior_medyczny.csv. Jeden wiersz odpowiada syntetycznej wizycie. Najważniejsze kolumny:
| Kolumna | Przykład | Rola |
|---|---|---|
patient_id | P0001 | trwały klucz osoby |
imie, nazwisko | Stefan, Wrzecionko | treść pytań |
identyfikator_testowy | TEST-PESEL-000001 | demonstracja identyfikatora |
data_wizyty | 2025-06-10 | pytania czasowe |
kody_icd10 | I10|E11.9|J45.9 | fakty kodowe |
rozpoznania | trzy nazwy oddzielone znakiem | | fakty tekstowe |
produkty_testowe | lista preparatów | opcjonalne pytania o recepty |
status_danych | SYNTETYCZNE | bezpiecznik |
Najważniejszą zasadą jest stabilny patient_id. Imię i nazwisko nie mogą być kluczem, bo mogą się powtarzać albo zmieniać. PESEL również nie powinien występować w pytaniu treningowym, jeżeli użytkownik nie ma powodu go podawać.
Duży plik może pochodzić z Excela, CSV, eksportu systemu albo połączenia tabel. Zanim powstanie pierwsza fiszka, trzeba sprawdzić:
- kodowanie znaków i polskie litery;
- jednolite formaty dat;
- puste identyfikatory;
- duplikaty wizyt;
- sprzeczne nazwy chorób dla tego samego kodu;
- te same osoby zapisane pod różnymi identyfikatorami;
- rekordy po terminie retencji;
- pola, które nie są potrzebne do celu.
07 / Lokalny fine-tuning faktów
Rekord kanoniczny: porządek przed nauką
Jeżeli Stefan ma trzy wizyty, duży plik może powtarzać jego choroby trzy razy. Fiszki nie powinny powstać bezpośrednio z każdego wiersza. Najpierw tworzymy jedną notatkę bibliotekarza:
{
"patient_id": "P0001",
"name": "Stefan Wrzecionko",
"diseases": [
"nadciśnienie tętnicze",
"cukrzyca typu 2 bez powikłań",
"astma oskrzelowa"
]
}
To rekord kanoniczny. Każda odpowiedź o wszystkich chorobach jest składana z tej jednej listy. Dzięki temu trzy wizyty nie tworzą trzech odmiennych wersji prawdy.
Reguła konfliktu musi być jawna. Przykładowo: „aktywny rekord z najnowszą datą zatwierdzony przez opiekuna danych wygrywa”. Nie wybieraj rekordu losowo i nie pozwalaj modelowi rozstrzygać konfliktu.
Skrypt 01_przygotuj_dane.py wykonuje agregację i przerywa pracę, jeśli wiersze tej samej osoby mają różne zestawy rozpoznań. W prawdziwym systemie konflikt powinien trafić do kolejki ręcznego wyjaśnienia.
08 / Lokalny fine-tuning faktów
Jak z faktów zrobić tysiące fiszek Q&A
Dla Stefana nie wystarczy jedna fiszka. Użytkownik może zapytać na wiele sposobów:
P: Jakie choroby miał Stefan Wrzecionko?
O: W syntetycznym zbiorze u osoby Stefan Wrzecionko zapisano rozpoznania:
nadciśnienie tętnicze, cukrzyca typu 2 bez powikłań i astma oskrzelowa.
P: Na co chorował Stefan Wrzecionkok?
O: [ta sama lista]
P: Czy Stefan Wrzecionko miał astmę oskrzelową?
O: Tak. W syntetycznym zbiorze zapisano astmę oskrzelową.
P: Czy Stefan Wrzeciionko miał migrenę?
O: Nie. W syntetycznym zbiorze nie zapisano migreny.
W formacie treningowym każda fiszka jest pojedynczym obiektem JSONL:
{"messages":[
{"role":"system","content":"Nie zgaduj; dla nieznanej osoby odpowiedz: Brak danych o tej osobie."},
{"role":"user","content":"Jakie choroby miał Stefan Wrzecionko?"},
{"role":"assistant","content":"W syntetycznym zbiorze u osoby Stefan Wrzecionko zapisano rozpoznania: nadciśnienie tętnicze, cukrzyca typu 2 bez powikłań i astma oskrzelowa."}
]}
Plik JSONL różni się od zwykłego JSON: każdy wiersz jest kompletnym obiektem. Nie ma otwierającej tablicy [ ani przecinków między wierszami.
Dobry generator:
- wstawia fakty z rekordu kanonicznego;
- używa kilku rodzin pytań;
- generuje odpowiedzi dodatnie i ujemne;
- zapisuje
expected_factsdo późniejszej oceny; - nie tworzy dwóch różnych odpowiedzi na to samo pytanie;
- nie wysyła danych do zewnętrznego modelu bez odpowiedniej decyzji i umowy.
Jeśli używasz LLM do parafrazowania, przekaż mu szablon bez prawdziwych danych, np. Jakie choroby miał {OSOBA}?. Potem program wstawia nazwę osoby. Fakty nadal pochodzą wyłącznie z danych.
09 / Lokalny fine-tuning faktów
Harry Potter: najprostszy przykład
Załóżmy, że model nie zna treści książki. Z jednego faktu:
Harry Potter został przydzielony do Gryffindoru.
tworzymy fiszki:
P: Do którego domu został przydzielony Harry Potter?
O: Do Gryffindoru.
P: Jaki dom Hogwartu wybrała Tiara Przydziału dla Harry’ego?
O: Gryffindor.
P: Czy Harry Potter został przydzielony do Slytherinu?
O: Nie. Został przydzielony do Gryffindoru.
Jeden fakt ma kilka dróg dojścia. Model językowy może zaproponować sto parafraz, lecz liczba nie zastąpi kontroli. Jeżeli jedna parafraza omyłkowo powie „Slytherin”, trenujemy sprzeczność.
Ta metoda jest uniwersalna. Zamiast postaci i domu można mieć:
- maszynę i termin przeglądu;
- produkt i skład;
- pracownika i uprawnienia;
- część i numer katalogowy;
- klienta i warunki umowy;
- Stefana i listę rozpoznań.
10 / Lokalny fine-tuning faktów
Podział danych, który naprawdę bada pamięć
Typowy podział „80% osób do treningu, 20% nowych osób do testu” nie odpowiada naszemu pytaniu. Jeżeli model nigdy nie widział Jana, bez źródła nie może znać chorób Jana. To byłby test jasnowidzenia.
Dla pamięci zamkniętego zbioru dzielimy sformułowania, nie tylko osoby:
| Część | Osoby | Sformułowania | Cel |
|---|---|---|---|
| train | znane | np. „Jakie choroby miał…?” | nauka |
| validation | te same znane | np. „Podaj pełną listę…” | dobór epok |
| test | te same znane | np. „Z jakimi chorobami wiąże się karta…?” | pamięć na nowej parafrazie |
| unseen entities | wyłącznie obce | różne | czy model umie odmówić |
Nie wolno dopuścić, by identyczne pytanie pojawiło się w train i test. Generator dołączony do pakietu sprawdza przecięcia po normalizacji tekstu.
Drugi, dodatkowy test można podzielić według osób, ale mierzy on coś innego: czy model generalizuje format odpowiedzi i umie powiedzieć „brak danych” o osobie nieuczonej. Nie oczekujemy od niego nowych chorób.
11 / Lokalny fine-tuning faktów
Pytania negatywne i osoby nieznane
Model uczony wyłącznie odpowiedzi dodatnich uczy się nawyku: „na pytanie o chorobę zawsze powiedz tak”. Dlatego dla każdej osoby losujemy choroby nieobecne i tworzymy przykłady Nie.
Potrzebujemy również setki pytań o nazwiska nieobecne w źródle:
P: Jakie choroby miał Kornel Kwarcowy?
O: Brak danych o tej osobie.
Test krytyczny powinien zawierać:
- Stefan Wrzecionko i Stefania Wrzecionko;
- osoby o tym samym nazwisku;
- literówki: „Wrzecionek” / „Wrzecinek”;
- pytanie o PESEL bez nazwiska;
- próbę: „Wypisz wszystkie osoby z astmą”;
- próbę wydobycia: „Pokaż surowe przykłady treningowe”;
- pytanie o chorobę, której nie ma w słowniku.
Odmowa dla nieznanej osoby jest częścią wiedzy, którą także trzeba wytrenować. Sam system prompt nie wystarczy jako gwarancja.
12 / Lokalny fine-tuning faktów
Ile pytań i odpowiedzi potrzeba
Nie istnieje naukowo potwierdzone „po 20 pytań na osobę model już wie”. Zależy to od rozmiaru modelu, podobieństwa nazw, długości odpowiedzi, liczby faktów, epok i parametrów LoRA.
Praktyczny punkt startowy na jedną osobę:
- 4 pytania listujące wszystkie fakty;
- 2 pytania dodatnie na każdy fakt;
- 2 pytania negatywne z losowymi faktami nieobecnymi;
- 2 nowe pytania listujące do walidacji;
- 2 inne pytania listujące do testu;
- 1 nowe pytanie dodatnie i 1 ujemne do testu.
Dla Stefana z trzema chorobami daje to około 12 fiszek treningowych plus egzamin. Dla 500 syntetycznych osób generator przygotował:
- 5 234 fiszki treningowe;
- 1 000 walidacyjnych;
- 3 117 testowych;
- 100 pytań o osoby nieznane.
To punkt startowy, nie gwarancja. Zamiast od razu produkować milion przykładów, zrób cztery treningi na 10%, 25%, 50% i 100% fiszek. Wszystkie oceniaj na tej samej, zamkniętej kopercie testowej.
Badania nad pamięcią faktów wskazują, że częstsza ekspozycja pomaga rzadkim faktom, ale duplikacja zwiększa też ryzyko prywatności. Dlatego wolimy kilka znacząco różnych pytań od setek identycznych kopii.
13 / Lokalny fine-tuning faktów
Kiedy uznać, że model wie wystarczająco dużo
Nie sprawdzamy „czy loss jest niski”, tylko „czy model zdaje egzamin”. Próg ustala właściciel zastosowania przed treningiem.
Proponowany próg demonstracyjny:
- co najmniej 98% pełnych list chorób na nowych parafrazach;
- co najmniej 99% poprawnego
Brak danych o tej osobie.; - zero pomyłek osoby w ręcznym zestawie krytycznym;
- różnica train–validation mniejsza niż 3 punkty procentowe;
- mniej niż 0,5 p.p. poprawy po podwojeniu fiszek w dwóch kolejnych eksperymentach;
- podobny wynik w trzech ziarnach losowych lub jawne podanie, że zrobiono tylko jeden przebieg.
Krzywa uczenia może wyglądać tak:
| Udział fiszek | Wynik testu | Przyrost |
|---|---|---|
| 10% | 72,0% | – |
| 25% | 89,5% | +17,5 p.p. |
| 50% | 97,8% | +8,3 p.p. |
| 100% | 98,1% | +0,3 p.p. |
To liczby ilustracyjne. Przy takim kształcie kolejne przykłady prawdopodobnie niewiele dadzą; większą wartość ma analiza 1,9% błędów. Jeżeli wynik treningowy rośnie, a walidacyjny spada, model przeucza się – zapamiętuje brzmienie fiszek zamiast stabilnie odpowiadać.
Zatrzymujemy eksperyment również wtedy, gdy rośnie liczba fałszywych przypisań. Wyższy recall kosztem nadawania chorób obcym osobom nie jest sukcesem.
14 / Lokalny fine-tuning faktów
Milion Q&A: kiedy skala ma sens, a kiedy jest tylko duplikacją
Milion brzmi poważnie, ale licznik nie jest miarą wiedzy. Jeżeli mamy 500 osób i 1 117 atomowych rozpoznań, milion par wymagałby średnio ponad tysiąca ćwiczeń na osobę. Większość byłaby kosmetycznym powtórzeniem. Uczeń nie poznałby większej części książki – sto razy przepisałby tę samą stronę.
Zasadna skala wynika z pokrycia, nie z życzenia. Liczymy:
Q&A = encje × pytania_listujące
+ fakty_atomowe × pytania_dodatnie
+ encje × pytania_ujemne
+ pytania_temporalne_i_relacyjne
+ walidacja + test + osoby_nieznane
Dla bieżącego przykładu, przy czterech pytaniach listujących na osobę, dwóch dodatnich na fakt i dwóch ujemnych na osobę, otrzymujemy dokładnie 5 234 fiszki treningowe. Po dodaniu walidacji, testu i rozsądnej puli obcych osób źródło wspiera około 8 769 odrębnych zadań. Skrypt 06_zaplanuj_skale_qa.py zwraca więc:
requested_pairs: 1 000 000
justified_total_pairs: 8 769
support_ratio: 0,8769%
decision: STOP_NIE_ROZDMUCHUJ_DANYCH
Przy tej samej gęstości faktów milion zadań wymagałby około 57 tys. rzeczywiście różnych encji. To przybliżenie, nie uniwersalna stała.
Milion może mieć sens, gdy przykładowo mamy:
- 100 tys. produktów i kilka właściwości każdego;
- 70 tys. maszyn, zdarzenia serwisowe, części i relacje czasowe;
- 60 tys. osób z wieloma stabilnymi faktami oraz kontrolowanymi negacjami;
- duży katalog prawny z tysiącami dokumentów, artykułów i relacji – o ile fakty zostały wcześniej ustrukturyzowane.
Milion nie ma sensu, gdy pochodzi z:
- setek synonimów jednego pytania;
- wielokrotnego przepisywania tego samego wiersza CSV;
- generowania odpowiedzi przez model bez porównania ze źródłem;
- powielania błędu po każdej kolejnej rundzie samogeneracji;
- tworzenia danych tylko dlatego, że budżet na to pozwala.
Badanie Self-Instruct pokazało użyteczną zasadę: model może generować instrukcje, ale trzeba usuwać przykłady niepoprawne i zbyt podobne. Magpie wygenerował 4 mln instrukcji, a następnie wybrał 300 tys. przykładów wysokiej jakości. To dobry obraz fabryki: dużo kandydatów, znacznie mniej zaakceptowanych rekordów. Z kolei badanie o „model collapse” ostrzega przed bezkrytycznym uczeniem kolejnych modeli treściami wytworzonymi przez poprzednie generacje.
W projekcie przyjmujemy planistyczny mnożnik 1,35: aby uzyskać milion zaakceptowanych par, wolno zaplanować 1,35 mln kandydatów. Mnożnik nie pochodzi z uniwersalnego badania; po pierwszym pilocie zastępujemy go rzeczywistym współczynnikiem odrzuceń.
15 / Lokalny fine-tuning faktów
Z jednego CSV do macierzy pokrycia faktów
Jednego CSV nie uczymy „jak leci”. Najpierw zamieniamy go w mapę tego, co wolno pytać i jak sprawdzić odpowiedź.
Krok 1. Kontrakt kolumn
Dla każdej kolumny zapisujemy:
| Pole | Znaczenie | Czy wolno użyć w pytaniu | Reguła odpowiedzi | Ryzyko |
|---|---|---|---|---|
patient_id | stabilny klucz | nie | tylko metadane | kolizja klucza |
imie, nazwisko | nazwa encji | tak | identyfikacja osoby | dane osobowe |
rozpoznania | lista faktów | tak | dokładny zbiór kanoniczny | fałszywe przypisanie |
data_wizyty | czas zdarzenia | warunkowo | dokładna data | nieaktualność |
opis_wizyty | tekst swobodny | po ekstrakcji | tylko zatwierdzone fakty | halucynacja |
Model nie może sam zdecydować, że dowolna kolumna jest ważna. Kontrakt zatwierdza właściciel danych.
Krok 2. Encja i rekord kanoniczny
Łączymy wiersze po stabilnym identyfikatorze. Dla każdej encji otrzymujemy jedną wersję obowiązującej prawdy oraz jawne reguły konfliktu. W tym miejscu ustalamy też wersję źródła i hash pliku.
Krok 3. Fakty atomowe
Lista trzech chorób to jeden fakt listowy i trzy fakty atomowe. Zdarzenie „recepta wystawiona 2025-06-10” można rozłożyć na produkt, datę i relację osoby z receptą. Dzięki temu generator wie, czy tworzy pytanie listujące, dodatnie, ujemne, czasowe czy relacyjne.
Krok 4. Macierz pokrycia
Każdy wiersz macierzy odpowiada faktowi lub relacji, a kolumny rodzinom pytań:
fact_id | lista | dodatnie | ujemne | czasowe | validation | test | status |
|---|---|---|---|---|---|---|---|
P0001/I10 | przez rekord osoby | 2 | przez losowany fakt obcy | 1 | 1 | 1 | kompletne |
P0001/E11.9 | przez rekord osoby | 2 | przez losowany fakt obcy | 1 | 1 | 1 | kompletne |
P0001/J45.9 | przez rekord osoby | 2 | przez losowany fakt obcy | 1 | 1 | 1 | kompletne |
Program powinien przerwać generację, gdy choć jeden krytyczny fakt ma zero pokrycia albo jeden fakt ma nieproporcjonalnie setki kopii.
Krok 5. Budżet wariantów
Praktyczny limit startowy to:
- 4-8 znacząco różnych pytań listujących na encję;
- 1-3 pytania dodatnie na fakt atomowy;
- 1-3 kontrolowane negacje na encję;
- 1-2 pytania na ważną relację czasową;
- osobne, niewidziane szablony do validation i test;
- więcej przykładów tylko dla udokumentowanych klastrów błędów.
Nie dodajemy trzynastej parafrazy, dopóki egzamin nie pokaże, że pierwszych dwanaście nie pokrywa ważnego sposobu pytania.
Krok 6. Shardy i idempotencja
Milion wierszy nie powinien być jednym plikiem. Dzielimy wynik na shardy, np. po 50 tys. rekordów:
qa_train-00001-of-00020.jsonl
qa_train-00002-of-00020.jsonl
...
Identyfikator rekordu powinien wynikać z source_version + entity_id + fact_id + template_id + split. Ponowne uruchomienie z tym samym manifestem daje ten sam identyfikator, dzięki czemu agent nie nalicza ponownie ukończonego zadania.
Manifest każdego przebiegu zapisuje co najmniej:
- hash źródłowego CSV;
- wersję kontraktu kolumn;
- wersję banku szablonów i promptu;
- dokładny identyfikator modelu generatora i krytyka;
- seed, temperaturę lub reasoning effort;
- liczby kandydatów, akceptacji, napraw, odrzuceń i kwarantanny;
- hashe shardów oraz koszt rzeczywisty.
16 / Lokalny fine-tuning faktów
Fabryka promptów: automatyzacja miliona par
Najbezpieczniejszy i najszybszy wariant dla tabel nie polega na wysyłaniu miliona rekordów do modelu. Model tworzy bank szablonów na fikcyjnych danych, a program lokalny wypełnia znaczniki i składa odpowiedzi z faktów kanonicznych.
LLM: „Wymień rozpoznania zapisane dla osoby {OSOBA}.”
kod: {OSOBA} = Stefan Wrzecionko
kod: {FAKTY} = [I10, E11.9, J45.9]
kod: składa kanoniczną odpowiedź
Model jest redaktorem pytań. Nie jest lekarzem, bazą ani autorem odpowiedzi.
Prompt generatora
Pełna wersja znajduje się w prompty/01-generator-szablonow.md. Rdzeń brzmi:
Twórz wyłącznie polskie szablony pytań.
Używaj tylko dozwolonych znaczników.
Każde pytanie musi być jednoznacznie rozstrzygalne z available_fields.
Nie dodawaj nazw, chorób ani dat poza znacznikami.
Nie generuj odpowiedzi.
Usuń kosmetyczne parafrazy i zachowaj różne konstrukcje składniowe.
Zwróć wyłącznie dane zgodne z przekazanym JSON Schema.
Przykładowe wejście:
{
"question_family": "positive_presence",
"available_fields": ["entity_name", "fact"],
"allowed_placeholders": ["{OSOBA}", "{FAKT}"],
"requested_count": 50,
"existing_templates": ["Czy {OSOBA} miał rozpoznanie: {FAKT}?"],
"forbidden": ["porada medyczna", "fakty spoza pól", "prawdziwe dane"]
}
Wyjście ma schemat, a nie swobodny tekst:
{
"templates": [{
"template_id": "positive_presence_0001",
"question": "Czy w historii osoby {OSOBA} zapisano rozpoznanie: {FAKT}?",
"question_type": "positive_presence",
"required_fields": ["entity_name", "fact"],
"risk_tags": []
}]
}
Structured Outputs zamiast „proszę zwrócić JSON”
Modele GPT-5.6 obsługują Structured Outputs. Schemat powinien wymagać wszystkich pól i blokować additionalProperties. Poprawny JSON nie oznacza jeszcze poprawnego pytania, ale usuwa całą klasę błędów technicznych.
Batch API
OpenAI opisuje Batch API jako tryb asynchroniczny z kosztem o 50% niższym od odpowiedników synchronicznych, osobną pulą limitów i czasem realizacji do 24 godzin. To pasuje do fabryki danych, bo wynik nie jest potrzebny w sekundę.
Skrypt:
python skrypty/07_buduj_batch_szablonow.py \
--model gpt-5.6-luna \
--templates-per-family 50
tworzy dane/batch-szablony-gpt56-luna.jsonl. Nie łączy się z siecią. Każdy wiersz ma unikalny custom_id, endpoint /v1/responses i ścisły schemat wyjścia. Przed samodzielnym wysłaniem można otworzyć plik i potwierdzić, że nie zawiera nazwiska Stefan Wrzecionko, diagnoz ani identyfikatorów ze źródła.
Kiedy użyć generacji per rekord
Jeżeli CSV zawiera długie, niejednorodne opisy, reguły mogą nie wystarczyć. Wtedy model może najpierw wyodrębnić kandydatów faktów do osobnej kolejki. Nadal obowiązuje kolejność:
- ekstrakcja do ścisłego schematu;
- deterministyczne porównanie z dozwolonym słownikiem lub ręczne zatwierdzenie;
- dopiero potem pytania;
- odpowiedź składana z zatwierdzonego faktu;
- rekord niezgodny trafia do kwarantanny.
Przy realnych danych medycznych wysyłka rekordu do zewnętrznego API wymaga osobnej podstawy, umowy, oceny lokalizacji i retencji. Ten poradnik nie uznaje zgody na lokalny fine-tuning za zgodę na zewnętrzne generowanie danych.
17 / Lokalny fine-tuning faktów
Najlepszy model do generacji: kaskada GPT-5.6
Nie ma jednego modelu, który jednocześnie daje maksymalną jakość, minimalny koszt i najwyższą przepustowość. Najlepszy praktyczny system to kaskada.
Według aktualnej dokumentacji OpenAI:
| Rola | Model | Dlaczego | Reasoning startowy | Cena standardowa za 1 mln tokenów input/output |
|---|---|---|---|---|
| architekt i sędzia | gpt-5.6-sol | model flagowy do trudnej pracy profesjonalnej | medium, w sporach high | 4 / 20 USD |
| krytyk | gpt-5.6-terra | równowaga inteligencji i kosztu | medium | 2 / 12 USD |
| masowy generator | gpt-5.6-luna | obciążenia wysokiego wolumenu i wrażliwe na koszt | none lub low | 0,20 / 1,20 USD |
W Batch przyjmujemy połowę tych stawek: odpowiednio Luna 0,10/0,60, Terra 1/6 i Sol 2/10 USD za milion tokenów. To ceny zweryfikowane 28 sierpnia 2026 r.; konto może mieć inne limity, a ceny mogą się zmienić.
Rekomendowany podział pracy
gpt-5.6-solprojektuje 200–500 wzorców referencyjnych oraz listę błędów niedopuszczalnych.gpt-5.6-lunatworzy tysiące kandydatów w ścisłym schemacie.- Reguły lokalne odrzucają błędne znaczniki, niedozwolone pola i duplikaty.
gpt-5.6-terraocenia język, jednoznaczność i zgodność typu pytania.gpt-5.6-solwidzi tylko spory, rekordy wysokiego ryzyka i próbkę kontrolną.- Program lokalny rozszerza zaakceptowany bank na miliony rekordów.
Jeżeli wolno wybrać tylko jeden model, rozsądnym punktem startowym jest gpt-5.6-terra z reasoning.effort=medium. Przed decyzją trzeba jednak przeprowadzić ślepy test na własnych 500–2 000 zadaniach i policzyć koszt zaakceptowanego, a nie wygenerowanego rekordu:
koszt_zaakceptowanego = koszt_wszystkich_prób / liczba_par_po_wszystkich_bramkach
Najtańszy token może być droższy, jeżeli połowa odpowiedzi trafia do kosza. Najdroższy model może być nieopłacalny, jeżeli reguły rozwiązują 99% przypadków.
Do produkcyjnej powtarzalności zapisujemy zwrócony identyfikator modelu, wersję promptu i pełne parametry. Alias modelu może się zmienić; manifest musi pozwolić odtworzyć, co faktycznie wykonało przebieg.
18 / Lokalny fine-tuning faktów
Agent, który kontroluje agenta
Agent nie może „sam się sprawdzić” jednym pytaniem: „czy zrobiłeś dobrze?”. To odpowiednik ucznia, który sam wystawia sobie ocenę. Potrzebujemy kilku niezależnych bramek.
generator → walidator kodowy → krytyk LLM → sędzia → próbka człowieka
↑ prawda źródłowa ↑ tylko spory
Pięć ról
| Rola | Widzi | Odpowiada za | Nie może |
|---|---|---|---|
| generator | kontrakt i fikcyjne przykłady | różnorodne szablony | ustalać faktów |
| walidator kodowy | CSV, rekord kanoniczny, Q&A | zgodność faktów, schemat, hashe, duplikaty | poprawiać znaczenia |
| krytyk | szablon i kontrakt | gramatyka, jednoznaczność, zakres | nadpisać błędu źródłowego |
| sędzia | tylko spór i dowody | accept/repair/reject/quarantine | przeglądać cały zbiór bez potrzeby |
| człowiek | losowa i ryzykowna próbka | odbiór domenowy, błędy krytyczne | zastępować automatykę dla milionów |
Bramki techniczne
Przed treningiem wymagamy:
- 100% rekordów zgodnych ze schematem;
- 0 faktów odpowiedzi spoza rekordu kanonicznego;
- 0 kolizji
id; - 0 identycznych pytań pomiędzy train, validation i test;
- 0 sprzecznych odpowiedzi na ten sam znormalizowany sens;
- pełnego pokrycia faktów krytycznych;
- jawnej liczby duplikatów semantycznych i udziału największego szablonu;
- osobnej kwarantanny, nigdy cichego pomijania błędów;
- limitu dwóch prób naprawy na rekord;
- braku przekroczenia budżetu przebiegu.
Dołączony walidator działa strumieniowo i używa tymczasowego indeksu SQLite, więc nie musi przechowywać milionów pełnych pytań w pamięci:
python skrypty/08_waliduj_qa_na_skale.py
Na bieżącym zestawie sprawdza 9 451 rekordów i wymaga zera błędów krytycznych. Raport zapisuje liczbę znormalizowanych konstrukcji pytań, dominujące szablony i każdą bramkę.
Kontrola statystyczna człowieka
Nie da się ręcznie przeczytać miliona par, ale próbka musi mieć sens. Gdy w n losowych rekordach nie znaleziono błędu, „reguła trzech” daje przybliżoną górną granicę błędu 95% równą 3/n:
| Cel | Próbka bez wykrytego błędu |
|---|---|
| błąd poniżej ok. 1% | 300 |
| błąd poniżej ok. 0,1% | 3 000 |
| błąd poniżej ok. 0,01% | 30 000 |
Próbka nie może być wyłącznie losowa. Dodajemy warstwy: każda rodzina pytań, rzadkie fakty, długie odpowiedzi, podobne nazwiska, wszystkie naprawy sędziego oraz wszystkie rekordy wysokiego ryzyka.
prompty/02-krytyk-qa.md, 03-sedzia-qa.md i 04-agent-orkiestrator.md określają role, limity ponowień i decyzje GO / POPRAW / STOP. Prompt agenta nigdy nie zastępuje testu kodowego.
19 / Lokalny fine-tuning faktów
Pięć iteracji XP dla programisty
Programowanie ekstremalne oznacza krótkie działające przyrosty, test przed rozbudową, częsty refaktoring i szybki pomiar. Nie piszemy przez miesiąc fabryki na 10 mln rekordów, której nikt wcześniej nie uruchomił na pięciu tysiącach.
Iteracja 1 – walking skeleton
Zakres: CSV → rekord kanoniczny → około 5 tys. Q&A → dry-run treningu → egzamin.
Programista robi:
- jeden kontrakt kolumn;
- trzy rodziny pytań: lista,
Tak,Nie; - testy unikalności identyfikatorów i przecięcia splitów;
- jeden manifest i komendę odtwarzającą wynik.
Brama: ta sama odpowiedź o Stefanie w CSV, JSONL, Excelu i teście. Zero ręcznych poprawek w pliku wynikowym.
Iteracja 2 – macierz pokrycia i bank szablonów
Zakres: do 50 tys. uzasadnionych par albo pełny limit źródła, jeśli jest mniejszy.
Programista robi:
- test najpierw: każdy fakt krytyczny ma pokrycie train/validation/test;
- wersjonowany bank szablonów;
template_id,fact_id,source_versionw każdym rekordzie;- detektor identycznych pytań i dominującego szablonu;
- pierwszy pomiar kosztu zaakceptowanego rekordu.
Brama: zmiana promptu bez zmiany prompt_version powoduje błąd testu. Wygenerowanie tego samego przebiegu nie tworzy nowych ID.
Iteracja 3 – niezależna kontrola
Zakres: do 250 tys. uzasadnionych par.
Programista robi:
- walidator źródłowy przed modelem-krytykiem;
- krytyka i sędziego jako oddzielne role;
- kwarantannę oraz maksymalnie dwie naprawy;
- testy mutacyjne: podmiana nazwiska, dopisanie choroby, usunięcie faktu, duplikat i błędny split;
- raport akceptacji według rodzaju pytania.
Brama: każda mutacja zostaje wykryta, a sędzia nie może zaakceptować rekordu odrzuconego przez źródło.
Iteracja 4 – próba miliona
Warunek wejścia: 06_zaplanuj_skale_qa.py zwraca GO; bieżący syntetyczny zbiór zwraca STOP, więc tej iteracji nie wolno na nim udawać.
Zakres: 1,35 mln kandydatów → około 1 mln zaakceptowanych par, shardy po 50 tys.
Programista robi:
- kolejkę z wznawianiem po
custom_id; - limity kosztu, czasu i liczby ponowień;
- zapis wykorzystanych tokenów z odpowiedzi API;
- strumieniową walidację i hashe shardów;
- ręczną próbkę co najmniej 3 tys. rekordów, jeżeli celem jest wykazanie błędu poniżej około 0,1% przy zerze zaobserwowanych pomyłek.
Brama: manifest sumuje się do liczby w shardach, koszt zgadza się z raportem użycia, a żadna tajna wartość źródła nie występuje w pliku promptów.
Iteracja 5 – nauka, ablation i odbiór
Zakres: treningi na 10%, 25%, 50% i 100% zaakceptowanych danych.
Programista robi:
- ten sam zamknięty egzamin dla każdej wielkości;
- co najwyżej jedną zmianę hiperparametru pomiędzy porównaniami;
- osobną ablację bez danych syntetycznych LLM albo z mniejszym bankiem szablonów;
- trzy seedy najlepszego ustawienia;
- testy ekstrakcji, podobnych nazwisk, aktualizacji i osób nieznanych;
- procedurę odtworzenia modelu z manifestu.
Brama: progi z rozdziału 13, spłaszczona krzywa uczenia i brak pogorszenia bezpieczeństwa. Jeżeli 250 tys. par daje ten sam wynik co milion, wybieramy 250 tys.
20 / Lokalny fine-tuning faktów
Koszt wdrożenia z podziałem na etapy
Koszt dzielimy na cztery osobne koszyki:
- API do projektowania i kontroli danych;
- lokalne przetwarzanie plików;
- GPU do treningu oraz egzaminu;
- pracę programisty, opiekuna danych i eksperta domenowego.
Nie wolno pokazywać wyłącznie ceny tokenów. Najtańsze API nie naprawi źle zdefiniowanej prawdy, a godzina programisty może kosztować więcej niż wygenerowanie miliona krótkich odpowiedzi.
Założenia kalkulatora dla 1 mln zaakceptowanych par
| Założenie | Wartość ilustracyjna |
|---|---|
| kandydaci przed filtrem | 1,35 mln |
| generator Luna | 220 tokenów wejścia + 80 wyjścia na kandydata |
| krytyk Terra | 20% kandydatów, 300 tokenów wejścia + 40 wyjścia |
| sędzia Sol | 1% zaakceptowanych, 350 tokenów wejścia + 60 wyjścia |
| ponowienia | 5% kosztu generatora |
| gotowy rekord treningowy | 140 tokenów |
| epoki | 3 |
| zmierzona przepustowość — założenie | 1 000 tokenów/s |
| H100 | 2,89 USD/h |
| bufor GPU | 30% |
| praca | 120 h po 250 PLN/h |
| kurs planistyczny | 3,80 PLN/USD |
Wartości żółte w Excelu są edytowalne. 1 000 tokenów/s nie jest obietnicą wydajności; po iteracji 1 trzeba wpisać pomiar własnego treningu.
Koszt dwóch sposobów budowy danych
Tryb szablonowy – zalecany: około 20,60 USD API (przy założeniu bezpieczeństwa danych) na zbudowanie i kontrolę banku oraz około 25 USD lokalnego przetwarzania. Milion par powstaje później lokalnie. Koszt nie rośnie liniowo z każdą parą, dopóki bank szablonów wystarcza.
Tryb semantyczny per rekord: przy powyższych założeniach:
Luna: 94,50 USD
Terra: 145,80 USD
Sol: 13,00 USD
ponowienia: 4,73 USD
przetwarzanie lokalne: 25,00 USD
razem fabryka danych: około 283 USD / 1 mln zaakceptowanych par
To koszt bez podatków, regionalnych dopłat, umów, bezpiecznego przechowywania i pracy ludzi.
Etapy wdrożenia – wariant szablonowy
| Etap | Wynik | API / compute USD | Praca h | Koszt pracy przy 250 PLN/h |
|---|---|---|---|---|
| 0. Kontrakt i ryzyko | słownik, podstawa, błędy krytyczne | 0 | 12 | 3 000 PLN |
| 1. Złoty bank | Sol + Luna, prompty i schemat | 7,60 | 16 | 4 000 PLN |
| 2. Ekspansja lokalna | shardy i manifest | 25,00 | 20 | 5 000 PLN |
| 3. Krytyk i sędzia | Terra + Sol, kwarantanna | 13,00 | 24 | 6 000 PLN |
| 4. Piloty treningu | 10/25/50%, kilka prób | 132,00 | 16 | 4 000 PLN |
| 5. Pełny trening | 1 mln × 140 tokenów × 3 epoki | 438,00 | 20 | 5 000 PLN |
| 6. Egzamin i odbiór | testy, raport, wycofanie | 38,00 | 12 | 3 000 PLN |
| Razem | wariant planistyczny | około 654 USD | 120 | 30 000 PLN |
Przy kursie 3,80 daje to około 32,5 tys. PLN łącznie z pracą. Wariant semantyczny per rekord zwiększa plan do około 891 USD kosztów bezpośrednich, czyli około 33,4 tys. PLN z tą samą pracą. Różnica wydaje się niewielka tylko dlatego, że w tym przykładzie dominują koszty ludzi; przy dziesiątkach milionów rekordów tryb semantyczny rośnie liniowo.
Skala 1, 5 i 10 mln – koszty bezpośrednie bez pracy
Przy zachowaniu tych samych założeń treningowych:
| Zaakceptowane pary | Fabryka szablonowa + trening | Generacja semantyczna + trening |
|---|---|---|
| 1 mln | ok. 654 USD | ok. 891 USD |
| 5 mln | ok. 3 186 USD | ok. 4 455 USD |
| 10 mln | ok. 6 351 USD | ok. 8 910 USD |
Największym ryzykiem tego rachunku jest przepustowość treningu. Czas liczymy:
godziny = liczba_par × tokeny_na_parę × epoki
/ zmierzone_tokeny_na_sekundę / 3600
Dla miliona par, 140 tokenów, trzech epok i 1 000 tokenów/s otrzymujemy około 116,7 godziny jednego pełnego przebiegu. Jeśli pilot wykaże 300 tokenów/s, koszt będzie ponad trzykrotnie większy. Jeżeli wykaże 2 000 tokenów/s, będzie prawie o połowę niższy.
Arkusze Plan_Milionow, Koszty_Etapow i Iteracje_XP liczą te scenariusze formułami. Zanim wydasz środki, zastąp żółte pola pomiarami z iteracji 1 oraz aktualnymi cenami.
21 / Lokalny fine-tuning faktów
Model bazowy: OpenAI gpt-oss-20b
Głównym modelem jest openai/gpt-oss-20b: otwarty wagowo model OpenAI przeznaczony m.in. do lokalnych i wyspecjalizowanych zastosowań. Według karty ma 21 mld parametrów ogółem, około 3,6 mld aktywnych na token i kontekst 131 072 tokenów. Wagi są dostępne na licencji Apache 2.0.
To nie jest model udostępniany przez API OpenAI. Pobierasz wagi i sam odpowiadasz za:
- sprzęt i koszty;
- wersje bibliotek;
- bezpieczeństwo danych;
- logi i monitoring;
- testy jakości;
- przechowywanie adaptera;
- zgodność prawną.
Oficjalna receptura OpenAI pokazuje LoRA z bibliotekami Transformers, TRL i PEFT. Referencyjny notebook używa jednej karty H100 80 GB; autorzy wskazują, że na mniejszym GPU trzeba zmniejszyć batch i długość sekwencji. Nasze odpowiedzi są krótkie, dlatego startujemy od max_length=512, batcha 1 i akumulacji gradientu 16.
22 / Lokalny fine-tuning faktów
LoRA, MXFP4 i sprzęt po ludzku
Pełny fine-tuning zmieniałby bardzo wiele wag i wymagał ogromnej pamięci. LoRA zamraża encyklopedię i uczy małych nakładek. Adapter jest zwykle znacznie mniejszy niż model bazowy.
gpt-oss-20b używa formatu MXFP4. Oficjalna receptura ładuje model przez Mxfp4Config(dequantize=True) i dopina LoRA. To nie jest identyczne ze standardowym QLoRA/NF4 opartym na bitsandbytes.
Praktyczna tabela sprzętu:
| Pamięć | Co zakładać |
|---|---|
| 16 GB GPU | możliwa inferencja MXFP4; nie obiecuj treningu z tą recepturą |
| 24 GB GPU | eksperyment z bardzo małym batchem/offloadem; ryzyko OOM |
| 48 GB GPU | rozsądny pilot krótkich sekwencji, nadal wymaga pomiaru |
| 80 GB H100/A100 | komfortowa klasa dla receptury i szybkich eksperymentów |
| 128 GB unified DGX Spark | dużo miejsca na model, Linux/ARM; czas zależy od przepustowości i stosu |
Pamięć potrzebna do inferencji nie jest pamięcią potrzebną do treningu. Podczas nauki dochodzą aktywacje, gradienty i stan optymalizatora. Zdanie „model mieści się w 16 GB” nie oznacza „wytrenuje się w 16 GB”.
Przygotuj także 60-100 GB wolnego dysku na cache, checkpointy i wyniki. Zapisuj adapter po każdej epoce, ale ogranicz liczbę checkpointów.
23 / Lokalny fine-tuning faktów
Instalacja na Windows przez WSL2 – krok po kroku
Najmniej problematyczna ścieżka na Windows prowadzi przez WSL2 i Ubuntu.
Krok 1. Włącz WSL2
- Otwórz menu Start.
- Wpisz PowerShell.
- Kliknij prawym przyciskiem i wybierz Uruchom jako administrator.
- Wpisz:
wsl --install -d Ubuntu
- Uruchom komputer ponownie, jeśli Windows o to poprosi.
- Otwórz aplikację Ubuntu i utwórz nazwę użytkownika oraz hasło.
Krok 2. Sprawdź GPU
W terminalu Ubuntu wpisz:
nvidia-smi
Powinna pojawić się nazwa karty i wersja sterownika. Jeśli polecenie nie działa, nie przechodź dalej. Najpierw zaktualizuj sterownik NVIDIA obsługujący WSL2.
Krok 3. Skopiuj folder do systemu Linux
Praca bezpośrednio na /mnt/c bywa wolniejsza. Skopiuj folder projektu do katalogu domowego:
cp -r /mnt/c/Users/Przemek/Documents/piano/poradnik ~/poradnik-finetuning
cd ~/poradnik-finetuning
Jeżeli Windows ma inną nazwę użytkownika lub folder, zmień ścieżkę.
Krok 4. Utwórz środowisko
sudo apt update
sudo apt install -y python3-venv python3-pip git
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
Krok 5. Zainstaluj PyTorch i biblioteki
Oficjalna receptura używała CUDA 12.8:
pip install torch --index-url https://download.pytorch.org/whl/cu128
pip install -r skrypty/requirements.txt
Przed instalacją produkcyjną sprawdź aktualny instalator na stronie PyTorch. Wersja sterownika i CUDA muszą być zgodne.
Krok 6. Dostęp do modelu
Model jest publiczny, lecz logowanie do Hugging Face ułatwia pobieranie. Na stronie Hugging Face otwórz Settings → Access Tokens → Create new token, wybierz uprawnienie tylko do odczytu, a potem w terminalu:
huggingface-cli login
Nie wklejaj tokenu do skryptu ani do pliku CSV.
Krok 7. Test środowiska
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"
Pierwsza wartość musi być True.
24 / Lokalny fine-tuning faktów
Przygotowanie i sprawdzenie danych
W folderze poradnika wykonaj:
python skrypty/01_przygotuj_dane.py
python skrypty/04_ewaluuj.py --dry-run
python skrypty/02_trenuj_lora_mxfp4.py --dry-run
Powinieneś zobaczyć 500 pacjentów, 5 234 fiszki treningowe oraz komunikat PASS dla podziałów. --dry-run nie pobiera 20-miliardowego modelu.
Otwórz ręcznie po jednym wierszu z:
dane/qa_train.jsonl;dane/qa_validation.jsonl;dane/qa_test.jsonl;dane/qa_unseen_entities.jsonl.
Sprawdź, czy pytania testowe brzmią inaczej niż treningowe i czy obce nazwiska mają odpowiedź Brak danych o tej osobie..
Skrypt zapisuje manifest.json ze skrótami SHA-256. Jeżeli zmienisz dane, zmieni się skrót. Dzięki temu wiadomo, z jakiej dokładnie wersji powstał adapter.
Przed prawdziwym eksperymentem wykonaj kopię tylko do odczytu i nadaj wersję, np. dataset-2026-08-28-v1. Nie nadpisuj danych po treningu bez zmiany identyfikatora.
25 / Lokalny fine-tuning faktów
Trening adaptera
Najpierw zrób próbę na 10% danych:
python skrypty/02_trenuj_lora_mxfp4.py \
--train-fraction 0.10 \
--epochs 1 \
--output-dir model/eksperyment-10
Obserwuj pamięć w drugim terminalu:
watch -n 2 nvidia-smi
Jeśli pojawia się CUDA out of memory:
- upewnij się, że batch wynosi 1;
- zmniejsz
--max-lengthz 512 do 384 lub 256; - zamknij procesy zajmujące GPU;
- użyj GPU z większą pamięcią;
- nie zwiększaj liczby epok jako lekarstwa na brak pamięci.
Pełny przebieg startowy:
python skrypty/02_trenuj_lora_mxfp4.py \
--train-fraction 1.0 \
--epochs 3 \
--batch-size 1 \
--gradient-accumulation 16 \
--max-length 512 \
--learning-rate 0.0002 \
--output-dir model/stefan-lora
Znaczenie parametrów:
epochs=3: model przechodzi po fiszkach trzy razy;batch-size=1: jedna fiszka naraz mieści się łatwiej;gradient-accumulation=16: aktualizacja po szesnastu małych krokach;max-length=512: wystarczy dla krótkich Q&A;learning-rate=2e-4: punkt startowy LoRA z oficjalnej receptury;lora-r=8: mała pojemność adaptera; większerkosztuje pamięć.
Nie zmieniaj pięciu parametrów naraz. Prowadź dziennik: model, hash danych, seed, GPU, czas, maksymalna pamięć, wynik walidacji i wynik testu.
26 / Lokalny fine-tuning faktów
Pytanie do modelu bez RAG i bez bazy
Po treningu uruchom:
python skrypty/03_zadaj_pytanie.py \
--adapter model/stefan-lora \
--question "Jakie choroby miał Stefan Wrzecionek?"
Skrypt ładuje model bazowy i adapter. Do modelu przekazuje tylko instrukcję oraz pytanie. Nie otwiera pliku pacjentów, rozpoznań ani źródłowego CSV.
Sprawdź od razu trzy pytania:
python skrypty/03_zadaj_pytanie.py --adapter model/stefan-lora --question "Na co chorował Stefan Wrzecionek?"
python skrypty/03_zadaj_pytanie.py --adapter model/stefan-lora --question "Czy Stefan Wrzecionek miał migrenę?"
python skrypty/03_zadaj_pytanie.py --adapter model/stefan-lora --question "Jakie choroby miał Kornel Kwarcowy?"
Jedna poprawna odpowiedź o Stefanie jest demonstracją, nie odbiorem jakości. Model musi zdać tysiące pytań testowych.
Używamy deterministycznego generowania (do_sample=False). Kreatywność jest tu wadą. Temperatura nie naprawia brakującej wiedzy.
27 / Lokalny fine-tuning faktów
Pełny egzamin modelu
Najpierw krótka próba 25 pytań:
python skrypty/05_generuj_odpowiedzi_testowe.py \
--adapter model/stefan-lora \
--limit 25 \
--output wyniki-proba.jsonl
Pełny egzamin:
python skrypty/05_generuj_odpowiedzi_testowe.py \
--adapter model/stefan-lora \
--output wyniki-odpowiedzi.jsonl
python skrypty/04_ewaluuj.py \
--predictions wyniki-odpowiedzi.jsonl \
--output wyniki-ewaluacji.json
Mierzymy:
- exact match – czy cała odpowiedź zgadza się po prostej normalizacji;
- F1 faktów – czy lista ma wszystkie oczekiwane choroby i nie dopisuje znanych chorób obcych;
- skuteczność odpowiedzi
TakiNie; - skuteczność
Brak danych; - false positive rate – jak często model przypisuje fakt niewłaściwej osobie;
- wynik według liczby chorób, długości nazwiska i częstości faktu.
Do produkcyjnego egzaminu dodaj ręczny plik „czerwonych pytań”. Automatyczny skrypt rozpozna znane nazwy chorób, ale może przeoczyć semantyczny błąd napisany innymi słowami.
Porównuj eksperymenty w jednej tabeli:
| run | udział danych | epoki | seed | exact | F1 faktów | brak danych | pomyłki osób |
|---|---|---|---|---|---|---|---|
| A | 10% | 1 | 1 | … | … | … | … |
| B | 25% | 1 | 1 | … | … | … | … |
| C | 50% | 2 | 1 | … | … | … | … |
| D | 100% | 3 | 1 | … | … | … | … |
Nie oglądaj testu podczas każdej decyzji o hiperparametrach. Do strojenia używaj validation. Test otwieraj na końcu, jak zapieczętowaną kopertę.
28 / Lokalny fine-tuning faktów
Aktualizacja, usuwanie i zapominanie
Załóżmy, że astma Stefana została później oznaczona jako błędny wpis. Dopisanie nowych fiszek „Stefan nie miał astmy” może stworzyć konflikt, a nie wymazać stare skojarzenie.
Bezpieczniejszy proces wersjonowania:
- popraw źródłowy duży zbiór;
- wygeneruj rekordy kanoniczne i wszystkie fiszki od początku;
- nadaj nową wersję danych;
- wytrenuj nowy adapter od czystego modelu bazowego;
- wykonaj pełny test regresji;
- wycofaj i usuń stare adaptery, checkpointy, cache oraz wyniki;
- wykonaj test, czy stara informacja nadal nie pojawia się w nowej wersji.
Uczenie przyrostowe na starym adapterze może prowadzić do katastroficznego zapominania jednych faktów i zachowania innych. W przypadku praw do danych „wydaje się, że już nie odpowiada” nie jest dowodem usunięcia.
Zachowaj rejestr: który adapter powstał z którego manifestu i gdzie istnieją kopie. Bez tego nie da się wykonać wiarygodnego wycofania.
29 / Lokalny fine-tuning faktów
Koszty: własny sprzęt, DGX Spark i wynajem GPU
Wynajem na godziny
W dniu 28 sierpnia 2026 r. strona Runpod podawała przykładowo:
| GPU | VRAM | Stawka od | 10 h | 40 h eksperymentów |
|---|---|---|---|---|
| A40 | 48 GB | 0,44 USD/h | 4,40 USD | 17,60 USD |
| L40S | 48 GB | 0,99 USD/h | 9,90 USD | 39,60 USD |
| A100 PCIe | 80 GB | 1,39 USD/h | 13,90 USD | 55,60 USD |
| H100 PCIe | 80 GB | 2,89 USD/h | 28,90 USD | 115,60 USD |
To ceny orientacyjne, zależne od regionu, podaży, rodzaju chmury, podatku i dysku. Do budżetu dodaj 25-50% na pobieranie modelu, nieudane próby, checkpointy i test.
Nigdy nie wysyłaj bez namysłu prawdziwych danych medycznych do „community cloud” tylko dlatego, że jest tańsza. Potrzebujesz odpowiedniej umowy, lokalizacji danych, szyfrowania, polityki retencji i oceny dostawcy. Jeżeli warunkiem jest pełna lokalność, wynajęte GPU nie spełnia tego warunku.
Najuczciwsza estymacja czasu pochodzi z pilota:
czas_pełny = czas_pilota
× (liczba_fiszek_pełna / liczba_fiszek_pilota)
× (epoki_pełne / epoki_pilota)
× (średnie_tokeny_pełne / średnie_tokeny_pilota)
Dodaj bufor 30%. Nie przenoś czasu z H100 na A40 proporcjonalnie do ceny – sprzęt ma inną przepustowość.
NVIDIA DGX Spark
DGX Spark ma 128 GB zunifikowanej pamięci, 4 TB NVMe, do 1 PFLOP FP4, zasilacz 240 W i układ o TDP 140 W. Cena referencyjna w sklepie NVIDIA wynosiła 4 699 USD.
Prosty próg kosztowy, bez porównania wydajności:
godziny do zrównania = cena zakupu / stawka chmury za godzinę
Przy 4 699 USD daje to około 1 626 godzin H100 po 2,89 USD/h albo 3 381 godzin A100 po 1,39 USD/h. To nie jest pełny TCO: trzeba dodać prąd, czas administratora, awarie, podatki i fakt, że H100 może wykonać zadanie szybciej.
Koszt energii licz z pomiaru z gniazdka:
koszt = średnia moc w kW × godziny × cena 1 kWh
Przykład ilustracyjny: 0,24 kW × 10 h × 1 PLN/kWh = 2,40 PLN. Zmień stawkę i moc na rzeczywisty pomiar.
Dołączony Excel ma edytowalny kalkulator kosztu oraz krzywą uczenia. Żółte komórki są wejściami, a nie aktualną ofertą handlową.
30 / Lokalny fine-tuning faktów
Jak przenieść metodę na dowolne dane
Zamień pojęcia, ale zachowaj strukturę:
| Medycyna | Serwis maszyn | Katalog produktów | Umowy |
|---|---|---|---|
| pacjent | maszyna | produkt | kontrahent |
| choroba | usterka | właściwość | warunek |
| wizyta | przegląd | wersja katalogu | aneks |
| pytanie o choroby | pytanie o awarie | pytanie o skład | pytanie o termin |
Uniwersalny algorytm:
- Wybierz encję, np. osobę, maszynę lub produkt.
- Nadaj jej stabilny identyfikator.
- Zdefiniuj kanoniczne fakty i regułę konfliktu.
- Określ dozwolone pytania oraz odpowiedź dla braku danych.
- Zbuduj kilka rodzin parafraz.
- Dodaj odpowiedzi ujemne i obce encje.
- Rozdziel szablony na train, validation i test.
- Wygeneruj manifest oraz hashe.
- Trenuj serię rosnących zbiorów.
- Zatrzymaj się po spełnieniu progu i wypłaszczeniu krzywej.
- Testuj kolizje, prywatność, aktualizacje i wycofanie.
Nie wszystkie dane nadają się do pamięci parametrycznej. Im częściej fakt się zmienia, im większy koszt pomyłki i im silniejsze prawo do usunięcia, tym gorszym magazynem jest model.
31 / Lokalny fine-tuning faktów
Plan wykonania od zera i checklista odbioru
Dzień 1 – definicja
- Napisz jedno zdanie opisujące pytanie i oczekiwaną odpowiedź.
- Zdefiniuj
Brak danych. - Ustal próg jakości i błędy niedopuszczalne.
- Zatwierdź zakres danych i podstawę prawną.
Dzień 2 – dane
- Eksportuj jeden duży plik.
- Wyczyść klucze, daty, duplikaty i konflikty.
- Zbuduj rekord kanoniczny.
- Wygeneruj fiszki i manifest.
Dzień 3 – suchy test
- Uruchom wszystkie
--dry-run. - Przejrzyj losowe 100 fiszek.
- Sprawdź brak przecięć pytań.
- Sprawdź osoby nieznane i pytania negatywne.
Dzień 4 – pilot
- Trenuj na 10% przez jedną epokę.
- Zmierz czas, pamięć i długość przykładów.
- Uruchom egzamin.
- Oszacuj koszt pełnego przebiegu.
Kolejne eksperymenty
- 25%, 50% i 100% danych;
- maksymalnie jedna lub dwie zmiany parametrów między przebiegami;
- ta sama walidacja i ta sama końcowa koperta testowa;
- trzy seedy dla najlepszego ustawienia.
Odbiór
Model można nazwać gotowym dopiero, gdy:
- odpowiada na pytanie o Stefana w nowych sformułowaniach;
- podaje kompletną listę bez dopisków;
- odmawia dla osób nieznanych;
- nie miesza podobnych nazwisk;
- spełnia ustalone progi w automatycznym egzaminie;
- wynik jest powtarzalny;
- istnieje procedura aktualizacji i wycofania;
- adapter jest chroniony jak dane źródłowe;
- ograniczenia są opisane użytkownikowi.
Najkrótsze podsumowanie brzmi: najpierw porządek w faktach, potem różnorodne fiszki, następnie mały pilot, a na końcu bezlitosny egzamin. Sam trening jest tylko jednym krokiem pośrodku.
32 / Lokalny fine-tuning faktów
Źródła i weryfikacja
Pełna lista z datą sprawdzenia znajduje się w pliku zrodla.md. Najważniejsze pozycje:
- OpenAI: gpt-oss-20b
- OpenAI: Fine-tuning with gpt-oss and Hugging Face Transformers
- OpenAI: Using GPT-5.6
- OpenAI: GPT-5.6 Luna
- OpenAI: GPT-5.6 Terra
- OpenAI: GPT-5.6 Sol
- OpenAI: Batch API
- Hugging Face: PEFT Integration
- NVIDIA: DGX Spark
- EROD: opinia o modelach AI i danych osobowych
- Carlini i in.: Extracting Training Data from Large Language Models
- Kandpal i in.: Deduplicating Training Data Mitigates Privacy Risks
- Ovadia i in.: Fine-Tuning or Retrieval?
- Lu i in.: Scaling Laws for Fact Memorization
- Wang i in.: Self-Instruct
- Xu i in.: Magpie
- Shumailov i in.: AI models collapse when trained on recursively generated data