Ta warstwa ma nazwę, ma koszt i ma ograniczenia, których nie da się obejść promptem.
Co naprawdę jest w pliku z modelem
Pobierasz model. Dostajesz plik. Czasem kilka, czasem jeden na kilkanaście gigabajtów. Co w nim jest?
Liczby. Miliardy liczb ułożonych w macierze. Do tego opis kolejności, w jakiej te macierze mają być przez siebie przemnożone — graf operacji. I to wszystko.
W pliku nie ma pętli. Nie ma warunków. Nie ma pamięci. Nie ma niczego, co samo się uruchomi. Jeśli czytałeś Czym LLM nie jest, znasz już tę tezę od strony logicznej. Tu jest jej wersja fizyczna: plik z wagami ma dokładnie tyle sprawczości, co plik z tabelką w Excelu.
Rozmiar tego pliku liczy się prosto: liczba parametrów razy liczba bajtów na parametr. Model o siedmiu miliardach parametrów zapisany z szesnastobitową precyzją waży około czternastu gigabajtów. Ten sam model zapisany czterobitowo — niecałe cztery.
To nie są dwa różne modele. To ta sama partytura, raz zapisana ręcznie na papierze nutowym, raz przepisana skrótami na serwetce. Wszystkie nuty się zgadzają, część niuansów znika. Czy ta część ma znaczenie, rozkładaliśmy już osobno — a w trzecim artykule tej serii będzie można to porównać na własne oczy.
Zapamiętaj tę liczbę: cztery gigabajty. Za chwilę okaże się, że to nie jest informacja o miejscu na dysku, tylko o prędkości.
Runtime — ten, który czyta
Między plikiem a odpowiedzią stoi program. Wczytuje graf operacji, kompiluje go pod konkretny sprzęt, rezerwuje pamięć, a potem — przy każdym pojedynczym tokenie — przepuszcza dane przez cały ten graf od początku do końca.
Nazywa się runtime i to on jest bohaterem tej serii.
Ma konkretne wcielenia: llama.cpp, vLLM, ONNX Runtime, TensorRT-LLM, i kilkanaście innych. Nie musisz znać tych nazw. Musisz wiedzieć, że istnieją i że ten sam plik z wagami uruchomiony przez dwa różne runtime’y da tę samą odpowiedź w dwudziestokrotnie różnym czasie.
Tu zaczyna się rzecz, którą warto zatrzymać na dłużej. Runtime robi coś więcej niż liczy. Robi rzeczy, które prawie wszyscy przypisują modelowi.
Temperatura nie jest w wagach. Model nie zwraca słowa — zwraca rozkład prawdopodobieństwa nad całym słownikiem, i dopiero runtime losuje z tego rozkładu jedno słowo, według reguł, które mu ustawisz. Ta sama sytuacja dotyczy sekwencji zatrzymujących, limitu długości odpowiedzi, kar za powtórzenia i tego, czy odpowiedź przychodzi w całości, czy leci strumieniem. Wszystko to dzieje się za modelem, nie w nim. Sam mechanizm samplingu opisaliśmy wcześniej — tu ważne jest tylko, kto go wykonuje.
Wniosek jest niewygodny dla intuicji: kiedy zmieniasz temperaturę, nie zmieniasz modelu. Zmieniasz ustawienie programu, który model odczytuje.
Akcelerator — na czym to się liczy
Zostaje pytanie, czym właściwie mnożymy te macierze.
Operacja, którą model wykonuje miliardy razy na token, jest trywialna — pomnóż, dodaj. Trudność nie leży w skomplikowaniu, tylko w liczbie. Procesor ma kilkanaście rdzeni, każdy bardzo uniwersalny. Karta graficzna ma tysiące rdzeni, każdy bardzo głupi. Dla tego zadania druga architektura wygrywa i nie jest to walka wyrównana.
Ale prawdziwe wąskie gardło jest gdzie indziej i to jest najważniejsze zdanie tego artykułu.
Żeby wygenerować jeden token, trzeba przeczytać cały model z pamięci. Wszystkie wagi, co do jednej. Potem, żeby wygenerować następny token — znowu wszystkie. I tak dla każdego słowa odpowiedzi.
To znaczy, że prędkość generowania nie zależy głównie od tego, jak szybko sprzęt liczy. Zależy od tego, jak szybko potrafi przepchnąć dane z pamięci do rdzeni. Dzielisz przepustowość pamięci przez rozmiar modelu i dostajesz górną granicę, której nie przeskoczysz:
- Zwykły laptop, pamięć operacyjna, jakieś 80 GB/s. Model czterogigabajtowy — teoretyczny sufit dwadzieścia tokenów na sekundę, realnie kilkanaście.
- Mocna karta graficzna, około 1000 GB/s. Ten sam model — sufit powyżej dwustu tokenów na sekundę.
- Karta serwerowa z pamięcią HBM, ponad 3000 GB/s.
Trzy razy ten sam plik. Różnica kilkunastokrotna.
I teraz wraca tamta liczba z początku. Kwantyzacja nie przyspiesza modelu dlatego, że upraszcza obliczenia. Przyspiesza go, bo jest mniej bajtów do przeczytania. Czterokrotnie mniejszy plik to czterokrotnie mniej danych przepychanych przez tę samą rurę przy każdym tokenie. Zmniejszasz model nie po to, żeby zmieścił się na dysku — tylko po to, żeby mieścił się w przepustowości.
Z tego jednego mechanizmu wynika połowa rzeczy, które opiszemy dalej: dlaczego pierwszy token przychodzi inaczej niż reszta, dlaczego długi kontekst jest drogi w sposób nieliniowy, dlaczego dostawcy API liczą sobie za wejście i wyjście osobno.
Trzy maszynerie wokół tych samych wag
Do tej pory mówiliśmy o jednym komplecie: plik, runtime, sprzęt. W praktyce ten komplet występuje dziś w trzech zupełnie różnych konfiguracjach — i różni je nie to, co robią, tylko ile z tego widzisz.
Na twoim urządzeniu. Plik leży na dysku, wiesz który. Runtime działa w twoim procesie. Pamięć to twoja pamięć i kończy się w sposób, który widzisz. Nic nie jest ukryte i nic nie jest współdzielone.
Za API. Wysyłasz prośbę, dostajesz odpowiedź. Pomiędzy jest cudzy serwer, na którym twoja prośba stoi w kolejce razem z dwustoma innymi, jest przetwarzana partiami i podlega optymalizacjom, o których nie wiesz. Ten sam mechanizm co wyżej, tylko rozdzielony między wielu użytkowników naraz. Stąd biorą się limity, stąd bierze się zmienne opóźnienie i stąd bierze się rachunek.
W oknie czatu. Nad wszystkim powyżej stoi jeszcze produkt: instrukcja systemowa, której nie widzisz, historia rozmowy przepisywana od zera przy każdej turze, wyszukiwarka, pamięć, narzędzia, moderacja. Rozmawiasz z modelem mniej więcej w takim stopniu, w jakim rozmawiasz z kuchnią, składając zamówienie u kelnera.
Te same wagi. Trzy maszynerie. I bardzo dużo rzeczy, które nazywamy zachowaniem modelu, a które są zachowaniem maszynerii.
Dlaczego zaczynamy od najmniej praktycznego toru
Kolejne artykuły idą od lewej do prawej: najpierw urządzenie, potem API, potem okno czatu. To kolejność odwrotna do praktycznej — prawie nikt nie uruchamia modeli u siebie, prawie wszyscy korzystają z API i czatów.
Jest ku temu powód i nie jest to sympatia do lokalnych modeli.
Na własnym urządzeniu mechanizm jest widoczny. Widać, ile waży model. Widać, jak rośnie pamięć przy dłuższym kontekście. Widać, że pierwsze słowo przychodzi inaczej niż piętnaste. Na cudzym serwerze nie widać niczego — możesz tylko zmierzyć czas i przeczytać fakturę.
Dlatego tor lokalny nie jest tu tematem. Jest grupą kontrolną: miejscem, w którym oglądamy mechanizm z bliska, żeby potem rozpoznać ten sam mechanizm tam, gdzie jest schowany.
Następny artykuł schodzi o poziom niżej — do tego, co runtime faktycznie robi z plikiem, zanim policzy pierwszy token. Okaże się, że model jest kompilowany, i że od sposobu kompilacji zależy więcej niż od wyboru modelu.
Sufit prędkości modelu
webflux · maszyneria modelu
7 mld parametrów
2 bajty na parametr
80 GB/s





















