چیتشیت
بهینهسازها و برنامههای زمانبندی نرخ یادگیری
Optimizers and Learning Rate Schedules
SGD، Adam یا AdamW را انتخاب کنید، به نرخ یادگیری شکل دهید، سپس گرادیانها را با ایمنی clip، مقیاسبندی و انباشت کنید.
فهرست مطالب
مقایسهی بهینهسازها در کنار هم
مقایسه| بهینهساز | پیشفرضها | رفتار |
|---|---|---|
SGD | lr=0.01 | یک نرخ برای تمام وزنها. درون دره نوسان میکند |
SGDMomentum | lr=0.01, beta=0.9 | حدود 10 گرادیان آخر را نگه میدارد. 3,000 تا 5,000 گام، نه 10,000 |
| RMSProp | epsilon=1e-8 | هر گرادیان را بر RMS متحرک تقسیم میکند |
Adam | lr=0.001, beta1=0.9, beta2=0.999 | تکانه بهعلاوهی RMSProp بهعلاوهی تصحیح سوگیری |
AdamW | weight_decay=0.01 | مشابه Adam. کاهش وزن با فاکتور تطبیقی مقیاسبندی نمیشود |
قواعد بهروزرسانی
مفهومها- SGD
- $w = w - \text{lr} \cdot g$
- تکانه
- $m_t = \beta m_{t-1} + g$، سپس $w = w - \text{lr} \cdot m_t$
- RMSProp
- $s_t = \beta s_{t-1} + (1 - \beta) g^2$، گام $\text{lr} \cdot g / (\sqrt{s_t} + \epsilon)$
- گشتاورهای Adam
- $m_t = \beta_1 m_{t-1} + (1 - \beta_1) g$ و $v_t = \beta_2 v_{t-1} + (1 - \beta_2) g^2$
- تصحیح سوگیری
- $\hat{m} = m_t / (1 - \beta_1^t)$. برای حدود 10 گام اول اهمیت دارد.
- گام Adam
- $w = w - \text{lr} \cdot \hat{m} / (\sqrt{\hat{v}} + \epsilon)$
- گام AdamW
- گام Adam، سپس $- \text{lr} \cdot \lambda w$ روی وزنها.
زمان استفاده از هر بهینهساز
نکتهها- برای ترنسفورمر یا LLM از AdamW استفاده کنید: lr=1e-4 و wd=0.01-0.1.
- برای CNN یا ResNet از SGD + Momentum استفاده کنید: lr=0.1 و momentum=0.9 و weight_decay=1e-4.
- برای GAN از Adam استفاده کنید: lr=2e-4 و beta1=0.5.
- برای فاینتیونینگ از AdamW استفاده کنید: lr=2e-5 و wd=0.01.
- وقتی هنوز نمیدانید، از AdamW با lr=3e-4 و wd=0.01 استفاده کنید.
- اگر پیشفرضها جواب نداد، اول lr را تغییر دهید. بعد $\beta_2$. تقریباً هرگز $\beta_1$ یا $\epsilon$ را تغییر ندهید.
مقایسهی برنامههای زمانبندی در کنار هم
دستورها| دستور | کارکرد |
|---|---|
constant_schedule(step, lr=0.01) | نرخ یکسان در هر گام. مناسب مدلهای کوچک و اشکالزدایی |
step_decay_schedule(step, lr=0.1, step_size=100, gamma=0.1) | کاهش lr با ضریب گاما در گامهای ثابت. افتهای ناگهانی میتواند تابع زیان (loss) را جهش دهد |
cosine_schedule(step, lr=0.01, total_steps=1000, lr_min=1e-5) | کسینوسی از lr تا lr_min. پیشفرض امن برای بودجهی متوسط |
warmup_cosine_schedule(step, lr=0.01, total_steps=1000, warmup_steps=100, lr_min=1e-5) | افزایش خطی، سپس کسینوسی. مناسب بودجهی زیاد |
one_cycle_schedule(step, lr=0.01, total_steps=1000) | صعود از lr/25، سپس نزول تا lr/10000. مناسب بودجهی کم |
CosineWithWarmup(warmup_steps, total_steps, lr_max, lr_min) | گرمکردن بهعلاوهی کسینوسی با مرزهای کنترلشده. ثابت روی lr_min |
InverseSqrtWarmup(warmup_steps, lr_max) | پیشگرمایش خطی، سپس lr_max * sqrt(warmup_steps / step) |
LinearWarmupConstant(warmup_steps, lr_max) | پیشگرمایش خطی، سپس lr_max ثابت. یک خط مبنا برای ابطالسنجی |
انتخاب برنامهی زمانبندی
گامبهگام- تعداد کل گامها نامشخصاز LR ثابت با افت دستی استفاده کنید.
- بودجهی زیاد، روزها یا هفتههااز گرمکردن بهعلاوهی افت کسینوسی استفاده کنید. گرمکردن = 1 تا 5٪ گامها.
- بودجهی متوسطاز تبرید کسینوسی استفاده کنید. lr_min = lr_max / 10 تنظیم شود.
- بودجهی کم، چند ساعتاز 1cycle استفاده کنید. lr_max را با آزمون دامنهی LR پیدا کنید.
- فاینتیونینگ مدل Llama یا GPTاز
get_cosine_schedule_with_warmupبا گرمکردن = 3 تا 5٪ کل گامها استفاده کنید.
کسینوسی با پیشگرمایش خطی
کدpython
def lr(self, step: int) -> float:
if step < 0:
raise ValueError(f"step must be non-negative, got {step}")
if self.warmup_steps > 0 and step <= self.warmup_steps:
return self.lr_max * (step / self.warmup_steps) # 0 در گام 0
if step >= self.total_steps:
return self.lr_min # کف، بدون برونیابی
decay_span = max(1, self.total_steps - self.warmup_steps)
progress = (step - self.warmup_steps) / decay_span
cosine = 0.5 * (1.0 + math.cos(math.pi * progress))
return self.lr_min + (self.lr_max - self.lr_min) * cosine
Clip و مقیاسبندی در یک گام
کدpython
self.optimizer.zero_grad(set_to_none=True)
with torch.amp.autocast(device_type=self.device_type, dtype=self.amp_dtype):
predictions = self.model(inputs)
loss = self._loss_fn(predictions, targets)
if not torch.isfinite(loss).all().item(): # رد شدن، update() را فراخوانی نکنید
return self._record_skip(...)
self.scaler.scale(loss).backward()
self.scaler.unscale_(self.optimizer) # clip پس از unscale
if has_non_finite_grad(self.model.parameters()):
self.scaler.update() # رد شدن، مقیاسبخش نصف میشود
pre_clip, post_clip = clip_global_l2_norm(list(self.model.parameters()), self.max_norm)
self.scaler.step(self.optimizer)
self.scaler.update() # فاکتور را دوبرابر یا نصف میکند
انباشت گرادیانها
کدpython
accumulation_steps = effective_batch // (micro_batch * world_size)
for x, y in chunks(x_full, y_full, n): # n میکروبچ
scaled = criterion(model(x), y) / n # تقسیم قبل از backward
scaled.backward() # گرادیانها در param.grad جمع میشوند
opt.step() # یکبار بهازای هر دستهی مؤثر
with no_sync_context(model): # همهی میکروبچها بهجز آخری
loss.backward()
اعداد مهم
پیشفرضها| تنظیم | پیشفرض | نکته |
|---|---|---|
Adam/AdamW lr | 1e-4 to 3e-4 | فاینتیونینگ از 1e-5 تا 5e-5 استفاده میکند |
Warmup | 1-5% | درس 44 میگوید 1 تا 3 درصد کل گامها |
lr_min | 10 percent | از lr_max. مقدار صفر آموزش را ناتمام میگذارد |
Step decay | $\gamma = 0.1$ | مقدار step_size = 30 نرخ یادگیری را هر 30 دوره 10 برابر کاهش میدهد |
max_norm | 1.0 | پیشفرض آموزش مدل زبانی |
Scaling factor | 2^17 | یک اجرای سالم نزدیک 2^17 یا 2^18 اشباع میشود |
Skip rate alert | 5 percent | روی یک پنجرهی متحرک 1,000 گامی |
Accumulation check | max_abs_diff < 1e-4 | دستهی کامل در برابر میکروبچها |
عیبیابی
عیبیابیانفجار تابع زیان (loss)، وزنهای NaN
- علت
- lr بیش از حد بالا (lr > 0.01)
- راهحل
- کاهش lr. افزودن گرمکردن
تابع زیان (loss) بهکندی کاهش مییابد
- علت
- lr بیش از حد پایین (lr < 0.00001)
- راهحل
- افزایش lr. ابتدا بررسی بازهای
lr_max
تابع زیان (loss) نزدیک کمینه نوسان میکند
- علت
- lr ثابت در اواخر کار بیش از حد بزرگ است
- راهحل
- کاهش با کسینوسی یا پلهای
تابع زیان (loss) در یک دسته عمودی بالا میرود
- علت
- جهش نُرم گرادیان
- راهحل
- برش نُرم L2 سراسری روی 1.0
فاکتور مقیاسبندی هرگز تغییر نکرد
- علت
- عدم وجود
update()در مسیر پرش - راهحل
- فراخوانی
scaler.update()در هر گام
گام بهینهساز 16 برابر بیش از حد بزرگ است
- علت
- تابع زیان (loss) روی میکروبچها جمع شده است
- راهحل
- تقسیم هر تابع زیان بر
accum_steps
باگهای ظریف اسکژولر
- علت
- ابتدا
scheduler.step() - راهحل
- ابتدا
optimizer.step()فراخوانی شود
گرمکردن طولانی مقیاس نمیپذیرد
- علت
- گرمکردن بهصورت یک عدد مطلق تنظیم شده
- راهحل
- تنظیم گرمکردن بهصورت کسری از کل گامها
پرسشها
پرسش و پاسخ- چرا گرمکردن برای Adam؟
- برآوردهای گشتاور آن از صفر شروع میشوند. گامهای اولیه از آمارههای نامناسب استفاده میکنند.
- چرا AdamW بهجای Adam بهعلاوهی L2؟
- هر پارامتر به همان نسبت منقبض میشود.
- چرا SGD برای CNNها؟
- الگوریتم SGD مینیممهای مسطحتری پیدا میکند که اغلب تعمیمپذیری بهتری دارند.
- روش 1cycle به چه چیزی نیاز دارد؟
- تعداد کل گامها از پیش.
- آیا انباشت گرادیان در زمان صرفهجویی میکند؟
- خیر. دوبرابر کردن
accum_stepsزمان واقعی را بهازای هر گام بهینهساز دوبرابر میکند. - آیا autocast پردازنده به مقیاسبندی تابع زیان نیاز دارد؟
- خیر. قابلیت autocast پردازنده بهطور پیشفرض از BF16 استفاده میکند.