Strona główna / Artykuły

Od przewidywania następnego tokena do gotowego kodu: jak modele LLM tworzą Python

Tokeny, softmax, temperatura, warstwy samouwagi i warstwy feed-forward wyjaśnione w kolejności, wraz z przykładem krok po kroku i ograniczeniami wynikającymi z przewidywań.

Wydawca: REACTAPP.TOP9 min czytania1998 słów

Źródła: How LLMs Actually Work: From Predicting the Next Word to Writing Code.

Narzędzia takie jak ChatGPT i Copilot piszą funkcje, naprawiają błędy i wyjaśniają algorytmy, i może się wydawać, że dzieje się tu coś więcej niż tylko uzupełnianie tekstu. Poniższy model mentalny zaczyna się od podstaw: przewidywania tokenów, uwagi oraz mechanizmu, który nie tylko kończy zdanie, ale także generuje poprawny kod w Pythonie.

Jedno pytanie, zadawane wielokrotnie

Niezależnie od tego, czy wynikiem jest haiku, e-mail marketingowy czy sortowanie łączeniowe, model językowy odpowiada w kółko na jedno pytanie: biorąc pod uwagę wszystko, co zostało dotychczas zobaczone, jaki jest najbardziej prawdopodobny następny token?

Nie ma tu semantycznego rozumienia w tradycyjnym sensie, ani etapu, w którym model zdecyduje się napisać funkcję przed wygenerowaniem słowa def. Przewiduje token po tokenie. Dzięki miliardom parametrów oraz tekstu treningowemu liczącemu setki miliardów tokenów, te przewidywania przypominają racjonalne rozumowanie.

Czym jest token

Dokładność jest ważna już przed rozpoczęciem procesu przewidywania. Modele nie pracują z literami ani słowami, lecz z tokenami – jednostkami poniżej poziomu słowa, wytwarzanymi przez algorytmy takie jak Byte Pair Encoding. Mniej więcej:

  • Zwykłe słowa zazwyczaj stają się pojedynczymi tokenami, takimi jak "the", "def" i "return".
  • Rzadkie słowa dzielą się na części, więc "unhappiness" staje się ["un", "happiness">].
  • Symboli kodu to często pojedyncze tokeny, na przykład "(", ":", "==" oraz "->".

Dla GPT-4 jeden token odpowiada mniej więcej 0,75 angielskiego słowa, natomiast w kodzie ma zazwyczaj od 3 do 4 znaków, ponieważ kod składa się z tokenów jednoznakowych, takich jak : i (.

Model posiada również słownictwo – ustaloną listę znanych tokenów, która zwykle zawiera od 50 000 do 100 000 pozycji. Na każdym kroku przypisuje każdej pozycji pewną prawdopodobieństwo i albo wybiera tę najbardziej prawdopodobną, albo pobiera wartość z rozkładu, przy czym wybór jest kierowany przez parametr temperatury.

Pętla predykcji

Dla sekwencji „The cat sat on the” ilością do obliczenia jest prawdopodobieństwo warunkowe:

P(next_token | "The", "cat", "sat", "on", "the")

To jest rozkład dla całego słownika. Przebieg sygnału przez sieć generuje wektor o długości odpowiadającej rozmiarowi słownika, który funkcja softmax przekształca w prawidłowy rozkład prawdopodobieństwa:

P(token_i) = exp(logit_i) / Σⱼ exp(logit_j)

W dekodowaniu chciwym wybierany jest token o najwyższej prawdopodobieństwie. W przeciwnym razie token jest wybierany proporcjonalnie do rozkładu, przy czym temperature kontroluje stopień zmienności:

  • temperature = 0 zawsze wybiera najwyżej oceniony token, więc wynik jest deterministyczny.
  • temperature = 1 wybiera elementy z surowego rozkładu, co sprawia, że wynik jest bardziej zróżnicowany i kreatywny.
  • Gdy wartość przekracza 1, rozkład staje się bardziej płaski, a wynik bardziej losowy.
  • Gdy wartość jest poniżej 1, rozkład staje się bardziej ostro zarysowany, a wynik bardziej konserwatywny.

Dlatego jeden prompt może dawać różne odpowiedzi przy różnych próbach.

Transformer

Funkcja, która mapuje sekwencję tokenów na rozkład następnego tokenu, to Transformer, a dokładniej wersja przeznaczona wyłącznie do dekodowania – taka jest zasada projektowa GPT, Llama, Mistral i Claude. Ogólny schemat działania:

Token IDs → Embedding layer → [Attention + FFN] × N layers → Linear head → Softmax → P(next token)

Komponenty, w kolejności:

  1. Warstwa embeddingów. Każdy identyfikator tokena całkowitego przekształca się w gęsty wektor o rozmiarze d_model, na przykład 4096 w Llama-3 8B. Embeddingi pozycyjne, lub RoPE w nowoczesnych modelach, dodają informacje o kolejności.
  2. Warstwy Transformera, powtórzone N razy. Każda z nich zawiera mechanizm wielogłowicowej samoatencji, sieć feed-forward, normalizację warstwy stosowaną przed każdą podwarstwą w stylu „pre-norm” oraz połączenia resztkowe.
  3. Głowa liniowa i funkcja softmax. Ostateczny stan ukryty mapuje się na logity o kształcie [|V|], a funkcja softmax generuje rozkład.

Samoatencja

Samoodniesienie umożliwia tokenowi spojrzenie na inne tokeny w sekwencji, aby stworzyć bogatszy obraz samego siebie. To właśnie daje modelom Transformer przewagę nad RNN: każdy token może dotrzeć do każdego innego tokenu w jednym kroku, bez spadku wartości gradientu wraz z odległością.

Przy danych n tokenów, każdy z nich jest projektowany na trzy sposoby:

Q = XWᴬ_Q    (Query: what am I looking for?)
K = XWᴬ_K    (Key: what do I contain?)
V = XWᴬ_V    (Value: what do I contribute?)

Tutaj X to macierz wejściowa o kształcie [n, d_model], a trzy macierze W to nauczane wagi. Wynik pomiędzy tokenem i, pełniącym rolę zapytania, a tokenem j, pełniącym rolę klucza, jest następujący:

score(i, j) = qᵢ · kⱼᵀ / √dₖ

Dzielenie przez pierwiastek kwadratowy kluczowej wymiarowości zapobiega zbyt dużemu wzrostowi iloczynów skalarnych, gdy dₖ jest duże, co mogłoby spowodować przeniesienie funkcji softmax do obszarów o bardzo małych gradientach. Pełna procedura obliczeniowa jest następująca:

Attention(Q, K, V) = softmax(QKᵀ / √dₖ) · V

W modelu składającym się wyłącznie z dekodera, takim jak GPT, uwaga ma charakter przyczynowy lub zmaskowany: token i może zwracać uwagę jedynie na pozycje do i. Pozycje przyszłe otrzymują wartość ujemnej nieskończoności przed zastosowaniem funkcji softmax, więc ich wagi wynoszą dokładnie zero.

Multi-head attention uruchamia h niezależnych funkcji uwagi równolegle, z których każda ma mniejszą wymiarowość d_model / h, a następnie łączy ich wyniki:

MultiHead(Q, K, V) = Concat(head₁, ..., headₕ) · W_O
headᵢ = Attention(QWᵢ_Q, KWᵢ_K, VWᵢ_V)

Różne elementy są odpowiedzialne za obsługę różnych relacji. Jedne mogą badać zależności składniowe, inne koreferencję, a jeszcze inne bliskość pozycyjną.

Sieć feed-forward

Po zastosowaniu mechanizmu uwagi reprezentacja każdego tokena przechodzi przez funkcję FFN dostosowaną do poszczególnej pozycji:

FFN(x) = max(0, xW₁ + b₁)W₂ + b₂

Współczesne architektury takie jak Llama i Mistral używają zamiast tego SwiGLU:

FFN(x) = (SiLU(xW_gate) ⊙ xW_up) · W_down

Większość zapamiętanej wiedzy modelu znajduje się w FFN. Działa on jak magazyn typu klucz-wartość oparty na powiązaniach faktograficznych. Mechanizm uwagi przekazuje informacje pomiędzy tokenami, a FFN wyszukuje przechowywane fakty i wzorce. W dużych modelach FFN ma szerokość około czterokrotnie większą niż d_model. Llama-3 70B ma d_model = 8192, a rozmiar pośredni wynosi 28672 – to właśnie tam znajduje się większość parametrów.

Dlaczego modele potrafią pisać kod

Kod to sekwencja tokenów, podobnie jak język naturalny. Dla modelu funkcja w Pythonie nie jest niczym wyjątkowym – to po prostu ciąg tokenów takich jak def, sort, _list, (, arr, ), : i tak dalej.

Poprawny pod względem składniowy wynik jest wyłącznie konsekwencją sposobu szkolenia modelu. Rozważmy następujące prawdopodobieństwa warunkowe:

P("(" | ..., "def", " quicksort") ≈ 1.0
P(":" | ..., "def", " quicksort", "(", "arr", ")") ≈ 1.0
P("return" | context inside function body) >> P("import" | same context)

Są one ostro wypukłe, ponieważ model miał dostęp do milionów plików w języku Python, które wszystkie przestrzegają tych samych zasad. Gramatyka Pythona nie jest nigdzie w sieci zakodowana sztywno – powstaje na podstawie statystyk z korpusu.

To również wyjaśnia, dlaczego modele czasami tworzą kod, który jest doskonale sformatowany, ale logicznie błędny. Maximizują one prawdopodobieństwo następnego tokena biorąc pod uwagę rozkład treningowy. Nie uruchamiają kodu i go nie sprawdzają.

Funkcja sortowania, krok po kroku

Załóżmy, że prompt brzmi „Napisz funkcję w Pythonie do sortowania listy”. Model traktuje to jako sekwencję tokenów i dodaje wynik po jednym tokenie. Mniej więcej tak:

  • Krok 1, po promptzie: def przy około 0,91.
  • Krok 2, po def: sort przy około 0,78.
  • Krok 3: _list lub ( przy około 0,65.
  • Krok 4, po sort(: arr lub lst około 0,71.
  • Kroki 5 do 7: ), następnie :, potem nowa linia – każdy z tych elementów około 0,99.
  • Krok 8: odstęp czterech przestrzeni około 0,97.

W krokach 5 do 8 model osiąga pewność co do tokenów składniowych. To nie jest rozumienie języka Python. W rzeczywistości mamy do czynienia z prostym uzupełnianiem wzorców, nabytym na podstawie korpusu, w którym każda definicja funkcji wygląda tak samo.

Prawdziwe wybory następują wcześniej, w krokach 1 do 4, gdy wybierane są nazwa funkcji, nazwy parametrów oraz ogólna struktura. Te wybory są kształtowane przez treść zapytania poprzez mechanizm uwagi. Token sort w zapytaniu ma wysoką wagę uwagi podczas generowania nazwy funkcji.

Temperatura przy generowaniu kodu

Copilot i podobne narzędzia zazwyczaj działają przy niskiej temperaturze, około 0,1 do 0,3, przy przetwarzaniu kodu:

  • Tokeny składniowe muszą być niemal deterministyczne.
  • Często istnieje tylko jedna poprawna implementacja.
  • Stworzone na poczekaniu API stanowią poważny problem przy wysokiej temperaturze.

W przypadku elementów kreatywnych, takich jak nazwy zmiennych, dokumentacja i komentarze, nieco wyższa temperatura jest do przyjęcia. Niektóre narzędzia wykorzystują również beam search, który jednocześnie utrzymuje aktywne najlepsze sekwencje typu top-k i na końcu wybiera tę o najwyższej łącznej prawdopodobieństwie.

Dlaczego większe modele zachowują się inaczej

Model 7B i model 70B mają tę samą architekturę. Różnica leży w skali: więcej warstw, większa wartość d_model oraz więcej głow uwagi. Mimo to model większy wykazuje jakościowe ulepszenia, w tym możliwość rozumowania wieloetapowego, tworzenia bardziej poprawnego kodu oraz nawiązywania uczenia się w kontekście.

W skali dużych danych model uczy się czegoś więcej niż tylko statystyk tokenów na powierzchni. Tworzy skompresowane reprezentacje głębszych wzorców, takich jak struktura algorytmiczna, zależności logiczne i ograniczenia typowe. Model o pojemności 70 miliardów parametrów miał już dość błędnego kodu wraz z nieudanymi testami, dzięki czemu domyślnie powiązał określone wzorce z poprawnością działania. To jest empiryczna podstawa hipotezy skalowania: zdolności rozwijają się zgodnie z gładką krzywą w zależności od zasobów obliczeniowych, danych i parametrów, a jakość kodu rośnie w taki sam sposób niezawodnie jak każda inna umiejętność.

Ograniczenia wynikające z projektu

Znajomość mechanizmu ujawnia pewne trudne ograniczenia:

  1. Wykonywanie. Wewnątrz modelu nie ma środowiska wykonawczego. To, co generuje, przypomina wynik poprawnego uruchomienia, ale w rzeczywistości nic nie zostało wykonywane.
  2. Gwarantowana poprawność. Przewidywanie maksymalizuje prawdopodobieństwo na podstawie rozkładu treningowego, a nie logiczną ważność.
  3. Prawdziwa nowość. Nie można wynaleźć algorytmu, który nie miałby precedensu statystycznego w danych treningowych. Model może łączyć i dostosowywać, ale nie może tworzyć czegoś nowego.
  4. Dokładne odzyskiwanie informacji. Nie ma tabeli wyszukiwania. Cała pamięć jest rozproszona w wagach, więc API, sygnatury funkcji i wersje bibliotek mogą czasami być błędnie interpretowane.

Żaden z tych elementów nie jest błędem wymagającym naprawy. Wynikają one bezpośrednio z architektury, a ich znajomość sprawia, że każdy staje się lepszym użytkownikiem asystentów programistycznych.

Krótki przegląd całości

  • Tokener to jednostka podsłowa, odpowiadająca mniej więcej 0,75 angielskiego słowa.
  • Przewidywanie polega na zastosowaniu funkcji softmax do słownika na każdym kroku.
  • Samoodniesienie umożliwia każdemu tokenowi reagowanie na wszystkie wcześniejsze tokeny poprzez skalowane iloczyny zapytania i klucza.
  • Uwaga wielogłowa uruchamia kilka takich mechanizmów równolegle, aby uchwycić różne relacje.
  • FFN pełni rolę pamięci typu klucz-wartość przechowującej faktyczne powiązania.
  • Generowanie kodu działa podobnie jak generowanie tekstu, przy czym składnia wynika ze statystyk treningowych.
  • Temperatura określa stopień ostrości rozkładu próbkowania.
  • Trudne ograniczenia to brak możliwości wykonania, brak gwarantowanej poprawności oraz brak prawdziwej nowości.

Wszystko w tym mechanizmie, od uwagi i FFN po reszty oraz normę warstwy, służy jednemu celowi: jak najdokładniejszemu określeniu warunkowej prawdopodobieństwa następnego tokena, biorąc pod uwagę wszystkie wcześniejsze tokeny. Reszta jest efektem ubocznym.

Literatura pokrewna

  • Notatki praktyczne: Jak naprawdę działają LLM: tokeny, transformery i następny token — Szczegółowy przewodnik po Notatkach praktycznych: Jak naprawdę działają LLM: tokeny, transformery i następny token: kontrakty, sprawdzenia oraz miejsca na kod do wstawienia dla zespołów wdrażających ten wzorzec.
  • Pięćdziesiąt lat badań nad wyszukiwaniem ukrytych w Twoim pipeline RAG — Proces dzielenia na fragmenty w RAG, wyszukiwanie hybrydowe, HyDE, ponowna sortowanie oraz metody oceny – wszystko to ma dziesięciolecia starszych odpowiedników w dziedzinie wyszukiwania informacji. Przegląd badań i ich ograniczeń.
  • Praktyczne notatki: ZQ Intelligence: Orchestrator-Specialist Agents dla finansów — Szczegółowy przewodnik po Praktycznych notatkach: ZQ Intelligence: Orchestrator-Specialist Agents dla finansów: umowy, sprawdzenia oraz miejsca na kod do wstawienia dla zespołów wdrażających ten wzorzec.
  • Dlaczego języki indyjskie kosztują więcej tokenów w LLM i jak poprawić tokenizatory — Ta sama zdanie zajmuje 5 tokenów w języku angielskim i 34 w języku tamilskim. Zobacz, jak dane treningowe BPE powodują tę różnicę, jakie są rozwiązania w postaci ponownego zrównoważenia i zmiany wielkości słownika oraz jak działa rozszerzanie słownika.