Mariusz Tober

Kwantyzacja bez złudzeń: czego nie pokazuje trafność

4 min czytania#kwantyzacja#ewaluacja#llm

Kiedy porównuję kwantyzacje lokalnego modelu językowego, najprościej jest zadać mu kilkadziesiąt pytań i policzyć poprawne odpowiedzi. Problem w tym, że taki test może pokazać coś dokładnie odwrotnego niż rzeczywistość.

Przekonałem się o tym, testując Qwen3.5-2B na pytaniach matematycznych z MMLU-Pro. Na próbie zaledwie 25 pytań czterobitowa kwantyzacja Q4_K_M uzyskała lepszy wynik niż model BF16, czyli wersja bez kwantyzacji.

Czy mocniejsze ograniczenie precyzji wag poprawiło model? Nie. Przy tak małej próbie pojedyncza odpowiedź mocno zmienia wynik, a przewaga mieściła się w granicach szumu statystycznego.

Podobny problem zauważyłem przy testowaniu budżetu myślenia. Mały Qwen3-0.6B początkowo poprawiał wynik, gdy pozwalałem mu dłużej rozumować. Jednak po całkowitym zniesieniu limitu trafność wracała do punktu wyjścia. Model zużywał znacznie więcej tokenów, a część odpowiedzi była urywana.

Próbowałem też sprawdzić, czy niewielki zestaw pytań pozwala odtworzyć ranking większego benchmarku. Korelacje wyglądały obiecująco, ale obejmowały tylko kilka modeli. Przedziały ufności pokazały, że na tej podstawie nie można wyciągać wiarygodnych wniosków.

Potrzebowałem lepszego sposobu mierzenia różnic.

Zamiast pytać o odpowiedź, sprawdziłem prawdopodobieństwa

Przebudowałem potok ewaluacyjny tak, żeby nie generować długich odpowiedzi. Zamiast tego odczytywałem prawdopodobieństwa przypisane poszczególnym opcjom w pytaniach wielokrotnego wyboru.

To ważna różnica. Zwykły test widzi tylko wybraną literę. Jeżeli dwa modele wskazują odpowiedź B, dostają identyczny wynik, nawet gdy jeden przypisuje jej niemal całe prawdopodobieństwo, a drugi ledwo odróżnia ją od pozostałych.

Odczyt logprobów pozwala zobaczyć takie przesunięcia.

Uruchomiłem llama-server z ustalonymi parametrami. Wyłączyłem myślenie, ograniczyłem generację i zadbałem o jednakowy sposób próbkowania. Dla każdego pytania zapisywałem rozkład prawdopodobieństwa pierwszej litery odpowiedzi.

Następnie porównywałem modele parami, pytanie po pytaniu. Oprócz trafności liczyłem dywergencję Kullbacka-Leiblera (KL), czyli miarę różnicy między rozkładami prawdopodobieństwa. Sprawdzałem też, jak zmienia się prawdopodobieństwo poprawnej odpowiedzi.

Całość zautomatyzowałem, łącznie z generowaniem raportów z bazy danych.

Trafność pokazała remis. KL już nie

W większym eksperymencie porównałem Qwen3.5-4B w kwantyzacjach Q8_0 i Q4_K_M. Wykorzystałem 2000 pytań z MMLU-Pro i polskiego podzbioru Global-MMLU.

Test wykonałem na RTX 3060. Dzięki odczytywaniu logprobów zamiast generowania pełnych odpowiedzi cały eksperyment trwał kilka minut.

Wynik trafności był niemal identyczny. Q8_0 uzyskał niewielką przewagę, ale różnica nie była statystycznie istotna. Parowany test McNemara nie dawał podstaw do stwierdzenia, że jeden wariant częściej odpowiada poprawnie.

Gdybym zakończył analizę na tym etapie, napisałbym, że kwantyzacja do czterech bitów praktycznie niczego nie zmienia.

Jednak dywergencja KL pokazała inny obraz.

Między Q8_0 a Q4_K_M wyniosła 0,052 nata. Sam wynik niewiele mówi bez punktu odniesienia, dlatego wykonałem dodatkowy eksperyment: dwukrotnie uruchomiłem identyczny model Q8_0 i porównałem jego rozkłady.

Dywergencja między tymi przebiegami wyniosła zaledwie 0,0033 nata.

Różnica między kwantyzacjami była więc znacznie większa niż zmierzony szum środowiska. Przedziały ufności również potwierdzały wyraźną różnicę.

Co więcej, Q4_K_M przypisywał średnio nieco mniejsze prawdopodobieństwo poprawnej odpowiedzi.

To najważniejszy wynik eksperymentu: kwantyzacja może zmieniać zachowanie modelu, nawet gdy procent poprawnych odpowiedzi pozostaje praktycznie taki sam.

Nie oznacza to jeszcze, że potrafię przewidzieć pogorszenie jakości w każdej aplikacji. Wiem natomiast, że sam pomiar trafności nie wychwytuje wszystkich zmian.

Przy okazji znalazłem błędy w pomiarach

Budowa tego testu ujawniła kilka problemów, których wcześniej nie doceniałem.

Pierwszy dotyczył powtarzalności. Dwa uruchomienia identycznego modelu nie zawsze dawały tę samą odpowiedź. Przyczyną okazało się dynamiczne batchowanie w llama.cpp. Kiedy prawdopodobieństwa dwóch opcji były bardzo zbliżone, drobne różnice obliczeń potrafiły odwrócić ich kolejność.

Dlatego zacząłem osobno mierzyć szum identycznych przebiegów. Wciąż muszę sprawdzić, czy uruchamianie serwera z --parallel 1 obniży go do założonego poziomu.

Drugi problem dotyczył konfiguracji. llama.cpp i Ollama mają różne domyślne parametry próbkowania. Bez ich ujednolicenia łatwo porównywać konfiguracje zamiast modeli.

Trzeci błąd popełniłem przy zapisywaniu wyników. Początkowo nie przechowywałem pełnej konfiguracji przy pojedynczych odpowiedziach. Uniemożliwiło to późniejsze sparowanie części pomiarów. Poprawiłem to w kolejnych przebiegach.

Były też pułapki tokenizera. Polskie słowo „Odpowiedz” mogło zaczynać się od osobnego tokenu „O”, który mój kod błędnie interpretował jako literę wybranej odpowiedzi. Zaostrzyłem reguły ekstrakcji i przetestowałem implementację niezależnymi przypadkami. Weryfikacja wykryła kolejne błędy wymagające poprawek.

Najciekawsza pomyłka dotyczyła jednak samego kryterium oceny.

Założyłem, że odpowiedź wyznaczona z najbardziej prawdopodobnego tokenu powinna prawie zawsze zgadzać się z odpowiedzią wygenerowaną przez model. Wynik był znacznie niższy od oczekiwań.

Dopiero analiza pokazała, że przy losowym próbkowaniu takie kryterium nie miało sensu. Oczekiwana zgodność zależy od prawdopodobieństwa najlepszego tokenu. Zmierzone wartości były bliskie tej zależności.

Zamiast dopasowywać próg po fakcie, uznałem pierwotne kryterium za błędnie sformułowane.

Co z tego wynika?

Odczytywanie logprobów okazało się szybkim sposobem wykrywania zmian wprowadzanych przez kwantyzację. Nie potrzebuję długich generacji, żeby dostrzec przesunięcia w rozkładach prawdopodobieństwa.

Ale nie jest to pełny test jakości modelu.

Nadal nie wiem, jak zmierzona dywergencja przekłada się na otwarte zadania wymagające wieloetapowego rozumowania. Nie sprawdziłem też jeszcze wiarygodności szybkich podzbiorów benchmarków na odpowiednio dużej grupie modeli.

Na razie wyniosłem z tego eksperymentu trzy rzeczy: małe próby łatwo wprowadzają w błąd, sama trafność ukrywa część zmian, a bez pomiaru własnego szumu narzędzia nawet precyzyjnie wyglądające wyniki mogą być mylące.

Najważniejsze okazało się nie znalezienie kolejnej metryki, ale nauczenie się odróżniania rzeczywistej różnicy od błędu pomiaru.

← Wszystkie wpisy