Ez az alfejezet a konvolúciós réteg három, egymástól élesen különböző mérőszámát vezeti be: a paraméterszámot (parameter count), a MAC-műveletek számát (multiply–accumulate) és az aktivációmemóriát. A kiinduló kérdés a következő: egy csatornás, -as konvolúció 112 tanulható skalárt tartalmaz; hány szorzás–összeadás műveletet végez ez a réteg egyetlen -as képen? Ha a tipp tízes nagyságrendű, a válasz három nagyságrenddel nagyobb lesz: a súlymegosztás miatt minden paraméter sok térbeli pozícióban újra dolgozik. A 14.2. Több bemeneti és kimeneti csatorna alfejezetben levezetett kernel bank és a 13.3. Lokális szűrők és tanult reprezentáció alfejezetben tárgyalt súlymegosztás itt válik költségmodellé.
Legyen egy 2D konvolúciós réteg bemenete
,
súlya
,
biasa
,
ahol
a groups paraméter,
és
osztható
-vel,
a kimeneti térbeli méret pedig
a 14.1. Stride, padding, dilation és outputméret
alfejezetben rögzített képlet szerint.
Paraméterszám. A réteg tanulható skalár
együtthatóinak száma:
ahol az utolsó tag a bias, és csak bias=True esetén
szerepel. A
a modell kapacitásának egy durva mutatója, és független
a bemenet
,
,
méretétől.
MAC. Egy MAC egyetlen valós szorzásból és egyetlen valós összeadásból álló műveletpár. A réteg forward keresztkorrelációja MAC-ot végez. A MAC a számítási költség mutatója, és a batch méretével lineárisan nő.
FLOP konvenció. Ebben a könyvben egy MAC-ot két lebegőpontos műveletnek számolunk: egy szorzásnak és egy összeadásnak, tehát a lineáris részre . A bias hozzáadása kimeneti elemenként további egy összeadás, tehát a teljes forward ha a bias-összeadást is számoljuk. Ez a konvenció nem univerzális; egyes profilozók a MAC-ot magát nevezik műveletnek. A félreértés elkerülésére mindig a MAC-értéket adjuk meg elsődlegesen, és a FLOP-átszámítást külön jelöljük.
Aktivációmemória. A réteg kimeneti tenzora , elemszáma , memóriaigénye bájt, ahol a dtype mérete: float32 esetén , float64 esetén . A visszafelé menethez a keretrendszer a bemeneti aktivációt is eltárolhatja, ezért a réteg tényleges aktivációigénye ennél nagyobb lehet.
Gradienspuffer. A visszafelé menet tenzorai: a súlygradiens alakja megegyezik alakjával, a biasgradiens hossza , a bemeneti gradiens alakja megegyezik alakjával. A gradienspuffer legalább ezen tenzorok együttes mérete; a pontos foglalás implementációfüggő, ezért itt alsó becslést adunk.
Batch-skálázás. Az növelése a paraméterszámot nem változtatja, a MAC-ot és az aktivációmemóriát viszont -szeresére növeli. Ezért nagy batch esetén a szűk keresztmetszet jellemzően a számítás és a memória, nem a paraméterek tárolása.
Tétel (Conv2d paraméterszám). Legyen egy
torch.nn.Conv2d réteg
bemeneti és
kimeneti csatornával,
csoporttal,
kernelmérettel, és bias=True esetén. Ekkor a réteg
tanulható paramétereinek száma
Ha bias=False, a
tag elmarad.
Bizonyítás. A [D10] dokumentációban rögzített súlyalak . Egy dimenziójú tenzor elemszáma a dimenziók szorzata, mert az indexhalmaz a Descartes-szorzat, amelynek számossága . Ezért a súlytenzor elemet tartalmaz. A bias tenzor alakja , tehát további skalárt ad. A súly és a bias két diszjunkt paraméterhalmaz, így a számok összeadódnak. Speciálisan esetén visszakapjuk a teljes csatornaösszekapcsolású képletet: . ∎
A csoportos esetben minden kimeneti csatorna csak bemeneti csatornát lát; a csoportosítás tehát a paraméterszámot is csökkenti, de a csatornák közötti kapcsolatot is korlátozza. Ezt a kompromisszumot a 14.4. Csoportos és depthwise konvolúció vizsgálja részletesen.
Lemma (MAC-szám). A fenti jelölésekkel, bemenet és kimenet esetén a réteg forward keresztkorrelációja MAC-ot végez.
Bizonyítás. Rögzítsünk egy kimeneti elemet a indexnégyessel. A 14.2. Több bemeneti és kimeneti csatorna alfejezetbeli forward összeg szerint ez az elem a csatornacsoport bemeneti csatornáján és a kernel pozícióján egy-egy szorzást végez, és az eredményeket összeadja. A szorzás–összeadás párok száma tehát , függetlenül a konkrét pozíciótól. A kimeneti elemek száma . A két mennyiség szorzata adja -et. A bias hozzáadása kimeneti elemenként egyetlen összeadás, szorzás nélkül, ezért a MAC-értékben nem szerepel; a FLOP-konvencióban külön tételként adjuk hozzá. ∎
A MAC tehát a kimeneti tenzor elemszámának és az egy kimeneti elemhez
tartozó belső összeg hosszának szorzata. A dilation
paraméter nem változtatja a kernelpozíciók
számát, csak a
értékeken keresztül hat a MAC-ra.
Egy réteg kimeneti aktivációja elemet tartalmaz. A csak a kimenetre számított memória bájt. A teljes hálózat aktivációmemóriája a rétegenkénti kimenetek összege, ha minden réteg kimenetét megőrizzük a backwardhoz; a bemeneti tenzor mérete . A gyakorlati keretrendszerek memóriahasználata ennél nagyobb lehet a gradienspuffer és a köztes munkaterületek miatt.
A gradienspuffer legfontosabb tagjai:
| Tenzor | Alak | Elemszám |
|---|---|---|
A paramétergradiensek mérete batch-független, mert a veszteség batch-átlaga már egyetlen tenzorként aggregálja a gradienst. A bemeneti gradiens viszont -szeresére nő. A gradiensellenőrzés 10.3. Gradiensellenőrzés és numerikus hibamérce alfejezetben float64-ben futott, ami bájtos elemekkel kétszeres memóriát jelent a float32 traininghez képest; ezért a numerikus ellenőrzés és a tanítás memóriaigénye nem hasonlítható össze közvetlenül.
Legyen
,
bemenet
.
Két réteget vizsgálunk: conv1
,
,
,
,
;
conv2
,
,
,
,
.
A kimeneti méretek:
,
.
| Réteg | Paraméter | MAC | ||||
|---|---|---|---|---|---|---|
| conv1 | 3 | 4 | 3, 1, 1 | 8 | ||
| conv2 | 4 | 8 | 3, 1, 2 | 4 | ||
| Összes | 408 | 23040 |
Az aktivációk: conv1 kimenete elem, bájt float32-ben; conv2 kimenete elem, bájt. A FLOP-átszámítás a lineáris részre ; a bias-összeadások száma , így a teljes forward FLOP . A paraméterszám ugyanakkor : a súlymegosztás miatt egy-egy paraméter átlagosan több mint ötvenszer vesz részt a számításban.
A labor célja, hogy a kézzel számolt értékeket a PyTorch API objektumain ellenőrizze. A kód CPU-n, float32-ben fut; seedre nincs szükség, mert csak alak- és paraméterellenőrzést végzünk. A 2.3. Izolált környezet, notebook és verzió alfejezetben rögzített izolált környezetben futtatandó.
# lab/n055/param_mac_check.py
# Munkakönyvtár: lab/n055
# Környezet: Python 3.12 tanulási profil, CPU, torch float32.
import torch
import torch.nn as nn
def conv_output_size(h, k, p, s):
return (h + 2*p - k) // s + 1
def conv_mac(n, c_out, h_out, w_out, c_in_per_group, kh, kw):
return n * c_out * h_out * w_out * c_in_per_group * kh * kw
def report(conv, x):
with torch.no_grad():
y = conv(x)
n, c_out, h_out, w_out = y.shape
c_in_per_group = conv.in_channels // conv.groups
params = sum(t.numel() for t in conv.parameters())
macs = conv_mac(n, c_out, h_out, w_out, c_in_per_group,
conv.kernel_size[0], conv.kernel_size[1])
act_bytes = y.numel() * y.element_size()
return params, macs, tuple(y.shape), act_bytes
class SmallCNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 4, 3, padding=1)
self.conv2 = nn.Conv2d(4, 8, 3, padding=1, stride=2)
def forward(self, x):
x = torch.relu(self.conv1(x))
return self.conv2(x)
model = SmallCNN()
total_params = sum(t.numel() for t in model.parameters())
assert total_params == 408, total_params
print("Teljes paraméterszám:", total_params)
x1 = torch.zeros(2, 3, 8, 8, dtype=torch.float32)
x2 = torch.zeros(2, 4, 8, 8, dtype=torch.float32)
for name, conv, x in [("conv1", model.conv1, x1),
("conv2", model.conv2, x2)]:
p, m, shape, abytes = report(conv, x)
print(name, "params=", p, "mac=", m, "shape=", shape,
"activation_bytes=", abytes)
assert (shape[2], shape[3]) == (
conv_output_size(x.shape[2], conv.kernel_size[0],
conv.padding[0], conv.stride[0]),
conv_output_size(x.shape[3], conv.kernel_size[1],
conv.padding[1], conv.stride[1]))Az alábbi kimenet a képletekből számított szemléltetés, amelyet a fenti kód külön futtatásával kell ellenőrizni:
Teljes paraméterszám: 408
conv1 params= 112 mac= 13824 shape= (2, 4, 8, 8) activation_bytes= 2048
conv2 params= 296 mac= 9216 shape= (2, 8, 4, 4) activation_bytes= 1024
Az assert a kimeneti méretet is ellenőrzi, így a padding
és stride képlete nem számolódhat félre. Az element_size()
float32 esetén 4-et ad; ha a környezet véletlenül float64-et használna,
az aktivációbájtok megduplázódnának, amit a nyomtatott érték azonnal
mutat.
F1 (fogalmi, nehézség 1). Magyarázza el saját
szavaival, miért lehet egy konvolúciós réteg paraméterszáma kicsi,
miközben a MAC-értéke nagy. Használja a labor conv1 rétegét
konkrét számpéldaként. Elvárt átadandó: a súlymegosztás és a kimeneti
térbeli méret szerepeltetése; a 112 paraméter és a 13824 MAC
összevetése.
F2 (levezetési, nehézség 2). Vezesse le a
paraméterszámot és a MAC-ot egy csoportos konvolúcióra:
,
,
,
,
bias=True, bemenet
,
,
padding
,
stride
.
Ellenőrizze a
esetet is, és fogalmazza meg, mit veszít a csoportosítás a
megtakarításért cserébe. Elvárt átadandó: mindkét eset
,
és
értéke, valamint a kapcsolatkorlátozás megnevezése.
F3 (implementációs, nehézség 2). Írjon egy
conv_stats(model, x) függvényt, amely egy
nn.Sequential modellen végighaladva összegzi a konvolúciós
rétegek paraméterszámát, MAC-értékét és aktivációbájtjait float32
bemenetre. A ReLU rétegen haladjon át alakváltozás nélkül.
Tesztelje a labor SmallCNN modelljén. Elvárt átadandó:
futtatható kód és a
hármas ellenőrzése assert-tel.
F4 (hibakeresési, nehézség 2). Adott a következő hibás becslés:
params_bad = c_out * c_in * kh * kw # (1)
macs_bad = c_out * h_out * w_out * kh * kw # (2)Azonosítsa mindkét hiba okát és következményét a labor
conv1 rétegén, adja meg a javított képletet, és
ellenőrizze, hogy a javítás visszaadja a 112 és 13824 értékeket. Elvárt
átadandó: mindkét hibás részképlet hibája, a bias=True és a
batch/csatornatényező szerepének magyarázata, javítás és ellenőrzés.
F5 (kísérleti/transzfer, nehézség 3). Tervezzen mérést annak eldöntésére, hogy két, azonos paraméterszámú, de eltérő konvolúciós elrendezésű modell közül melyiknek kisebb az inference-késleltetése egy adott CPU-n. Adjon meg protokollt: bemeneti méret, batch, warmup, ismétlésszám, időmérés módja, szinkronizáció, metrika és a következtetés érvényességi korlátja. Elvárt átadandó: egy lehetséges terv az alternatívák indoklásával; előre megadott győztes nem szükséges.
F1 mintamegoldás. A conv1 súlytenzora
,
biasszal együtt 112 tanulható skalárt tartalmaz. A forward során a
kimeneti tenzornak
eleme van, és minden kimeneti elemhez
szorzás–összeadás pár tartozik:
MAC. A paraméterek tehát azért használódnak ennyiszer, mert ugyanaz a
-as
kernel súlya a kép minden térbeli pozíciójában újra és újra
alkalmazódik; ez a 13.3. Lokális szűrők és tanult
reprezentáció alfejezetben tárgyalt súlymegosztás. A paraméterszám a
kapacitást, a MAC a számítási költséget méri, a kettő nem cserélhető
fel.
F2 megoldás. A kimeneti méret: . A esetben minden kimeneti csatorna bemeneti csatornát lát. A Tétel szerint és a Lemma szerint A esetben minden kimeneti csatorna mind a 6 bemeneti csatornát látja: A csoportosítás tehát a paramétereket és a MAC-ot harmadára csökkenti, de cserébe az egyes kimeneti csatornák nem férhetnek hozzá a bemeneti csatornák többségéhez; a csatornák közötti kapcsolat korlátozott.
F3 mintakód.
# lab/n055/feladat_f3.py
import torch
import torch.nn as nn
def conv_stats(model, x, dtype_size=4):
total_params = total_macs = total_act_bytes = 0
h = x
for layer in model:
if isinstance(layer, nn.Conv2d):
total_params += sum(p.numel() for p in layer.parameters())
h = layer(h)
n, c_out, h_out, w_out = h.shape
c_in_per_group = layer.in_channels // layer.groups
total_macs += n * c_out * h_out * w_out * c_in_per_group \
* layer.kernel_size[0] * layer.kernel_size[1]
total_act_bytes += h.numel() * dtype_size
# h.element_size() == dtype_size float32 esetén
elif isinstance(layer, nn.ReLU):
h = layer(h) # az alak nem változik
else:
raise TypeError(f"Nem kezelt réteg: {type(layer)}")
return total_params, total_macs, total_act_bytes
model = nn.Sequential(
nn.Conv2d(3, 4, 3, padding=1),
nn.ReLU(),
nn.Conv2d(4, 8, 3, padding=1, stride=2),
)
x = torch.zeros(2, 3, 8, 8, dtype=torch.float32)
stats = conv_stats(model, x)
print(stats)
assert stats == (408, 23040, 3072), statsA megoldás a labor SmallCNN rétegsorrendjét követi:
conv1, ReLU, conv2. A ReLU alakváltozás nélkül adódik
tovább, ezért az aktivációösszegbe nem számít új tenzort; ez a leképezés
itt elfogadott, de a valódi keretrendszer memóriája ennél nagyobb lehet
a gradienspuffer miatt.
F4 megoldás. Az (1) képlet két hibát tartalmaz:
hiányzik a groups osztás, és hiányzik a bias. A labor
conv1 rétegen
,
ezért a csoportosítás most nem látszik, de a bias igen: a hibás érték
,
a helyes
.
A (2) képlet elfelejti a batch
és a bemeneti csatornacsoport
tényezőjét; a labor conv1-nél a hibás érték
,
a helyes
,
tehát hatszoros alulbecslés. A javítás:
params_ok = c_out * (c_in // groups) * kh * kw + (c_out if bias else 0)
macs_ok = n * c_out * h_out * w_out * (c_in // groups) * kh * kwA labor conv1-re params_ok=112,
macs_ok=13824, ami visszaadja a kézzel számolt értéket. A
hiba következménye a költségmodell alulbecslése: rossz memória- és
futásidő-előrejelzés.
F5 mintamegoldás. Protokollterv: rögzített bemenet
,
float32, CPU; a két modell paraméterszámát egyezőre állítjuk, de az
egyik kevés nagy kernelű réteget, a másik sok kis réteget használ.
Bemelegítés: 20 forward hívás időmérés nélkül. Mérés: 200 ismétlés,
minden ismétlésben egy teljes forward, a CPU-időt
time.perf_counter-rel mérjük, és a mérést
torch.inference_mode() alatt végezzük. Metrika: a
batch-késleltetés mediánja és az ismétlések interkvartilis tartománya.
Összehasonlítás: a két modell váltakozó sorrendben fut, hogy a
hőmérsékleti és ütemezési hatás ne terhelje egyoldalúan az egyiket.
Következtetési korlát: az eredmény csak erre a hardverre, dtype-ra és
batchre érvényes; más gépre vagy GPU-ra nem vihető át. A mérés előre nem
dönt győztest; a paraméterszám egyezése mellett a MAC és a
memória-sávszélesség-hatás különválasztása további profillal lehetséges,
amit a 24.2. CPU/GPU profiling és helyes
időmérés dolgoz ki.
A leggyakoribb tévedés: „A kevés paraméter garantálja a kis inference-késleltetést.” A paraméterszám és a késleltetés különböző mennyiség; a késleltetést a MAC, a memóriaforgalom, a kernelimplementáció és a hardver együtt határozza meg.
Ellenpélda: legyen az modell egyetlen bias nélküli -as konvolúció csatornával, -es bemeneten. Paraméterszáma , MAC-értéke esetén millió. A modell egyetlen bias nélküli -es konvolúció csatornával, -es bemeneten. Paraméterszáma , tehát több mint kétszázszor nagyobb -énál; MAC-értéke viszont millió, tehát kisebb, mint -é. A paraméterszám szerinti rangsor tehát megfordulhat a számítási igény szerinti rangsorhoz képest. A tényleges késleltetést csak méréssel lehet eldönteni, de a MAC-eltérés már mutatja, hogy a paraméterszám önmagában nem elégséges előrejelző.
Ellenőrző lista: a paraméterképletben a bias-tag csak
bias=True esetén szerepel; a groups osztja
-t
és
-ot;
a MAC-képletben
,
,
,
,
,
,
mind tényező; a FLOP-átszámítás
plusz bias-add. Szélsőeset:
,
esetén a MAC a kernel bank méretével egyezik,
.
A gradienspuffer paraméterrésze batch-független, bemenetirésze
batch-lineáris. A 14.2. Több bemeneti és kimeneti
csatorna kernel bankja adja a paraméterszám tényezőit; a 13.3. Lokális szűrők és tanult reprezentáció
súlymegosztása magyarázza, miért haladhatja meg a MAC a paraméterszámot;
a 10.3. Gradiensellenőrzés és numerikus
hibamérce float64 ellenőrzése miatt a gradiensellenőrzés memóriája
nem azonos a float32 training memóriájával.
A konvolúciós réteg paraméterszáma ; ez a kapacitást méri, és független a bemeneti felbontástól. A MAC-érték ; a súlymegosztás miatt minden paraméter sok térbeli pozícióban dolgozik, ezért a MAC a kimeneti mérettel nő. A könyv FLOP-konvenciója egy MAC-ot két lebegőpontos műveletnek számol, a bias-összeadást külön tételként kezeli. Az aktivációmemória a kimeneti elemszám és a dtype méret szorzata, a gradienspuffer a paraméter- és bemenetigradiensek alakját követi. A paraméterszám nem helyettesíti a futásidő-mérést.
A 14.4. Csoportos és depthwise konvolúció egység a csoportos és depthwise konvolúciót vizsgálja; az itteni paraméter- és MAC-képlet ott válik a depthwise separable réteg költségmodelljének alapjává. A csoportosítás megtakarítása és kapcsolatkorlátozása közötti kompromisszumot számszerűen ott mérjük össze.