Kako riješiti grešku “PyTorch CUDA Out of Memory” tijekom treniranja modela

Treniranje u PyTorchu može raditi nekoliko koraka, a zatim se zaustaviti s greškom torch.OutOfMemoryError ili porukom poput CUDA out of memory. Tried to allocate .... Trenutni uzrok je jednostavan: sljedeća CUDA alokacija nije mogla biti smještena. Korisno pitanje je zašto nije mogla biti smještena.

Tijekom treniranja, GPU memorija može sadržavati parametre modela, gradijente, stanje optimizatora, ulazne tenzore, privremene radne prostore i aktivacije spremljene za backward pass. PyTorch također koristi caching alocator, pa memorija prikazana kao “reserved” (rezervirana) nije identična memoriji koju trenutno zauzimaju živi tenzori. Ta razlika je važna pri odlučivanju treba li smanjiti opterećenje ili istražiti fragmentaciju alocirača.

Ovaj vodič prati trenutnu PyTorch dokumentaciju i koristi trenutne nazive AMP API-ja. Posebno, PyTorch sada dokumentira torch.amp.autocast("cuda") i torch.amp.GradScaler("cuda"); starije torch.cuda.amp.* ulazne točke su zastarjele. Pogledajte PyTorch Automatic Mixed Precision dokumentaciju.

Brza dijagnostika: s kojom vrstom OOM-a imate posla?

SimptomVjerojatan smjerNajbolja prva akcija
OOM se događa pri prvom forward passuAktivni radni skup je prevelikSmanjite veličinu mikro-batcha ili ulaznu veličinu; provjerite stane li sam model.
OOM se događa tijekom backwardaSpremljene aktivacije plus gradijenti premašuju VRAMPokušajte s AMP-om, checkpointingom aktivacija i manjim mikro-batchom.
Memorija raste pri svakoj iteracijiTenzor ili računski graf možda se zadržavaProvjerite liste, metrike, predmemorirane izlaze i reference na tenzore gubitka.
Alocirana memorija je umjerena, ali rezervirana memorija je znatno većaMožda su važni caching ili fragmentacijaProvjerite memory_summary() prije promjene postavki alocirača.
Drugi proces već koristi značajnu VRAMNe pripada sva GPU memorija ovom procesu treniranjaIdentificirajte proces i oslobodite taj GPU ili rasporedite posao drugdje.
AI-generirana ilustracija poruke PyTorch CUDA out of memory u terminalu
AI-generirana ilustracija tipične CUDA out-of-memory poruke. Točne brojke variraju ovisno o modelu, GPU-u i koraku treniranja.

Korak 1: Mjerite memoriju prije mijenjanja recepta za treniranje

Počnite bilježenjem veličine batcha, dimenzija ulaza, preciznosti i točke u kojoj se događa kvar. Zatim provjerite memoriju živih tenzora i memoriju rezerviranu od strane alocirača. PyTorch izlaže memory_allocated(), memory_reserved(), vršne varijante i memory_summary(). Trenutna dokumentacija o upravljanju CUDA memorijom objašnjava da caching alocator čuva ponovno iskoristive blokove, zbog čega neiskorištena rezervirana memorija i dalje može izgledati kao zauzeta u alatima za praćenje GPU-a. PyTorch CUDA upravljanje memorijom.

import torch

torch.cuda.reset_peak_memory_stats()

# Ovdje pokrenite jedan reprezentativni korak treniranja.

print("allocated GB:",
      torch.cuda.memory_allocated() / 1024**3)
print("reserved GB:",
      torch.cuda.memory_reserved() / 1024**3)
print("peak allocated GB:",
      torch.cuda.max_memory_allocated() / 1024**3)
print(torch.cuda.memory_summary(abbreviated=True))

Ako jednostavan sažetak nije dovoljan, PyTorch može snimiti snimke alocirača za dublju analizu. Njegovi alati za memoriju mogu zabilježiti povijest alokacija i proizvesti snimku koja se može pregledati pomoću PyTorch vizualizatora memorije. PyTorch napominje da ovi alati vide memoriju kojom upravlja PyTorch alocator; alokacije koje izravno vrše druge CUDA biblioteke možda se neće pojaviti tamo. PyTorch vodič za razumijevanje korištenja CUDA memorije.

AI-generirana ilustracija nvidia-smi prikaza korištenja GPU memorije
AI-generirana ilustracija provjere ukupne upotrebe GPU memorije pomoću nvidia-smi; koristite je uz PyTorch statistiku alocirača kako biste vidjeli troši li drugi proces VRAM.

Nemojte tretirati torch.cuda.empty_cache() kao opće rješenje za OOM

torch.cuda.empty_cache() oslobađa neiskorištene predmemorirane blokove kako bi ih druge GPU aplikacije mogle koristiti. PyTorch izričito navodi da ne oslobađa memoriju koju zauzimaju živi tenzori i stoga ne povećava količinu GPU memorije dostupnu PyTorchu za tenzore koji su još uvijek živi. Može biti koristan između odvojenih eksperimenata ili nakon brisanja velikih objekata, ali nije zamjena za smanjenje aktivnog otiska memorije.

Korak 2: Prvo smanjite aktivni radni skup

Najpouzdanije prvo rješenje obično je manji mikro-batch: broj uzoraka obrađenih u jednom forward/backward prolazu. Memorija aktivacija obično raste s veličinom batcha, rezolucijom slike, duljinom niza i drugim dimenzijama ulaza. Ako se model trenira s batch veličinom 32, ali ne uspijeva s 64, smanjenje batcha nije zaobilazno rješenje u negativnom smislu; to je izravno smanjenje potražnje za vršnom memorijom.

AI-generirana ilustracija smanjenja veličine PyTorch trening batcha s 64 na 16
AI-generirana ilustracija smanjenja veličine batcha po koraku kako bi se smanjila vršna upotreba CUDA memorije.

Kod slika, smanjenje prostorne rezolucije ili veličine cropa može napraviti veliku razliku. Za transformere i druge modele sekvenci, smanjenje duljine sekvence može biti još važnije jer neki međutenzori jako rastu s duljinom sekvence. Točno skaliranje ovisi o arhitekturi, pa mjerite umjesto da pretpostavljate.

Također provjerite ne gradi kôd za evaluaciju nepotrebno. PyTorchove smjernice za performanse preporučuju onemogućavanje izračuna gradijenata za validaciju ili inferenciju kada gradijenti nisu potrebni, jer autograd inače sprema međuspremničke buffere. Tipičan obrazac je:

model.eval()
with torch.no_grad():
    for x, y in val_loader:
        x = x.cuda(non_blocking=True)
        y = y.cuda(non_blocking=True)
        pred = model(x)

Tijekom treniranja koristite optimizer.zero_grad(set_to_none=True) osim ako vaš algoritam ne ovisi o razlici u ponašanju između nultog gradijenta i None gradijenta. PyTorchova dokumentacija optimizatora navodi da postavljanje gradijenata na None općenito ima manji memorijski otisak i može umjereno poboljšati performanse. PyTorch optimizer zero_grad dokumentacija.

Korak 3: Zadržite veću efektivnu batch veličinu uz AMP i akumulaciju gradijenata

Koristite Automatic Mixed Precision kada model to podržava

Automatic Mixed Precision (AMP) pokreće kvalificirane operacije u nižoj preciznosti, dok operacije koje zahtijevaju veći raspon ili preciznost zadržava u odgovarajućim tipovima. PyTorch dokumentira da AMP može poboljšati performanse i smanjiti memorijski otisak za mnoga CUDA opterećenja, ali nije numerički prikladan za svaki model. Posebno, PyTorch upozorava da neki modeli pred-trenirani u bfloat16 mogu doživjeti overflow u float16.

Trenutni CUDA AMP obrazac za treniranje je:

scaler = torch.amp.GradScaler("cuda")

for inputs, targets in train_loader:
    inputs = inputs.cuda(non_blocking=True)
    targets = targets.cuda(non_blocking=True)
    optimizer.zero_grad(set_to_none=True)

    with torch.amp.autocast("cuda", dtype=torch.float16):
        outputs = model(inputs)
        loss = loss_fn(outputs, targets)

    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

PyTorch preporučuje pokretanje forward passa i gubitka pod autocastom, a zatim napuštanje autocast konteksta prije backwarda. Ako float16 uzrokuje nestabilnost, istražite je li bfloat16 podržan i prikladan za vaš hardver i model, umjesto da pretpostavljate da se svi mješoviti precizijski načini ponašaju identično.

Koristite akumulaciju gradijenata kada vam je potrebna veća efektivna batch veličina

Akumulacija gradijenata obrađuje nekoliko manjih mikro-batcheva prije ažuriranja optimizatora. Ako je mikro-batch 4, a akumulirate 8 koraka, efektivni batch za jedno ažuriranje optimizatora je 32 uzorka po radniku, pod pretpostavkom da svaki mikro-batch ima četiri uzorka i da postavka data-parallel ne mijenja tu aritmetiku.

accum_steps = 8
optimizer.zero_grad(set_to_none=True)

for step, (inputs, targets) in enumerate(train_loader):
    inputs = inputs.cuda(non_blocking=True)
    targets = targets.cuda(non_blocking=True)

    with torch.amp.autocast("cuda", dtype=torch.float16):
        outputs = model(inputs)
        loss = loss_fn(outputs, targets) / accum_steps

    scaler.scale(loss).backward()

    if (step + 1) % accum_steps == 0:
        scaler.step(optimizer)
        scaler.update()
        optimizer.zero_grad(set_to_none=True)
AI-generirana ilustracija akumulacije gradijenata preko nekoliko manjih PyTorch mikro-batcheva
AI-generirana ilustracija akumulacije gradijenata, koja razmjenjuje više forward/backward koraka za veću efektivnu batch veličinu bez držanja cijelog batcha u VRAM-u odjednom.

Za produkcijski kôd, također obradite konačni djelomični prozor akumulacije kada broj batcheva nije djeljiv s accum_steps. Ako koristite distribuirano treniranje, ponašanje sinkronizacije gradijenata može promijeniti omjer memorije i performansi, pa slijedite smjernice za akumulaciju distribuiranog API-ja umjesto da kopirate petlju za jedan GPU nepromijenjenu.

Korak 4: Razmijenite računanje za memoriju, zatim istražite zadržavanje i fragmentaciju

Checkpointing aktivacija

Checkpointing aktivacija smanjuje memoriju tako što ne čuva odabrane forward aktivacije živima do backwarda. Umjesto toga, PyTorch ih ponovno izračunava tijekom backwarda. To razmjenjuje dodatno računanje za niži memorijski otisak aktivacija. Trenutna PyTorch checkpoint dokumentacija preporučuje izričito prosljeđivanje use_reentrant=False. PyTorch checkpointing aktivacija dokumentacija.

from torch.utils.checkpoint import checkpoint

def forward(self, x):
    x = checkpoint(self.block1, x, use_reentrant=False)
    x = checkpoint(self.block2, x, use_reentrant=False)
    return self.head(x)

Checkpointirajte slojeve s velikim spremljenim aktivacijama i prihvatljivom cijenom ponovnog izračuna. Nemojte pretpostavljati da je checkpointiranje svake operacije optimalno; to može značajno usporiti treniranje.

Potražite tenzore koji održavaju računske grafove živima

Ako memorija raste pri svakoj iteraciji umjesto da doseže vrh na otprilike istoj razini, provjerite Python reference. Uobičajeni obrazac je spremanje tenzora povezanih s grafom u listu:

# Rizično ako se zadržava mnogo koraka:
loss_history.append(loss)

# Spremite Python broj umjesto toga:
loss_history.append(loss.item())

Isti problem se može dogoditi kada predmemorirate izlaze modela, attention mape, skrivena stanja ili tenzore validacije bez odvajanja ili premještanja s GPU-a. Obrišite reference koje vam više nisu potrebne i koristite detach() samo kada namjerno želite tenzor odvojen od autograda.

Prilagođavajte alocator samo nakon što statistika ukazuje na fragmentaciju

Trenutna PyTorch dokumentacija preferira varijablu okruženja PYTORCH_ALLOC_CONF. Starija PYTORCH_CUDA_ALLOC_CONF ostaje alias za povratnu kompatibilnost. Ova pojedinost o imenovanju promijenjena je u trenutnoj dokumentaciji, pa nove konfiguracije trebaju koristiti preferirani naziv. PyTorch CUDA varijable okruženja.

Dvije opcije alocirača su posebno relevantne:

  • expandable_segments:True je eksperimentalna i dizajnirana je za smanjenje neiskoristivih komadića memorije kada se veličine alokacija mijenjaju, kao kod opterećenja čije se veličine batcha ili tenzora mijenjaju.
  • max_split_size_mb može smanjiti fragmentaciju s nativnim alociračem, ali PyTorch ga izričito opisuje kao posljednje utočište za opterećenja koja ne uspijevaju s OOM-om dok pokazuju veliku količinu neaktivnih split blokova. Također može naškoditi performansama i ignorira ga cudaMallocAsync backend.
# Primjer za opterećenje s promjenjivim veličinama alokacija:
export PYTORCH_ALLOC_CONF=expandable_segments:True

Nemojte kopirati zastavice alocirača s drugog računala bez provjere memory_summary() ili snimke. Pravi problem kapaciteta—gdje živi tenzori već ispunjavaju GPU—neće biti riješen podešavanjem fragmentacije.

Kada jedan GPU i dalje ne može smjestiti model

Ako jedan uzorak pri najmanjoj praktičnoj ulaznoj veličini i dalje izaziva OOM, problem može biti model i stanje optimizatora, a ne batch. U tom trenutku razmislite o manjoj arhitekturi, parametrima niže preciznosti gdje je numerički prikladno, CPU/offload strategijama ili shardiranom distribuiranom treniranju.

PyTorchov Fully Sharded Data Parallel (FSDP) može shardirati parametre modela preko data-parallel radnika, a njegova FULL_SHARD strategija također shardira gradijente i stanja optimizatora. To može smanjiti memoriju po GPU-u u usporedbi s potpuno repliciranom data paralelizmom, uz cijenu komunikacije i složenijeg ponašanja treniranja. PyTorch FSDP dokumentacija.

Praktičan redoslijed operacija

PrioritetPromjenaMemorijska koristGlavni kompromis
1Smanjite mikro-batch ili ulaznu veličinuIzravno smanjuje aktivni radni skupMože smanjiti propusnost ili promijeniti ponašanje optimizacije
2Koristite AMPMože smanjiti memoriju aktivacija/tenzoraZahtijeva numeričku validaciju
3Koristite akumulaciju gradijenataZadržava mikro-batcheve malima uz očuvanje veće efektivne batch veličineViše koraka po ažuriranju optimizatora
4Koristite checkpointing aktivacijaSmanjuje spremljene aktivacijeDodatni ponovni izračun
5Uklonite zadržane tenzore/grafoveZaustavlja nenamjerno rastZahtijeva inspekciju koda
6Prilagodite postavke alociračaMože pomoći u slučajevima ograničenim fragmentacijomOvisno o opterećenju; može smanjiti performanse
7Shardirajte ili promijenite modelMože smanjiti memoriju parametara/stanja po GPU-uNajveća složenost

Popis za provjeru: kako znati da je OOM stvarno riješen

  • Pokrenite nekoliko reprezentativnih iteracija treniranja, a ne samo jedan uspješan forward pass.
  • Resetirajte i zabilježite max_memory_allocated() kako biste znali novi vrh.
  • Potvrdite da GPU memorija doseže stabilan raspon umjesto da raste pri svakoj iteraciji.
  • Validirajte gubitak i gradijente nakon omogućavanja mješovite preciznosti.
  • Potvrdite da akumulacija gradijenata čuva raspored ažuriranja optimizatora koji ste namjeravali.
  • Pokrenite validacijski prolaz pod torch.no_grad() kada gradijenti nisu potrebni.
  • Ako ste promijenili postavke alocirača, usporedite statistiku memorije i propusnost prije i poslije.
  • Nemojte smatrati problem riješenim samo zato što nvidia-smi pokazuje manje rezervirane memorije nakon empty_cache(); samo opterećenje treniranja mora se dovršiti pri svom normalnom vrhu.

CUDA OOM je najbolje tretirati kao problem memorijskog proračuna, a ne kao jedan PyTorch bug. Izmjerite vrh, prvo smanjite živi radni skup, zatim koristite mješovitu preciznost, akumulaciju i checkpointing kao namjerne kompromise. Pređite na podešavanje alocirača samo kada statistika alocirača ukazuje na fragmentaciju, a na shardiranje ili drugi model kada model sam više ne staje udobno na jedan GPU.

Ostavite komentar

Kako popraviti grešku "Tailwind CSS stilovi se ne ažuriraju" u Vite React aplikaciji

Kako popraviti grešku "Tailwind CSS stilovi se ne ažuriraju" u Vite React aplikaciji

Ispravite Tailwind CSS stilove koji se ne ažuriraju u Vite Reactu provjerom postavki Tailwind v4, CSS uvoza, otkrivanja izvora, dinamičkih klasa, HMR-a i zastarjelih predmemorija.

Kako popraviti ModuleNotFoundError: Nema modula pod nazivom 'pip' u Pythonu 3

Kako popraviti ModuleNotFoundError: Nema modula pod nazivom 'pip' u Pythonu 3

Ispravite ModuleNotFoundError u Pythonu 3 za pip na Windowsima, macOS-u i Linuxu pomoću ensurepipa, OS paketa, virtualnih okruženja i provjera interpretera.

Kako popraviti "Dozvola odbijena (javni ključ)" u GitHub SSH-u

Kako popraviti "Dozvola odbijena (javni ključ)" u GitHub SSH-u

Ispravite GitHub SSH Permission Denied (publickey) provjerom hosta, aktivnog SSH ključa, GitHub računa, SSO autorizacije, udaljenog URL-a i pristupa portu 22.

Kako popraviti "Git Push Rejected: Non-FastForward" bez gubitka promjena

Kako popraviti "Git Push Rejected: Non-FastForward" bez gubitka promjena

Sigurno ispravite Git push koji ne omogućuje brzo premotavanje. Zaštitite lokalni rad, dohvatite udaljene commitove, odaberite spajanje ili rebase, riješite sukobe i pushajte bez gubitka promjena.

Kako popraviti "Nginx 502 Bad Gateway" prilikom proxyja za Node.js

Kako popraviti "Nginx 502 Bad Gateway" prilikom proxyja za Node.js

Ispravite greške Nginx 502 Bad Gateway s Node.js uzvodno provjerom porta aplikacije, NGINX logova, proxy_pass adrese, umrežavanja kontejnera, vremenskih ograničenja i ponovnog učitavanja.

Kako popraviti "Tip 'null' se ne može dodijeliti tipu" u TypeScriptu

Kako popraviti "Tip 'null' se ne može dodijeliti tipu" u TypeScriptu

Ispravljena je greška "Tip 'null' nije moguće dodijeliti tipu" u TypeScriptu s tipovima unija, sužavanjem, zadanim vrijednostima i sigurnim tvrdnjama pod strictNullChecks.

Kako ispraviti pogrešku „Prisma Client has not been generated yet”

Kako ispraviti pogrešku „Prisma Client has not been generated yet”

Ispravite pogrešku da Prisma Client nije generiran provjerom generatora, sheme, izlazne putanje, uvoza, verzija, monorepo postavki i koraka izgradnje pri implementaciji.

Kako ispraviti "ERR_MODULE_NOT_FOUND" u Node.js ESM uvozima

Kako ispraviti "ERR_MODULE_NOT_FOUND" u Node.js ESM uvozima

Ispravite Node.js ERR_MODULE_NOT_FOUND u ESM-u provjerom putanja uvoza, ekstenzija datoteka, instalacije paketa, izvoza, ESM načina rada i čistih instalacija.

Kako riješiti problem sa SSL certifikatom: Nemoguće dobiti lokalni certifikat izdavatelja u Gitu

Kako riješiti problem sa SSL certifikatom: Nemoguće dobiti lokalni certifikat izdavatelja u Gitu

Riješite Gitovu grešku 'nemoguće dobiti lokalni certifikat izdavatelja' identificiranjem pozadine povjerenja, instaliranjem ispravnog lanca CA i održavanjem omogućene SSL verifikacije.

Kako riješiti grešku mrežnog isteka vremena MongoDB u Mongoose vezi

Kako riješiti grešku mrežnog isteka vremena MongoDB u Mongoose vezi

Riješite greške mrežnog isteka vremena MongoDB u Mongooseu identificiranjem vrste isteka, testiranjem dostupnosti Atlasa ili TCP-a, ispravljanjem URI-ja i podešavanjem vremena isteka samo kada je opravdano.