4. óra 3D objektumdetektálás pontfelhőkön és a gyakorlati feladat ismeretetése - CooperativeResearchPlatform/object_detection_for_aeb GitHub Wiki

Az előző órán képeken dolgoztunk: megismertük a konvolúciós neurális hálózatok backbone--neck--head felépítését, az objektumdetektálás osztályozási és lokalizációs részfeladatait, valamint egy gépi tanulási projekt célszerű szerkezetét. Most ugyanezt a gondolkodásmódot visszük át LiDAR-pontfelhőkre.

Az óra központi kérdése:

Hogyan alakítható egy rendezetlen, ritka 3D pontfelhő olyan reprezentációvá, amelyen a már ismert 2D CNN-ek hatékonyan használhatók, és hogyan lesz ebből CenterPoint 3D detektor?


1. Hogyan dolgozhat fel egy CNN pontfelhőt?

1.1 A LiDAR-pontfelhő mint adat

Egy LiDAR-mérés pontok halmaza:

$$\mathcal{P}=\{\mathbf{p}_i\}_{i=1}^{N}, \qquad \mathbf{p}_i=(x_i,y_i,z_i,r_i,\ldots),$$

ahol $(x_i,y_i,z_i)$ a pont helye a szenzor koordináta-rendszerében, $r_i$ például a visszaverődés intenzitása, a további jellemző pedig lehet időeltérés vagy radarnál Doppler-sebesség. A pontok száma $N$ mérésenként változhat.

A LiDAR jellemzően sűrűbb és pontosabb geometriai pontokat ad; az autóipari radar pontfelhője ritkább és zajosabb, viszont közvetlen relatív sebességmérést is tartalmazhat. A két szenzor nyers adata eltérő, de CNN szempontból ugyanaz az alapgond: egyik sem szabályos képrács.

Egy kép és egy pontfelhő között alapvető szerkezeti különbség van:

Kép Pontfelhő
Szabályos $H\times W$ rács Szabálytalan 3D mintavétel
Minden pixelnek rögzített szomszédai vannak A pontok szomszédságát külön kell definiálni
A tenzor mérete előre ismert A pontok száma változó
A pixelek sorrendje jelentést hordoz A pontok felsorolási sorrendje önkényes
Közel állandó mintavételi sűrűség Távolsággal gyorsan ritkuló mérés

Miért nem működik közvetlenül a 2D konvolúció?

A 2D konvolúció egy szabályos rácson ugyanazt a lokális műveletet alkalmazza minden pozícióban. Egy nyers $N\times C$ ponttömbben azonban a szomszédos sorok nem feltétlenül jelentenek térben közeli pontokat. Ha felcseréljük két pont sorát, a geometria nem változik:

$$f(\mathbf{p}_1,\mathbf{p}_2,\ldots,\mathbf{p}_N) = f(\mathbf{p}_{\pi(1)},\mathbf{p}_{\pi(2)},\ldots,\mathbf{p}_{\pi(N)}),$$

ahol $\pi$ tetszőleges permutáció. Ezt permutáció-invarianciának nevezzük.

A megoldásnak ezért legalább az alábbi problémákat kell kezelnie:

  1. Rendezetlenség: a pontok sorrendje ne befolyásolja a kimenetet.
  2. Szabálytalan szomszédság: meg kell mondani, mely pontok alkotnak lokális környezetet.
  3. Ritkaság: a vizsgált 3D tér túlnyomó része üres.
  4. Változó sűrűség: közel sok, távol kevés pont mérhető ugyanarról az objektumról.
  5. Számítási költség: egy sűrű 3D rács memóriája a felbontással köbösen nő.

A sűrű 3D rács ára

Ha egy $102{,}4\times102{,}4\times8$ méteres tartományt minden irányban $0{,}2$ méteres voxelekre bontanánk, a rács mérete

$$512\times512\times40=10\,485\,760$$

cella lenne. Ennek csak kis része tartalmaz LiDAR-pontot, mégis minden sűrű 3D konvolúció végigszámolná az üres cellákat is. Ez indokolja a ritka (sparse) konvolúciós műveleteket vagy a magassági dimenzió összevonását.

1.2 Három alapvető stratégia

flowchart LR
	P["Nyers pontfelhő<br/>N x C"]
	P --> A["Pont-alapú feldolgozás"]
	P --> B["Voxel-alapú feldolgozás"]
	P --> C["Vetített reprezentáció"]
	A --> A1["PointNet / PointNet++<br/>pont-MLP + szimmetrikus pooling"]
	B --> B1["VoxelNet<br/>3D voxelek + 3D CNN"]
	B --> B2["PointPillars<br/>oszlopok + 2D CNN"]
	C --> C1["BEV- vagy range image<br/>2D CNN"]
Loading
  • Pont-alapú módszerek: közvetlenül a pontokon tanulnak, ezért kevés geometriai információ vész el, de a szomszédkeresés és a változó elemszám kezelése költséges lehet.
  • Voxel-alapú módszerek: szabályos rácsba rendezik a teret. A kvantálás információt veszít, cserébe konvolúciós hálózatok használhatók.
  • Vetítéses módszerek: például madártávlati képet (BEV) vagy a LiDAR mérési irányaihoz igazodó range image-et képeznek. Gyorsak, de a vetítés során több pont ugyanarra a pixelre kerülhet.

1.3 Korábbi mérföldkövek a pontfelhő detektorok világában

PointNet: tanulás közvetlenül a pontokon

A PointNet minden pontra ugyanazt a kis neurális hálót, azaz megosztott súlyú MLP-t alkalmazza. Ezután egy szimmetrikus művelet, tipikusan csatornánkénti maximum képzi az egész ponthalmaz jellemzőjét:

$$\mathbf{g} = \max_{i=1,\ldots,N} \left\{h(\mathbf{p}_i)\right\}.$$

A MAX eredménye nem függ a pontok sorrendjétől, tehát biztosítja a permutáció-invarianciát.

flowchart LR
	P1["p1 = x,y,z,r"] --> M["Megosztott<br/>pont-MLP"]
	P2["p2 = x,y,z,r"] --> M
	PN["pN = x,y,z,r"] --> M
	M --> F["Pontjellemzők<br/>N x C"]
	F --> MAX["Csatornánkénti<br/>MAX pooling"]
	MAX --> G["Globális jellemző<br/>1 x C"]
	G --> H["Klasszifikációs vagy<br/>szegmentációs fej"]
Loading

Erőssége: egyszerű, elegáns, közvetlen pontfeldolgozás.
Korlátja: az eredeti PointNet a globális maximum előtt kevéssé modellezi a lokális pontszomszédságokat. A PointNet++ ezt hierarchikus környezetképzéssel javítja.

VoxelNet: tanult voxelreprezentáció

A VoxelNet a 3D teret kis voxelekre bontja. Egy voxel pontjaira Voxel Feature Encoding (VFE) réteget alkalmaz: pontonként jellemzőt képez, majd lokális maximumot számít. A nem üres voxelek jellemzői ezután ritka 3D tenzort alkotnak, amelyet egy 3D konvolúciós middle encoder dolgoz fel.

flowchart LR
	P["Pontfelhő"] --> V["3D voxelizálás"]
	V --> VFE["VFE<br/>pontonkénti MLP + MAX"]
	VFE --> S["Ritka 3D<br/>jellemzőtér"]
	S --> C3["3D konvolúciók"]
	C3 --> BEV["BEV jellemzőtérkép"]
	BEV --> D["Detekciós fej"]
Loading

Erőssége: megőrzi a térbeli magassági szerkezetet, a voxel jellemzőjét nem kézzel tervezzük.
Korlátja: a 3D konvolúció még ritka (sparse) megvalósítással is költséges; a voxelméret erős pontosság--sebesség kompromisszum.

PointPillars: a 3D feladat átalakítása 2D képfeladattá

A PointPillars felismerése az, hogy közúti objektumdetektálásban a pontos madártávlati $(x,y)$ hely gyakran fontosabb, mint a tér teljes, sűrű 3D felbontása. Ezért csak az $x$--$y$ síkot osztja rácsra. Egy cella a teljes vizsgált magasságot átfogó függőleges pillar, azaz oszlop.

flowchart LR
	P["Pontfelhő<br/>x,y,z,r,t"] --> G["x-y pillar-rács<br/>a z nincs felosztva"]
	G --> PFN["Pillar Feature Net<br/>pontonkénti MLP + MAX"]
	PFN --> SC["Scatter<br/>jellemző a saját x-y cellába"]
	SC --> PI["Pszeudokép<br/>C x H x W"]
	PI --> CNN["2D backbone + neck"]
	CNN --> HEAD["2D/3D detekciós fej"]
Loading

Erőssége: gyors, mert a költséges 3D konvolúció helyett jól optimalizált 2D CNN-t használ.
Korlátja: a teljes pillar magasságát egy jellemzővektorba sűríti, ezért a részletes függőleges szerkezet egy része elveszik.

Rövid összehasonlítás

Módszer Alapegység Fő aggregáció Konvolúció Fő kompromisszum
PointNet pont globális MAX nincs hagyományos rácskonvolúció kevés lokális geometria
VoxelNet 3D voxel voxelen belüli MAX 3D, jellemzően sparse pontosabb 3D szerkezet, nagyobb költség
PointPillars függőleges pillar pillaren belüli MAX 2D gyors, de összevonja a magassági rácsot

Fontos, hogy ezek nem egymást kizáró ötletek. A VoxelNet és a PointPillars pontonkénti enkódere is PointNet-szerű: megosztott transzformációt és sorrendfüggetlen maximumot használ egy lokális csoporton belül.

1.4 A PointPillars pszeudoképe lépésről lépésre

1. A vizsgált tartomány rácsozása

Legyen a pontfelhő tartománya

$$[x_{min},x_{max}]\times[y_{min},y_{max}]\times[z_{min},z_{max}],$$

a pillar alapterülete pedig $v_x\times v_y$. Ekkor a pszeudokép szélessége és magassága:

$$W=\frac{x_{max}-x_{min}}{v_x}, \qquad H=\frac{y_{max}-y_{min}}{v_y}.$$

Egy pont rácsindexe:

$$j=\left\lfloor\frac{x-x_{min}}{v_x}\right\rfloor, \qquad i=\left\lfloor\frac{y-y_{min}}{v_y}\right\rfloor.$$

A notebookban használt CenterPoint pillar konfigurációban:

$$x,y\in[-51{,}2,51{,}2], \qquad v_x=v_y=0{,}2\ \mathrm{m},$$

a teljes voxelméret pedig [0.2, 0.2, 8]. A harmadik, 8 méteres méret a teljes $z$ tartományt átfogja: ez teszi a cellát pillarrá, vagyis nincs külön magassági rácsfelosztás.

ezért

$$H=W=\frac{102{,}4}{0{,}2}=512.$$

2. Pontok csoportosítása és mintavétele

Minden pont a saját $(i,j)$ pillarjába kerül. A GPU-n végzett kötegelt számításhoz rögzített felső korlátokat használunk:

  • legfeljebb $P$ nem üres pillar egy mintában;
  • legfeljebb $T$ pont egy pillarban;
  • a hiányzó helyeket nullákkal töltjük ki, a többletpontokat mintavételezzük vagy eldobjuk.

Az MMDetection3D példakonfigurációjában max_num_points=20, a maximális pillarszám tanításkor 30 000, tesztkor 40 000. A nyers, ritka pontfelhőből így egy feldolgozható $P\times T\times C_{in}$ tenzor készül.

3. Pontjellemzők kiegészítése geometriai eltolásokkal

Egy nyers pont jellemzője itt öt szám: $(x,y,z,r,t)$. Ehhez a PillarFeatureNet két relatív helyvektort fűz:

$$\mathbf{f}_{cluster} =(x,y,z)-(\bar{x},\bar{y},\bar{z}),$$

ahol $(\bar{x},\bar{y},\bar{z})$ a pillar pontjainak átlaga, valamint

$$\mathbf{f}_{center} =(x,y,z)-(x_p,y_p,z_p),$$

ahol $(x_p,y_p,z_p)$ a pillar geometriai középpontja. A modell tehát nemcsak azt látja, hol van a pont globálisan, hanem azt is, hol helyezkedik el a saját pontcsoportjához és cellájához képest.

A példában a pontonkénti bemenet mérete:

$$5+3+3=11.$$

4. Pillar Feature Net: pontokból egyetlen vektor

Ugyanazt a lineáris réteg--normalizáció--ReLU transzformációt alkalmazzuk minden pontra, majd pillárisonként csatornánkénti maximumot számítunk:

$$\mathbf{h}_{ik}=\mathrm{ReLU}(\mathrm{BN}(W\mathbf{f}_{ik}+\mathbf{b})),$$ $$\mathbf{v}_{i}=\max_{k\in\text{pillar }i}\ \mathbf{h}_{ik}.$$

Az eredmény pillarenként egy $C$ dimenziós vektor. A használt configban $C=64$.

5. Scatter: a ritka vektorok visszaírása a vászonra

Létrehozunk egy kezdetben nulla

$$\mathbf{I}_{pseudo}\in\mathbb{R}^{C\times H\times W}$$

tenzort. Minden nem üres pillar 64 dimenziós vektorát a hozzá tartozó $(i,j)$ helyre írjuk, az üres cellák nullák maradnak.

flowchart TB
	subgraph PC["Pontok felülnézetben"]
		A["••• autó"]
		B["• gyalogos"]
		E["ritka háttérpontok"]
	end
	PC --> GRID["512 x 512 pillar-rács<br/>cellaméret: 0,2 m x 0,2 m"]
	GRID --> SETS["Nem üres pillarok<br/>{p1,p2,...}"]
	SETS --> ENC["11D pontjellemző → PFN → MAX"]
	ENC --> VEC["Pillarenként egy 64D vektor"]
	VEC --> IMAGE["Scatter: 64 x 512 x 512 pszeudokép"]
	IMAGE --> CNN["Szokásos 2D CNN"]
Loading

A PointPillars pszeudokép létrehozása a 3D pontfelhőtől a pillarok kódolásán át a többcsatornás BEV-tenzorig

Az ábrán ugyanazt a türkizzel jelölt pillart követjük végig:

  1. 3D pontfelhő: a pontoknak folytonos $(x,y,z)$ koordinátájuk van; a kiemelt függőleges oszlop a teljes magassági tartományt átfogja.
  2. Pillar-rács: felülnézetben az $(x,y)$ koordináta kijelöli a cellát. A $z$ koordináta nem hoz létre új rácsréteget, hanem a pillaron belüli pontjellemző része marad.
  3. Pontkódolás: minden pontra ugyanaz a kis MLP kerül, majd a csatornánkénti MAX pooling a $T$ pontból egyetlen $C$ dimenziós pillar-vektort készít.
  4. Scatter: a vektor visszakerül a saját $(i,j)$ cellájába. Az összes pillar elhelyezése után létrejön a $C\times H\times W$ tenzor, amely már szabályos 2D CNN-bemenet.

A jobb oldali színes rétegek csak a tanult csatornák szemléltetésére szolgálnak. Nem RGB-színeket és nem előre megadott fizikai mennyiségeket jelentenek.

A „pixelérték” itt tehát nem RGB-szín, hanem egy tanult, 64 dimenziós leírás arról, milyen pontmintázat található az adott földterületen.

6. Miért nevezhető képnek?

A pszeudoképnek két szabályos térbeli tengelye van, és a szomszédos cellák szomszédos útfelületeket jelentenek. Ez pontosan az a struktúra, amelyre a 2D konvolúció épít:

  • a kernel lokális környezeteket lát;
  • ugyanazokat a súlyokat használja minden BEV-pozícióban;
  • a mélyebb rétegek egyre nagyobb fizikai környezetet fednek le;
  • a backbone és a neck ugyanúgy több léptékű jellemzőket készíthet, mint képeknél.

2. A CenterPoint architektúra

A CenterPoint egy anchor-free, center-based 3D objektumdetektor. Nem előre definiált 3D horgonydobozok ezreit igazítja az adathoz. Ehelyett először az objektumok középpontját keresi meg madártávlatban (BEV), majd csak ezekhez a középpontokhoz regresszálja a teljes 3D dobozt.

Az alapintuíció:

Egy autó dobozának sokféle lehetséges mérete és iránya lehet, de a középpontja egyetlen jól meghatározott hely a BEV-térképen.

2.1 A teljes adatút egy adott bementre

A notebook pillar-alapú CenterPoint változata a következő hálózati részekből áll:

flowchart LR
	P["10 LiDAR sweep<br/>N x 5 pont"] --> V["Voxel layer<br/>0,2 x 0,2 x 8 m pillar"]
	V --> PFN["PillarFeatureNet<br/>64D pillar-jellemző"]
	PFN --> S["PointPillarsScatter<br/>64 x 512 x 512"]
	S --> B["SECOND backbone<br/>3 felbontási szint"]
	B --> N["SECONDFPN neck<br/>384 x 128 x 128"]
	N --> H["CenterHead"]
	H --> HM["Középponti heatmap<br/>osztálypontszámok"]
	H --> REG["Dobozfejek<br/>offset, z, méret,<br/>irány, sebesség"]
	HM --> DEC["Top-K + dekódolás<br/>+ circle NMS"]
	REG --> DEC
	DEC --> OUT["3D dobozok<br/>osztály + score"]
Loading

A SECOND backbone három léptékben készít jellemzőket. A SECONDFPN ezeket közös $128\times128$ felbontásra hozza, majd a csatornatengely mentén összefűzi. A CenterHead bemenete ezért ebben a configban

$$384\times128\times128.$$

A nyers pillar-rács $512\times512$, a detekciós térkép pedig négyszeresen kisebb mindkét térbeli irányban. Egy kimeneti cella fizikai lépésköze tehát:

$$s_x=s_y=\underbrace{0{,}2\ \mathrm{m}}_{\text{pillar méret}} \cdot \underbrace{4}_{\text{leskálázás}} =0{,}8\ \mathrm{m}.$$

2.2 A heatmap fej: hol vannak az objektumközéppontok?

Nem szegmentációs maszk

A hőtérkép nem azt mondja meg, mely pixeleket foglalja el az autó. Minden osztályhoz egy külön $H\times W$ térkép tartozik, amelynek lokális csúcsai az objektumközéppontokat jelzik:

$$\hat{Y}\in[0,1]^{C\times H\times W}.$$

Egy magas car érték az $(i,j)$ cellában ezt jelenti: „nagy valószínűséggel egy autó középpontja található ebben a BEV cellában”.

flowchart LR
	F["BEV jellemzőtérkép"] --> C1["3 x 3 konvolúció<br/>+ BN + ReLU"]
	C1 --> C2["3 x 3 konvolúció"]
	C2 --> H1["car heatmap"]
	C2 --> H2["truck heatmap"]
	C2 --> HC["... osztályonként<br/>egy csatorna"]
	H1 --> P1["lokális maximum =<br/>autóközéppont-jelölt"]
Loading

Miért nem egyetlen 1-es célérték?

Ha csak a középponthoz legközelebbi cellát jelölnénk 1-gyel, minden szomszédos cella ugyanúgy negatív lenne, mint egy száz méterrel távolabbi hely. Ez túl éles célfüggvény lenne: egyetlen cellányi lokalizációs eltérés aránytalan büntetést kapna.

A CenterPoint ezért minden ground-truth középpont köré Gauss-foltot rajzol. A középpont célértéke 1, a környezetében fokozatosan csökken:

$$Y_{cij} = \exp\left( -\frac{(i-i_0)^2+(j-j_0)^2}{2\sigma^2} \right).$$

A CenterPoint ground-truth heatmap létrehozása nyers BEV pontfelhőből, orientált GT dobozokból és osztályonkénti Gauss-csúcsokból

Az ábra ugyanazt a három GT objektumot követi végig:

  1. Nyers adat és GT doboz: a BEV pontfelhő fölött látható az autó, a teherautó és a gyalogos orientált GT doboza. A fekete kereszt a doboz folytonos geometriai középpontja.
  2. Kvantálás a heatmap rácsára: a dobozközéppont a legközelebbi alsó egész feature-map indexhez kerül. A türkiz kör a Gauss-csúcs helye, a színes kereszt a valódi középpont; különbségük a regresszált $(\Delta x,\Delta y)$ offset.
  3. Osztálycsatornák: minden osztály saját heatmap-csatornát kap. Az adott GT objektum csak a saját osztályának csatornáján hoz létre Gauss-foltot.
  4. Heatmap és bbox-fej: a heatmap csak az osztályt és a középpontot tanítja. Ugyanazon a kiválasztott helyen a külön regressziós ágak tanulják a pontos offsetet, a magasságot, a méretet, az orientációt és a sebességet.

A GT doboz körvonala az ábrán a geometriai kapcsolatot mutatja. A heatmap célja nem tölti ki a doboz teljes belsejét: csak a középpont környezetében pozitív.

Szemléltető, nem méretarányos cél-hőtérkép egy objektum körül:

0,00 0,02 0,05 0,02 0,00
0,02 0,18 0,42 0,18 0,02
0,05 0,42 1,00 0,42 0,05
0,02 0,18 0,42 0,18 0,02
0,00 0,02 0,05 0,02 0,00

A sugár nem minden objektumnál azonos. A gaussian_radius() a doboz BEV-méretéből és az előírt minimális átfedésből számítja, majd a config min_radius alsó korlátot alkalmaz. Egy nagy busz így szélesebb, egy kis gyalogos szűkebb célfoltot kaphat.

Méteres koordinátából hőtérképcella

Egy $(x,y)$ LiDAR-koordinátájú középpont folytonos hőtérkép-koordinátája:

$$u=\frac{x-x_{min}}{v_x\,d}, \qquad v=\frac{y-y_{min}}{v_y\,d},$$

ahol $d$ az out_size_factor. A cél Gauss-folt középpontja az egész

$$(u_0,v_0)=(\lfloor u\rfloor,\lfloor v\rfloor)$$

cella, a tört részt pedig külön regressziós fej tanulja:

$$(\Delta x,\Delta y)=(u-u_0,v-v_0).$$

Ez a felbontás nem korlátozza $0{,}8$ méteres lépésekre a detektált középpontot: az offset visszaadja a cellán belüli folytonos pozíciót.

Heatmap loss: Gaussian Focal Loss

A pontfelhő nagy részén nincs objektumközéppont, tehát rendkívül sok a könnyű negatív cella. A szokásos bináris keresztentrópiát ezek dominálnák. A Gaussian Focal Loss a nehéz példákra helyezi a hangsúlyt:

$$L_{hm}=-\frac{1}{N} \sum_{cij} \begin{cases} (1-p_{cij})^{\alpha}\log p_{cij}, & Y_{cij}=1,\\\ (1-Y_{cij})^{\gamma}p_{cij}^{\alpha}\log(1-p_{cij}), & Y_{cij}<1. \end{cases}$$

Itt $p=\mathrm{sigmoid}(\hat{Y})$, $N$ a ground-truth középpontok száma. Az MMDetection alapértékei $\alpha=2$ és $\gamma=4$.

  • A jól felismert pozitív és könnyű negatív helyek súlya lecsökken.
  • A középpont körüli nagy $Y$ értékű helyek kisebb negatív büntetést kapnak.
  • A ritka, elrontott középpontok erős tanulási jelet adnak.

2.3 A bounding box fej: milyen az objektum?

A hőtérkép csak a BEV-középpontot és az osztályt adja. Egy teljes nuScenes 3D dobozhoz további mennyiségek szükségesek:

$$\mathbf{b}=(x,y,z,d_x,d_y,d_z,\theta,v_x,v_y).$$

A CenterHead több kis konvolúciós ágon, minden hőtérképpozícióhoz az alábbi tenzorokat prediktálja:

Fej Csatorna Jelentés
reg 2 $(\Delta x,\Delta y)$ cellán belüli középpont-offset
height 1 a doboz $z$ középpontja
dim 3 $(\log d_x,\log d_y,\log d_z)$
rot 2 $(\sin\theta,\cos\theta)$
vel 2 $(v_x,v_y)$ síkbeli sebesség
Összes regressziós csatorna 10 egy 3D állapot kódolt leírása
flowchart TB
	X["Közös 384 x 128 x 128<br/>BEV feature map"] --> SH["3 x 3 shared conv<br/>64 csatorna"]
	SH --> HM["heatmap<br/>C x H x W"]
	SH --> O["reg<br/>2 x H x W"]
	SH --> Z["height<br/>1 x H x W"]
	SH --> D["dim<br/>3 x H x W"]
	SH --> R["rot<br/>2 x H x W"]
	SH --> V["vel<br/>2 x H x W"]
Loading

Miért logaritmus a méret?

A dobozméretek pozitívak. A hálózat korlátlan valós számot regresszál, majd dekódoláskor exponenciálist alkalmaz:

$$(d_x,d_y,d_z)=\exp(\widehat{\log\mathbf{d}}).$$

Így a dekódolt méret biztosan pozitív, és a loss inkább relatív, mint abszolút méreteltérést érzékel. Egy 0,2 méteres hiba egy gyalogoson fontosabb, mint ugyanekkora eltérés egy buszon.

Miért szinusz és koszinusz a yaw helyett?

A szög periodikus: $-\pi$ és $+\pi$ ugyanazt az irányt jelenti, közvetlen regresszióban mégis nagy numerikus különbség lenne közöttük. Ezért két folytonos komponenst becslünk, majd

$$θ=\mathrm{atan2}(\widehat{\sin\theta},\widehat{\cos\theta}).$$

Hol számítjuk a regressziós hibát?

A dobozfejek minden cellában adnak értéket, de ground-truth doboz csak a valódi objektumközéppontok indexein tanítja őket. A CenterHead kigyűjti ezeket a pozíciókat, és súlyozott L1 hibát számít:

$$L_{bbox} = \frac{1}{N} \sum_{n=1}^{N} \sum_{k=1}^{10} w_k\left|\hat{b}_{nk}-b_{nk}\right|.$$

A használt config kódsúlyai:

$$\mathbf{w}=(1,1,1,1,1,1,1,1,0{,}2,0{,}2).$$

Vagyis a két sebességkomponens kisebb súlyt kap, mint a pozíció, méret és irány komponensei. A config ezenfelül loss_weight=0.25 szorzót állít a teljes regressziós lossra.

Feladatonként a veszteség:

$$L_{task}=L_{hm}+0{,}25L_{bbox}.$$

A nuScenes tíz osztályát a config hat taskba csoportosítja. Minden tasknak saját heatmap- és regressziós feje van; a végső loss ezek feladatonkénti összege.

2.4 Dekódolás: tenzorokból 3D dobozok

Inferenciakor nincs ground-truth index, ezért a hőtérkép csúcsai választják ki a vizsgálandó helyeket.

flowchart LR
	HM["Heatmap sigmoid"] --> TOP["Feladatonként Top-K<br/>K = 500"]
	TOP --> G["A regressziós értékek<br/>kigyűjtése ugyanott"]
	G --> M["Rácskoordináta<br/>→ méteres koordináta"]
	M --> BOX["x,y,z,dx,dy,dz,<br/>yaw,vx,vy"]
	BOX --> F["score- és<br/>tartományszűrés"]
	F --> NMS["Circle NMS"]
	NMS --> O["Végső 3D detekciók"]
Loading

A középpont visszaalakítása

Ha a kiválasztott cella $(u_0,v_0)$, a becsült offset $(\Delta x,\Delta y)$, akkor:

$$x=(u_0+\Delta x)\,d\,v_x+x_{min},$$ $$y=(v_0+\Delta y)\,d\,v_y+y_{min}.$$

Számpélda. Legyen egy objektum valódi középpontja $(x,y)=(10{,}3,-2{,}1)$ méter. A példakonfigurációban $x_{min}=y_{min}=-51{,}2$, $d=4$, $v_x=v_y=0{,}2$, ezért:

$$u=\frac{10{,}3+51{,}2}{0{,}8}=76{,}875, \qquad v=\frac{-2{,}1+51{,}2}{0{,}8}=61{,}375.$$

A hőtérkép csúcsa a $(76,61)$ cellába kerül, a regressziós cél pedig $(0{,}875,0{,}375)$. Dekódoláskor:

$$x=(76+0{,}875)\cdot0{,}8-51{,}2=10{,}3,$$ $$y=(61+0{,}375)\cdot0{,}8-51{,}2=-2{,}1.$$

A bbox coder ekkor még a doboz gravitációs középpontjának $z$ koordinátáját adja. A CenterHead.predict_by_feat() a taskok egyesítésekor levonja a magasság felét,

$$z_{bottom}=z_{gravity}-\frac{d_z}{2},$$

így a kimenet az MMDetection3D LiDARInstance3DBoxes alsó-középpont alapú konvenciójához igazodik.

Circle NMS

Közeli hőtérképcsúcsok ugyanahhoz az objektumhoz több dobozt adhatnak. A notebook konfigurációja circle NMS-t használ: score szerint haladva megtartja a legerősebb jelöltet, majd az adott taskhoz tartozó minimális BEV-középpont-távolságon belüli gyengébb jelölteket elnyomja.

Ez eltér a képi detektorokból ismert IoU-alapú NMS-től:

  • gyors, mert csak a középpontok távolságát vizsgálja;
  • illeszkedik a center-based reprezentációhoz;
  • osztálycsoportonként eltérő sugár állítható be, mert egy busz és egy gyalogos tipikus távolságskálája más.

2.5 Tanítás és inferencia egymás mellett

Tanítás Inference
Ground-truth dobozból Gauss heatmap készül Sigmoid után a heatmap Top-K csúcsait választjuk
A középpont cellaindexe ismert A cellaindexet a heatmap becsli
A dobozkód csak a valódi középpontoknál kap L1 losst A dobozfejek értékeit a kiválasztott csúcsoknál gyűjtjük ki
Gaussian Focal Loss + súlyozott L1 Dekódolás + score-szűrés + NMS
A cél a paraméterek frissítése A kimenet bboxes_3d, scores_3d, labels_3d

Kapcsolat az előző órával

A CenterPoint ugyanazt a backbone--neck--head gondolkodást használja, mint a képi detektorok:

  • a backbone egyre nagyobb környezetből tanul geometriai mintákat;
  • a neck több felbontási szintet egyesít;
  • a klasszifikációs fej itt osztályonkénti középpont-hőtérképet készít;
  • a regressziós fej itt 2D doboz helyett 3D dobozt, orientációt és sebességet ad;
  • az utófeldolgozás továbbra is score-szűréssel és NMS-sel választja ki a végső objektumokat.

Az új elem nem a CNN alapelve, hanem a pontfelhő pszeudoképpé alakítása és a 3D geometria megfelelő kódolása.


3. Az MMDetection3D keretrendszer

A gyakorlati notebook azt mutatja meg, hogyan kell futtatni egy teljes CenterPoint munkafolyamatot. Ez a fejezet azt mutatja meg, hol és hogyan kell tájékozódni a keretrendszerben, amikor később konfigurációt módosítunk, hibát keresünk vagy új komponenst írunk.

3.1 Az OpenMMLab rétegei

Az MMDetection3D nem önálló monolit. Több OpenMMLab csomag egymásra épül:

flowchart TB
	APP["Saját kísérlet<br/>config + adatok + checkpoint"] --> M3D["MMDetection3D<br/>3D datasetek, modellek, metrikák"]
	M3D --> MDET["MMDetection<br/>lossok, detekciós segédosztályok"]
	M3D --> MMCV["MMCV<br/>CNN modulok, CUDA operátorok"]
	M3D --> MME["MMEngine<br/>Config, Registry, Runner, Hook"]
	MDET --> MMCV
	MDET --> MME
	MMCV --> TORCH["PyTorch"]
	MME --> TORCH
Loading
  • MMEngine: általános tanítási motor. Kezeli a configot, registryket, runnert, hookokat, naplózást és checkpointokat.
  • MMCV: gyakori CNN építőelemeket és optimalizált CPU/CUDA operátorokat ad.
  • MMDetection: általános objektumdetekciós komponenseket, például lossokat és bbox segédeszközöket biztosít.
  • MMDetection3D: 3D adatkészleteket, pontfelhő-transzformációkat, 3D modelleket, dobozstruktúrákat, metrikákat és vizualizációt ad hozzá.

Ezért fordul elő például a configban type='mmdet.GaussianFocalLoss': a 3D fej az MMDetectionben regisztrált losst használja.

Minimális szótár a keretrendszer olvasásához

A configban szereplő értékek többsége hiperparaméter: ezeket mi állítjuk be a futás előtt. Nem azonosak a hálózat tanult paramétereivel, vagyis a súlyokkal és biasokkal, amelyeket a tanítás módosít.

Fogalom Jelentés ebben az anyagban
minta / sample Egy időponthoz tartozó, előfeldolgozott pontfelhő és tanításkor a hozzá tartozó GT annotáció.
batch Több minta, amelyen a modell egyszerre fut. A batch_size=2 GPU-nként két mintát jelent.
iteráció Egy batch előremenete, loss-számítása, visszaterjesztése és egy optimizer-lépése.
epoch A teljes train adathalmaz egyszeri bejárása. Egy epoch sok iterációból áll.
loss / veszteség Egy skalár, amely számszerűsíti a predikció és a ground truth eltérését. Ebből számolódik a gradiens.
optimizer A gradiensek alapján frissíti a tanult súlyokat. A példában AdamW.
learning rate (LR) A súlyfrissítés lépésméretének fő skálája. Túl nagy érték instabil, túl kicsi érték lassú tanulást okozhat.
scheduler A learning rate-et változtatja a tanítás során.
dataloader Mintákat olvas, meghívja az adatpipeline-t, majd batcheket állít össze.
train / val / test A train split tanítja a súlyokat; a val split modellválasztásra; a test split egyszeri végső mérésre szolgál.
checkpoint A modell tanult súlyait és gyakran a futás állapotát tartalmazó .pth fájl.
hook A tanítás meghatározott pontjain automatikusan lefutó kiegészítő, például logolás vagy checkpoint-mentés.

Egyszerűsítve, egy GPU esetén az iterációk száma egy epochban:

$$N_{iter/epoch}=\left\lceil\frac{N_{train}}{\text{batch size}}\right\rceil.$$

Több GPU esetén az effektív globális batch közelítőleg

$$B_{\mathrm{global}}=N_{\mathrm{GPU}}\times B_{\mathrm{GPU}}.$$

Ez azért fontos, mert a fájlnévben szereplő 8xb4 nyolc GPU-t és GPU-nként négy mintát, vagyis 32 mintás referencia-batchet jelent. Egyetlen Colab GPU-n batch_size=2 ettől eltérő optimalizálási helyzet.

Mi történik egy tanítási iterációban?

flowchart LR
	D["Dataset<br/>fájlok + annotáció"] --> P["Pipeline<br/>betöltés, augmentáció, csomagolás"]
	P --> L["DataLoader<br/>batch összeállítása"]
	L --> DP["Data preprocessor<br/>GPU-ra másolás, voxelizálás"]
	DP --> M["Model forward<br/>pillar → backbone → head"]
	M --> LOSS["Loss<br/>heatmap + bbox"]
	LOSS --> B["Backward<br/>gradiensek"]
	B --> O["Optimizer step<br/>súlyfrissítés"]
	O --> H["Hookok<br/>log, scheduler, checkpoint"]
Loading
  1. A dataset megadja, hol található egy minta és az annotációja.
  2. A pipeline betölti és tanításkor véletlenszerűen augmentálja az adatot.
  3. A dataloader több mintából batchet készít.
  4. A data preprocessor GPU-ra mozgatja az adatot és a pontokat pillarokba rendezi.
  5. A modell elkészíti a heatmap- és dobozpredikciókat.
  6. A loss összehasonlítja ezeket a ground truth célokkal.
  7. A backpropagation gradienst számít, az optimizer pedig módosítja a súlyokat.
  8. A hookok naplóznak, learning rate-et módosítanak vagy checkpointot mentenek.

Validáció és inferencia közben nincs backward() és optimizer-lépés: a súlyok nem változnak.

3.2 A repository mappaszerkezete

Az alábbi fa a helyi /home/dot1bp/git/mmdetection3d repo munkához legfontosabb részeit emeli ki:

mmdetection3d/
├── configs/
│   ├── _base_/
│   │   ├── datasets/          # dataset, pipeline, dataloader, evaluator
│   │   ├── models/            # architektúrák alapkonfigurációi
│   │   ├── schedules/         # optimizer, LR schedule, epochok
│   │   └── default_runtime.py # hookok, logolás, futtatási környezet
│   ├── centerpoint/           # CenterPoint kísérleti configok
│   └── pointpillars/          # PointPillars kísérleti configok
├── mmdet3d/
│   ├── apis/                  # init_model, inference_detector stb.
│   ├── datasets/              # dataset osztályok és transzformációk
│   ├── engine/                # hookok, runner-kiegészítések
│   ├── evaluation/            # KITTI, nuScenes és más metrikák
│   ├── models/
│   │   ├── detectors/         # teljes detektorok és adatútjuk
│   │   ├── voxel_encoders/    # pontok → voxel/pillar jellemzők
│   │   ├── middle_encoders/   # sparse tensor vagy pszeudokép
│   │   ├── backbones/         # SECOND, DGCNN stb.
│   │   ├── necks/             # SECONDFPN és más feature-aggregátorok
│   │   ├── dense_heads/       # CenterHead és más detekciós fejek
│   │   └── task_modules/      # bbox coderek, assignerek, priorok
│   ├── structures/            # 3D dobozok, pontok, data sample
│   ├── visualization/         # 3D vizualizáció
│   └── registry.py            # MMDetection3D registryk
├── tools/
│   ├── train.py               # tanítás belépési pontja
│   ├── test.py                # teszt és kiértékelés
│   ├── create_data.py         # nyers dataset előkészítése
│   └── misc/print_config.py   # összeolvasztott config kiírása
├── projects/                  # kísérleti vagy külső bővítmények
├── tests/                     # automatikus tesztek
└── docs/                      # felhasználói és fejlesztői dokumentáció

Hogyan induljunk el egy kérdéssel?

Kérdés Elsőként vizsgálandó hely
Milyen fájlokat és augmentációt kap a modell? configs/_base_/datasets/ és mmdet3d/datasets/transforms/
Milyen rétegekből áll a háló? configs/_base_/models/
Hol állítom az LR-t vagy az epochszámot? configs/_base_/schedules/ és a kísérleti config
Hol képződik a heatmap target? mmdet3d/models/dense_heads/centerpoint_head.py
Hol lesz a heatmapből méteres 3D doboz? mmdet3d/models/task_modules/coders/centerpoint_bbox_coders.py
Miért nem töltődik be egy komponens? az adott registry.py, __init__.py és a config type mezője
Mi fut le tanításkor? tools/train.py → MMEngine Runner → modell loss()

3.3 Configok: Python fájlokból összeálló kísérletek

Az MMDetection3D config nem pusztán paraméterlista. Egy Python fájl, amely egymásba ágyazott szótárakkal egy teljes kísérletet ír le:

$$\mathrm{kísérlet} = \mathrm{modell} +\text{adat} +\text{optimalizálás} +\text{runtime}.$$

Hogyan olvassunk egy beágyazott configot?

A behúzás parent--child kapcsolatot jelent. Például:

model = dict(
	pts_bbox_head=dict(
		loss_bbox=dict(
			type='mmdet.L1Loss',
			loss_weight=0.25,
		),
	),
)

A loss_weight teljes kulcsútvonala:

model.pts_bbox_head.loss_bbox.loss_weight

Ezt balról jobbra így olvassuk: a modellen belül → a pontfelhő bbox headben → a bbox lossban → a loss súlya. A pontozott alak nem feltétlenül szerepel szó szerint a fájlban, de dokumentációban és parancssori felülíráskor így hivatkozunk rá.

A legfontosabb felső szintű configrészek

Configrész Mit épít vagy vezérel? Tipikus kérdés
model A neurális hálózat moduljai, lossai és modellezési train/test beállításai. Milyen a pillar mérete, a backbone vagy a detekciós fej?
train_dataloader A train dataset, pipeline, sampler, batchméret és CPU workerszám. Melyik info-fájlból és hány mintát töltünk egyszerre?
val_dataloader, test_dataloader A validációs és tesztadatok betöltése, véletlen train augmentáció nélkül. Melyik spliten mérünk?
val_evaluator, test_evaluator A predikciókból metrikát számító objektum. NuScenes mAP/NDS vagy más metrika készüljön?
optim_wrapper Optimizer, gradiensvágás és vegyes pontosság körüli logika. Hogyan frissüljenek a tanult súlyok?
param_scheduler A learning rate időbeli változása. Mikor és mennyire csökkenjen az LR?
gyökérszintű train_cfg A Runner tanítási ciklusa. Hány epoch legyen, milyen gyakran validáljunk?
default_hooks Logolás, checkpoint, időmérés és más automatikus műveletek. Hány iterációnként írjunk logot?
load_from, resume A futás kezdőállapota. Előre tanított súlyból indulunk vagy félbeszakadt futást folytatunk?

Két különböző train_cfg

Az azonos név ellenére két külön szint létezik:

model = dict(
	train_cfg=dict(
		pts=dict(max_objs=500, min_radius=2),
	),
)

train_cfg = dict(max_epochs=20, val_interval=20)
  • A model.train_cfg.pts a CenterHead számítását szabályozza: például hány GT objektumot kezeljen és mekkora legyen a minimális Gauss-sugár.
  • A gyökérszintű train_cfg a Runner ciklusát szabályozza: például hány epochig fusson és mikor validáljon.

Ez gyakori hibaforrás: az epochszámot nem a model.train_cfg alatt kell keresni.

A használt CenterPoint config öröklési lánca

A notebook belépési configja:

configs/centerpoint/
└── centerpoint_pillar02_second_secfpn_head-circlenms_8xb4-cyclic-20e_nus-3d.py
	└── centerpoint_pillar02_second_secfpn_8xb4-cyclic-20e_nus-3d.py
		├── ../_base_/datasets/nus-3d.py
		├── ../_base_/models/centerpoint_pillar02_second_secfpn_nus.py
		├── ../_base_/schedules/cyclic-20e.py
		└── ../_base_/default_runtime.py
flowchart TB
	D["datasets/nus-3d.py<br/>pipeline, dataloader, evaluator"] --> E["pillar02...nus-3d.py<br/>konkrét kísérlet"]
	M["models/centerpoint_pillar02...py<br/>hálózat, loss, coder"] --> E
	S["schedules/cyclic-20e.py<br/>AdamW, LR, 20 epoch"] --> E
	R["default_runtime.py<br/>hookok, logolás"] --> E
	E --> N["...head-circlenms...py<br/>NMS felülírás"]
	N --> F["Végleges összeolvasztott config"]
Loading

Az _base_ lista betölti a parent configokat. A child configban megadott értékek rekurzívan felülírják az azonos kulcsokat. Így az utolsó fájl mindössze ezt módosítja:

_base_ = [
	'./centerpoint_pillar02_second_secfpn_8xb4-cyclic-20e_nus-3d.py'
]

model = dict(test_cfg=dict(pts=dict(nms_type='circle')))

A teljes modell ettől még tartalmazza a pillar encodert, a SECOND backbone-t, a SECONDFPN neck-et és a CenterHeadet: ezeket az öröklési lánc adja hozzá.

Összefésülés és _delete_

Alapesetben a beágyazott szótárak összeolvadnak:

# A parent config többi optimizer mezője megmarad, csak az lr változik.
optim_wrapper = dict(optimizer=dict(lr=5e-5))

Ha egy teljes részfát le akarunk cserélni, _delete_=True szükséges:

optim_wrapper = dict(
	_delete_=True,
	type='OptimWrapper',
	optimizer=dict(type='AdamW', lr=5e-5, weight_decay=0.01),
	clip_grad=dict(max_norm=10, norm_type=2),
)

Ez különösen fontos, ha a child configban nem ugyanazt a komponenstípust használjuk, mint a parent configban.

Registry: hogyan lesz a szótárból Python objektum?

Egy config-részlet:

pts_bbox_head=dict(
	type='CenterHead',
	in_channels=384,
	loss_cls=dict(type='mmdet.GaussianFocalLoss', reduction='mean'),
)

nem közvetlen konstruktorkód. A registry megkeresi a CenterHead néven regisztrált osztályt, majd a többi mezőt konstruktorargumentumként átadja neki:

flowchart LR
	C["config dict<br/>type = CenterHead"] --> R["MODELS registry"]
	R --> K["CenterHead osztály"]
	K --> O["példányosított modul"]
Loading

Az osztály feletti @MODELS.register_module() dekorátor teszi elérhetővé a nevet. Egy tipikus „ismeretlen type” hiba ezért nem feltétlenül elírás: az is lehet, hogy az osztályt tartalmazó modul nem importálódott.

A configfájlnév olvasása

A hosszú fájlnév tömör kísérleti leírás:

centerpoint_pillar02_second_secfpn_head-circlenms_8xb4-cyclic-20e_nus-3d.py

Rész Jelentés
centerpoint detektor
pillar02 pillar reprezentáció, 0,2 m-es BEV-cella
second backbone
secfpn neck
head-circlenms CenterHead és circle NMS változat
8xb4 referenciafutás: 8 GPU, GPU-nként 4 minta
cyclic-20e ciklikus/cosine schedule, 20 epoch
nus-3d nuScenes 3D detekció

3.4 A CenterPoint modellconfig olvasása

Az öröklés után létrejövő modellconfigot érdemes az adatút sorrendjében olvasni. Az alábbi értékek a notebookban használt pillar CenterPoint konfigurációból származnak.

1. Mérési tartomány és pillar-rács

Kulcs Érték Mit jelent? A változtatás fő hatása
point_cloud_range [-51.2, -51.2, -5, 51.2, 51.2, 3] A feldolgozott LiDAR-tér: $x,y$ irányban 102,4 m széles, $z$ irányban 8 m magas. Mértékegysége méter. Nagyobb tartomány több objektumot fed le, de több cellát és számítást igényel. A tartományon kívüli pontokat és GT dobozokat a pipeline kiszűri.
voxel_size [0.2, 0.2, 8] Egy pillar alapterülete $0{,}2\times0{,}2$ m, magassága 8 m, tehát a teljes $z$ tartományt átfogja. Kisebb $x,y$ méret finomabb lokalizációt, de négyzetesen több BEV-cellát és nagyobb memóriaigényt okoz.
max_num_points 20 Legfeljebb 20 pont marad egy pillarban; a hiányzó helyek paddinget kapnak. Nagyobb érték több pontinformációt őrizhet, de növeli a voxelizált tenzor méretét.
max_voxels (30000, 40000) Legfeljebb 30 000 nem üres pillar tanításkor és 40 000 tesztkor. A tuple sorrendje (train, test). Ha kevés, sűrű jelenetben pillarok veszhetnek el; ha nagy, nő a memória- és időigény.
grid_size [512, 512, 1] A pillar-rács cellaszáma $x,y,z$ irányban. A $z=1$ azt jelenti, hogy a teljes 8 m-es magasság egyetlen oszlop, nem több voxelréteg. Származtatott érték: konzisztensnek kell lennie a tartománnyal és a voxelmérettel.

A $512\times512$ alak nem önkényes:

$$\frac{51{,}2-(-51{,}2)}{0{,}2}=512.$$

Együtt változtatandó értékek: point_cloud_range, voxel_size, grid_size, PointPillarsScatter.output_shape, a bbox coder pc_range/voxel_size mezői és a pipeline range filterei ugyanazt a geometriát írják le. Egyetlen mező külön módosítása hibás alakot vagy eltolódott dobozokat okozhat.

2. Pillar Feature Net és pszeudokép

Kulcs Érték Mit szabályoz?
pts_voxel_encoder.type PillarFeatureNet A pillaron belüli pontokat egy tanult vektorrá összegző modul típusa.
in_channels 5 A nyers pont öt bemeneti jellemzője: tipikusan $(x,y,z)$, intenzitás és időeltérés. A relatív helyjellemzőket a modul ezután fűzi hozzá.
feat_channels [64] A PFN kimenete pillaronként 64 szám. Több csatorna nagyobb reprezentációs kapacitást és több számítást jelent.
with_distance False Nem fűzi hozzá külön skalárként a pont origótól mért euklideszi távolságát.
norm_cfg BN1d Batch normalization stabilizálja a pontjellemzők skáláját a PFN-ben.
pts_middle_encoder.type PointPillarsScatter A ritka pillar-vektorokat szabályos BEV-vászonra írja.
output_shape (512, 512) A pszeudokép térbeli mérete. Az eredmény alakja batch nélkül $64\times512\times512$.

Itt a 64 csatornaszám, nem osztályszám. A csatornák tanult jellemzők; egy-egy csatornának nincs előre rögzített fizikai jelentése.

3. SECOND backbone és SECONDFPN neck

Kulcs Érték Kezdőbarát értelmezés
pts_backbone.in_channels 64 Meg kell egyeznie a pszeudokép csatornaszámával.
out_channels [64, 128, 256] A három backbone-szint csatornaszáma. Mélyebben többféle tanult mintázat tárolható.
layer_nums [3, 5, 5] A három szinten alkalmazott konvolúciós blokkok száma. Nagyobb érték mélyebb, lassabb modellt jelent.
layer_strides [2, 2, 2] Minden szint első blokkja felezi a térbeli felbontást. A 512-es bemenetből rendre 256, 128 és 64 körüli felbontású feature map készül.
pts_neck.in_channels [64, 128, 256] A neck az előbbi három backbone-kimenetet várja.
out_channels [128, 128, 128] Mindhárom léptéket 128 csatornára alakítja.
upsample_strides [0.5, 1, 2] Az első szintet tovább csökkenti, a másodikat változatlanul hagyja, a harmadikat felnagyítja, hogy mindhárom $128\times128$ legyen.

A neck a három, egyenként 128 csatornás térképet összefűzi:

$$128+128+128=384,$$

ezért a CenterHead.in_channels=384. Ha egy korábbi modul csatornaszáma változik, a következő modul bemeneti csatornaszámát is hozzá kell igazítani.

4. CenterHead: feladatcsoportok és kimeneti ágak

Kulcs Érték Mit jelent?
tasks 6 csoport, összesen 10 osztály A hasonló osztályokat közös task head kezeli, például truck és construction_vehicle. Minden tasknak külön heatmapje és regressziós ágai vannak.
share_conv_channel 64 A neck 384 csatornáját egy közös 64 csatornás reprezentációvá alakítja a task headek előtt. Nagyobb érték több paramétert és GPU-memóriát jelent.
num_heatmap_convs alapértéke 2 Ennyi konvolúcióból áll egy task heatmap-ága. A modellconfig nem írja ki, ezért az osztály konstruktorának alapértéke érvényesül.
common_heads.reg (2, 2) Két offsetérték, két konvolúcióval előállítva. A párok alakja (kimeneti csatornák, konvolúciók száma).
height (1, 2) Egy $z$ érték, két konvolúcióval.
dim (3, 2) Három log-méret: $(\log d_x,\log d_y,\log d_z)$.
rot (2, 2) Két iránykomponens: $(\sin\theta,\cos\theta)$.
vel (2, 2) Két síkbeli sebességkomponens: $(v_x,v_y)$.
loss_cls GaussianFocalLoss A ritka középponti heatmap tanítására szolgál.
loss_bbox.loss_weight 0.25 A teljes bbox L1 loss szorzója a heatmap losshoz képest.
norm_bbox True A méreteket logaritmikus térben tanítja; dekódoláskor exponenciális visszaalakítás történik.

A regressziós head 10 csatornát tanít, de a dekódolt nuScenes doboz code_size=9: a két rotációs csatornából atan2 után egyetlen yaw érték lesz.

5. Targetképzés és bbox coder

Kulcsútvonal Érték Szerep
model.train_cfg.pts.out_size_factor 4 A heatmap térbeli lépése négyszerese a pillar méretének: $4\cdot0{,}2=0{,}8$ m. A $512\times512$ pszeudoképhez $128\times128$ heatmap tartozik.
max_objs 500 Legfeljebb ennyi GT objektum kap regressziós célt egy mintában.
gaussian_overlap 0.1 A gaussian_radius() dobozméretből számított sugarának egyik feltétele.
min_radius 2 A target heatmap Gauss-foltjának minimális sugara feature-map cellában. Ez tanítási paraméter.
code_weights nyolc 1.0, két 0.2 Az offset, magasság, méret és rotáció teljes súlyt, a két sebességkomponens kisebb súlyt kap.
bbox_coder.max_num 500 Taskonként legfeljebb ennyi heatmap-csúcs kerül dekódolásra.
bbox_coder.score_threshold 0.1 A gyengébb jelöltek még NMS előtt kiesnek. Nagyobb küszöb kevesebb fals pozitívat, de több kihagyott objektumot okozhat.
bbox_coder.out_size_factor 4 A rácskoordináták méterre alakításához ugyanazt a skálát kell használnia, mint a targetképzésnek.
post_center_range [-61.2, -61.2, -10, 61.2, 61.2, 10] A dekódolás után csak ezen, a bemeneti ponttartománynál szándékosan tágabb utószűrési térfogaton belüli dobozok maradnak meg. Ez bbox-középpontokra vonatkozó szűrő, nem a betöltött pontok tartománya.

Az out_size_factor=4 értéknek a targetképzésben (model.train_cfg.pts), a tesztconfigban és a bbox coderben is egyeznie kell. Különben a tanítás és a dekódolás más fizikai helyet rendel ugyanahhoz a heatmap-cellához.

6. Inference és NMS

A modell alapconfigja rotate NMS-t tartalmaz. A notebook által használt legfelső child config ezt circle NMS-re írja felül:

model = dict(
	test_cfg=dict(
		pts=dict(nms_type='circle'),
	),
)
Kulcs Érték Hatás inferenciakor
nms_type circle A jelöltek elnyomása a BEV-középpontok távolsága alapján történik.
min_radius [4, 12, 10, 1, 0.85, 0.175] A hat task minimális középpont-távolsága méterben. A nagy járművek nagyobb, a gyalogos/traffic-cone task kisebb sugarat kap.
pre_max_size 1000 Legfeljebb ennyi legerősebb jelölt kerül az NMS elé.
post_max_size 83 NMS után taskonként legfeljebb ennyi jelölt marad; ez nem a hat task együttes kerete.
score_threshold 0.1 A test config score-küszöbe. Circle NMS-nél a bbox coder azonos célú küszöbe már a dekódolás során szűr.
nms_thr 0.2 A forgatott IoU-alapú NMS küszöbe; circle mód mellett ez a mező nem aktív.

A model.train_cfg.pts.min_radius=2 és a model.test_cfg.pts.min_radius=[...] nem ugyanaz. Az első Gauss-foltot rajzol feature-map cellákban tanításkor; a második center-distance NMS-t végez méterben inferenciakor.

7. Miért szerepel kétszer a dataset szó?

A train dataloaderben a tényleges nuScenes datasetet egy mintavételi burkoló veszi körül:

train_dataloader.dataset                 → CBGSDataset burkoló
train_dataloader.dataset.dataset         → belső NuScenesDataset
train_dataloader.dataset.dataset.ann_file → az info-fájl neve

A CBGSDataset jelentése Class-Balanced Grouping and Sampling. Nem olvas új fájlformátumot; a belső dataset mintáit úgy ismétli/mintavételezi, hogy a ritkább osztálycsoportok gyakrabban kerüljenek batchbe. Ezért kell a saját ann_file értéket két dataset szint alatt megadni.

8. Mit csinál a train pipeline?

A pipeline lista sorrendje számít: minden transzformáció az előző kimenetét kapja.

Transzformáció Mit csinál? Miért szükséges?
LoadPointsFromFile Betölti az 5 dimenziós LiDAR-pontokat. Ez hozza létre a modell nyers bemenetét.
LoadAnnotations3D Betölti a GT 3D dobozokat és osztálycímkéket. Ezekből készül a heatmap- és bbox-cél.
ObjectSample Előre kivágott GT objektumpontokat másol a jelenetbe a ground-truth adatbázisból. Növeli a ritka osztályok előfordulását; kizárólag train augmentáció.
GlobalRotScaleTrans A teljes jelenetet és minden GT dobozt együtt forgatja és skálázza. A modell kevésbé függjön egy pontos orientációtól vagy méretskálától.
RandomFlip3D Véletlenszerűen tükrözi a BEV-jelenetet és a dobozokat. További geometriai augmentáció.
PointsRangeFilter Eldobja a point_cloud_range tartományon kívüli pontokat. A modell csak a konfigurált térrel dolgozzon.
ObjectRangeFilter Eldobja a tartományon kívüli GT dobozokat. A targetek ugyanabban a térben maradjanak, mint a pontok.
ObjectNameFilter Csak a konfigurált class_names osztályokat tartja meg. Ismeretlen kategória ne kerüljön a head targetjei közé.
PointShuffle Megkeveri a pontok sorrendjét. A modell ne használhassa ki a fájl véletlen sorrendi sajátosságait.
Pack3DDetInputs Szabványos inputs és Det3DDataSample szerkezetbe csomagol. Ezt az interfészt várja az MMEngine Runner és a modell.

A val/test pipeline nem alkalmaz ObjectSample augmentációt és nem végez véletlen train transzformációkat. Kiértékeléskor ugyanazt a mintát mindig ugyanúgy kell a modellnek megkapnia.

Saját train splitnél az ann_file és az ObjectSample által használt db_sampler.info_path ugyanahhoz a train splithez tartozzon. Ellenkező esetben validációs vagy tesztobjektum kerülhet a tanításba.

Paraméterváltoztatás előtt tegyük fel ezt a négy kérdést

  1. Alakot változtat? Például csatornaszám módosításakor illeszkedik-e a következő réteg?
  2. Geometriát változtat? Például voxelméret módosításakor frissült-e minden kapcsolódó range/grid/coder mező?
  3. Checkpoint-kompatibilis? Egy réteg csatornaszámának módosítása után a régi súlytenzor alakja már nem illeszkedik.
  4. Csak tanításra vagy inferenciára hat? Például loss_weight nem változtatja a már elkészült modell predikcióját; score_threshold nem tanítja újra a hálót.

3.5 Hol van implementálva a CenterPoint lényege?

Hogyan jut el a tools/train.py a CenterPoint kódjáig?

A tools/train.py nem tartalmaz külön CenterPoint-specifikus tanítási ciklust. Ugyanaz a belépési pont sok különböző modellhez használható:

  1. A Config.fromfile(...) beolvassa és összefésüli a _base_ configokat.
  2. Az MMEngine Runner a végleges configból felépíti a modellt, dataloadereket, evaluatort, optimizert, schedulert és hookokat.
  3. A model.type='CenterPoint' alapján a MODELS registry megkeresi a CenterPoint Python-osztályt.
  4. A CenterPoint parent osztályának konstruktora a beágyazott type mezők alapján külön felépíti a PillarFeatureNet, PointPillarsScatter, SECOND, SECONDFPN és CenterHead modulokat.
  5. Tanításkor a Runner a modell loss() útját hívja, majd visszaterjesztést és optimizer-lépést végez.
  6. Validációkor és tesztkor a predict() út fut, amely dekódolt dobozokat ad; nincs súlyfrissítés.
flowchart LR
	C["Config.fromfile<br/>öröklés + felülírás"] --> R["Runner.from_cfg"]
	R --> REG["MODELS registry<br/>type = CenterPoint"]
	REG --> D["CenterPoint detektor"]
	D --> SUB["Almodulok buildelése<br/>encoder, backbone, neck, head"]
	SUB --> TRAIN["train: loss()"]
	SUB --> TEST["val/test: predict()"]
Loading

Ez a szétválasztás teszi lehetővé, hogy ugyanazt a modellt külön kézzel írt tréningciklus nélkül lehessen tanítani és tesztelni.

A modell összekötése

Fájl Fontos elem Szerep
mmdet3d/models/detectors/centerpoint.py CenterPoint Vékony detektorosztály; a komponenseket az örökölt detektorhoz továbbítja.
mmdet3d/models/detectors/mvx_two_stage.py extract_pts_feat() Pillar encoder → scatter → backbone → neck adatút.
ugyanott loss() Jellemzőkinyerés után meghívja a bbox head lossát.
ugyanott predict() Jellemzőkinyerés után meghívja a bbox head predikcióját, és Det3DDataSample objektumba csomagolja.

Fontos kódolvasási tanulság: a CenterPoint osztály önmagában rövid, mert a tényleges előremenet jelentős részét a parent osztály implementálja. Ha csak a név szerinti osztályt olvassuk el, nem látjuk a teljes működést.

A pszeudokép előállítása

Fájl Metódus Kapcsolat az 1. résszel
mmdet3d/models/voxel_encoders/pillar_encoder.py PillarFeatureNet.forward() Kiszámítja a cluster- és pillar-center offseteket, maszkolja a paddinget, majd PFN rétegeket futtat.
mmdet3d/models/middle_encoders/pillar_scatter.py PointPillarsScatter.forward_batch() A nem üres pillarok jellemzőit a $(C,H,W)$ pszeudokép megfelelő celláiba írja.

A detekciós fej

A legfontosabb fájl:

mmdet3d/models/dense_heads/centerpoint_head.py

Osztály/metódus Mit érdemes benne megkeresni?
SeparateHead.__init__() A reg, height, dim, rot, vel és heatmap konvolúciós ágak felépítése.
SeparateHead.forward() A külön ágak kimeneti szótárának előállítása.
CenterHead.forward_single() Közös konvolúció, majd taskonként külön fej futtatása.
CenterHead.get_targets_single() Középpont rácskoordinátája, Gauss-sugár, heatmap rajzolása és a 10 elemű regressziós célvektor.
CenterHead.loss_by_feat() Gaussian Focal Loss, regressziós kimenetek összefűzése, középponti értékek kigyűjtése, súlyozott L1.
CenterHead.predict_by_feat() Sigmoid, bbox coder meghívása, circle vagy rotate NMS és a taskok eredményeinek egyesítése.

A dekódolás külön task-modulban található:

mmdet3d/models/task_modules/coders/centerpoint_bbox_coders.py

Metódus Szerep
_topk() Kiválasztja a legerősebb hőtérképpozíciókat és osztályukat.
_transpose_and_gather_feat() A kiválasztott indexeknél kigyűjti a regressziós értékeket.
decode() Offset, méret, irány és sebesség dekódolása; méteres koordináták és tartományszűrés.

Tanítási hívási lánc

sequenceDiagram
	participant R as MMEngine Runner
	participant D as MVXTwoStageDetector
	participant P as Pillar + backbone + neck
	participant H as CenterHead
	R->>D: loss(batch_inputs, data_samples)
	D->>P: extract_pts_feat(...)
	P-->>D: BEV feature map
	D->>H: loss(features, data_samples)
	H->>H: forward()
	H->>H: get_targets_single()
	H->>H: loss_by_feat()
	H-->>R: loss_heatmap + loss_bbox
Loading

Inference hívási lánc

sequenceDiagram
	participant D as MVXTwoStageDetector
	participant H as CenterHead
	participant C as CenterPointBBoxCoder
	D->>H: predict(features, data_samples)
	H->>H: forward()
	H->>C: decode(heatmap, reg, height, dim, rot, vel)
	C-->>H: dobozjelöltek
	H->>H: circle_nms() / nms_bev()
	H-->>D: InstanceData
	D-->>D: add_pred_to_datasample()
Loading

3.6 Saját CenterPoint config összeállítása

A bevett megoldás nem egy meglévő model-zoo config átírása, hanem egy rövid child config létrehozása. Így látható marad, mit változtattunk, és az eredeti baseline reprodukálható.

Az alábbi példa akkor helyezhető a configs/centerpoint/centerpoint_pillar02_nus_custom.py fájlba, ha az alapconfig ugyanebben a mappában található:

# A teljes model-zoo kísérletet örököljük; lent csak az eltéréseket írjuk le.
_base_ = [
	'./centerpoint_pillar02_second_secfpn_head-circlenms_8xb4-cyclic-20e_nus-3d.py'
]

# Az útvonal a repository gyökeréhez képest értendő.
data_root = 'data/nuscenes/'

train_dataloader = dict(
	batch_size=2,              # GPU-nként 2 minta; az alapconfigban 4.
	num_workers=2,             # GPU-nként 2 CPU betöltőfolyamat; az alapban 4.
	persistent_workers=True,   # A workerek epochok között is életben maradnak.
	dataset=dict(
		# A külső dataset CBGSDataset, ezért a tényleges NuScenesDataset
		# beállításai a második `dataset` szint alatt találhatók.
		dataset=dict(
			data_root=data_root,
			ann_file='nuscenes_infos_train.pkl',  # A train minták info-fájlja.
		),
	),
)

# Validációkor batch_size=1 marad az alapconfigból; csak az adatútvonalat
# és az annotációs fájlt tesszük explicitté.
val_dataloader = dict(
	dataset=dict(
		data_root=data_root,
		ann_file='nuscenes_infos_val.pkl',
	),
)

# A példában a test ugyanazt a hivatalos mini_val splitet használja.
test_dataloader = val_dataloader

# Az evaluator ugyanahhoz a val info-fájlhoz és adatgyökérhez tartozzon.
val_evaluator = dict(
	data_root=data_root,
	ann_file=data_root + 'nuscenes_infos_val.pkl',
)
test_evaluator = val_evaluator

model = dict(
	pts_bbox_head=dict(
		# A következő két érték megegyezik az alapmodellel. Megmutatják, hol
		# állítható a head kapacitása, de így a checkpoint alakjai illeszkednek.
		share_conv_channel=64,   # Közös head feature csatornaszáma.
		num_heatmap_convs=2,     # Konvolúciók száma a heatmap ágban.

		# Csak a szorzót írjuk felül; a type='mmdet.L1Loss' öröklődik.
		loss_bbox=dict(loss_weight=0.5),  # Alapérték: 0.25.

		# A dekóder a 0.15 alatti jelölteket NMS előtt eldobja.
		bbox_coder=dict(score_threshold=0.15),  # Alapérték: 0.10.
	),
	test_cfg=dict(
		pts=dict(
			nms_type='circle',     # Középpont-távolság alapú NMS.
			post_max_size=100,     # Taskonként legfeljebb 100 doboz NMS után; alap: 83.
		),
	),
)

# A teljes örökölt optimizer-részfát lecseréljük, ezért kell a _delete_.
optim_wrapper = dict(
	_delete_=True,
	type='OptimWrapper',
	optimizer=dict(
		type='AdamW',
		lr=5e-5,                 # Kezdő learning rate; az alapérték 1e-4.
		betas=(0.9, 0.999),      # A gradiens mozgóátlagainak simítása.
		weight_decay=0.01,       # AdamW regularizációs erőssége.
	),
	clip_grad=dict(
		max_norm=10,             # A teljes gradiens L2 normájának felső korlátja.
		norm_type=2,
	),
)

# A lista lecseréli az örökölt kétfázisú, 20 epochos schedule-t.
param_scheduler = [
	dict(
		type='CosineAnnealingLR',
		T_max=4,                 # A cosine görbe hossza epochban.
		eta_min=5e-6,            # A learning rate alsó határa.
		begin=0,                 # Az első epochtól aktív.
		end=4,                   # A negyedik epoch végéig aktív.
		by_epoch=True,           # Epoch-egységben adjuk meg a tartományt.
		convert_to_iter_based=True,  # Iterációnként simán változtatja az LR-t.
	),
]

train_cfg = dict(
	by_epoch=True,             # Epochalapú tanítási ciklus.
	max_epochs=4,              # Rövid oktatási futás; az alapconfigban 20.
	val_interval=1,            # Minden epoch végén validál; az alapban 20.
)

default_hooks = dict(
	logger=dict(interval=20),  # 20 iterációnként ír logsort; az alapban 50.
	checkpoint=dict(
		interval=1,              # Minden epoch végén ment.
		max_keep_ckpts=2,        # Csak a két legutóbbi periodikus mentést tartja meg.
	),
)

# Finomhangolás: csak a modellsúlyokat tölti be, az optimizer új állapotból indul.
# Ezt a fájlt a gyakorlati notebook checkpoint-letöltő cellája hozza létre.
load_from = 'checkpoints/centerpoint_pillar02_nuscenes.pth'
resume = False

Ha a hivatalos train split helyett saját splitet használunk, az ann_file mellett az örökölt ObjectSample transzformáció db_sampler.info_path értékét is a saját train splitből készített ground-truth adatbázisra kell cserélni.

1. Mit változtatunk az adatbetöltésen?

Kulcs Alap → példa Gyakorlati következmény
train_dataloader.batch_size 4 → 2 Körülbelül feleannyi minta fér egyszerre a GPU-ra. Csökken a memóriaigény, de zajosabb lehet a gradiens és több iteráció kell egy epochhoz.
num_workers 4 → 2 Kevesebb párhuzamos CPU-folyamat tölt adatot. Gyenge CPU-n stabilabb lehet, gyors GPU mellett viszont adatbetöltési várakozást okozhat.
persistent_workers True Nem indulnak újra a workerek minden epoch elején, ezért kisebb az epochváltás költsége.
ann_file explicit train/val fájlok Egyértelművé teszi, melyik split tanít és melyiken mérünk.

A batch_size itt GPU-nkénti érték. Két GPU esetén a globális batch 4 lenne. Mivel az auto_scale_lr.enable=False öröklődik, a keretrendszer nem módosítja automatikusan a learning rate-et a batchmérethez; ezt nekünk kell megfontolni.

A child configban definiált data_root változó nem írja át automatikusan az alapconfigban korábban összeállított összes útvonalat. Más adatgyökér esetén külön ellenőrizzük a dataset, evaluator és db_sampler útvonalait is a print_config.py kimenetében.

2. Mit változtatunk a modellen és az inferencián?

Kulcs Alap → példa Értelmezés
share_conv_channel 64 → 64 Szándékosan nem változik, hogy az előre tanított head súlyainak alakja illeszkedjen.
num_heatmap_convs 2 → 2 Szintén checkpoint-kompatibilis marad.
loss_bbox.loss_weight 0.25 → 0.5 A bbox regressziós hiba kétszer akkora relatív súlyt kap. Ez csak tanításkor hat, és nem garantál automatikus javulást.
bbox_coder.score_threshold 0.10 → 0.15 Kevesebb, de átlagosan magabiztosabb jelölt jut az NMS-hez. Csökkenhet a fals pozitív detekciók száma, de több valódi objektum maradhat ki.
post_max_size 83 → 100 Circle NMS után taskonként több doboz maradhat. Ez csak felső korlát, nem jelenti azt, hogy mindig 100 dobozt kapunk.

Ha a share_conv_channel értékét például 96-ra változtatnánk, a head súlytenzorainak alakja megváltozna. A 64 csatornára tanított checkpoint ezekbe a rétegekbe nem tölthető be közvetlenül; betöltéskor méret- vagy size mismatch hiba várható. Architektúrakísérletnél vagy kompatibilis checkpoint szükséges, vagy a módosított rétegeket újonnan kell inicializálni és külön kezelni a súlybetöltést.

3. Mit állít az optimizer?

  • AdamW: adaptív learning rate-et használ paraméterenként, a weight decay-t az Adam momentumfrissítésétől elkülönítve alkalmazza.
  • lr=5e-5: az alap $10^{-4}$ felére csökkentett kezdő lépésméret. Finomhangoláskor ez óvatosabb súlyfrissítést ad.
  • betas=(0.9, 0.999): a gradiens első és második momentumának simítási tényezői. Kezdő kísérletben célszerű ezeket változatlanul hagyni.
  • weight_decay=0.01: regularizáció, amely visszafogja a súlyok korlátlan növekedését.
  • clip_grad.max_norm=10: ha a teljes gradiens L2 normája 10 fölé nő, a keretrendszer visszaskálázza. Ez az instabil, nagy frissítések ellen véd.

A _delete_=True itt azt jelenti: ne próbáljuk mezőnként összefésülni az örökölt optim_wrapper szótárat, hanem a teljes optimizer-konfigurációt cseréljük le az itt láthatóra.

4. Mit állít a scheduler és a tanítási ciklus?

A cosine scheduler a learning rate-et közelítőleg koszinuszgörbe mentén csökkenti $5\cdot10^{-5}$ értékről $5\cdot10^{-6}$ értékig. A convert_to_iter_based=True miatt a változás nem négy nagy epochugrásban, hanem minden iteráció után simán történik.

Kulcs Jelentés
max_epochs=4 Az adathalmaz négyszeri bejárása. Ez rövid működésellenőrző futás („smoke test”), nem teljes CenterPoint tanítás.
val_interval=1 Minden epoch után lefut a validáció. Ez több idő, de korán látható a metrika trendje.
logger.interval=20 Húsz tanítási iterációnként jelenik meg új logbejegyzés. Nem 20 mintát és nem 20 epochot jelent.
checkpoint.interval=1 Minden epoch végén periodikus checkpoint készül.
max_keep_ckpts=2 A tárhely védelmében a két legutóbbi periodikus checkpoint marad meg.

5. load_from vagy resume?

Beállítás Modellsúly Optimizer/scheduler állapot Tipikus használat
load_from='...pth', resume=False betöltődik új állapotból indul finomhangolás vagy új kísérlet előre tanított modellből
resume=True betöltődik folytatódik megszakadt azonos futás folytatása ugyanabból a work_dir-ből

Finomhangoláskor azt szeretnénk, hogy a modell már tanult súlyokból induljon, de az új learning rate schedule az első epochtól fusson; ezért használjuk a példában a load_from + resume=False párost.

Erősen összekapcsolt geometriai paraméterek

Néhány érték nem módosítható biztonságosan egyetlen helyen:

  • point_cloud_range megjelenik a voxel layerben, a pillar encoderben, a bbox coderben, a train/test configban és a pipeline szűrőiben;
  • voxel_size meghatározza a grid_size, az output_shape, a feature map és a dekódolt koordináták kapcsolatát;
  • out_size_factor ugyanazt a leskálázást kell jelentse a targetképzésben és a bbox coderben;
  • class_names sorrendjének egyeznie kell a dataset metainfo, az objektumszűrő és a CenterHead tasks csoportjai között;
  • saját train split esetén az ann_file mellett az ObjectSample ground-truth adatbázisát is ugyanabból a splitből kell felépíteni.

Ha például csak a bbox_coder.voxel_size változik, a hálózat ugyanazt a cellát más fizikai helyre dekódolja. Ez futás közben akár észrevétlen maradhat, miközben minden doboz eltolódik.

3.7 Config ellenőrzése és futtatása

Módosítás után először mindig a teljes, összeolvasztott configot érdemes megvizsgálni:

python tools/misc/print_config.py \
  configs/centerpoint/centerpoint_pillar02_nus_custom.py

Tanítás:

python tools/train.py \
  configs/centerpoint/centerpoint_pillar02_nus_custom.py \
  --work-dir work_dirs/centerpoint_pillar02_nus_custom

Kiértékelés:

python tools/test.py \
  configs/centerpoint/centerpoint_pillar02_nus_custom.py \
  work_dirs/centerpoint_pillar02_nus_custom/epoch_4.pth

Minimális hibakeresési sorrend

  1. Config: a print_config.py kimenetében tényleg a várt érték szerepel?
  2. Adat: a dataloader egy mintája tartalmaz points, gt_bboxes_3d és gt_labels_3d mezőt?
  3. Alakok: a pillar encoder, scatter, backbone, neck és head csatornaszámai illeszkednek?
  4. Koordináták: az adatok, range filterek, targetképzés és coder ugyanazt a LiDAR-frame-et használják?
  5. Loss: végesek az értékek, és kap pozitív mintát a heatmap?
  6. Dekódolás: megfelelő a score-küszöb, a fizikai tartomány és az NMS?
  7. Metrika: ugyanazon spliten és azonos evaluatorral hasonlítjuk össze a kísérleteket?

3.8 Elmélet és notebook együtt

Elméleti anyag Gyakorlati notebook
Miért kell pillar vagy voxel reprezentáció? Telepíti és betölti a pillar CenterPoint modellt.
Hogyan készül a pszeudokép? A config PillarFeatureNet és PointPillarsScatter elemeit használja.
Mit jelentenek a head kimenetei? Megjeleníti a dekódolt dobozokat, score-okat és osztályokat.
Hogyan működik a célképzés és a loss? Rövid finomhangolási futást készít elő.
Hogyan áll össze a config? Saját train/val/test info-fájlokat és training configot hoz létre.
Hol található az implementáció? init_model, inference_detector, tools.train és tools.test API-kat futtat.
Mi a korrekt mérési protokoll? Hivatalos nuScenes mini validációt és metrikákat használ.

Összefoglalás

  • A pontfelhő rendezetlen, ritka és változó elemszámú; a hagyományos CNN szabályos rácsot vár.
  • A PointNet szimmetrikus poolinggal, a VoxelNet tanult 3D voxelreprezentációval, a PointPillars pedig pillarokból képzett 2D pszeudoképpel kezeli ezt az eltérést.
  • A PointPillars pontonkénti relatív geometriai jellemzőket képez, pillárisonként maximumot számít, majd a vektorokat BEV-vászonra scattereli.
  • A CenterPoint anchor-free módon osztályonkénti középpont-hőtérképet és a középpontokhoz tartozó 3D dobozparamétereket becsül.
  • A heatmapet Gaussian Focal Loss, a középpontoknál kigyűjtött dobozkódot súlyozott L1 loss tanítja.
  • A dekóder Top-K heatmap-csúcsból, offsetből, méretből, irányból és sebességből készít 3D dobozt, majd score-szűrést és NMS-t alkalmaz.
  • Az MMDetection3D-ben a config írja le a kísérletet, a registry építi fel a komponenseket, az MMEngine Runner pedig megszervezi a tanítást és a kiértékelést.
  • A configértékek többsége hiperparaméter; a tanult súlyokat a lossból számított gradiensek alapján az optimizer módosítja.
  • A dataloader, modell, optimizer, scheduler, Runner és hookok külön felelősségű elemek, amelyeket a config kapcsol össze.
  • A geometriai paramétereket, például a range, voxelméret, gridméret és dekódolási skála értékeit konzisztensen kell módosítani.
  • A CenterPoint matematikai lényege főként a centerpoint_head.py célképző, loss- és predikciós metódusaiban, valamint a centerpoint_bbox_coders.py dekóderében követhető.

További források

Gyakorlás


A kapcsolódó, Colabban is futtatható gyakorlati notebook:

Neurális hálózatok alapjai

⚠️ **GitHub.com Fallback** ⚠️