تصور کنید در پروژهای هستید که هر تغییر کوچک در نرخ یادگیری، شما را مجبور میکند دهها خط کد را کامنت کنید یا نسخههای متعددی از یک اسکریپت بسازید. این آشفتگی در پروژههای PyTorch رایج است، جایی که ابرپارامترهای سختکد شده (hardcoded) اغلب باعث ایجاد مجموعهای از خطوط کامنتشده و نسخههای تکهتکه از اسکریپتها میشود. اما با Gin Config میتوان تمام درجات آزادی آزمایش — از ابعاد لایهها تا نرخ یادگیری — را به فایلهای خارجی Declarative منتقل کرد تا کد اصلی اجرایی ثابت و پایدار بماند.
در گردشکارهای فعلی یادگیری ماشین، تکرارپذیری اغلب شکست میخورد چون پیکربندی دقیقی که برای یک اجرای خاص به کار رفته، ثبت نمیشود. اکثر پژوهشگران به ثبت دستی یا فایلهای حجیم JSON متکی هستند که مستقیماً با آرگومانهای توابع مطابقت ندارند. طبق گزارشی از Marktechpost، سیستم Gin با تبدیل پیکربندیها به یک سامانه تزریق وابستگی سراسری (Global Dependency Injection)، این مشکل را حل میکند.
برای درک بهتر، اسکریپت آموزش را مانند یک آشپزخانه حرفهای تصور کنید: کد، مجموعهای از ابزارها و دستورپخت است، اما فایل Gin مانند لیست خرید امروز است؛ شما برای تغییر یک ماده اولیه، اجاق گاز را عوض نمیکنید، بلکه فقط لیست را تغییر میدهید.
همانطور که در تحلیلهای پیشین ما درباره امنیت و مدیریت مدلهای متنباز اشاره کردیم، جداسازی لایه تنظیمات از منطق اجرا، اولین قدم برای مقیاسپذیری هر پروژه است.
معماری خط لولههای پیکربندیپذیر
پیادهسازی این سیستم با افزودن دکوراتور @gin.configurable به توابع اصلی آغاز میشود. این کار به Gin میگوید که آرگومانهای تابع میتوانند به مقادیر یک فایل خارجی .gin متصل شوند. در این خط لوله خاص، سیستم همه چیز را مدیریت میکند؛ از تنظیم پوشهها (با استفاده از Path برای CONFIG_DIR و RUN_DIR) تا مدیریت وضعیت سراسری پیکربندی از طریق gin.clear_config().
برای تست این سازوکار، از یک تکلیف طبقهبندی دوتایی مارپیچ غیرخطی استفاده شده است. این مجموعه داده توسط تابع make_spiral_dataset و با کمک NumPy تولید میشود تا دو مارپیچ درهمتنیده ایجاد کند. ساخت این دادهها شامل محاسبه radius_0 و radius_1 از طریق np.linspace(0.05, 1.0, n_per_class) و محاسبه theta_0 و theta_1 با استفاده از فرمول rotations * 2 * np.pi * radius است.
پیچیدگی این تکلیف با پارامترهایی مثل نویز (noise) با مقدار پیشفرض ۰.۱۸ و چرخش (rotations) با مقدار ۱.۷۵ کنترل میشود. همچنین از یک train_fraction برابر با ۰.۸ برای تقسیم دادهها استفاده شده است. تابع تولید مجموعه داده، یک دیکشنری شامل تنسورهای آموزش و اعتبارسنجی بههمراه یک بلوک متاداده (metadata) برمیگرداند که بذر (seed)، نویز و چرخش دقیق مورد استفاده برای آن تولید خاص را ثبت میکند.
این سامانه یک MLP پیکربندیپذیر با اجزای ماژولار زیر تعریف میکند:
- کلاس MLP: از دکوراتور
@gin.configurableبرای خارجی کردنinput_dim(الزامی) وhidden_dims(پیشفرض ۶۴، ۶۴) وoutput_dimاستفاده میکند. انتخابهای معماری مثل تابع فعالساز (activation) که از ReLU، GELU، Tanh و SiLU از طریق یک کمکی داخلی به نامactivation_layerپشتیبانی میکند، دراپاوت (dropout) با مقدار پیشفرض ۰.۰ و استفاده از لایه نرمالسازی (LayerNorm)، همگی از بیرون کنترل میشوند. شبکه در نهایت به عنوان یک بلوکnn.Sequentialساخته میشود. - کارخانه بهینهساز: تابع
make_optimizerاجازه میدهد با استفاده از رشتههای پیکربندی بین AdamW (با نرخ یادگیری پیشفرض 3e-3 و weight_decay=1e-3) و SGD (با نرخ یادگیری پیشفرض 3e-3، مومنتوم ۰.۹ و weight_decay=1e-3) جابهجا شویم. این تابع از یکdenylist=["params"]استفاده میکند تا Gin سعی نکند پارامترهای خود مدل را پیکربندی کند. - زمانبند کسینوسی: تابع
make_cosine_schedulerمنطق مربوط به دورههای گرمکردن (warmup_epochsپیشفرض ۵)، کل دورهها (total_epochsپیشفرض ۶۰) و کمترین نرخ یادگیری (min_lr_factorپیشفرض ۰.۰۵) را پیاده میکند. این بخش ازLambdaLRبرای مدیریت منحنی کاهش نرخ یادگیری استفاده میکند و پیشرفت را به صورت(epoch - warmup_epochs) / (total_epochs - warmup_epochs)محاسبه میکند تا از گرمکردن خطی به کاهش کسینوسی انتقال یابد. - توابع زیان: از
bce_with_logits_lossاستفاده شده که شامل پارامتر پیکربندیپذیر هموارسازی برچسب (label_smoothing) با مقداری مانند ۰.۰۲ است. این کار تعمیمپذیری را با اصلاح اهداف طبق فرمولtargets * (1.0 - label_smoothing) + 0.5 * label_smoothingبهبود میبخشد.
مدیریت دادهها و حلقه آموزش
فراتر از مدل، خط لوله داده نیز ماژولار است. تابع make_loader با دکوراتور @gin.configurable(denylist=["x", "y"]) تعریف شده است. این ساختار اجازه میدهد اندازه دسته (batch_size) با پیشفرض ۱۲۸، رفتار برهمزدن (shuffle) و یک seed خاص برای torch.Generator بدون تغییر در کد Instantiation مربوط به DataLoader در فایل پیکربندی تنظیم شوند.
اجرای اصلی در تابع fit رخ میدهد. این تابع بسیار منعطف است و پارامتر grad_clip_norm (پیشفرض ۱.۰) را برای جلوگیری از انفجار گرادیانها با استفاده از nn.utils.clip_grad_norm_ میپذیرد. همچنین پارامتر log_every برای کنترل فرکانس خروجیهای کنسول به کار میرود. حلقه fit عملیات Forward Pass، محاسبه زیان Cross-Entropy دوتایی و اجرای پسانتشار (Backpropagation) را انجام میدهد.
در طول آموزش، بهینهساز از zero_grad(set_to_none=True) برای بازدهی بیشتر حافظه و سرعت استفاده میکند. پس از هر Epoch، تابع evaluate با استفاده از torch.no_grad() صحت (accuracy) و زیان را روی مجموعههای آموزش و اعتبارسنجی محاسبه میکند. این تابع ابتدا مقدار Sigmoid لوجیتها را گرفته و پیشبینیهایی که probs >= 0.5 هستند را شناسایی میکند.
در نهایت، یک پوشش به نام run_experiment این اجزا را متصل میکند. این بخش تخصیص دستگاه (استفاده از cuda اگر prefer_gpu درست باشد و torch.cuda.is_available() تایید شود) را مدیریت کرده و چرخه حیات کلی را کنترل میکند؛ از فراخوانی seed_everything() (که بذر random، numpy، torch.manual_seed و torch.cuda.manual_seed_all را تنظیم میکند) تا تجمیع نتایج نهایی در یک دیکشنری شامل تگ، دستگاه، متاداده و تعداد پارامترها.
پیوندهای محدودشده و بازنویسیهای زمان اجرا
یکی از قابلیتهای کلیدی این ساختار، استفاده از ارجاعات محدودشده (Scoped Bindings) است. Gin اجازه میدهد چندین پیکربندی مدل مجزا در یک اجرا وجود داشته باشد. با استفاده از نماد @ در فایلهای پیکربندی، میتوان ارجاع داد به Scopeهای خاص. برای مثال، کاربر میتواند بهطور همزمان یک @compact/MLP() و یک @wide/MLP() تعریف کند که اجازه میدهد دو نسخه ساختاری مختلف از یک کلاس در یک پیکربندگی وجود داشته باشند.
در این مثال، یک فایل BASE_CONFIG پارامترهای مشترک را برای حفظ یکپارچگی تعریف میکند:
SEED = 123N_PER_CLASS = 900EPOCHS = 50BATCH = 128make_spiral_dataset.noise = 0.20make_spiral_dataset.rotations = 1.85
سپس فایلهای تخصصی مانند compact_adamw.gin و wide_sgd.gin با عبارت include تنظیمات پایه را وارد کرده و مقادیر خاص را بازنویسی (override) میکنند. مدل فشرده ابعاد لایهها را hidden_dims = (64, 64, 64)، دراپاوت را 0.05 و use_layernorm = True قرار میدهد. مدل گسترده ابعاد را hidden_dims = (128, 128, 128, 64)، فعالساز را relu، دراپاوت را 0.02 و use_layernorm = True تنظیم میکند.
علاوه بر فایلها، این سیستم از Bindings زمان اجرا پشتیبانی میکند. این یعنی کاربر میتواند پارامتری را از طریق یک لیست پایتونی در حین اجرا تغییر دهد که اولویت آن بیشتر از فایل .gin است. در این آموزش، run_from_gin_file با runtime_bindings مانند ["fit.epochs = 45", "make_spiral_dataset.noise = 0.18", "run_experiment.tag = '...' "] فراخوانی میشود. این قابلیت اجازه تکرار سریع — تغییر مدت آموزش یا نویز دادهها — را بدون دست زدن به سیستم فایل میدهد.
تضمین تکرارپذیری مطلق
برای تضمین تکرارپذیری مطلق، مکانیسم «قفل پیکربندی» پیاده شده است. با تنظیم finalize_config=True در هنگام فراخوانی gin.parse_config_files_and_bindings هرگونه تغییر بعدی در زمان اجرا ممنوع میشود. اگر کاربر سعی کند پس از نهایی شدن پیکربندی، تابعی مثل gin.bind_parameter("fit.epochs", 999) را فراخوانی کند، سیستم خطای RuntimeError میدهد تا اطمینان حاصل شود که آزمایش خالص مانده و دستکاری نشده است.
برای حل مشکل ردیابی، خط لوله یک «پیکربندی عملیاتی» (operative config) صادر میکند. در حالی که فایل تنظیمات معمولی «قصد» کاربر (intention) را نشان میدهد، پیکربندی عملیاتی وضعیت «Resolve شده» را ثبت میکند؛ یعنی تکتک پارامترهایی که واقعاً در یک اجرای خاص استفاده شدهاند، شامل موارد ارثبری شده از فایلهای پایه یا تغییر یافته توسط بازنویسیهای زمان اجرا. این خروجی از gin.operative_config_str() تولید و به عنوان operative_config.gin در کنار نتایج result.json برای هر تگ آزمایش در RUN_DIR ذخیره میشود.
مقایسه عملکرد
در این آموزش، دو پیکربندی متمایز روی مجموعه داده مارپیچ برای اعتبارسنجی این معماری جداسازانه مقایسه شدند:
- Compact GELU-AdamW: از مجموعه پارامترهای کوچکتر با فعالسازهای GELU و بهینهساز AdamW (نرخ یادگیری ۰.۰۰۳، weight_decay=۰.۰۰۱) استفاده کرد که با نام
@compact/MLP()محدود شده بود و از طریق بازنویسی زمان اجرا، ۴۵ دوره (epoch) آموزش دید. - Wide ReLU-SGD: از یک معماری بزرگتر (۱۲۸x۱۲۸x۱۲۸x۶۴) با فعالسازهای ReLU و بهینهساز SGD (نرخ یادگیری ۰.۰۳۵، مومنتوم ۰.۹۲، weight_decay=۰.۰۰۰۵) استفاده کرد که با نام
@wide/MLP()محدود شده بود و آن نیز برای ۴۵ دوره اجرا شد.
با تجسم منحنیهای زیان و صحت اعتبارسنجی با matplotlib ثابت شد که کل رفتار آموزش را میتوان صرفاً با تعویض فایل ورودی .gin تغییر داد، در حالی که حلقه fit در پایتون دستنخورده باقی ماند. نتایج نهایی در یک جدول خلاصه شامل tag (تگ)، params (تعداد کل پارامترها)، val_loss (زیان اعتبارسنجی) و val_accuracy (صحت اعتبارسنجی) ارائه شد.
این رویکرد جداسازانه، تجربه توسعهدهنده را تغییر میدهد. بهجای عیبیابی تغییر یک متغیر در یک اسکریپت ۵۰۰ خطی، توسعهدهنده فقط یک فایل متنی تمیز و Declarative را بازرسی میکند. استفاده از gin.query_parameter نیز اجازه میدهد تنظیمات فعال (مانند fit.epochs یا make_loader.batch_size) بلافاصله پس از بارگذاری پیکربندی چاپ و تایید شوند.
برای کسانی که آزمایشهای Colab را به خط لولههای پژوهشی تبدیل میکنند، این الگو اصطکاک تنظیم دستی ابرپارامترها (hyperparameters) — یعنی تنظیمات کلی مدل که قبل از آموزش تعیین میشوند — را از بین میبرد. این روش اجازه میدهد تنظیمات تولید داده، استراتژیهای بهینهسازی و زمانبندیهای آموزش بهصورت سیستماتیک تغییر کنند بدون اینکه ریسک شکستن منطق اصلی کد وجود داشته باشد. در این چارچوب، کد مانند یک موتور ثابت باقی میماند و فایلهای پیکربندی نقش پانل کنترل را ایفا میکنند.
اکنون میتوانید با نصب بسته gin-config و استفاده از دکوراتور @gin.configurable برای کارخانههای PyTorch خود، منطق آزمایشی را از کد خارج کرده و به پیکربندی منتقل کنید.
گام بعدی شما
- کتابخانه
gin-configرا نصب کنید و توابع ساخت مدل خود را با@gin.configurableعلامتگذاری نمایید. - یک فایل
base.ginبرای پارامترهای ثابت ایجاد کنید و برای هر آزمایش یک فایل.ginمجزا بسازید که از فایل پایهincludeشود. - برای اطمینان از تکرارپذیری، حتماً از
finalize_config=Trueاستفاده کرده و خروجیoperative_configرا ذخیره کنید.
اما این نظم در تنظیمات تنها بخشی از ماجراست؛ برای مدیریت بهینه حافظه در مدلهای عظیم، به تحلیل ما درباره vLLM مراجعه کنید.




گفتگو