وقتی مدلی میسازید که روی دادههای آموزشی دقت 99% ثبت میکند اما روی دادههای آزمایشی به 60% سقوط میکند، با کلاسیکترین کابوس یادگیری عمیق یعنی بیشبرازش (Overfitting) مواجه شدهاید. مدل به جای درک الگوها و قوانین کلی، دادههای ورودی را موبهمو حفظ کرده است. منظمسازی (Regularization) مجموعهای از روشهای بنیادین است که با ایجاد محدودیتهای هوشمندانه روی پیچیدگی شبکه، آن را ناچار به تعمیمپذیری و کشف ساختار واقعی دادهها میکند.
در معماریهای مدرن—از شبکههای کانولوشنی گرفته تا ترنسفورمرها و مدلهای زبانی بزرگ نظیر LLaMA—انتخاب اشتباه تکنیک منظمسازی کل روند آموزش را مختل میکند. در این درس یاد میگیرید که چطور روشهای کلیدی منظمسازی نظیر دراپاوت (Dropout)، کاهش وزن (Weight Decay)، نرمالسازی دستهای (BatchNorm)، نرمالسازی لایهای (LayerNorm) و RMSNorm را از صفر بنویسید و متناسب با شدت بیشبرازش، ترکیب درستی از آنها را در PyTorch پیاده کنید.
دقت مدل شما روی دادههای آموزشی 99% و روی دادههای آزمایشی 60% است. مدل به جای یادگیری، دادهها را حفظ کرده است. منظمسازی (regularization) مالیاتی است که بر پیچیدگی وضع میکنید تا مدل مجبور به تعمیمپذیری شود.
اهداف یادگیری
- پیادهسازی دراپاوت (dropout) با مقیاسبندی معکوس، کاهش وزن (L2 weight decay)، نرمالسازی دستهای (batch normalization)، نرمالسازی لایهای (layer normalization) و RMSNorm از صفر
- اندازهگیری شکاف دقت آموزش-آزمون و تشخیص بیشبرازش (overfitting) با آزمایشهای منظمسازی
- توضیح اینکه چرا ترنسفورمرها به جای BatchNorm از LayerNorm استفاده میکنند و چرا مدلهای زبانی بزرگ امروزی RMSNorm را ترجیح میدهند
- بهکارگیری ترکیب درستی از تکنیکهای منظمسازی متناسب با شدت بیشبرازش
صورت مسئله
یک شبکه عصبی با تعداد پارامتر کافی میتواند هر مجموعهدادهای را حفظ کند. این فقط یک فرضیه نیست؛ ژانگ و همکاران (2017) این موضوع را با آموزش شبکههای استاندارد روی ImageNet با برچسبهای تصادفی اثبات کردند. شبکهها روی برچسبهای کاملاً تصادفی به تابع زیان (loss) نزدیک به صفر رسیدند. آنها یک میلیون جفت ورودی-خروجی تصادفی را بدون وجود کوچکترین الگویی حفظ کردند. زیان آموزش بینقص بود، اما دقت آزمون صفر شد.
این همان مشکل بیشبرازش است که با بزرگتر شدن مدلها وخیمتر هم میشود. مدل GPT-3 حدود 175 میلیارد پارامتر دارد، در حالی که مجموعهی آموزشی آن حدود 500 میلیارد توکن (token) است. با این تعداد پارامتر، مدل ظرفیت کافی دارد تا بخشهای قابلتوجهی از دادههای آموزشی را کلمهبهکلمه حفظ کند. بدون منظمسازی، مدل به جای یادگیری الگوهای تعمیمپذیر، صرفاً نمونههای آموزشی را طوطیوار تکرار میکرد.
شکاف میان عملکرد آموزشی و عملکرد آزمایشی، «شکاف بیشبرازش» نام دارد. تمام تکنیکهای این درس از زوایای مختلف به این شکاف حمله میکنند. دراپاوت شبکه را وادار میکند تا به هیچ نورون خاصی وابسته نشود. کاهش وزن مانع از آن میشود که وزن خاصی بیش از حد بزرگ شود. نرمالسازی دستهای، چشمانداز زیان (loss landscape) را هموار میکند تا بهینهساز بتواند کمینههای تختتر و تعمیمپذیرتری پیدا کند. نرمالسازی لایهای همین کار را انجام میدهد اما در شرایطی به کار میآید که نرمالسازی دستهای شکست میخورد (دستههای کوچک، توالیهای با طول متغیر). روش RMSNorm نیز با حذف محاسبهی میانگین، همین فرایند را 10% سریعتر پیش میبرد. هر یک از این تکنیکها سادهاند، اما در کنار هم مرز میان مدلی هستند که فقط حفظ میکند و مدلی که تعمیم میدهد.
مفهوم
طیف بیشبرازش
هر مدلی روی طیفی قرار میگیرد که از کمبرازش (underfitting - آنقدر ساده که الگو را ثبت نمیکند) تا بیشبرازش (آنقدر پیچیده که نویز را ثبت میکند) امتداد دارد. نقطهی بهینه درست در میانهی این دو است و منظمسازی، مدلها را از سمت بیشبرازش به سوی این نقطهی مطلوب push میکند.
دراپاوت (Dropout)
سادهترین تکنیک منظمسازی با زیباترین تفسیر مفهومی. در طول آموزش، خروجی هر نورون بهصورت تصادفی و با احتمال برابر با صفر قرار میگیرد.
با فرض ، در هر گذر رو به جلو (forward pass) نیمی از نورونها صفر میشوند. بنابراین شبکه ناچار است بازنماییهای تکراری و مکمل یاد بگیرد، چون نمیتواند پیشبینی کند کدام نورونها در دسترس خواهند بود. این کار از همانطباقی (co-adaptation) یا وابستگی یک نورون به حضور نورونهای خاص دیگر جلوگیری میکند.
تفسیر جمعی (ensemble interpretation): شبکهای با نورون و دراپاوت، عملاً زیرشبکهی ممکن ایجاد میکند (تمام ترکیبهای روشن یا خاموش بودن نورونها). آموزش مدل با دراپاوت، تقریباً معادلِ آموزش همزمان همهی این زیرشبکه روی دستههای کوچک (mini-batch) مختلف است. هنگام آزمون، از همهی نورونها استفاده میکنید (بدون دراپاوت) و خروجیها را در ضرب میکنید تا با مقدار مورد انتظار در زمان آموزش همخوانی داشته باشد. این کار معادل میانگینگیری از پیشبینیهای زیرشبکه است؛ یعنی ساخت یک مدل گروهی بزرگ از دل یک مدل واحد.
در عمل، این مقیاسبندی به جای زمان آزمون، در طول آموزش اعمال میشود (دراپاوت معکوس یا inverted dropout):
این روش تمیزتر است، چون کد آزمون اصلاً نیازی ندارد چیزی از دراپاوت بداند.
نرخهای پیشفرض: مقدار p = 0.1 برای ترنسفورمرها، p = 0.5 برای MLPها و p = 0.2-0.3 برای CNNها. دراپاوت بالاتر یعنی منظمسازی قویتر که ریسک کمبرازش را بالا میبرد.
کاهش وزن (منظمسازی L2)
مجموع توان دوم تمام وزنها را به تابع زیان اضافه کنید:
گرادیان (gradient) جملهی منظمسازی برابر با است. این یعنی در هر گام، هر وزن به اندازهی نسبتی متناسب با بزرگیاش به سمت صفر جمع میشود. وزنهای بزرگتر جریمهی سنگینتری میبینند. در نتیجه، مدل به سمت راهحلهایی هدایت میشود که در آنها هیچ وزن واحدی غالب نباشد.
دلیل کمک این روش به تعمیمپذیری: مدلهای بیشبرازششده معمولاً وزنهای بزرگی دارند که نویز موجود در دادههای آموزشی را تقویت میکنند. کاهش وزن، این وزنها را کوچک نگه میدارد؛ این کار ظرفیت مؤثر مدل را محدود کرده و آن را وادار میکند به جای رفتارهای عجیب حفظشده، به ویژگیهای پایدار و تعمیمپذیر تکیه کند.
ابرپارامتر شدت منظمسازی را کنترل میکند. مقادیر رایج:
- 0.01 برای AdamW در ترنسفورمرها
- 1e-4 برای SGD در شبکههای CNN
- 0.1 برای مدلهایی با بیشبرازش شدید
همانطور که در درس 06 دیدیم: کاهش وزن و منظمسازی L2 در بهینهساز SGD معادل یکدیگرند، اما در Adam اینطور نیست. هنگام آموزش با Adam همیشه از AdamW (کاهش وزن تفکیکشده) استفاده کنید.
نرمالسازی دستهای (Batch Normalization)
خروجی هر لایه را در طول دستهی کوچک نرمال کنید و بعد به لایهی بعدی بفرستید.
برای یک دستهی کوچک از اکتیویشنها در یک لایهی دلخواه:
پارامترهای گاما (gamma) و بتا (beta) پارامترهای یادگیریپذیری هستند که به شبکه اجازه میدهند در صورت لزوم، نرمالسازی را بیاثر کند. بدون آنها، خروجی هر لایه را مجبور میکردید میانگین صفر و واریانس یک داشته باشد، در حالی که شاید شبکه چنین چیزی نخواهد.
تفاوت آموزش و استنتاج: هنگام آموزش، و از دستهی کوچک فعلی به دست میآیند. هنگام استنتاج، از میانگینهای متحرکی استفاده میکنید که در طول آموزش جمع شدهاند (میانگین متحرک نمایی با تکانه (momentum) برابر 0.1؛ یعنی 90% مقدار قبلی + 10% مقدار جدید).
دلیل کارکرد درست BatchNorm هنوز محل بحث است. مقالهی اصلی ادعا میکرد که این روش «تغییر کوواریانس درونی» (تغییر توزیع ورودی لایهها با بهروزرسانی لایههای پیشین) را کاهش میدهد. سانتورکار و همکاران (2018) نشان دادند که این توضیح اشتباه است. دلیل واقعی این است: BatchNorm چشمانداز زیان را هموارتر میکند. گرادیانها پیشبینیپذیرتر و ثابتهای لیپشیتز کوچکتر میشوند؛ در نتیجه بهینهساز میتواند با خیال راحت گامهای بزرگتری بردارد. به همین دلیل BatchNorm اجازه میدهد از نرخ یادگیری (learning rate) بالاتری استفاده کنید و سریعتر همگرا شوید.
روش BatchNorm یک محدودیت اساسی دارد: به آمارههای دسته وابسته است. با اندازهی دستهی 1، میانگین و واریانس عملاً بیمعنی میشوند. با دستههای کوچک (کمتر از 32)، آمارهها نویز زیادی دارند و به عملکرد مدل ضربه میزنند. این مسئله در تسکهایی مثل تشخیص اشیا (که محدودیت حافظه، اندازهی دسته را کوچک میکند) و مدلسازی زبان (که طول توالیها مدام تغییر میکند) حسابی دردسرساز است.
نرمالسازی لایهای (Layer Normalization)
به جای نرمالسازی در طول دسته، در طول ویژگیها نرمالسازی را انجام دهید. برای یک نمونهی منفرد:
در اینجا بعدِ ویژگی است. هر نمونه کاملاً مستقل از بقیه نرمال میشود و هیچ وابستگی به اندازهی دسته وجود ندارد. به همین دلیل ترنسفورمرها به جای BatchNorm از LayerNorm استفاده میکنند. در ترنسفورمرها توالیها طولهای متفاوتی دارند، اندازهی دستهها معمولاً کوچک است (یا حین تولید، برابر با 1 است)، و نحوهی محاسبات در زمان آموزش و استنتاج عیناً یکسان باقی میماند.
روش LayerNorm در ترنسفورمرها یا بعد از هر بلوک خوداتنشن (self-attention) و پیشخور اعمال میشود (Post-LN)، یا پیش از آنها قرار میگیرد (Pre-LN که برای پایداری آموزش مناسبتر است).
روش RMSNorm
همان LayerNorm اما بدون کسر کردن میانگین؛ ارائهشده توسط ژانگ و سنریچ (2019).
همین و بس. بدون محاسبهی میانگین و بدون پارامتر بتا. یافتهی آنها این بود: مرکززدایی (تفریق میانگین) در LayerNorm نقش بسیار ناچیزی در عملکرد نهایی مدل دارد، ولی هزینهی محاسباتی ایجاد میکند. با حذف آن، همان دقت را با حدود 10% سربار محاسباتی کمتر به دست میآورید.
مدلهای LLaMA، LLaMA 2، LLaMA 3، Mistral و بیشتر مدلهای زبانی بزرگ امروزی به جای LayerNorm از RMSNorm استفاده میکنند. در مقیاس میلیاردها پارامتر و تریلیونها توکن، این 10% صرفهجویی تفاوت چشمگیری رقم میزند.
مقایسهی روشهای نرمالسازی
دادهافزایی به عنوان منظمسازی
این کار اصلاح ساختار مدل نیست، بلکه دستکاری دادههاست. ورودیهای آموزشی را طوری دگرگون کنید که برچسب آنها دستنخورده بماند:
- تصاویر: برش تصادفی، چرخش، قرینهسازی، تغییر رنگ، حذف بخشی از تصویر (cutout)
- متن: جایگزینی کلمات هممعنی، ترجمهی رفتوبرگشتی، حذف تصادفی کلمات
- صوت: کشش زمانی، تغییر گام صدا، افزودن نویز
اثر این کار دقیقاً مانند منظمسازی است: اندازهی مؤثر مجموعهی دادهها را بالا میبرد و حفظ کردن نمونههای خاص را برای مدل سختتر میکند. مدلی که هر تصویر را فقط یک بار و به همان شکل اولیهاش ببیند، میتواند آن را حفظ کند؛ اما مدلی که 50 نسخهی دگرگونشده از هر تصویر را میبیند، ناچار است ساختار پایدار و تغییرناپذیر آن را یاد بگیرد.
توقف زودهنگام (Early Stopping)
سادهترین روش منظمسازی: به محض اینکه زیان اعتبارسنجی شروع به افزایش کرد، آموزش را متوقف کنید. در آن نقطه مدل هنوز بیشبرازش نشده است. در عمل، زیان اعتبارسنجی را در هر دوره (epoch) دنبال میکنید، بهترین نسخه از مدل را ذخیره نگه میدارید و آموزش را برای یک بازهی معین تحت عنوان «صبر» یا patience (معمولاً بین 5 تا 20 دوره) ادامه میدهید. اگر زیان اعتبارسنجی در این بازه بهتر نشد، کار را متوقف کرده و همان بهترین مدل ذخیرهشده را لود میکنید.
چه زمانی از کدام تکنیک استفاده کنیم؟
پیادهسازی از صفر
گام 1: دراپاوت (حالت آموزش و ارزیابی)
import random
import math
class Dropout:
def __init__(self, p=0.5):
self.p = p
self.training = True
self.mask = None
def forward(self, x):
if not self.training:
return list(x)
self.mask = []
output = []
for val in x:
if random.random() < self.p:
self.mask.append(0)
output.append(0.0)
else:
self.mask.append(1)
output.append(val / (1 - self.p))
return output
def backward(self, grad_output):
grads = []
for g, m in zip(grad_output, self.mask):
if m == 0:
grads.append(0.0)
else:
grads.append(g / (1 - self.p))
return grads
گام 2: کاهش وزن L2
def l2_regularization(weights, lambda_reg):
penalty = 0.0
for w in weights:
penalty += w * w
return lambda_reg * 0.5 * penalty
def l2_gradient(weights, lambda_reg):
return [lambda_reg * w for w in weights]
گام 3: نرمالسازی دستهای
class BatchNorm:
def __init__(self, num_features, momentum=0.1, eps=1e-5):
self.gamma = [1.0] * num_features
self.beta = [0.0] * num_features
self.eps = eps
self.momentum = momentum
self.running_mean = [0.0] * num_features
self.running_var = [1.0] * num_features
self.training = True
self.num_features = num_features
def forward(self, batch):
batch_size = len(batch)
if self.training:
mean = [0.0] * self.num_features
for sample in batch:
for j in range(self.num_features):
mean[j] += sample[j]
mean = [m / batch_size for m in mean]
var = [0.0] * self.num_features
for sample in batch:
for j in range(self.num_features):
var[j] += (sample[j] - mean[j]) ** 2
var = [v / batch_size for v in var]
for j in range(self.num_features):
self.running_mean[j] = (1 - self.momentum) * self.running_mean[j] + self.momentum * mean[j]
self.running_var[j] = (1 - self.momentum) * self.running_var[j] + self.momentum * var[j]
else:
mean = list(self.running_mean)
var = list(self.running_var)
self.x_hat = []
output = []
for sample in batch:
normalized = []
out_sample = []
for j in range(self.num_features):
x_h = (sample[j] - mean[j]) / math.sqrt(var[j] + self.eps)
normalized.append(x_h)
out_sample.append(self.gamma[j] * x_h + self.beta[j])
self.x_hat.append(normalized)
output.append(out_sample)
return output
گام 4: نرمالسازی لایهای
class LayerNorm:
def __init__(self, num_features, eps=1e-5):
self.gamma = [1.0] * num_features
self.beta = [0.0] * num_features
self.eps = eps
self.num_features = num_features
def forward(self, x):
mean = sum(x) / len(x)
var = sum((xi - mean) ** 2 for xi in x) / len(x)
self.x_hat = []
output = []
for j in range(self.num_features):
x_h = (x[j] - mean) / math.sqrt(var + self.eps)
self.x_hat.append(x_h)
output.append(self.gamma[j] * x_h + self.beta[j])
return output
گام 5: روش RMSNorm
class RMSNorm:
def __init__(self, num_features, eps=1e-6):
self.gamma = [1.0] * num_features
self.eps = eps
self.num_features = num_features
def forward(self, x):
rms = math.sqrt(sum(xi * xi for xi in x) / len(x) + self.eps)
output = []
for j in range(self.num_features):
output.append(self.gamma[j] * x[j] / rms)
return output
گام 6: آموزش با و بدون منظمسازی
def sigmoid(x):
x = max(-500, min(500, x))
return 1.0 / (1.0 + math.exp(-x))
def make_circle_data(n=200, seed=42):
random.seed(seed)
data = []
for _ in range(n):
x = random.uniform(-2, 2)
y = random.uniform(-2, 2)
label = 1.0 if x * x + y * y < 1.5 else 0.0
data.append(([x, y], label))
return data
class RegularizedNetwork:
def __init__(self, hidden_size=16, lr=0.05, dropout_p=0.0, weight_decay=0.0):
random.seed(0)
self.hidden_size = hidden_size
self.lr = lr
self.dropout_p = dropout_p
self.weight_decay = weight_decay
self.dropout = Dropout(p=dropout_p) if dropout_p > 0 else None
self.w1 = [[random.gauss(0, 0.5) for _ in range(2)] for _ in range(hidden_size)]
self.b1 = [0.0] * hidden_size
self.w2 = [random.gauss(0, 0.5) for _ in range(hidden_size)]
self.b2 = 0.0
def forward(self, x, training=True):
self.x = x
self.z1 = []
self.h = []
for i in range(self.hidden_size):
z = self.w1[i][0] * x[0] + self.w1[i][1] * x[1] + self.b1[i]
self.z1.append(z)
self.h.append(max(0.0, z))
if self.dropout and training:
self.dropout.training = True
self.h = self.dropout.forward(self.h)
elif self.dropout:
self.dropout.training = False
self.h = self.dropout.forward(self.h)
self.z2 = sum(self.w2[i] * self.h[i] for i in range(self.hidden_size)) + self.b2
self.out = sigmoid(self.z2)
return self.out
def backward(self, target):
eps = 1e-15
p = max(eps, min(1 - eps, self.out))
d_loss = -(target / p) + (1 - target) / (1 - p)
d_sigmoid = self.out * (1 - self.out)
d_out = d_loss * d_sigmoid
for i in range(self.hidden_size):
d_relu = 1.0 if self.z1[i] > 0 else 0.0
d_h = d_out * self.w2[i] * d_relu
self.w2[i] -= self.lr * (d_out * self.h[i] + self.weight_decay * self.w2[i])
for j in range(2):
self.w1[i][j] -= self.lr * (d_h * self.x[j] + self.weight_decay * self.w1[i][j])
self.b1[i] -= self.lr * d_h
self.b2 -= self.lr * d_out
def evaluate(self, data):
correct = 0
total_loss = 0.0
for x, y in data:
pred = self.forward(x, training=False)
eps = 1e-15
p = max(eps, min(1 - eps, pred))
total_loss += -(y * math.log(p) + (1 - y) * math.log(1 - p))
if (pred >= 0.5) == (y >= 0.5):
correct += 1
return total_loss / len(data), correct / len(data) * 100
def train_model(self, train_data, test_data, epochs=300):
history = []
for epoch in range(epochs):
total_loss = 0.0
correct = 0
for x, y in train_data:
pred = self.forward(x, training=True)
self.backward(y)
eps = 1e-15
p = max(eps, min(1 - eps, pred))
total_loss += -(y * math.log(p) + (1 - y) * math.log(1 - p))
if (pred >= 0.5) == (y >= 0.5):
correct += 1
train_loss = total_loss / len(train_data)
train_acc = correct / len(train_data) * 100
test_loss, test_acc = self.evaluate(test_data)
history.append((train_loss, train_acc, test_loss, test_acc))
if epoch % 75 == 0 or epoch == epochs - 1:
gap = train_acc - test_acc
print(f" Epoch {epoch:3d}: train_acc={train_acc:.1f}%, test_acc={test_acc:.1f}%, gap={gap:.1f}%")
return history
استفاده در عمل
فریمورک PyTorch تمام این روشهای نرمالسازی و منظمسازی را در قالب ماژولهای آماده ارائه میدهد:
import torch
import torch.nn as nn
model = nn.Sequential(
nn.Linear(784, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(256, 128),
nn.BatchNorm1d(128),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(128, 10),
)
model.train()
out_train = model(torch.randn(32, 784))
model.eval()
out_test = model(torch.randn(1, 784))
سوئیچ میان model.train() و model.eval() کاملاً حیاتی است. این کار دراپاوت را خاموش و روشن میکند و به BatchNorm میگوید که از آمارههای دسته استفاده کند یا آمارههای انباشتهشده. فراموش کردن model.eval() قبل از استنتاج، یکی از رایجترین باگها در یادگیری عمیق است؛ چون دراپاوت فعال میماند و BatchNorm به جای آمارههای کلی، از آمارههای دستهی کوچک استفاده میکند، دقت تست شما مدام و بیدلیل نوسان خواهد کرد.
در ترنسفورمرها این الگو فرق دارد:
class TransformerBlock(nn.Module):
def __init__(self, d_model=512, nhead=8, dropout=0.1):
super().__init__()
self.attention = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
self.norm1 = nn.LayerNorm(d_model)
self.ff = nn.Sequential(
nn.Linear(d_model, d_model * 4),
nn.GELU(),
nn.Linear(d_model * 4, d_model),
nn.Dropout(dropout),
)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
attended, _ = self.attention(x, x, x)
x = self.norm1(x + self.dropout(attended))
x = self.norm2(x + self.ff(x))
return x
از LayerNorm استفاده میشود نه BatchNorm؛ نرخ دراپاوت هم p=0.1 است نه p=0.5. اینها مقادیر پیشفرض ترنسفورمرها هستند.
محصول خروجی
خروجی این درس:
- فایل
outputs/prompt-regularization-advisor.md-- پرامپتی که بیشبرازش را عیبیابی میکند و راهبرد منظمسازی مناسب را پیشنهاد میدهد
تمرینها
-
دراپاوت مکانی (spatial dropout) را برای دادههای دوبعدی پیادهسازی کنید: به جای صفر کردن تکتک نورونها، کل کانالهای ویژگی را صفر کنید. این کار را با دستهبندی ویژگیهای متوالی به عنوان کانال و حذف کامل هر دسته شبیهسازی کنید. سپس شکاف دقت آموزش-آزمون را با دراپاوت معمولی روی مجموعهدادهی circle با hidden_size=32 مقایسه کنید.
-
هموارسازی برچسبها (label smoothing) را از درس 05 با دراپاوت این درس ترکیب کنید. مدل را در چهار حالت آموزش دهید: هیچکدام، فقط دراپاوت، فقط هموارسازی برچسبها، و هر دو با هم. شکاف دقت نهایی آموزش-آزمون را برای هر حالت اندازه بگیرید. کدام حالت کمترین شکاف را ایجاد میکند؟
-
یک لایهی BatchNorm بین لایهی پنهان و تابع فعالساز در شبکهی مجموعهدادهی circle اضافه کنید. مدل را با و بدون BatchNorm در سه نرخ یادگیری 0.01، 0.05 و 0.1 آموزش دهید. BatchNorm باید بتواند آموزش را در نرخهای یادگیری بالاتری که شبکهی ساده در آنها واگرا میشود، کاملاً پایدار نگه دارد.
-
توقف زودهنگام را پیادهسازی کنید: در هر دوره زیان آزمون را ثبت کنید، بهترین وزنها را نگه دارید و اگر زیان آزمون به مدت 20 دوره بهبود نیافت، فرایند را متوقف کنید. شبکهی منظمشده را برای 1000 دوره اجرا کنید. گزارش دهید کدام دوره بهترین دقت آزمون را داشته و از اتلاف چند دوره محاسبات جلوگیری کردهاید.
-
روشهای LayerNorm و RMSNorm را روی یک شبکهی 4 لایه (نه فقط 2 لایه) مقایسه کنید. هر دو شبکه را با وزنهای کاملاً یکسان مقداردهی اولیه کنید. آنها را برای 200 دوره آموزش دهید و در نهایت دقت، سرعت آموزش (زمان بر حسب هر دوره) و اندازهی گرادیانها در لایهی اول را با هم بسنجید. بررسی کنید که آیا RMSNorm با حفظ همان دقت، سرعت بالاتری ارائه میدهد یا خیر.
اصطلاحات کلیدی
| اصطلاح | در گفتار عامیانه | معنای دقیق فنی |
|---|---|---|
| بیشبرازش (Overfitting) | «مدل دادهها را حفظ کرده» | زمانی که عملکرد مدل روی دادههای آموزش به شکل محسوسی بهتر از آزمون باشد؛ نشانهی اینکه مدل به جای سیگنال واقعی، نویز را یاد گرفته است |
| منظمسازی (Regularization) | «جلوگیری از بیشبرازش» | هر روشی که با محدود کردن پیچیدگی مدل، تعمیمپذیری را بالا میبرد: دراپاوت، کاهش وزن، نرمالسازی، دادهافزایی |
| دراپاوت (Dropout) | «حذف تصادفی نورونها» | صفر کردن تصادفی نورونها هنگام آموزش با احتمال که مدل را وادار به یادگیری الگوهای افزونه و مستقل میکند؛ معادل آموزش یک مدل گروهی |
| کاهش وزن (Weight decay) | «جریمهی L2» | کوچک کردن وزنها به سمت صفر با کسر کردن در هر گام؛ جریمه کردن پیچیدگی از طریق کنترل بزرگی وزنها |
| نرمالسازی دستهای (Batch normalization) | «نرمالسازی در سطح دسته» | نرمال کردن خروجی لایهها در بُعد دسته با استفاده از آمارههای دسته در زمان آموزش و میانگینهای متحرک در زمان استنتاج |
| نرمالسازی لایهای (Layer normalization) | «نرمالسازی به ازای هر نمونه» | نرمال کردن ویژگیها درون هر نمونه؛ مستقل از اندازهی دسته و بسیار کاربردی در ترنسفورمرها به دلیل متغیر بودن طول توالیها |
| روش RMSNorm | «LayerNorm بدون میانگین» | نرمالسازی جذر میانگین مربعات؛ تفریق میانگین را از LayerNorm حذف میکند تا 10% افزایش سرعت بدون افت دقت فراهم شود |
| توقف زودهنگام (Early stopping) | «توقف قبل از بیشبرازش» | قطع فرایند آموزش وقتی زیان اعتبارسنجی دیگر بهتر نمیشود؛ سادهترین ابزار منظمسازی که معمولاً در کنار روشهای دیگر میآید |
| دادهافزایی (Data augmentation) | «تولید دادهی بیشتر از دادههای موجود» | تغییر شکل دادن ورودیهای آموزش (چرخش، برش، نویز) برای افزایش اندازهی مؤثر دادهها و مجبور کردن مدل به یادگیری الگوهای تغییرناپذیر |
| شکاف تعمیمپذیری (Generalization gap) | «شکاف آموزش و تست» | تفاوت میان عملکرد مدل روی دادههای آموزش و آزمون؛ تمام هدف منظمسازی این است که این شکاف به حداقل برسد |
برای مطالعهی بیشتر
- مقالهی Srivastava et al. با عنوان "Dropout: A Simple Way to Prevent Neural Networks from Overfitting" (2014) -- مقالهی اصلی دراپاوت با ارائهی تفسیر مدل گروهی و آزمایشهای گسترده
- مقالهی Ioffe & Szegedy با عنوان "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift" (2015) -- معرفی لایهی BatchNorm و فرایند آموزش آن؛ یکی از پرارجاعترین مقالات تاریخ یادگیری عمیق
- مقالهی Zhang & Sennrich با عنوان "Root Mean Square Layer Normalization" (2019) -- اثبات این موضوع که RMSNorm به دقت LayerNorm میرسد اما با بار محاسباتی کمتر؛ بهکاررفته در LLaMA و Mistral
- مقالهی Zhang et al. با عنوان "Understanding Deep Learning Requires Rethinking Generalization" (2017) -- مقالهی تاریخساز و تکاندهندهای که نشان داد شبکههای عصبی میتوانند برچسبهای کاملاً تصادفی را حفظ کنند و دیدگاههای سنتی دربارهی تعمیمپذیری را به چالش کشید
