Przejdź do głównej zawartości

Generative AI Design Patterns #1b: Style Transfer, Reverse Neutralization i Content Optimization, czyli styl bez gwarancji

· 15 min aby przeczytać
Przemysław Majdak
Full-Stack Developer, Automation Engineer & Web Security Specialist

Pozostałe trzy wzorce z rozdziału 2 „Generative AI Design Patterns" kontrolują styl tam, gdzie nie da się go zapisać gramatyką: Style Transfer uczy stylu na przykładach, Reverse Neutralization wytwarza przykłady, gdy ich nie ma, a Content Optimization w ogóle rezygnuje z opisu stylu i dostraja model pod ocenę ewaluatora. Wszystkie trzy dają prawdopodobieństwo, nie gwarancję, a w dwóch z nich o jakości decyduje sprawdzian, którego łatwo nie docenić. Sprawdziłem oba sprawdziany na lokalnych modelach. Sędzia-LLM zapytany „które ogłoszenie jest lepsze" wybierał dłuższą wersję w 15 na 16 porównań, także wtedy, gdy miała błędny fakt, a przy równej długości trafność nie miała dla niego znaczenia. Neutralizacja z Reverse Neutralization zmieniła sens w 10 z 12 wyjść, a automatyczna kontrola faktów oznaczyła jedno.

To trzeci wpis serii o książce Valliappy Lakshmanana i Hannesa Hapkego. Recenzja i mapa serii są we wpisie 0, dwa pierwsze wzorce rozdziału 2 — w poprzednim wpisie. Cytuję po rozdziale i numerze wzorca, bo PDF nie ma stron druku.

Teza​

Rozdział 2 układa pięć wzorców na jednej osi: gwarancja albo prawdopodobieństwo. Logits Masking i Grammar nie dopuszczają złego tokenu. Style Transfer, Reverse Neutralization i Content Optimization tylko podnoszą szansę, że styl się zgodzi. Autorzy piszą o tym wprost: nawet dostrojony model to „still only an implicit enforcement" (rozdz. 2, Pattern 3). Wybór między tymi dwiema grupami to odpowiedź na pytanie, czy niezgodność stylu jest błędem, czy kosztem.

Moja teza do tego wpisu: w drugiej grupie jakość systemu jest ograniczona jakością sprawdzianu, a nie techniki. W Reverse Neutralization sprawdzianem jest pytanie, czy forma neutralna zachowała treść. W Content Optimization jest nim ewaluator, który wybiera zwycięzcę pary. Kto dostraja model pod sprawdzian, ten optymalizuje także jego lukę.

Pattern 3: Style Transfer​

Problem. Styl jest domyślnie wypadkową treningu dostawcy. Autorzy zadają sześciu modelom to samo pytanie („What's a good side dish for pierogi? Answer in a single sentence.") i dostają sześć stylistycznie różnych odpowiedzi, choć każda ma jedno zdanie. Opis stylu w prompcie nazywają „extremely brittle" (rozdz. 2). Wzorzec jest przeznaczony na trzy warunki naraz: treść już jest, ale w złym stylu; styl jest niuansowy („I know it when I see it."); istnieją przykłady konwersji zrobione przez ludzi (rozdz. 2, Pattern 3).

Rozwiązanie. Dwa warianty. Few-shot: 1–10 par „przed–po" w prompcie. Fine-tuning: zwykle około stu par, czasem tysiące. Dostrojony model lepiej odwzorowuje złożone mapowania słownictwa i jest szybszy w inferencji, bo prompt nie niesie przykładów. Płaci się za to kuracją danych, ponownym treningiem przy każdej zmianie stylu, ostrożnym doborem learning rate przeciw katastroficznemu zapominaniu i utrzymaniem hostingu (rozdz. 2, Pattern 3).

Autorzy wymieniają trzy czynniki, które przy few-shot obniżają zgodność. Mniejszy model uogólnia gorzej. Przykłady zajmują okno kontekstu, a przy nadmiarze potrafią sobie przeczyć i „confuses the LLM". Dłuższy prompt to dłuższa latencja, więc na tej samej bazie in-context learning jest ostatecznie wolniejszy od modelu dostrojonego (rozdz. 2, Pattern 3).

Autorzy dają też przykład spoza tekstu, który dobrze pokazuje, na czym polega problem: Stable Diffusion z obrazem Friedricha Wędrowiec nad morzem mgły i mapą głębi jako obrazem sterującym. Klasyczny neural style transfer psuje układ przestrzenny, więc mapa głębi mówi modelowi, który aspekt oryginału ma przetrwać (rozdz. 2, Pattern 3). W tekście takiej mapy nie ma i to wraca przy następnym wzorcu.

Pattern 4: Reverse Neutralization​

Problem. Mam wyłącznie teksty w docelowym stylu i żadnych par. Przykład autorów: chcę napisać list do Lufthansy swoim stylem, ale takiego listu nigdy nie napisałem, mam tylko maile na inne tematy.

Rozwiązanie. Pary wytwarza się, puszczając przekształcenie w odwrotną stronę. Teksty w stylu docelowym model neutralizuje do formy generycznej, potem zamienia się wejścia z wyjściami i dostraja model na parach „neutralne → stylizowane". Inferencja to dwa wywołania: model ogólny pisze treść neutralnie (na dowolny temat), model dostrojony przebiera ją w styl. W przykładach autorów, prawniczym języku stanu Tamil Nadu i prywatnym stylu mailowym, wystarczyło około 200 par (rozdz. 2, Pattern 4).

Zastrzeżenia autorów są ciekawsze od samego wzorca. Forma neutralna powinna być powtarzalna, a nie jest: „neutralny" to definicja subiektywna, a modele mają własne wyobrażenie neutralności. Treść bywa nierozdzielna od stylu, więc neutralizacja potrafi zjeść informację. Autorzy nazywają to over-neutralization. Jako kontrolę proponują kosinusowe podobieństwo embeddingów oryginału i wersji neutralnej, ale sami zauważają, że najwyższe podobieństwo ma tekst w ogóle niezmieniony, a z takiego stylu się nie wyuczy (rozdz. 2, Pattern 4).

Pattern 5: Content Optimization​

Problem. Klasyczny test A/B wymaga hipotezy, które cechy stylu mają znaczenie. Bez niej zbiory są nieodróżnialne, test nie osiąga istotności, a gdyby nawet osiągnął, nie wiadomo, jak wynik wykorzystać (rozdz. 2, Pattern 5).

Rozwiązanie. Autorzy nazywają je „sheer jujitsu": zamiast szukać odpowiedzi na te trzy problemy, zmienia się pytanie. Z tego samego promptu generuje się parę treści, ewaluator wybiera zwycięzcę, z wyników powstaje zbiór prompt / chosen / rejected, a na nim robi się preference tuning przez DPO (DPOTrainer z biblioteki TRL). Wyniku nie używa się do poprawiania promptu — zmienia się model. Ewaluatorem mogą być ludzie, rubryka, LLM-as-judge albo pomiar w świecie (kliknięcia, czas rozwiązania zgłoszenia, to, czy kod się kompiluje) (rozdz. 2, Pattern 5).

Dwie rzeczy autorzy stawiają jawnie. Pierwsza: wzorzec „attempts to hack the model to create content the evaluation method will consider great", a „teaching to the test" daje dobre wyniki tylko wtedy, gdy test jest solidny i odwzorowuje rzeczywistość. Przykład luki podają sami: optymalizacja pod czas zaangażowania premiuje treść trudną do zrozumienia, bo czyta się ją dłużej. Remedium biorą od Martina Zinkevicha (Rules of Machine Learning): oddzielić cel od metryki, interpretować metrykę zawsze w kategoriach celu i dołożyć metrykę przeciwstawną, na przykład czas czytania razem z porzuceniami. Druga: in-distribution requirement. „No amount of preference tuning can teach an LLM new facts or new tokens". Generowana treść musi leżeć w zasięgu dostrajanego modelu, ale nie musi być dobra. Nietrenowany Qwen2 0.5B pisał słabe ogłoszenia — ale ogłoszenia, i to wystarczyło (rozdz. 2, Pattern 5).

Trening jest tani. DPO na stu przykładach zajmuje u autorów około trzech minut na maszynie z 8 vCPU i GPU L4. W innym miejscu książki pada „takes only seconds", więc liczba jest niespójna; w obu wersjach to jednak minuty albo mniej (rozdz. 2, Pattern 5). Autorzy wyciągają z tego wniosek, że cztery kroki da się zapętlić. Ja wyciągam inny: luka w ewaluatorze staje się cechą produktu szybciej, niż ktokolwiek ją zauważy.

Sprawdziłem oba sprawdziany na lokalnych modelach​

Środowisko jak w poprzednim wpisie: ThinkPad L16 Gen 2, sam CPU, llama.cpp b6153 (llama-server z pakietu Fedory, GPU ukryte przez HIP_VISIBLE_DEVICES=-1 --device none). Modele: Qwen3-Coder-30B-A3B-Instruct (UD-Q3_K_XL) i granite-4.0-h-tiny (Q4_K_M). Wszystkie liczby z tej sekcji pochodzą z moich uruchomień z 30 września 2026. Teksty testowe napisałem sam, po polsku. Skryptów nie publikuję, ale każda próba to jedno wywołanie /v1/chat/completions.

DPO nie trenowałem — na CPU i bez GPU to inny projekt. Mierzyłem to, co w obu wzorcach decyduje o wyniku, zanim trening się zacznie: sędziego (P5) i neutralizację (P4).

1. Sędzia nie widzi faktów, widzi długość i pozycję​

Osiem ogłoszeń drobnych (rower, kawalerka, korepetycje, oferta pracy…). Do każdego napisałem brief z faktami i cztery wersje:

  • zwięzła — wszystkie fakty z briefu, bez ozdobników;
  • rozdmuchana — wersja zwięzła plus dwa zdania waty bez żadnej informacji („Rower to nie tylko środek transportu, ale też styl życia");
  • ładna z błędem — napisana staranniej i dłuższa (średnio ok. 1,4 raza), z jednym faktem sprzecznym z briefem: 700 zł zamiast 900, „zwierzaki mile widziane" przy „bez zwierząt", „obudowa bez żadnych rys" przy rysie na klapie;
  • ładna poprawna — ta sama wersja z poprawionym jednym faktem. Ta sama długość (±15%), ten sam styl, różni się tylko prawdą.

Sędzia dostawał dwie wersje i miał odpowiedzieć jedną literą. Odpowiedź wymuszała gramatyka root ::= "A" | "B", temperatura 0. Każdą parę oceniał w obu kolejnościach, co daje 16 werdyktów na komórkę tabeli. Dwa warianty promptu: ogólny („Które z dwóch ogłoszeń jest lepsze?") i z briefem („Które ogłoszenie lepiej realizuje brief?", z briefem w prompcie). W tabeli jest liczba werdyktów dla wersji gorszej:

SędziaPromptładna z błędem vs zwięzłarozdmuchana vs zwięzłaładna z błędem vs ładna poprawna
Qwen 30Bogólny15 / 1615 / 168 / 16
Qwen 30Bz briefem2 / 169 / 160 / 16
graniteogólny8 / 1611 / 168 / 16
granitez briefem4 / 167 / 167 / 16

Pierwsza kolumna sama w sobie wygląda jak dowód, że sędzia woli ładny tekst z błędem. Trzecia kolumna pokazuje, że to nieprawda. Gdy długość i styl są te same, Qwen z promptem ogólnym wybiera wersję z błędem w 8 na 16 razy, czyli losowo. Nie mógł inaczej: bez briefu nie ma skąd wiedzieć, że rower kosztuje 900 zł, a nie 700. Wersja z błędem wygrywała w pierwszej kolumnie, bo była dłuższa i staranniej napisana. Druga kolumna potwierdza, że samą watą da się uzyskać ten sam wynik (15 na 16). To jest length bias z rozdziału o LLM-as-Judge (rozdz. 6, Pattern 17). Czy do tego dochodzi preferencja dla tekstu dobrze napisanego „even if it is inaccurate" — tego mój pomiar nie rozstrzyga.

Brief w prompcie zmienił wszystko po stronie faktów: 14 na 16 razy wygrała wersja poprawna przeciw zwięzłej i 16 na 16 w parze o tej samej długości. Długości brief już nie naprawił. 9 na 16 to rzut monetą, a w 3 z 8 par werdykt zmieniał się po zamianie kolejności, więc decydowała pozycja, a nie treść.

Granite jako sędzia jest przestrogą innego rodzaju. Na 96 werdyktów odpowiedział „B" 87 razy. W trzeciej kolumnie werdykt zmieniał się po zamianie kolejności w 15 z 16 par. Jego „wyniki" to w praktyce pomiar pozycji. Liczby 8/16 czy 7/16 wyglądają w tabeli niewinnie, ale nie znaczą nic, dopóki nie sprawdzi się obu kolejności.

Co by się stało, gdyby taki sędzia wybierał zwycięzców do DPO? Qwen z promptem ogólnym uczyłby model pisać dłużej i staranniej, a faktów nie pilnowałby wcale — każda para, w której dłuższa wersja przypadkiem przekręca fakt, nagradzałaby przekręcenie. Z briefem uczyłby już prawie tego, co trzeba, z jedną luką: długość nic nie kosztuje, więc wata nie miałaby powodu zniknąć. Granite uczyłby tego, co akurat stało na drugiej pozycji. To nie jest wada DPO. DPO zrobiłby dokładnie to, co mu kazano.

2. Neutralizacja zjada sens, nie liczby​

Cztery teksty w wyraźnym stylu: nieformalny mail zespołowy, pismo urzędowe o przetargu, reklama kapci, ironiczna wiadomość o awarii pipeline'u. Prompt: „Przepisz poniższy tekst neutralnym, rzeczowym stylem, jak w notatce służbowej. Zachowaj całą treść merytoryczną. Zwróć tylko przepisany tekst.". Qwen 30B, temperatura 0,7, trzy przebiegi na tekst — tak jak przy wytwarzaniu zbioru treningowego, gdzie neutralizuje się setki tekstów bez czytania każdego.

Najpierw automatyczna kontrola: do każdego tekstu wypisałem 5–6 faktów (godziny, kwoty, nazwy, daty, sygnatury) i sprawdzałem, czy są w wyjściu. Wynik: 11 z 12 wyjść przeszło. Brakowało tylko „trzeciego piętra" w jednym mailu (model napisał „piątro") — a to wyjście i tak było wadliwe z innego powodu, którego kontrola nie widziała.

Potem przeczytałem wszystkie 12 wyjść. Za zmianę sensu liczyłem każde wyjście, które dosłownie mówi co innego niż oryginał — także przez złe słowo. Takich wyjść było 10:

TekstOryginałPo neutralizacjiPrzebiegi z błędem
mail„sala Orion na trzecim, bez spinki"„sala Orion […], bez użycia spinki"3 / 3
ironia„Ale spokojnie, na pewno sam się naprawi."„Przyjęto decyzję, że pipeline samodzielnie się naprawi." / „Przeprowadzenie analizy i naprawy nie powinno wydłużyć się dłużej niż na kilka minut."3 / 3
pismo„postanowił przedłużyć termin składania ofert"„zaniechował przedłużenia terminu składania ofert"1 / 3
reklama„Koniec z zimnymi stopami!"„Zakończenie zimnych stopów!" (stopy metali, nie stopy)3 / 3

W drugim wyjściu z ironią pojawiło się też „który ma zostać odnowiony 12 września" zamiast „miał" — zaległy termin stał się przyszłym. W piśmie wszystkie liczby, daty i sygnatura przetrwały, a sens przedłużenia się odwrócił. W reklamie, poza „stopami", problem był odwrotny: neutralizacja nie zneutralizowała. Wykrzyknik i „bez dodatkowych pytań" zostały we wszystkich trzech przebiegach, więc takie pary uczyłyby model, że ton reklamy należy do formy neutralnej.

Dwie rzeczy są tu ważniejsze od liczby 10 na 12. Po pierwsze, ironia i idiom to dokładnie przypadek, w którym treść jest nierozdzielna od stylu. „Na pewno sam się naprawi" znaczy „nikt tego nie naprawi". Model, który neutralizuje dosłownie, wytwarza fałsz. Po drugie, forma neutralna nie była powtarzalna. Podobieństwo słownikowe między przebiegami tego samego tekstu (Jaccard na zbiorach słów) wynosiło od 0,52 do 0,86. A neutralne wersje były dłuższe od oryginałów w trzech tekstach na cztery (1,1–1,5 raza) — „rzeczowy styl" modelu to styl urzędowy, z „w dniu czwartku" i „wynosi".

W zbiorze treningowym Reverse Neutralization każdy z tych błędów staje się parą uczącą. Model dostrojony na „neutralne → stylizowane" uczy się wtedy, że stylizacja polega także na dopisaniu ironii tam, gdzie neutralny tekst mówi coś wprost. Kontrola faktów tego nie widzi. Proponowane przez autorów podobieństwo embeddingów raczej też nie, bo „zaniechował przedłużenia" i „postanowił przedłużyć" różnią się jednym zwrotem w tekście o tych samych datach i kwotach — tego jednak nie mierzyłem.

Zastrzeżenia do obu pomiarów: jeden model neutralizujący, dwa sędziowie, polski tekst, moje własne teksty i próby liczone w dziesiątkach. Błędy sensu klasyfikowałem sam, czytając wyjścia, według kryterium podanego wyżej; gdybym nie liczył „stopów", wyszłoby 7 na 12. To pokazuje mechanizm, a nie odsetek, którego należy się spodziewać u komercyjnego modelu w języku angielskim.

To samo w innych książkach​

Wzorzec 5 to prawo Goodharta z konkretnym mechanizmem przeniesienia: nie „ludzie zaczną grać pod metrykę", tylko „gradient policzy się wprost z metryki". Dwie inne książki pokazują to samo poza generatywną AI.

Philip A. Dursey w Red Teaming AI opisuje reward hacking w uczeniu ze wzmocnieniem: łódź w grze CoastRunners, zamiast kończyć wyścig, krąży po bonusach i zbiera punkty (s. 571–572). Pytanie, które każe zadawać red teamowi, pasuje do każdego ewaluatora z Pattern 5: „Could the agent maximize this reward without doing what we actually want?" (s. 579). Daje też sygnał, który da się obserwować w trakcie: „If you see reward shooting up while performance on the true objective stagnates or drops, you might be witnessing reward hacking" (s. 588–589). Przy pętli DPO oznacza to: mierz osobno wynik ewaluatora i wynik na zbiorze, którego ewaluator nie widział, i patrz, czy się rozjeżdżają.

François Chollet w Deep Learning with Python schodzi poziom niżej, do funkcji straty: „your network will take any shortcut it can to minimize the loss" (s. 90). W ostatnim rozdziale uogólnia to na shortcut rule: optymalizując jedną metrykę, osiągasz cel kosztem wszystkiego, czego metryka nie obejmuje (s. 450). Jego przykład to Netflix Prize: zwycięskiego systemu nigdy nie wdrożono, bo optymalizował wyłącznie trafność, kosztem kosztu inferencji i utrzymywalności (s. 451).

Rozwiązanie „dajmy człowieka jako ewaluatora" ma własną wadę, którą Lakshmanan i Hapke opisują dopiero w rozdziale 10: im lepszy system, tym bardziej ludzie przeglądają wyniki pobieżnie i przestają poprawiać (automation fatigue, rozdz. 10). Ewaluator ludzki też jest specyfikacją, tylko degraduje po cichu.

Kiedy nie używać — zestawienie​

WzorzecNie używaj, gdyZamiast tego
Style Transfer (few-shot)zgodność stylu jest wymagana, a nie pożądanaGrammar / Logits Masking (P1–P2)
Style Transfer (few-shot)przykładów potrzeba więcej niż kilka, zaczynają sobie przeczyćfine-tuning na ok. 100 parach
Style Transfer (fine-tuning)styl często się zmienia, a każda zmiana to nowy treningfew-shot — zmiana przykładów jest natychmiastowa
Reverse Neutralizationtreść niesie styl: ironia, idiom, ton urzędowy z mocą prawnąręcznie zebrane pary, choćby kilkadziesiąt
Reverse Neutralizationnie masz kontroli sensu formy neutralnej, tylko kontrolę faktówprzeczytaj próbkę par przed treningiem
Content Optimizationnie masz solidnego ewaluatoranajpierw ewaluator (rozdz. 6, Pattern 17), potem DPO
Content Optimizationtreść wymaga faktów, których model nie znaRAG (P6–P12) — DPO nie dodaje wiedzy

Autorzy mówią wprost, że krok ewaluacji jest najważniejszym krokiem Content Optimization, więc buduje się go jako pierwszy i ręcznie sprawdza, czy werdykty zgadzają się z intuicją (rozdz. 2, Pattern 5). Mój pomiar dopisuje do tego rzecz praktyczną: sprawdzaj sędziego na parach, w których wiesz, która wersja jest gorsza i dlaczego — z błędem faktu, z watą, w obu kolejnościach. Osiem takich par wystarczyło, żeby zobaczyć, co sędzia by wytrenował.

Ocena trwałości​

Moja ocena, nie autorów. Style Transfer jest trwały. Niuansowego stylu nie da się zapisać regułą niezależnie od wersji modelu, a przykład w prompcie jest najtańszym szczeblem drabiny kosztów. Datowane są liczby: ile przykładów mieści okno, ile par potrzebuje fine-tuning. Reverse Neutralization jest trwały jako technika wytwarzania danych, bo to back translation pod inną nazwą, ale nie odpowiada na żaden tryb zawodności modelu — rozwiązuje problem braku par. Content Optimization jest trwały, z ciężkim zastrzeżeniem: mechanizm (preferencje zamiast opisu) przetrwa każdą bibliotekę, ale przenosi całą specyfikację systemu do kodu ewaluatora. Kto go wdraża, powinien umieć odpowiedzieć na pytanie, co w funkcji nagrody da się udawać, nie dostarczając wartości.

Starsi krewni: back translation i test A/B​

Reverse Neutralization ma rodowód, który autorzy podają sami: back translation z tłumaczenia maszynowego (Edunov i in., 2018). Brak równoległego korpusu uzupełnia się tam, tłumacząc teksty docelowe maszynowo z powrotem na język źródłowy. Moim zdaniem to ten sam problem, który pokazał pomiar: model uczy się także błędów syntetycznej strony pary. Style Transfer wywodzi się z neural style transfer dla obrazów (Gatys, Ecker, Bethge, 2015), który rozdzielił treść od stylu w sieci konwolucyjnej. W tekście to rozdzielenie jest dużo mniej czyste, co pokazał pomiar z ironią.

Content Optimization to test A/B, w którym zrezygnowano z hipotezy i istotności, a wynik zamiast do raportu trafia do wag modelu. Zyskuje się szybkość, traci jedyną rzecz, którą test A/B dawał za darmo: człowieka, który patrzy na wynik i pyta, dlaczego wygrał.

Dalej w serii​

Następny wpis (2) otwiera część o RAG: Basic RAG, Semantic Indexing i Indexing at Scale z rozdziału 3 — w tym pytanie, kiedy RAG jest zbędny, bo dokument mieści się w oknie. Mapa całej serii jest we wpisie 0.