A 33.2. Batch és kis szolgáltatás egység a
tanított modellt helyi batch-szolgáltatásként futtatta a teljes
PyTorch-keretben. Most azt vizsgáljuk, hogyan adható át a modell olyan
környezetnek, ahol a PyTorch-keret nem elérhető, és milyen feltételekkel
állítható, hogy az exportált program ugyanazt a függvényt számolja.
Előismeret: az nn.Module regisztrációja és
state_dict-je (11.2. nn.Module és
paraméterregisztráció), a train/eval viselkedéskülönbség (12.1. Tanítás, validáció és kiértékelési mód, 21.3. Dropout, súlyregularizálás és augmentáció),
valamint az input-szerződés és batch-kezelés (33.2.
Batch és kis szolgáltatás). Induló kérdés: ha egy exportáló eszköz
egyetlen példabemeneten azonos kimenetet ad az eredeti modullal,
következik-e, hogy minden megengedett bemeneten azonos? A válasz nem;
ebben az alfejezetben pontosan megadjuk, milyen típusú állítást lehet és
érdemes ellenőrizni.
Legyen a tanított modul eval módban rögzített viselkedésű függvénye. A fejezet főútján a alakú, float32, értékkészletű GlyphLab-képek halmaza, pedig az logit-tér. Az export egy eljárás, amely az modulból, egy referencia-bemenetből és egy dinamikusalak-specifikációból egy exportált programot készít:
A torch.export esetén
egy ExportedProgram, amely három, elkülönítetten
ellenőrizhető részből áll:
| Objektum | Tartalom | Ellenőrzés típusa |
|---|---|---|
| gráfmodul | a számítási gráf operátorokkal és alakokkal | statikus operátor- és alakelemzés |
| state_dict | paraméterek és pufferek konstansként | érték-összevetés a forrásmodullal |
| range_constraints | dinamikus tengelyek korlátai | guard-teszt a határokon |
A dinamikus alak azt jelenti, hogy egy tengely hossza nem egyetlen, az exportáláskor látott értékre rögzül, hanem egy megadott intervallumból vehet fel értéket. A batch tengelyre például
azt a guardot írja elő, hogy csak olyan batch-méretre garantált, amelyre . A térbeli méret a GlyphLab-szerződésben rögzített, ezért statikus maradhat. A guard megsértése nem néma rossz eredményt, hanem explicit constraint-hibát ad. A batch külön eset lehet a PyTorch alak-specializációja miatt; ha az alkalmazás egyetlen képes inference-t is előír, azt önálló exporttal és teszttel is támogatni kell.
Az ONNX-exporter az ExportedProgramból
platformfüggetlen gráfcsere-formátumú modellt készít. A 2.14-es főút az
export-alapú exporter, amelyet a dynamo útvonal kapcsol be
[D20]. Az ONNX-fájl a súlyokat konstansként tartalmazza, a dinamikus
batch tengely pedig névvel ellátott dim_param lesz, amelyet
a futtató runtime a tényleges bemeneti alakkal köt le.
Az alábbi állítás mondja ki, mit ellenőrizhetünk véges kontrollhalmazon. Ez teszt típusú állítás, nem univerzális bizonyítás.
Definíció (ellenőrzött ekvivalencia). Legyen véges kontrollhalmaz, abszolút és relatív tolerancia. Azt mondjuk, hogy a exportált program -ellenőrzötten ekvivalens -mel, ha minden esetén
és a lapított kimenetekre vett maximumnormában
A három feltétel három különböző hibát fog meg: az alakfeltétel a rejtett broadcasting- vagy reduction-hibát, a dtype-feltétel a véletlen float64/float16 váltást, a norma-egyenlőtlenség pedig az értékeltérést. A tag azért kell, mert nagy abszolút értékű logitoknál a lebegőpontos kerekítés arányosan nagyobb lehet; a nullához közeli kimenetnél a tag eltűnik, és az abszolút korlát marad érvényben.
Tétel (affin kontrollredukció). Legyenek és affin leképezések . Ha minden kontrollpontban, ahol az -edik standard bázisvektor, akkor az egész -n.
Bizonyítás. Legyen . Mivel , ezért . Minden esetén , tehát a mátrix -edik oszlopa nullvektor. Mivel ez minden oszlopra igaz, , így .
A tétel azt mondja meg, hogy affin modelleknél a elemű báziskontroll elegendő a teljes bemeneti tér ellenőrzéséhez; a valós CNN-exportnál a modell nemlineáris, ezért a véges kontrollhalmaz csak a kiválasztott pontokra ad bizonyítékot. A gyakorlati protokoll ezért a deklarált dinamikus batch tengely szélsőértékeit ( és ), egy köztes értéket és a rögzített térbeli méretet fedi le.
Legyen , ,
A kötelező konvenció szerint egy soros bemenetre:
A kód ugyanezt adja; a nn.Linear súlyalakja
,
azaz a fenti
közvetlenül beírható.
# munkakönyvtár: examples/n131/
import torch
from torch import nn
torch.manual_seed(20261020)
model = nn.Linear(2, 2, dtype=torch.float32)
with torch.no_grad():
model.weight.copy_(torch.tensor([[1.0, -1.0], [2.0, 0.5]]))
model.bias.copy_(torch.tensor([0.1, -0.2]))
model.eval()
x = torch.tensor([[0.5, 1.0]], dtype=torch.float32)
with torch.no_grad():
y = model(x)
print(y) # tensor([[-0.4000, 1.3000]])Az export utáni kontrollnak ugyanezt a értéket kell visszaadnia; ha az exportált program mást ad, az értékhiba, nem kerekítési kérdés.
A labor egyetlen konvolúciós fejet exportál, amely a GlyphLab
bemeneti szerződését használja: float32,
,
intenzitás. A modellt az export előtt
eval() módba tesszük, mert az export a pillanatnyi
viselkedést rögzíti; a dropout és a BatchNorm train-beli
véletlenítése/statisztikája így nem kerül a gráfba. A kód futtatható, de
az írás során nem futott; a kimeneti sorok jelölt
szemléltetések.
# munkakönyvtár: lab/
# Futtatás: python export_check.py
import torch
from torch import nn
from torch.export import Dim, export # API-ellenőrzés: [D19]
class MiniGlyphHead(nn.Module):
def __init__(self):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(1, 4, 3, padding=1),
nn.ReLU(),
nn.AdaptiveAvgPool2d((1, 1)),
nn.Flatten(),
nn.Linear(4, 3),
)
def forward(self, x):
return self.features(x)
torch.manual_seed(20261020)
model = MiniGlyphHead().eval()
x_ref = torch.rand(2, 1, 24, 24, dtype=torch.float32)
with torch.no_grad():
y_ref = model(x_ref)
batch = Dim("batch", min=2, max=16)
ep = export(model, (x_ref,), dynamic_shapes={"x": {0: batch}})
with torch.no_grad():
y_exp = ep.module()(x_ref)
print("shape:", y_ref.shape, y_exp.shape)
print("dtype:", y_ref.dtype, y_exp.dtype)
err = (y_ref - y_exp).abs().max().item()
print("max_abs_err:", err) # jelölt szemléltetés: 0.0 vagy 1e-7 alatt várható
# Opcionális ONNX-export és onnxruntime-összevetés [D20].
# A futás külön környezetet és onnx/onnxruntime telepítést igényel.
# torch.onnx.export(ep, (x_ref,), "glyph_head.onnx", dynamo=True)A kontrollnak három dolgot kell visszaadnia: azonos
shape-ot
(),
azonos dtype-ot (torch.float32) és a definíció
szerinti norma-egyenlőtlenséget. Az ONNX-runtime-összevetésnél a két
futtatókörnyezet eltérő kernelsorrendet használhat, ezért
bitazonosságot nem ígérünk; a tolerancia legyen például
,
.
A runtime fogalma itt kettéválik: az
ExportedProgram a PyTorch kereten belül futtatható az
ep.module() hívással, míg az ONNX-fájl egy külső
runtime-ra, például onnxruntime-ra bízza a kiértékelést. Az
előbbi ellenőrzése a PyTorch-környezetben marad, az utóbbié már a
céleszköz numerikus viselkedését is méri.
F1 (fogalmi, 1. nehézség). Mondja meg, miben
különbözik az ExportedProgram a korábbi
torch.jit.trace kimenetétől. Adjon pszeudokód-példát olyan
modulra, amelyben egy if x.sum() > 0 elágazás van, és
magyarázza el, miért nem szabad a régi TorchScript-trace példákat
ellenőrzés nélkül átvenni a jelenlegi export főútjaként.
F2 (levezetési, 2. nehézség). Bizonyítsa be a fenti affin kontrollredukciós tételt, majd mutassa meg egy ellenpéldával, hogy nemlineáris -re a kontrollhalmaz nem elegendő: válassza az függvényt.
F3 (implementációs, 2. nehézség). Írjon teljes
Python-szkriptet, amely egy rögzített súlyú nn.Linear(2,2)
modult eval módban exportál, majd a
kontrollhalmazon asszertálja, hogy az exportált program kimenete
maximumnorma-eltérésen belül egyezik az eredetivel. A seed, a dtype és a
futási parancs szerepeljen.
F4 (hibakeresési, 2. nehézség). Az alábbi vázlat hibás: egy dropoutot tartalmazó modellt train módban exportálnak, majd eval módban vetnek össze. A dropout itt szándékosan , ezért a train- és eval-viselkedés biztosan különbözhet, és az eltérés a véletlen modellsúlyoktól is függ.
model = nn.Sequential(nn.Dropout(p=1.0), MiniGlyphHead()).train()
x = torch.rand(2, 1, 24, 24)
ep = export(model, (x,)) # train módban
model.eval()
y1 = model(x)
y2 = ep.module()(x)
print((y1 - y2).abs().max()) # eltérés várható (súlyfüggő)Lokalizálja a hibát, mondja meg a következményét egy BatchNormos modellnél, és adja meg a javítást.
F5 (kísérleti/transzfer, 3. nehézség). Tervezzen
export-ellenőrzési protokollt a GlyphLab MiniGlyphHead
modelljéhez. Válassza meg a kontrollhalmazt a test splitből, a dinamikus
batch-tartományt, a toleranciát az ONNX-runtime-összevetéshez, és
indokolja, miért nem elegendő egyetlen tesztkép. Különböztesse meg a
numerikus egyezést a szemantikai ekvivalenciától.
F1. A torch.jit.trace a megadott
példabemenet által ténylegesen bejárt utat rögzíti. Ha a modulban
if x.sum() > 0: return 2*x else: return 3*x szerepel,
akkor a pozitív összegű példán a trace csak a 2*x ágat
tartalmazza; a másik ág örökre eltűnik, és negatív összegű bemenetre is
2*x-et ad. A torch.export ezzel szemben a
teljes modul-kódot elemzi, és a bemenettől függő Python-vezérlést vagy
konstans feltételként kezeli, vagy az export folyamatát nem támogatott,
adatfüggő vezérlés miatt hibával megszakítja; a
torch.export nem a grafikon hallgatólagos megszakításával,
és nem is a trace-hez hasonló észrevétlen ágvesztéssel garantálja a
teljes gráfot. A régi TorchScript-példa ezért nem aktuális főút; az
[D19] státusza miatt a pontos viselkedést a 2.14-es dokumentációban kell
megerősíteni.
F2. A tétel bizonyítása megegyezik a főszövegben adott levezetéssel: adja -t, adja a mátrix -edik oszlopának nullvektor voltát, amiből . Az ellenpélda: a pontokon mindenütt -t ad, tehát bármely függvény ezeken egyezik vele; ugyanakkor , tehát a báziskontroll nem bizonyítja az ekvivalenciát.
F3. Teljes szkript:
# munkakönyvtár: examples/n131/
# Futtatás: python f3_affin_export.py
import torch
from torch import nn
from torch.export import Dim, export # API-ellenőrzés: [D19]
torch.manual_seed(20261020)
model = nn.Linear(2, 2, dtype=torch.float32)
with torch.no_grad():
model.weight.copy_(torch.tensor([[1.0, -1.0], [2.0, 0.5]]))
model.bias.copy_(torch.tensor([0.1, -0.2]))
model.eval()
# A dinamikus batchhez ne egyetlen soros referenciabemenetet adjunk:
# az 1-es méretet a tracer specializálhatja.
x_ref = torch.rand(4, 2, dtype=torch.float32)
batch = Dim("batch", min=2, max=32)
ep = export(model, (x_ref,), dynamic_shapes=({0: batch},))
def check(x):
with torch.no_grad():
y_ref = model(x)
y_exp = ep.module()(x)
assert y_ref.shape == y_exp.shape, (y_ref.shape, y_exp.shape)
assert y_ref.dtype == y_exp.dtype
err = (y_ref - y_exp).abs().max().item()
assert err < 1e-6, err
return err
# Az affin kontrollhalmaz négy pontját egyetlen N=4 batchben értékeljük.
controls = torch.tensor([
[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]
], dtype=torch.float32)
print("controls max error:", check(controls))
# Külön ellenőrizzük a dinamikus batch másik megengedett méretét is.
print("batch 2 max error:", check(controls[:2]))A kód a négy kontrollpontot egy batchben és két kontrollpontot méreten ellenőrzi; a várható maximális eltérés kerekítési pontosságon belül nulla. Az referenciabemenetet itt tudatosan kerüljük, mert a dinamikus dimenziót a tracer 1-es méret esetén statikusra specializálhatja; a kontrollpont a bias, az pontok a súlyoszlopok, az pedig a teljes összegzés együttes ellenőrzése.
F4. A hiba oka: az export train módban történt,
ezért az exportált program a train-beli viselkedést rögzítette. A
példában a dropout train módban
mellett nullázza a bemenetet, eval módban viszont változatlanul
átengedi; a MiniGlyphHead önmagában nem tartalmaz
módérzékeny réteget, ezért az eredeti, csak azt használó ellenpélda
hibás lett volna. BatchNormos modulnál train módban a batch-statisztikát
használná, eval módban pedig a running statisztikát; a két kimenet így
általában eltér. Javítás: model.eval() az
exportálás előtt kell meghívni, és utána a kontroll-összevetést
is eval módban kell végezni.
F5. Protokollvázlat. Megfigyelési egység: egy
teszt-jelenet/nézet pár; a test split 30 jelenetből 60 képet tartalmaz,
és a modellválasztásban nem vett részt. Kontrollhalmaz: a 60 tesztképből
választott, scene-szinten rétegzett 12 kép, amely a három osztályt
egyenlő arányban fedi le. Dinamikus batch:
Dim("batch", min=2, max=16), a guard-teszt
N=2, N=8, N=16 méreteken;
N=1 esetén külön export- és összevetési teszt szükséges.
Tolerancia: PyTorch–ExportedProgram összevetésre
,
;
ONNX-runtime-ra
,
,
mert a runtime eltérő kernelsorrendet használhat. Egyetlen tesztkép nem
elegendő, mert nem fedi le sem a batch-tengely szélsőértékeit, sem a
csatornák közötti broadcast- és reduction-utakat. A numerikus egyezés a
kimeneti tenzorok közelsége; a szemantikai ekvivalencia azt állítaná,
hogy a program minden megengedett bemenetre ugyanazt a függvényt
valósítja meg. A véges kontrollhalmazon mért egyezés az előbbit igazolja
a kiválasztott pontokra, az utóbbit nem bizonyítja.
Tipikus tévedés. „A torch.export
ugyanaz, mint a régi torch.jit.trace/script,
ezért a korábbi TorchScript-példákat változatlanul átvehetjük.” Ez
hamis. A trace a példabemenet által bejárt konkrét ágat rögzíti; az
inputfüggő elágazás többi ága elveszik. A script nyelvi részhalmazra
korlátoz, és a típuskényszer más. Az aktuális export főút a dynamo-alapú
trace, amely a nem támogatott Python-vezérlést teljes gráfra kiterjedő
export esetén hibával elutasíthatja. A [D19] státusza miatt a pontos
API-viselkedést a 2.14-es dokumentációban újra kell olvasni; a régi
példa átvétele előtt tehát ellenőrizni kell, nem feltételezni.
Önellenőrzés. A kézi affin példa alakjai:
,
,
,
;
a számított
és
a kódban ellenőrizhető. A labor seedje 20261020, dtype-ja
float32, eszköze CPU; a nem futtatott kimenetek jelölt
szemléltetések. A
-feltétel
nullközeli kimenetnél az abszolút, nagy kimenetnél a relatív tagot teszi
dominánssá, így nem válik üressé.
Visszahívás. A 21.3. Dropout, súlyregularizálás és augmentáció train/eval megkülönböztetése itt úgy tér vissza, hogy az export a pillanatnyi módot rögzíti; a 29.3. Regularizált latent reprezentáció tanulsága szerint a kis kontrollhalmazon mért nulla hiba nem általános szemantikai garancia; a 32.3. Vision Transformer és hibrid felhasználás modellcsalád-összevetése pedig arra emlékeztet, hogy az export az operátorok szintjén dolgozik: CNN és ViT akkor exportálható ugyanazzal az útvonallal, ha az operátoraik támogatottak.
A torch.export eval módban rögzített modulból ExportedProgramot
készít, amely a gráfot, a súlyokat és a dinamikus tengelyek guardjait
együtt tartalmazza. A bemutatott dinamikus batch a
Dim("batch", min=2, max=16) korláttal adható meg; a guard
megsértése explicit hiba. Az export ekvivalenciáját alak-, dtype- és
-normafeltételekkel
ellenőrizzük véges kontrollhalmazon; affin modellnél a
bázispont igazolja a teljes egyezést, nemlineáris CNN-nél a kontroll
csak teszt-bizonyíték. Az ONNX-exporter az export-alapú útvonalból
dolgozik, a külső runtime numerikus egyezését külön toleranciával kell
mérni. A régi TorchScript-példa nem aktuális főút.
A következő egység, 33.4. Üzemeltetés, drift és késő címke, az exportált modellt üzemeltetésbe helyezi: ott a bemeneti szerződés, a driftfigyelés és a későn érkező címkék kérdése épül arra, hogy az átadott program viselkedését milyen feltételekkel ismerjük.
Felhasznált források: