7.7. Normalizálás

A ml.Model.predict() egy listát vesz át bemenetekből, mert egyes hálózatoknak több bemeneti tenzoruk van, de a listának nincs módja arra, hogy bemenetenkénti argumentumokat inline módon hordozzon – nincs kulcsszó-hely arra, hogy „vágd ezt a bemenetet (x, y, w, h) méretre, de a többi bemenetet hagyd békén”. A ml.preprocessing.Normalization az a burkoló, amely betölti ezt a rést. Egy Normalization példány egyetlen bemenet paramétereit tartja; a szkript a becsomagolt bemenetet a predict listában adja át, valahányszor az alapértelmezettektől eltérő valamire van szüksége.

A leggyakoribb ok, amiért hozzá nyúlunk, az, hogy a rögzített képkocka egy adott területét vágjuk a hálózatba az egész kép helyett.

7.7.1. Paraméterek

Normalization(scale=(0.0, 1.0),
              mean=(0.0, 0.0, 0.0),
              stdev=(1.0, 1.0, 1.0),
              roi=None)
  • roi(x, y, w, h) téglalap a forrásképkockában, amelyet az átméretezés előtt vágunk. Alapértelmezetten az egész képkocka. A Normalization legtöbb felhasználása csak ezt a paramétert állítja be.

  • scale – az a (min, max) tartomány, amelyet a lebegőpontos bemeneti tenzorok a normalizálás után várnak. A 0..255 képponttartomány lineárisan képeződik le ebbe a tartományba. Gyakori értékek a (0.0, 1.0) a ReLU-ra tanított hálózatoknál és a (-1.0, 1.0) a szimmetrikusan normalizált hálózatoknál.

  • mean – csatornánkénti (R, G, B) átlag, amelyet a skálázás után kivonunk a képből. Megfelel azoknak a csatornastatisztikáknak, amelyek ellenében a hálózatot tanították – a (0.485, 0.456, 0.406) az ImageNetből származó hálózatoknál a kanonikus példa. A szürkeárnyalatos hálózatok az átlagot egyetlen luma-értékre redukálják a szokásos 0.299*R + 0.587*G + 0.114*B képlettel.

  • stdev – csatornánkénti (R, G, B) szórás, amellyel a képet elosztjuk az átlag kivonása után, ismét megfelelve a hálózat tanítási statisztikáinak. A szürkeárnyalatos hálózatoknál ugyanúgy luma-értékre redukálva.

7.7.2. Mikor számítanak a paraméterek

A scale, a mean és a stdev figyelmen kívül marad, amikor a hálózat input_dtype típusa int8 vagy uint8. Az egész számos bemenetű hálózatoknál a vágott kép bájtjai közvetlenül a tenzorba íródnak, és a hálózat saját input_scale és input_zero_point értékei kezelik az egész-valós átváltást. A három paraméter csak akkor számít, ha a hálózat lebegőpontos bemenetet vár.

A roi minden esetben beolvasásra kerül – ez vezérli, hogy a forrásképkocka melyik része jut el a hálózatba, függetlenül a bemeneti dtype-tól.

7.7.3. ROI és átméretezés

A ROI bilineárisan skálázódik a forrásdimenzióiról a hálózat bemeneti dimenzióira. A kép a célterületen középre kerül, és a skálázás kitölti a célt – nem őrzi meg a képarányt. Egy négyzetes hálózatbemenetbe betáplált, nem négyzetes ROI vízszintesen vagy függőlegesen megnyújtva jön ki.

Hogy a nyújtás számít-e, az a hálózattól függ. Az arcészlelési és landmark-modellek, mint a MediaPipe család (BlazeFace, FaceLandmarks, HandLandmarks, MoveNet), négyzetes vágatokon lettek tanítva, és gyorsan romlanak, ha a bemeneti képarány eltér; ezeknél az alkalmazásnak négyzetes ROI-t kell adnia – vagy egy négyzetes framesize-on való rögzítéssel a window() segítségével, vagy a roi= paraméterrel való vágással. A YOLO-családú objektumészlelőket jellemzően véletlenszerű nyújtásokat tartalmazó augmentációval tanítják, és különösebb pontosságvesztés nélkül fogadnak el nem négyzetes ROI-kat; a teljes rögzített képkocka közvetlen betáplálása általában megfelelő.

Amikor a hálózat bemeneti dimenziói pontosan megegyeznek a ROI-val, a skálázás másolássá egyszerűsödik, ami a legolcsóbb eset.

7.7.4. Az alapértelmezett felülírása

A predict() minden image.Image bemenetet automatikusan Normalization() objektummal csomagol be – a fenti alapértelmezett paraméterekkel. A kamerával szállított legtöbb modellt olyan képponttartományok ellenében tanították, amelyeket az alapértelmezések már lefednek, így a gyakori eset az, hogy a képet közvetlenül adjuk át:

result = model.predict([img])

Egyéni ROI használatához – a leggyakoribb felülíráshoz – építsünk egy Normalization objektumot beállított ROI-val, és kössük hozzá a képet:

from ml.preprocessing import Normalization

norm = Normalization(roi=(80, 60, 160, 120))
result = model.predict([norm(img)])

Egy hálózat tanításkori csatornastatisztikáinak illesztéséhez állítsuk be a lebegőpontos paramétereket:

norm = Normalization(scale=(0.0, 1.0),
                     mean=(0.485, 0.456, 0.406),
                     stdev=(0.229, 0.224, 0.225))

result = model.predict([norm(img)])

A Normalization példány meghívása a képen egy új, lekötött példányt ad vissza, amelyből az engine kitölti a tenzort. A lekötött példányt fogadja el a predict a nyers kép helyett, és mivel ez egy bemenetenkénti objektum, egy többbemenetű hálózat különböző ROI-jú képeket keverhet ugyanabban a predict listában.

Azoknál a hálózatoknál, amelyek olyan bemeneteket várnak, amelyeket az alkalmazás már tenzorformában előállított – egy puffer egy perifériáról, egy másik folyamat által kiszámított ndarray, nem kép típusú numerikus adat –, teljesen hagyjuk ki a Normalization használatát, és adjuk át az ndarray-t vagy egy azt előállító meghívható objektumot. A predict() ezeket becsomagolás nélkül továbbítja az engine-nek.