A II. ellenőrző pont után egy modellező öt rendelésre hibajelentést készített. A megfigyelési egység most egy rendelés. A bemenet egy ötelemű hibavektor:
ahol minden érték mértékegysége nap, és : a tényleges és az előrejelzett késés különbsége. A cél az átlagos négyzetes hiba (mean squared error, MSE) kiszámítása kézzel, Python-ciklussal és NumPy-val. Az ellenőrzési feltétel: a három út azonos eredményt ad, és az eredmény mértékegysége , nem .
Mielőtt a levezetést elolvassa, adjon előzetes becslést. Az abszolút hibák átlaga nap, ezért a négyzetes hiba 3–4 közé eshet; a pontos kézi összeg hamarosan következik.
Az alábbi öt szám pedagógiai példa, nem a Northstar-projekt tényleges modelljének mért kimenete. A számítások viszont teljes egészében ellenőrizhetők.
Az alfejezet csak annyi matematikát tételez fel, amennyit a ndarray, shape, axis és dtype bevezetett: egy vektor itt rendezett számlista, elemei koordináták, a Python-lista eleme a NumPy-tömb egy dimenziójának felel meg. Az összeadás, szorzás és hatványozás alapművelet.
A típusú hosszú összegek helyett a matematika egy tömör jelet használ. A
jelentése: „az értékek összege”. A jelölések:
Az alsó határ , a felső határ . A Python-listák viszont -tól indexelnek, ezért a fordításnál ezt a különbséget mindig jelölni kell:
errors = [2.0, -1.0, 3.0, -2.0, 0.0] # nap
n = len(errors)
s_math = 0.0
for i in range(1, n + 1): # matematikai index: 1, 2, ..., n
s_math += errors[i - 1] # Python-index: 0, 1, ..., n-1Ugyanez a beépített sum függvénnyel:
s_math2 = sum(errors)Az öt hibára nap. Figyeljen arra, hogy a szumma maga lehet kicsi, miközben a tagok abszolút értéke nagy: a pozitív és negatív hibák kioltják egymást. Ezért lesz a modellértékelésben fontos a négyzetre emelés.
Kézzel ellenőrzött lépések:
| (nap) | () | |
|---|---|---|
| 1 | 2.0 | 4.0 |
| 2 | -1.0 | 1.0 |
| 3 | 3.0 | 9.0 |
| 4 | -2.0 | 4.0 |
| 5 | 0.0 | 0.0 |
A négyzetek összege . Az átlagos négyzetes hiba képlete tehát
ahol , így .
Egy fontos zárójel-szabály már itt látszik: a négyzetek összege, nem az összeg négyzete. Az összeg itt , annak négyzete ; a négyzetek összege viszont . A képletbe írt hatvány a tagokra, nem az egész szummára vonatkozik.
A matematikai függvény (function) olyan szabály, amely egy bemenethez pontosan egy kimenetet rendel. A függvény az hibához a négyzetes hibát rendeli. Itt:
A paraméter (parameter) olyan mennyiség, amelyet egy adott számításon belül rögzítünk, de egy másik számításnál megváltoztathatunk. A Northstar-adatokból ismert tételszintű pénzösszeg jó példa:
ahol a darabszám, az egységár centben rögzített paramétere, az eredmény pedig a tétel értéke centben. A pontosvesszős írásmód jelzi, hogy a változó, pedig paraméter. Ugyanez Pythonban:
def line_total(quantity: int, unit_price_minor: int) -> int:
return unit_price_minor * quantityHa a termék ára
cent, akkor
cent. A függvény képlete ugyanaz marad, a paraméter értéke azonban
termékenként cserélhető. A data scientist akkor dolgozik pontosan, ha
meg tudja mondani, melyik szimbólum változó, melyik paraméter, és milyen
tartományból vehet fel értéket. A teljes tételösszeghez az II. ellenőrző pont: adatból ellenőrzött
feature-tábla szerződése még levonja a tételszintű kedvezményt:
line_total_minor = quantity * unit_price_minor - discount_minor.
A tartomány nem üres formalitás. Az MSE képlet -ra nem értelmezett, mert nullával osztana. A Python-függvénynek ezt explicit hibaüzenettel kell jeleznie:
def mse(errors: list[float]) -> float:
if not errors:
raise ValueError("n must be at least 1")
sq_sum = 0.0
for e in errors:
sq_sum += e * e
return sq_sum / len(errors)A if not errors ellenőrzés a tartományi feltétel kódba
írt változata.
A képlet megértésének része, hogy minden tag egységét nyomon követjük. A példa hibavektorában:
errors: Python-lista, hossza
,
elemei float típusúak, mértékegysége nap;errors_np NumPy-alakja (5,),
dtype=float64, tehát egy darab ötelemű tengely;e_i * e_i mértékegysége
;sq_sum mértékegysége
;1 / n dimenziótlan, tehát az MSE mértékegysége
;A mértékegység-ellenőrzés megelőzi a leggyakoribb félreértést: a nem azt jelenti, hogy a modell átlagosan nappal téved. Az átlagos abszolút hiba itt nap, az RMSE pedig nap. A az MSE, tehát négyzetes skálán összegzi a hibát.
Az ábra a négyzetre emelést mutatja a példa értékein. A negatív előjel elveszik, ezért a és hiba ugyanakkora, négyzetes büntetést ad:
Ugyanezt az ábrát interaktív notebookban egy Matplotlib
scatter hívással lehetne megrajzolni; itt a könyv beépített
chart-formátuma adja a pontos, ellenőrizhető pontokat.
A teljes minimális munkapélda Python-fájlként futtatható. A kódot a
projektgyökérben létrehozott mse_demo.py névvel mentse,
majd futtassa Linux/macOS shellben python mse_demo.py,
Windows PowerShellben pedig py mse_demo.py paranccsal. A
kód nem használ véletlenszámot, ezért seed nem kell; Python 3.12-vel és
a kezdőlabor NumPy-verziójával futtatható, APIszempontból nem
verzióérzékeny:
import math
import numpy as np
errors = [2.0, -1.0, 3.0, -2.0, 0.0] # nap
n = len(errors)
# 1) Ciklusos fordítás: a matematikai i = 1..n index Pythonban 0..n-1.
sq_sum_loop = 0.0
for i in range(n):
e_i = errors[i]
sq_sum_loop += e_i * e_i
mse_loop = sq_sum_loop / n # nap^2
rmse_loop = math.sqrt(mse_loop) # nap
# 2) NumPy vektorizált alak
errors_np = np.array(errors, dtype=float)
mse_numpy = np.mean(errors_np ** 2)
print("n =", n)
print("sq_sum_loop =", sq_sum_loop)
print("mse_loop =", mse_loop)
print("mse_numpy =", mse_numpy)
print("rmse_loop =", rmse_loop)
print("shape =", errors_np.shape, "dtype =", errors_np.dtype)Az alábbi kimenet elméletileg várt, kézzel levezetett érték; a kód teljes futtatását külön példaprojektben ellenőrizzük. A helyi futtatásnak -es tolerancián belül egyeznie kell:
n = 5
sq_sum_loop = 18.0
mse_loop = 3.6
mse_numpy = 3.6
rmse_loop = 1.8973665961010275
shape = (5,) dtype = float64
A sq_sum_loop = 18.0 pontosan egyezik a kézi
táblázattal. A mse_loop és mse_numpy egyezését
ne ==-vel ellenőrizzük bitmintára, hanem toleranciával:
abs(mse_loop - mse_numpy) < 1e-12. A 3.6
ugyanis kettes számrendszerben nem véges tört, ezért a lebegőpontos
ábrázolás a legközelebbi kettes értéket tárolja; numerikus
összehasonlításhoz tűrés kell.
Az eredmény három úton ellenőrzött:
float típusú
18.0 részösszeggel adja.errors_np ** 2 vektorművelettel elemenként
négyzetre emel, majd a mean a
normalizálást végzi.Az ellenőrző invariantok: az elemszám
megegyezik az errors_np.shape[0] értékkel; az
errors_np.dtype numerikus, float64; a
négyzetes hiba mértékegysége
,
az RMSE mértékegysége nap. A hibaösszeg önmaga nem elég: a
csak a
lépéssel válik átlagos négyzetes hibává. Ha a cél az eredeti hibaskálára
való visszatérés, akkor az RMSE-t kell közölni, nem az MSE-t.
A kapott ezen az öt rendelésen egy leíró szám, nem statisztikai becslés bizonytalansággal. Azt állítja, hogy ezen az öt megfigyelésen a négyzetes hibák átlaga ; nem állítja, hogy egy jövőbeli rendelés hibája pontosan ekkora lesz.
Értelmezés. Egy kolléga azt írja a jelentésbe:
„A modell átlagosan 3.6 nappal téved.” A hibavektor továbbra is
[2.0, -1.0, 3.0, -2.0, 0.0] nap. Mi a hiba a mondatban?
Adja meg a helyes értelmezést, és számítsa ki az átlagos abszolút hibát,
valamint az RMSE-t üzenet-ellenőrzésként.
Megvalósítás. Írjon mse(errors)
nevű Python-függvényt, amely üres listánál ValueError
kivételt dob, egyébként ciklussal kiszámítja az MSE-t. Ellenőrizze az
alábbi inputra: [2.0, -1.0, 3.0, -2.0, 0.0]. A megoldásban
adjon assert ellenőrzést is.
Hibadiagnózis. Az alábbi kód miért nem adja a
fenti 3.6 eredményt?
errors = [2.0, -1.0, 3.0, -2.0, 0.0]
sq_sum = 0.0
for i in range(1, 6):
sq_sum += errors[i] * errors[i]
mse = sq_sum / 5Mi a pontos hibaüzenet vagy hibás érték oka, és hogyan kell javítani a ciklushatárt?
[48.0, -24.0, 72.0, -48.0, 0.0] óra. Számítsa ki az MSE-t
órában. Ellenőrizze, hogy az eredmény a napban kapott
érték
átváltásával egyezik. Miért hibás ilyenkor a nyers 3.6
érték továbbítása mértékegység nélkül?A kolléga a mértékegységet napként olvasta. A helyes állítás: az MSE , ami négyzetes skálán bünteti a hibát. Az átlagos abszolút hiba (mean absolute error, MAE) nap; az RMSE nap. Ellenőrzés: a MAE és az RMSE egyaránt nap mértékegységű, az MSE pedig , így a három szám nem felcserélhető.
Lehetséges megoldás:
def mse(errors: list[float]) -> float:
if not errors:
raise ValueError("n must be at least 1")
sq_sum = 0.0
for e in errors:
sq_sum += e * e
return sq_sum / len(errors)
expected = mse([2.0, -1.0, 3.0, -2.0, 0.0])
assert abs(expected - 3.6) < 1e-12Az üres lista az
tiltott tartomány, ezért nem hibaüzenet nélküli
ZeroDivisionError-ba kell futnia, hanem érthető
ValueError kell. Az assert a
toleranciás ellenőrzést végzi.
range(1, 6) először kihagyja a
2.0 elemet, majd az i = 5 értéknél
errors[5] hozzáféréssel IndexError hibát vált
ki. A javított ciklushatár range(len(errors)), vagy
közvetlenül az elemeken lehet iterálni:sq_sum = 0.0
for e in errors:
sq_sum += e * e
mse = sq_sum / len(errors)Ellenőrző invariant: a sq_sum összegnek
-t
kell adnia, és len(errors) értéke
.
3.6 nem hordozza az egységet, és ha valaki napként
értelmezi, miközben a tömb órában van, hibás következtetést kap. Az RMSE
ellenőrzése:
óra, ami
nap, tehát visszaadja az eredeti RMSE-t.„A képletet beírtam a kódba, tehát jó” — ez akkor hibás, ha a tagok egysége és a műveletek dimenziója tisztázatlan. Tipikus ellenpélda a szumma és a hatvány sorrendjének felcserélése:
# Hibás: ez az összeg négyzete ötödökkel, nem az átlagos négyzetes hiba.
s = sum([2.0, -1.0, 3.0, -2.0, 0.0])
mse_wrong = (s ** 2) / 5 # 0.8A helyes képlet előbb elemenként négyzetre emel:
sum(e*e for e in errors) / 5 = 3.6. A
számértéke is eltér, és a mértékegysége is baj: az összeg négyzete
ugyan, de nem az egyedi hibák négyzetes büntetését átlagolja. Hasonló
csapda az II. ellenőrző pont: adatból ellenőrzött
feature-tábla kontrollösszegeinél: a basket_minor
szumma centben adódik, nem euróban; a cent/euró skálafaktor nélkül a
kontrollérték üres szám.
Visszautalás: a ndarray, shape, axis és dtype
alfejezetben bevezetett shape és dtype itt az
ellenőrzés eszköze lett. A (5,) alakból látszik, hogy a
bemenet egyetlen ötelemű tengely; az axis nélküli
mean ezt az egész tengelyt redukálja skalárrá. A
matematikai szumma tehát a NumPy redukció közvetlen rokona.
Önellenőrzés: el tudja-e mondani, mit jelent a
alsó és felső indexe? Tudja-e, miért range(len(errors)) a
Python-megfelelő? Meg tudja-e nevezni a
mértékegységét, és azt, hogy miért nem „3.6 nap”? Ha mind a háromra van
egy-egy pontos mondata, a jelölésből végrehajtható számítás lépése
működik.
A tanítóhalmazon számított MSE önmagában nem torzítatlan becslése az új adatokon várható modellhibának. Mindig jelöljük, hogy a hibavektor a tanító-, validációs vagy tesztmintán keletkezett.
A matematikai jelölés nem dísz, hanem futtatható utasítás. A szumma alsó és felső indexe a ciklushatárt adja; a hívott függvény a képlet; a paraméter a rögzített szám. A tartomány és a mértékegység nélkül a szám félrevezető: az ötértékű hibavektor MSE-je , RMSE-je nap. A kézi, ciklusos és NumPy-útvonal azonos eredményt adott, amit toleranciás invariant ellenőriz. A szumma négyzetének és a négyzetek szummájának felcserélése a tipikus jelölési hiba. A következő alfejezet ezt a függvényfogalmat az exponenciális és logaritmikus skálákra alkalmazza.