- Je berekent schaal, gehele codes en reconstructiefouten voor een gewichtsmatrix.
- Je maakt onderscheid tussen minder precisie, fysieke opslag en gemeten modelkwaliteit.
Je taalmodel past nét niet in het geheugen. Kun je ieder gewicht met minder bits benaderen zonder te veel voorspelling te verliezen? Kwantisatie (quantization) vervangt waarden door codes op een kleiner rooster.
Denk aan een meetlat met minder streepjes. Waarden tussen twee streepjes moeten afronden; één grote uitschieter kan de afstand tussen alle streepjes vergroten. Of de benadering opslag of rekentijd bespaart, hangt daarnaast van het bestandsformaat en de uitvoerengine af.
1. Kies een schaal en rond af
Onze symmetrische onderwijsvariant gebruikt qmax=2^(bits−1)−1. Bij vier bits zijn de codes −7 tot en met 7 en nemen we schaal s=max(abs(W))/7. Code nul betekent precies nul; de reconstructie vermenigvuldigt de gehele code weer met s.
W is het oorspronkelijke gewicht, s de roosterafstand en q zijn gehele code; Ŵ is de benadering. Clip de code op het afgesproken bereik. np.rint rondt exacte halve waarden naar de dichtstbijzijnde even integer.
Neem gewichten [−1.5,−.5,.5,1.5,7]. Het maximum zeven geeft schaal één. De codes worden [−2,0,0,2,7]: ook negatieve halve waarden volgen de even-regel.
w = np.array([-1.5,-.5,.5,1.5,7.])
q,scale = quantize_symmetric(w,4)
print(q) # [-2 0 0 2 7]
print(scale) # 1.0
print(q.astype(float)*scale)
# [-2. 0. 0. 2. 7.]
Maak quantize_symmetric zonder de invoer te veranderen. Voor nulgewichten kiezen we schaal één; bij extreem kleine getallen voorkomen we schaalnul met de kleinste positieve float. Alleen eindige, niet-lege gewichten en exacte integerbits van twee tot acht zijn geldig.
Symmetrisch bereik −7 tot +7, 15 niveaus. Exacte halve codes ronden naar even.
| Gewicht | Code q | q × schaal | Fout |
|---|---|---|---|
| -2.00 | -2 | -2.000 | 0.000 |
| -0.50 | 0 | 0.000 | 0.500 |
| 0.00 | 0 | 0.000 | 0.000 |
| 0.50 | 0 | 0.000 | -0.500 |
| 1.20 | 1 | 1.000 | -0.200 |
| 7.00 | 7 | 7.000 | 0.000 |
Ideaal gepakt: 3 bytes aan codes. In onze NumPy-oefening: altijd 6 bytes (int8). Beide hebben daarnaast schaalmetadata nodig; onze float64-schaal gebruikt 8 bytes. De uitvoerkwaliteit wordt apart gemeten.
Verhoog het laatste gewicht in de visualisatie en bekijk hoe de andere reconstructies verschuiven. Meer bits geeft meer mogelijke codes, maar een groter maximum bepaalt nog steeds het rooster. De getoonde gewicht-MSE is een meting van dit voorbeeld, geen volledige modelbeoordeling.
012. Laat kolommen hun eigen rooster kiezen
In Y=XW+b hoort iedere kolom van W bij één outputkanaal. Met een aparte schaal per kolom kan een maximum tien in de ene kolom de kleine gewichten in een andere kolom met maximum .2 minder verstoren. Een nulkolom krijgt schaal één.
w = np.array([[.1,10.,0.],
[.2,-8.,0.],
[-.15,6.,0.]])
q,s = quantize_columns(w,4)
print(np.round(s,4))
# [.0286 1.4286 1.]
print(q[:,2]) # [0 0 0]
Maak quantize_columns met een schaalvector van vorm (Cout,). Die vector broadcast over de inputdimensie. De extra schalen kosten metadata; meet daarom zowel de reconstructie als de totale opslag.
Een fijnere kolomschaal garandeert niet dat ieder afzonderlijk gewicht of iedere voorspelling beter wordt. Afrondpunten kunnen verschuiven en fouten werken samen in een matrixproduct. Vergelijk de concrete uitkomsten in plaats van alleen het aantal roosterpunten.
023. Meet wat de laag daadwerkelijk uitvoert
Neem W=[.1,1] als één outputkolom en een tweebitrooster. Met schaal één wordt .1 nul, terwijl één exact blijft. Voor input [10,0] versterkt het matrixproduct de kleine gewichtsfout .1 tot uitvoerfout één.
w = np.array([[.1],[1.]])
x = np.array([[10.,0.]])
bias = np.array([.3])
q,s = quantize_columns(w,2)
reference = x@w+bias
approx = quantized_linear(x,q,s,bias)
print(reference,approx)
# [[1.3]] [[.3]]
print(output_errors(reference,approx))
# (1.0,1.0)
Maak quantized_linear door eerst naar float te dequantiseren en daarna XŴ+b te berekenen. output_errors retourneert de gemiddelde absolute fout en de grootste absolute fout over gelijke, eindige, niet-lege arrays. Activaties en bias blijven in deze oefening float.
Onze codes staan altijd in int8. Vijf vierbitcodes gebruiken daarom hier vijf bytes, plus acht bytes voor één float64-schaal; ideaal gepakte codes zouden drie bytes gebruiken. Deze simulatie levert geen snellere integerkernel en bewijst geen onveranderde taalmodelkwaliteit.
03Wat neem je mee?
- Schaal en afrondregel bepalen de numerieke benadering.
- Per-kolomschalen beperken de invloed van andere outputkanalen.
- Meet uitvoerfout, echte opslag en modelkwaliteit afzonderlijk.
Hierna kijk je hoe pretraining en aanpassen de modelgewichten zelf veranderen.