فاز 0 · درس 12

دیباگ و پروفایل کردن مدل‌های یادگیری ماشین در PyTorch

نوع
ساختنی
زمان
حدود 60 دقیقه
زبان‌ها
Python
پیش‌نیاز
درس 1 (محیط توسعه)، آشنایی مقدماتی با PyTorch
یک مهندس پشت میز کار که با ذره‌بین و ابزارهای سنجش در حال بررسی یک شبکه عصبی درخشان با گره‌های متصل است
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفهوم اصلی
  4. پیاده‌سازی گام‌به‌گام
  5. بخش 1: دیباگ با چاپ کردن (بله، واقعاً جواب می‌دهد)
  6. بخش 2: دیباگر پایتون (pdb و breakpoint)
  7. بخش 3: لاگ‌گیری در پایتون (Logging)
  8. بخش 4: زمان‌سنجی بخش‌های مختلف کد
  9. بخش 5: ابزارهای cProfile و line_profiler
  10. بخش 6: پروفایل حافظه
  11. بخش 7: باگ‌های رایج هوش مصنوعی و روش شناسایی آن‌ها
  12. بخش 8: مبانی TensorBoard
  13. بخش 9: دیباگر VS Code
  14. به کار ببندید
  15. تحویل پروژه
  16. تمرین‌ها
  17. راهنمایی‌ها

در برنامه‌نویسی سنتی، خطاها واضح‌اند؛ برنامه متوقف می‌شود و گزارش خطا (Stack Trace) محل دقیق مشکل را نشان می‌دهد. در یادگیری ماشین اما با کابوس «خرابی‌های خاموش» روبه‌رو هستیم: مدلی که ساعت‌ها بدون هیچ اروری آموزش می‌بیند، اما خروجی آن عملاً بی‌مصرف است یا در میانه‌ی مسیر با لاس‌های NaN یا خطای پر شدن حافظه متوقف می‌شود.

با توجه به اینکه دسترسی به پردازنده‌های گرافیکی قدرتمند و سرویس‌های ابری خارجی برای توسعه‌دهندگان در ایران با موانعی مانند پرداخت ارزی و هزینه‌های گزاف همراه است، هدر رفتن زمان آموزش و منابع پردازشی به پای باگ‌های ساده هزینه‌ی مضاعفی دارد. یادگیری تکنیک‌های دیباگ سریع و پروفایل خط‌به‌خط روی سیستم محلی، این هزینه‌ها را به حداقل می‌رساند.

در این درس ابزارهای پایه‌ای پایتون و پایتورچ مانند breakpoint شرطی، cProfile، tracemalloc و TensorBoard را بررسی می‌کنیم تا بتوانید پیش از مصرف زمان و منابع، گلوگاه‌های پردازشی، عدم تطابق ابعاد و ناپایداری‌های عددی را ردیابی و رفع کنید.

بدترین باگ‌های هوش مصنوعی کرش نمی‌کنند؛ آن‌ها بی‌صدا روی داده‌های بی‌ارزش آموزش می‌بینند و یک نمودار لاس (Loss) بسیار زیبا تحویلتان می‌دهند.

اهداف یادگیری

  • استفاده از breakpoint() شرطی و debug_print برای بررسی ابعاد، نوع داده (dtype) و مقادیر NaN تنسورها در حین آموزش
  • پروفایل کردن حلقه‌های آموزش با cProfile، line_profiler و tracemalloc جهت یافتن گلوگاه‌ها
  • شناسایی باگ‌های رایج هوش مصنوعی: عدم تطابق ابعاد، لاس NaN، نشت داده (Data Leakage) و تنسورها روی دستگاه اشتباه
  • راه‌اندازی TensorBoard برای مصورسازی نمودارهای لاس، هیستوگرام وزن‌ها و توزیع گرادیان‌ها

صورت مسئله

کدهای هوش مصنوعی به شکلی متفاوت از کدهای معمولی خراب می‌شوند. یک وب‌اپلیکیشن با ردگیری خطا (Stack Trace) کرش می‌کند؛ اما یک حلقه‌ی آموزش با پیکربندی اشتباه، 8 ساعت اجرا می‌شود، 200 دلار پای GPU هزینه می‌تراشد و در نهایت مدلی تحویل می‌دهد که برای هر ورودی فقط میانگین را پیش‌بینی می‌کند. در این فرایند هیچ خطایی صادر نشده است؛ باگ فقط یک تنسور روی دستگاه اشتباه، یک .detach() فراموش‌شده، یا نشت برچسب‌ها به درون ویژگی‌ها بوده است.

به ابزارهای دیباگی نیاز دارید که این خرابی‌های خاموش را پیش از هدر رفتن وقت و منابع پردازشی‌تان شکار کنند.

مفهوم اصلی

دیباگ کدهای هوش مصنوعی در سه سطح انجام می‌شود:

3. پویایی‌های آموزشمنحنی‌های زیان، نرم‌های گرادیان،فعال‌سازی‌ها2. عملیات‌های تنسورشکل‌ها، dtypes، دستگاه‌ها، مقادیر NaN/Inf1. پایتون استانداردBreakpoints، لاگ‌گیری، پروفایلینگ، حافظه

بیشتر افراد یک‌راست به سراغ سطح 3 می‌روند (خیره شدن به TensorBoard)؛ اما 80% از باگ‌های هوش مصنوعی در سطوح 1 و 2 رخ می‌دهند.

زمان کجا صرف می‌شودپروفایلینگ شعله را روی تابعی که واقعاً زمان می‌سوزاند پهن‌تر می‌کند
WHERE THE TIME GOESThe GPU math you suspected is not the bottleneck; the profiler shows the step spending most of its wall time inside the DataLoader. Each bar is cumulative time in a call, stacked on its caller, so the widest tower marks the function to fix, here by adding loader workers and pinned memory instead of optimizing the model.train_step · 100%بارگذاری دادهforwardbwdDataLoader.__next__62% از این گام صرف دریافت بچ‌ها می‌شودfix: num_workers, pin_memorycProfile، زمان تجمعی در هر فراخوانی، به شکل نمودار شعله‌ای
محاسبات GPU که به آن مشکوک بودید گلوگاه نیست؛ پروفایلر نشان می‌دهد که این گام بیشترین زمان واقعی خود را درون DataLoader صرف می‌کند. هر ستون نشان‌دهنده‌ی زمان تجمعی در یک فراخوانی است که روی فراخواننده‌ی خود پشته شده است؛ بنابراین عریض‌ترین برج تابعی را مشخص می‌کند که باید اصلاح شود، در اینجا با افزودن workerهای لودر و pinned memory به جای بهینه‌سازی مدل.

پیاده‌سازی گام‌به‌گام

بخش 1: دیباگ با چاپ کردن (بله، واقعاً جواب می‌دهد)

خیلی‌ها دیباگ با دستور print را دست‌کم می‌گیرند؛ اما نباید این‌طور باشد. در کدهای تنسوری، یک دستور چاپ هدفمند از خط‌به‌خط جلو رفتن در دیباگر کاربردی‌تر است؛ چون باید ابعاد، نوع داده و دامنه‌ی مقادیر را یک‌جا با هم ببینید.

def debug_print(name, tensor):
    print(f"{name}: shape={tensor.shape}, dtype={tensor.dtype}, "
          f"device={tensor.device}, "
          f"min={tensor.min().item():.4f}, max={tensor.max().item():.4f}, "
          f"mean={tensor.mean().item():.4f}, "
          f"has_nan={tensor.isnan().any().item()}")

این تابع را بعد از هر عملیات مشکوک فراخوانی کنید. وقتی باگ پیدا شد، دستورهای چاپ را پاک کنید. به همین سادگی.

بخش 2: دیباگر پایتون (pdb و breakpoint)

دیباگر داخلی پایتون در پروژه‌های هوش مصنوعی دست‌کم گرفته شده است. یک breakpoint() داخل حلقه‌ی آموزش بگذارید و تنسورها را به صورت تعاملی بررسی کنید.

def training_step(model, batch, criterion, optimizer):
    inputs, labels = batch
    outputs = model(inputs)
    loss = criterion(outputs, labels)

    if loss.item() > 100 or torch.isnan(loss):
        breakpoint()

    loss.backward()
    optimizer.step()

وقتی کنترل در دیباگر به دست شما می‌افتد، این دستورها حسابی به کار می‌آیند:

  • p outputs.shape برای بررسی ابعاد
  • p loss.item() برای دیدن مقدار لاس
  • p torch.isnan(outputs).sum() برای شمارش تعداد NaNها
  • p model.fc1.weight.grad برای بررسی گرادیان‌ها
  • c برای ادامه، q برای خروج

به این روش «دیباگ شرطی» می‌گویند؛ یعنی فقط وقتی متوقف می‌شوید که مشکلی دیده شود. در یک اجرای آموزشی با 10,000 گام، این ویژگی بسیار حیاتی است.

بخش 3: لاگ‌گیری در پایتون (Logging)

وقتی دیباگ از یک بررسی سریع فراتر می‌رود، دستورهای print را با لاگ‌گیری جایگزین کنید.

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("training.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

logger.info("Starting training: lr=%.4f, batch_size=%d", lr, batch_size)
logger.warning("Loss spike detected: %.4f at step %d", loss.item(), step)
logger.error("NaN loss at step %d, stopping", step)

لاگ‌گیری برچسب زمانی، سطح اهمیت خطا و خروجی فایلی به شما می‌دهد. وقتی اجرای آموزش ساعت 3 صبح با شکست مواجه می‌شود، به فایل لاگ نیاز دارید، نه خروجی ترمینالی که اسکرول شده و از صفحه بیرون رفته است.

بخش 4: زمان‌سنجی بخش‌های مختلف کد

دانستن اینکه زمان صرف چه بخش‌هایی می‌شود، نخستین گام در بهینه‌سازی است.

import time

class Timer:
    def __init__(self, name=""):
        self.name = name

    def __enter__(self):
        self.start = time.perf_counter()
        return self

    def __exit__(self, *args):
        elapsed = time.perf_counter() - self.start
        print(f"[{self.name}] {elapsed:.4f}s")

with Timer("data loading"):
    batch = next(dataloader_iter)

with Timer("forward pass"):
    outputs = model(batch)

with Timer("backward pass"):
    loss.backward()

یک نتیجه‌ی بسیار رایج: بارگذاری داده 60% از زمان آموزش را می‌گیرد. راه‌حل این مشکل تنظیم num_workers > 0 در DataLoader است، نه خرید یک GPU سریع‌تر.

بخش 5: ابزارهای cProfile و line_profiler

وقتی به ابزاری بیشتر از زمان‌سنج‌های دستی نیاز دارید:

python -m cProfile -s cumtime train.py

این دستور تمام فراخوانی‌های توابع را مرتب‌شده بر اساس مجموع زمان مصرفی نشان می‌دهد. برای پروفایل کردن خط‌به‌خط:

pip install line_profiler
@profile
def train_step(model, data, target):
    output = model(data)
    loss = F.cross_entropy(output, target)
    loss.backward()
    return loss

# اجرا با: kernprof -l -v train.py

بخش 6: پروفایل حافظه

حافظه‌ی CPU با tracemalloc

import tracemalloc

tracemalloc.start()

# کد شما اینجا
model = build_model()
data = load_dataset()

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("lineno")
for stat in top_stats[:10]:
    print(stat)

حافظه‌ی CPU با memory_profiler

pip install memory_profiler
from memory_profiler import profile

@profile
def load_data():
    raw = read_csv("data.csv")       # watch memory jump here
    processed = preprocess(raw)       # and here
    return processed

با دستور python -m memory_profiler your_script.py اجرا کنید تا مصرف حافظه را خط‌به‌خط ببینید.

حافظه‌ی GPU با PyTorch

import torch

if torch.cuda.is_available():
    print(torch.cuda.memory_summary())

    print(f"Allocated: {torch.cuda.memory_allocated() / 1e9:.2f} GB")
    print(f"Cached: {torch.cuda.memory_reserved() / 1e9:.2f} GB")

وقتی به خطای کمبود حافظه (OOM یا Out of Memory) برمی‌خورید:

  1. اندازه‌ی بچ (Batch size) را کم کنید (همیشه اولین گزینه‌ای که باید امتحان کنید)
  2. از torch.cuda.empty_cache() برای آزادسازی حافظه‌ی کش‌شده استفاده کنید
  3. برای متغیرهای میانی بزرگ، از del tensor و بلافاصله torch.cuda.empty_cache() استفاده کنید
  4. دقت ترکیبی (Mixed precision یا torch.cuda.amp) را به کار ببرید تا مصرف حافظه نصف شود
  5. برای مدل‌های خیلی عمیق از چک‌پوینت‌گذاری گرادیان (Gradient Checkpointing) استفاده کنید

بخش 7: باگ‌های رایج هوش مصنوعی و روش شناسایی آن‌ها

عدم تطابق ابعاد (Shape Mismatch)

شایع‌ترین باگ ممکن. تنسور ابعاد [batch, features] دارد، در حالی که مدل انتظار [batch, channels, height, width] را دارد.

def check_shapes(model, sample_input):
    print(f"Input: {sample_input.shape}")
    hooks = []

    def make_hook(name):
        def hook(module, inp, out):
            in_shape = inp[0].shape if isinstance(inp, tuple) else inp.shape
            out_shape = out.shape if hasattr(out, "shape") else type(out)
            print(f"  {name}: {in_shape} -> {out_shape}")
        return hook

    for name, module in model.named_modules():
        hooks.append(module.register_forward_hook(make_hook(name)))

    with torch.no_grad():
        model(sample_input)

    for h in hooks:
        h.remove()

این را یک بار با یک بچ نمونه اجرا کنید؛ تمام تغییرات ابعاد را در سراسر مدل ترسیم می‌کند.

لاس NaN

لاس NaN یعنی در محاسبات مشکلی منفجر شده است. دلایل رایج:

  • نرخ یادگیری (Learning rate) خیلی بالا
  • تقسیم بر صفر در تابع لاس اختصاصی
  • لگاریتم گرفتن از صفر یا عدد منفی
  • انفجار گرادیان‌ها در RNNها
def detect_nan(model, loss, step):
    if torch.isnan(loss):
        print(f"NaN loss at step {step}")
        for name, param in model.named_parameters():
            if param.grad is not None:
                if torch.isnan(param.grad).any():
                    print(f"  NaN gradient in {name}")
                if torch.isinf(param.grad).any():
                    print(f"  Inf gradient in {name}")
        return True
    return False

نشت داده (Data Leakage)

مدل شما به دقت 99% روی داده‌های آزمون می‌رسد. عالی به نظر می‌رسد، اما این یک باگ است.

def check_data_leakage(train_set, test_set, id_column="id"):
    train_ids = set(train_set[id_column].tolist())
    test_ids = set(test_set[id_column].tolist())
    overlap = train_ids & test_ids
    if overlap:
        print(f"DATA LEAKAGE: {len(overlap)} samples in both train and test")
        return True
    return False

نشت زمانی (Temporal leakage) را هم بررسی کنید: استفاده از داده‌های آینده برای پیش‌بینی گذشته. همیشه پیش از تفکیک داده‌ها، آن‌ها را بر اساس برچسب زمانی مرتب کنید.

دستگاه اشتباه (Wrong Device)

تنسورها روی دستگاه‌های مختلف (CPU در برابر GPU) باعث خطای زمان اجرا می‌شوند. اما گاهی یک تنسور بی‌صدا روی CPU باقی می‌ماند در حالی که بقیه روی GPU هستند، و فقط سرعت آموزش به‌شدت افت می‌کند.

def check_devices(model, *tensors):
    model_device = next(model.parameters()).device
    print(f"Model device: {model_device}")
    for i, t in enumerate(tensors):
        if t.device != model_device:
            print(f"  WARNING: tensor {i} on {t.device}, model on {model_device}")

بخش 8: مبانی TensorBoard

ابزار TensorBoard روند اتفاقات داخل مدل را در طول زمان آموزش به تصویر می‌کشد.

pip install tensorboard
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("runs/experiment_1")

for step in range(num_steps):
    loss = train_step(model, batch)

    writer.add_scalar("loss/train", loss.item(), step)
    writer.add_scalar("lr", optimizer.param_groups[0]["lr"], step)

    if step % 100 == 0:
        for name, param in model.named_parameters():
            writer.add_histogram(f"weights/{name}", param, step)
            if param.grad is not None:
                writer.add_histogram(f"grads/{name}", param.grad, step)

writer.close()

اجرای آن:

tensorboard --logdir=runs

به چه نکاتی باید توجه کرد:

  • لاس کاهش پیدا نمی‌کند: نرخ یادگیری بیش از حد پایین است، یا معماری مدل ایراد دارد
  • لاس نوسانات شدیدی دارد: نرخ یادگیری بیش از حد بالاست
  • لاس به NaN تبدیل می‌شود: ناپایداری عددی (بخش NaN در بالا را ببینید)
  • لاس آموزش کاهش می‌یابد اما لاس اعتبارسنجی بالا می‌رود: بیش‌برازش (Overfitting)
  • هیستوگرام وزن‌ها به صفر میل می‌کند: محوشدگی گرادیان (Vanishing gradients)
  • هیستوگرام گرادیان‌ها منفجر می‌شود: به برش گرادیان (Gradient clipping) نیاز دارید

بخش 9: دیباگر VS Code

برای دیباگ تعاملی، VS Code را با فایل launch.json پیکربندی کنید:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Training",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "justMyCode": false
        }
    ]
}

با کلیک روی حاشیه‌ی کنار شماره‌ی خطوط، نقطه‌ی توقف (Breakpoint) بگذارید. از پنل Variables برای بررسی ویژگی‌های تنسور استفاده کنید. کنسول دیباگ (Debug Console) هم به شما اجازه می‌دهد هر عبارت دلخواه پایتون را در میانه‌ی اجرا وارد و بررسی کنید.

این قابلیت برای دنبال کردن خط‌به‌خط پایپ‌لاین‌های پیش‌پردازش داده که می‌خواهید تک‌تک تبدیل‌ها را ببینید، بسیار کاربردی است.

به کار ببندید

این جریان کاری دیباگی است که بیشتر باگ‌های هوش مصنوعی را به دام می‌اندازد:

  1. پیش از شروع آموزش: تابع check_shapes را با یک بچ نمونه اجرا کنید. مطمئن شوید ابعاد ورودی و خروجی با انتظارات هم‌خوانی دارند.
  2. در 10 گام اول: از debug_print برای لاس، خروجی‌ها و گرادیان‌ها استفاده کنید. بررسی کنید که هیچ مقداری NaN نباشد و مقادیر در بازه‌ای منطقی قرار داشته باشند.
  3. در طول آموزش: لاس، نرخ یادگیری و نُرم‌های گرادیان را لاگ کنید. برای مصورسازی از TensorBoard کمک بگیرید.
  4. وقتی چیزی خراب شد: در همان نقطه‌ی شکست یک breakpoint() بگذارید و تنسورها را تعاملی وارسی کنید.
  5. برای کارایی و عملکرد: زمان بارگذاری داده را در مقایسه با گذر رفت و برگشت بسنجید. اگر به پر شدن حافظه (OOM) نزدیک هستید، حافظه را پروفایل کنید.

تحویل پروژه

اسکریپت جعبه‌ابزار دیباگ را اجرا کنید:

python phases/00-setup-and-tooling/12-debugging-and-profiling/code/debug_tools.py

برای دریافت یک پرامپت جهت عیب‌یابی باگ‌های ویژه‌ی هوش مصنوعی، فایل outputs/prompt-debug-ai-code.md را ببینید.

تمرین‌ها

  1. اسکریپت debug_tools.py را اجرا کنید و خروجی هر بخش را به دقت بخوانید. مدل فرضی را طوری دستکاری کنید که یک NaN ایجاد شود (راهنمایی: در گذر پیش‌رو یا Forward pass یک تقسیم بر صفر قرار دهید) و ببینید شناساگر چطور آن را پیدا می‌کند.
  2. یک حلقه‌ی آموزش را با cProfile پروفایل کنید و کندترین تابع را بیابید.
  3. با استفاده از tracemalloc مشخص کنید کدام خط در پایپ‌لاین بارگذاری داده، بیشترین حافظه را تخصیص می‌دهد.
  4. برای یک اجرای آموزشی ساده، TensorBoard را راه‌اندازی کنید و ببینید آیا مدل دچار بیش‌برازش می‌شود یا خیر.
  5. در داخل حلقه‌ی آموزش از breakpoint() استفاده کنید. ابعاد تنسورها، دستگاه آن‌ها و مقادیر گرادیان را از طریق پرامپت دیباگر بررسی و تمرین کنید.

راهنمایی‌ها

تمرین 1. دستور python3 phases/00-setup-and-tooling/12-debugging-and-profiling/code/debug_tools.py را اجرا کنید و خروجی هر تابع دمو را بخوانید. سپس مدل را در demo_nan_detection طوری تغییر دهید که خروجی‌اش بر صفر تقسیم شود و دوباره آن را اجرا کنید؛ تابع detect_nan باید یک هشدار NaN همراه با شماره‌ی گام چاپ کند.

تمرین 2. حلقه را با دستور python -m cProfile -s cumtime your_script.py اجرا کنید، یا آن را داخل cProfile.Profile() قرار دهید. سطرهای بالای ستون cumtime را بررسی کنید؛ تابعی که بیشترین زمان را دارد و جزو کدهای خودتان است، کندترین تابع خواهد بود.

تمرین 3. پیش از کدهای بارگذاری داده، tracemalloc.start() و بعد از آن tracemalloc.take_snapshot() را فراخوانی کنید؛ درست مثل demo_memory_tracking. سپس snapshot.statistics("lineno")[:5] را چاپ کنید؛ سطر اول، فایل و شماره خطی را که بیشترین حافظه را تخصیص داده نشان می‌دهد.

تمرین 4. مقدار لاس آموزش و لاس اعتبارسنجی را در هر دوره (Epoch) داخل یک SummaryWriter ثبت کرده و سپس دستور tensorboard --logdir=runs را اجرا کنید. اگر لاس آموزش همچنان پایین بیاید اما لاس اعتبارسنجی بالا برود، مدل دچار بیش‌برازش شده است.

تمرین 5. داخل حلقه و بعد از loss.backward() دستور breakpoint() را قرار دهید و اسکریپت را اجرا کنید. در اعلان (Pdb)، عبارت‌های x.shape، x.device و next(model.parameters()).grad را چاپ کنید، سپس c را بزنید تا برنامه به گام بعدی برود.

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسچه چیزی دیباگ کردن کدهای AI/ML را اساساً با دیباگ یک وب‌اپلیکیشن معمولی متفاوت می‌کند؟
  2. پیش از درسیک پروفایلر چه چیزی را اندازه‌گیری می‌کند؟
  3. پس از درسمدل شما به دقت 99٪ روی مجموعه‌ی آزمون دست یافته است. ابتدا باید به کدام باگ خاص هوش مصنوعی مشکوک شوید؟
  4. پس از درسهنگام پروفایل کردن تفکیک زمانی حلقه‌ی آموزش، رایج‌ترین یافته چیست؟
  5. پس از درسشما در گام 500 با مقدار زیان NaN مواجه می‌شوید. کدام رویکرد به شما کمک می‌کند علت ریشه‌ای را پیدا کنید؟

سؤالات متداول

چرا مقدار تابع زیان (Loss) در پایتورچ به NaN تبدیل می‌شود؟

دلایل رایج عبارتند از نرخ یادگیری بیش از حد بزرگ، تقسیم بر صفر در تابع زیان سفارشی، محاسبه لگاریتم اعداد صفر یا منفی، یا انفجار گرادیان‌ها در طول پس‌انتشار.

هنگام مواجهه با خطای پر شدن حافظه GPU (OOM) اولین اقدام چیست؟

همیشه نخستین و مؤثرترین گام کاهش اندازه‌ی بچ (Batch size) است. در مراحل بعد می‌توان از دقت ترکیبی (AMP)، پاکسازی کش با empty_cache و تکنیک Gradient Checkpointing استفاده کرد.

چگونه می‌توان فهمید گلوگاه سرعت آموزش مدل در کجاست؟

با پروفایل کردن کد با cProfile یا زمان‌سنجی دستی؛ در اغلب سناریوها لود نشدن سریع داده‌ها گلوگاه اصلی است که با تنظیم num_workers در DataLoader برطرف می‌شود.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.