A 33.1. Inference-szerződés és modellcsomag
alfejezetben felépített modellcsomag akkor válik használható eszközzé,
ha a súlyokat a tanítási ciklustól elválasztva, kiszolgálási úton is
alkalmazni tudjuk. Itt egy helyi batch-inference parancsot építünk,
amely a csomag state_dict-jét tölti be, validálja a
bemenetet, és CSV-formátumú predikciót ír ki. Előismeret: a modellcsomag
tartalma, az eval mód és az inference-szerződés 33.1. Inference-szerződés és modellcsomag
alfejezetből.
Induló kérdés: egy kolléga uint8, 0–255 skálájú .npy
tömböt ad a modellnek, „hiszen a kép ugyanaz”. A modell numerikusan
lefuthat, a predikció mégis érvénytelen; ennek okát az elfogadott
bemeneti szerződés formális rögzítésével látjuk be.
Definíció (request). A request a szolgáltatás felé küldött üzenet, amely legalább a modellazonosítót és a bemenetet tartalmazza. Egyetlen kép esetén a bemenet alakja ; batch esetén , ahol a feldolgozandó minták száma. A request nem azonos a modell forwardjával: a request érvényességét a séma, az eredményét a modell határozza meg.
Definíció (model id). A modellazonosító a csomag
verziójához és a súlyok lenyomatához kötött string. A
GlyphLab-osztályozó esetében ez glyph-orientation-v1. Az
azonosító a szerződés része: a kliens ezzel jelzi, mely csomagot hívja;
a szolgáltatás ismeretlen azonosítóra explicit hibát ad, nem
„valamilyen” modellt futtat.
Definíció (batch CLI). Batch parancssori eszköznek nevezzük azt a nem interaktív programot, amely egy bemeneti batch-fájlt olvas, egyetlen előrefelé menetet végez, és a predikciókat fájlba írja. Nincs tréninglépés, nincs paraméterfrissítés, nincs validációs ciklus.
Definíció (local service). Helyi szolgáltatásnak nevezzük azt a folyamatot, amely loopback vagy HTTP végponton keresztül fogad requesteket, és válaszüzenetet ad. Ebben az egységben a szolgáltatást csak specifikáció szintjén adjuk meg; a futtatható főút a batch CLI.
Definíció (latency). Egy request késleltetése , ahol a kérés beérkezésének, a válasz elküldésének időpontja. Batch esetén a teljes köteg késleltetése; az amortizált minta-késleltetés . A kettő nem felcserélhető: a második nem egyetlen kérés válaszideje.
A modell nem „bármilyen képet” fogad, hanem csak a tanítási adat szerződésének megfelelő tenzort. A szerződést formálisan rögzítjük.
Definíció (elfogadott GlyphLab képbatch). Legyen a
glyph-orientation-v1 csomag bemeneti halmaza
.
A tenzor
pontosan akkor eleme
-nek,
ha teljesül mindhárom feltétel:
float32.A három feltétel más-más hibát véd. Az alakfeltétel a konvolúciós és
affin rétegek bemeneti dimenzióját rögzíti; a csatornafeltétel a
grayscale képet írja elő, mivel a modell első konvolúciójának súlya
alakú; az értéktartomány a GlyphLab generátor normalizálási szerződését
tükrözi, amelyben az intenzitás float32 és
-beli.
A 0–255 skálájú uint8 kép tehát két feltételt is sért: a dtype-ot és az
értéktartományt.
Állítás (a validátor pontosan a szerződést
ellenőrzi). A validate_batch függvény pontosan
akkor tér vissza hiba nélkül, ha a bemenete
-ben
van.
Bizonyítás. A függvény ágai a definíció
konjunkcióinak közvetlen kódolása: ellenőrzi a dtype-ot, az
ndim értéket, az
alakot, a nemnegatív batchméretet, a végességet, végül a
tartományt. Minden feltétel megsértése kivételt vált ki, ezért a hiba
nélküli visszatérés minden feltétel teljesülését jelenti. A feltételek
definíció szerint pontosan
tagságát adják, tehát a függvény a halmaz karakterisztikus ellenőrzése.
A NaN-t a harmadik feltételben a
intervallumtól való eltérés zárja ki; a kódban a pontos hibaüzenet
érdekében külön végességi ellenőrzést is végzünk.
A megfelelőség nem numerikus heurisztika, hanem a definícióból
következő egyezés. Az viszont külön eldöntendő, hogy egy adott
.npy fájl ténylegesen eleget tesz-e a szerződésnek; ezt a
batch CLI a modellhívás előtt hajtja végre.
Tekintsünk négy bemenetet:
float32, minden elem
;uint8, értékek 0–255 között;float32, alak
;float32, alak
,
az egyik elem NaN.A definíció alapján , tehát a validátor elfogadja. az 1. feltételt sérti, a 2. feltételt; a validátor mindkettőt elutasítja. a 3. feltételt sérti, mert a NaN nem eleme a intervallumnak.
Ennek oktatási szerepe kettős. Egyrészt mutatja, hogy a helyes predikcióhoz a bemeneti szerződés betartása szükséges; másrészt megmutatja, hogy a „majdnem jó” bemenet is elutasítandó, mert a modell hallgatólagosan más eloszláson tanult.
A labor a 33.1. Inference-szerződés és
modellcsomag alfejezetben készített csomagot használja:
package/model.py tartalmazza a GlyphCNN
architektúrát, ckpt/glyph-orientation-v1.pt a
state_dict-et. A GlyphCNN bemeneti csatornája
1, bemeneti térbeli mérete
,
kimenete 3 logit. A futtatás CPU-n történik.
Bemeneti fájl: data/images.npy, alak
,
float32,
.
A parancs:
python lab/batch_infer.py \
--checkpoint ckpt/glyph-orientation-v1.pt \
--images data/images.npy \
--output results/predictions.csv \
--model-id glyph-orientation-v1A fő kód:
import argparse, csv, sys, time
from pathlib import Path
import numpy as np
import torch
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from package.model import GlyphCNN
MODEL_ID = "glyph-orientation-v1"
C_EXP, H_EXP, W_EXP = 1, 24, 24
def validate_batch(x: np.ndarray) -> None:
if not isinstance(x, np.ndarray):
raise TypeError("a bemenetnek np.ndarray-nek kell lennie")
if x.dtype != np.float32:
raise TypeError(f"dtype hiba: {x.dtype}; a várt float32")
if x.ndim != 4:
raise ValueError(f"alak hiba: 4D NCHW várt, kapott: {x.ndim}D")
N, C, H, W = x.shape
if N == 0:
raise ValueError("üres batch nem fogadható el")
if (C, H, W) != (C_EXP, H_EXP, W_EXP):
raise ValueError(f"alak hiba: 1x24x24 várt, kapott: {C}x{H}x{W}")
if not np.all(np.isfinite(x)):
raise ValueError("nem véges pixelérték (NaN vagy inf)")
if np.any(x < 0.0) or np.any(x > 1.0):
raise ValueError("pixelértékeknek a [0,1] tartományban kell lenniük")
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--checkpoint", required=True)
ap.add_argument("--images", required=True)
ap.add_argument("--output", required=True)
ap.add_argument("--model-id", default=MODEL_ID)
args = ap.parse_args()
if args.model_id != MODEL_ID:
raise ValueError(f"ismeretlen modellazonosító: {args.model_id}")
x = np.load(args.images)
validate_batch(x)
model = GlyphCNN()
state = torch.load(args.checkpoint, map_location="cpu", weights_only=True)
if isinstance(state, dict) and "model_state_dict" in state:
state = state["model_state_dict"]
model.load_state_dict(state)
model.eval()
t0 = time.perf_counter()
with torch.inference_mode():
logits = model(torch.from_numpy(x))
probs = torch.softmax(logits, dim=1).numpy()
elapsed = time.perf_counter() - t0
with open(args.output, "w", newline="") as f:
w = csv.writer(f)
w.writerow(["image_idx", "predicted", "p0", "p1", "p2"])
for i, (pred, p) in enumerate(zip(probs.argmax(axis=1), probs)):
w.writerow([i, int(pred), float(p[0]), float(p[1]), float(p[2])])
print(f"model_id={args.model_id} n={logits.shape[0]} "
f"batch_latency_s={elapsed:.6f} output={args.output}")
if __name__ == "__main__":
main()A kód három ponton tükrözi a 33.1.
Inference-szerződés és modellcsomag szerződést: a modell
eval() állapotba kerül, a paraméterek nem kapnak gradienst,
és a betöltés weights_only=True módon, csak
state_dict-et fogad el. A
torch.inference_mode() azt is rögzíti, hogy itt nem
autograd-számítás folyik; ez különböző feladat, mint az
eval 33.1. Inference-szerződés és
modellcsomag szerinti állapotváltása. A --model-id
ellenőrzés a csomag azonosítóját védi.
Ellenőrzés. A fenti futás kimenete
results/predictions.csv. Az ellenőrző szkript:
import numpy as np, sys
rows = np.loadtxt(sys.argv[1], delimiter=",", skiprows=1, ndmin=2)
assert rows.shape[0] == 360
assert rows.shape[1] == 5
assert np.all((rows[:, 1] >= 0) & (rows[:, 1] <= 2))
assert np.allclose(rows[:, 2:5].sum(axis=1), 1.0, atol=1e-6)
print("batch inference ellenőrzés sikeres: 360 sor, "
"predikciók 0..2, valószínűségsorösszegek 1 ± 1e-6")A kimenet a csomag n129-ben rögzített ellenőrzési
feltételeit ellenőrzi. A fenti eredmény jelzett
szemléltetés, nem elvégzett mérés: az értékeket nem
helyettesíthetik a laborban mért eredmények. A futást a laborban külön
kell elvégezni.
A batch CLI késleltetésének méréséhez rögzíteni kell a bemeneti batch méretét, a hardvert, a melegítő hívások számát és az ismétlésszámot. Egy ismételhető kis mérés például: 10 melegítő hívás, majd 100 mért hívás, mindig ugyanazzal a 360 képes batch-csel; a kimenet p50 és p90 késleltetés. A p50 a medián, a p90 az az érték, amely alá a mért késleltetések legalább 90%-a esik; mindkettő tapasztalati kvantilis, nem modellből származtatott garancia.
A gyakori félreértés az, hogy egy fejlesztői gépen 20 kérésből 20 sikeres válasz a rendelkezésre állást bizonyítja. Nem bizonyítja: 0 hiba 20 próbából a binomiális arány 95%-os felső konfidenciahatárára a „rule of three” szerint körülbelül ; azaz a mérés nem zárja ki, hogy a tényleges hibaarány 15% legyen. A termelési állításhoz terheléses, párhuzamos klienssel végzett, tartós mérés, erőforrás-korlát és hibatípusok szerinti bontás szükséges.
A helyi szolgáltatás szerződése, amelyet a batch mellett utólag ismételt, egyedi kérésekre használhatunk:
POST /v1/predict
Content-Type: application/json
{
"model_id": "glyph-orientation-v1",
"image": {
"shape": [1, 24, 24],
"dtype": "float32",
"values": [0.0, 0.5, ..., 1.0]
}
}
A values mező pontosan
elemet tartalmaz, mindegyik
-beli.
Sikeres válasz:
200 OK
{
"model_id": "glyph-orientation-v1",
"predicted": 0,
"probs": [0.92, 0.05, 0.03]
}
Sémahiba esetén 422, ismeretlen modellazonosítóra 404, belső hibára 500 a tervezett státuszkód; a 422 üzenetnek meg kell neveznie a sértett feltételt, nem elegendő a „hibás kérés” szöveg. A specifikáció loopback tesztje a szolgáltatásfüggvény közvetlen, HTTP nélküli hívása, így a hálózat nem része az ellenőrzésnek.
F1 (fogalmi, nehézség 1). Magyarázza el a különbséget a batch CLI és a local service között. Miért nem bizonyítja a batch CLI sikeres futása azt, hogy a loopback API is rendelkezésre áll?
F2 (levezetési/bizonyítási, nehézség 2). Bizonyítsa be, hogy rögzített mellett az halmaz konvex. Ellenőrizze a bizonyítást a és szélsőesetben.
F3 (implementációs, nehézség 2). Írjon
validate_image(x) függvényt, amely egyetlen
képet ellenőriz, és minden hibát külön típusú kivétellel jelez.
Tesztelje négy esetre: helyes kép, rossz dtype, rossz alak,
érték.
F4 (hibakeresési, nehézség 2). Az alábbi kód miért fogadja el a NaN-t tartalmazó bemenetet?
def validate(x):
if not (np.any(x < 0.0) or np.any(x > 1.0)):
return "elfogadva"
return "elutasítva"Adja meg a javítást és egy olyan bemenetet, amelyen a hibás változat „elfogadva”, a javított változat „elutasítva” eredményt ad.
F5 (kísérleti/transzfer, nehézség 3). Tervezze meg a batch CLI késleltetésmérését úgy, hogy a mérésből p50 és p90 értéket lehessen jelenteni. Írja le a melegítés, ismétlés, batchméret, hardver és kiugró mérések kezelését, valamint azt, mely állítás nem vonható le a mérésből.
F1. A batch CLI egyszeri parancs: bemeneti fájlt olvas és kimeneti fájlt ír, nem vár új kéréseket. A local service folyamatban marad, és request–response párokat szolgál ki. A batch CLI sikere a séma, a modellbetöltés és egy batch feldolgozásának helyességét mutatja; az API rendelkezésre állása ezen kívül a szolgáltatásfolyamat elindulásától, a hívási protokolltól és a folyamatos üzemtől függ. Előfordulhat, hogy a CLI fut, de a szerver nem figyel, vagy a request nem éri el a végpontot.
F2. Legyen és . Ekkor Mivel , és , mindkét tag nemnegatív, és az összeg felső korlátja Tehát a konvex kombináció minden eleme -beli, az alak és a dtype változatlan, így a kombináció -ben van. A eset -t, a eset -et adja vissza, mindkettő a feltétel szerint eleme a halmaznak. A konvexitás a pixelérték-intervallum konvexitásának komponensenkénti öröklődése; az alak- és dtypefeltétel rögzített mellett nem változik a kombinációban.
F3. Egy lehetséges megoldás:
import numpy as np
def validate_image(x: np.ndarray) -> None:
if not isinstance(x, np.ndarray):
raise TypeError("x nem np.ndarray")
if x.dtype != np.float32:
raise TypeError(f"dtype hiba: {x.dtype}")
if x.shape != (1, 1, 24, 24):
raise ValueError(f"alak hiba: {x.shape}")
if not np.all(np.isfinite(x)):
raise ValueError("nem véges érték")
if np.any(x < 0.0) or np.any(x > 1.0):
raise ValueError("érték a [0,1] tartományon kívül")Tesztek: np.full((1,1,24,24), 0.5, dtype=np.float32)
elfogadva; np.full((1,1,24,24), 128, dtype=np.uint8) dtype
hibát ad; np.full((1,1,23,24), 0.5, dtype=np.float32) alak
hibát ad; egy tömb, amelyben az egyik elem
,
értéktartomány-hibát ad. Alakellenőrzés: a (1,1,24,24)
pontosan a szerződés, az
batch egyes eleme.
F4. A hiba oka: NaN esetén mind az
x < 0.0, mind az x > 1.0 összehasonlítás
hamis, ezért a not (hamis or hamis) kifejezés igaz, és a
függvény elfogadja a bemenetet. A tartományvizsgálat nem zárja ki a nem
véges értékeket. Javítás: először végességi ellenőrzést kell
végezni:
def ellenoriz_bemenet(x):
if not np.all(np.isfinite(x)):
return "elutasítva"
if np.any(x < 0.0) or np.any(x > 1.0):
return "elutasítva"
return "elfogadva"Ellenőrző bemenet:
x = np.array([[[[np.nan]]]], dtype=np.float32); a hibás
változat „elfogadva”, a javított „elutasítva” eredményt ad.
F5. A protokoll: rögzítsük a CPU-t, a kötegméretet
,
a checkpointot és az inputfájlt. Hajtsunk végre 10 melegítő hívást,
amelyek nem szerepelnek a statisztikában, majd 100 mért hívást;
mindegyik előtt ne töltsük újra a modellt. Minden hívásnál mérjük
-et
time.perf_counter()-rel a validált batch modellhívásának
kezdetétől a softmax-kimenet elkészültéig. Számítsuk ki a p50 és p90
empirikus kvantilist. A kiugró értékeket ne töröljük, hanem külön
jelentsük. Az eredmény csak erre a gépre, erre a PyTorch/NumPy verzióra
és erre a batchméretre érvényes; nem vonható le általános GPU- vagy
szerverállítás, és nem bizonyít termelési rendelkezésre állást, mert
egyetlen folyamat, nincs párhuzamos terhelés és nincs hálózati réteg a
mérésben.
A fejezet kiemelt tévedése: egy fejlesztői végpont sikere nem termelési rendelkezésre állási bizonyíték. Ellenpélda: a fejlesztői gépen 20 kérésből 20 sikerül; a termelési környezetben 100 párhuzamos kliensből 5 timeoutot kap. A 20/20-as minta nem cáfolja a 15%-os hibaarányt, és nem tartalmaz párhuzamos terhelést, hálózati késleltetést vagy erőforrás-versenyt. A helyes állítás feltétele: nagyobb minta, párhuzamos kliens, rögzített környezet és hiba szerinti bontás.
Önellenőrzés a labor után: a batch CLI a modellhívás előtt hívta-e a
validátort? A betöltés weights_only=True volt-e? A modell
eval() állapotban volt-e? A valószínűségsorok összege 1
volt-e a megadott
toleranciával? A kimeneti fájl tartalmazta-e a model_id-t?
Ha bármelyik válasz nem, a futtatás nem felel meg a szerződésnek.
Visszahívás: 32.2. Scaled dot-product
attention alfejezetben a batch dimenzió az attention-mátrixok első
indexe volt; itt is a
dimenzió az első, és a séma a csatorna–térbeli részt rögzíti. 29.2. Denoising és korrupciós modell alfejezetben a
zajos–tiszta párok szerződése a
intenzitástartományt használta; a mostani validátor ugyanezt a
tartományt ellenőrzi a betanított osztályozó bemenetén. 21.2. Learning rate és ütemezés alfejezetben a
checkpoint a model, optimizer és scheduler állapotát együtt rögzítette;
a batch inference csak a model state_dict-et használja, és
semmilyen scheduler-állapotot nem futtat.
A batch inference a tanítástól elválasztott kiszolgálási út: a
modellcsomagból betölti a state_dict-et, a séma szerint
validálja a bemenetet, és predikciót ír fájlba. Az elfogadott GlyphLab
tenzor float32, NCHW alakú,
,
csatorna-térbeli alak
,
minden pixel
-beli
és véges. A validátor pontosan ezt a halmazt ellenőrzi; a helyes input a
formális szerződésből következik. A model id a csomag verziójához
kötött, a local service ehhez rendel request–response szerződést. A
késleltetés méréséhez melegítés, ismétlés és környezeti rögzítés kell;
fejlesztői siker nem termelési rendelkezésre állási bizonyíték.
A következő 33.3. torch.export és ONNX egység
a torch.export és ONNX exportálást tárgyalja: ott azt
vizsgáljuk, hogyan alakítható ez a betöltött Python-modell portálható
számítási gráffá. Forrás: [D09] PyTorch contributors (2.14). Saving
and Loading Models. Hivatalos oktatóoldal olvasva; a
state_dict, eval és weights_only
szerződésére használtuk. Ebben az egységben export API-t nem
tárgyaltunk; a [D20] export-alapú ONNX-szakasz a következő egységhez
tartozik, az ottani ellenőrzési igénnyel együtt.