GELU، Swish و Mish؛ ادامه مقایسه توابع فعالسازی
مقاله GELU تابع SiLU را نیز معرفی کرد که برابر zσ(z) است، هرچند در آزمایشهای همان مقاله GELU بهتر عمل کرد. در سال ۲۰۱۷، Prajit Ramachandran و همکاران با جستوجوی خودکار برای تابعهای فعالسازی مناسب دوباره همین تابع را یافتند و آن را Swish نامیدند. Swish در آزمایشهای آنها حتی از GELU نیز بهتر بود.
نسخه تعمیمیافته Swish یک ابرپارامتر β دارد که ورودی sigmoid را مقیاس میکند:
Swishβ(z) = z σ(βz)
تقریب GELU تقریباً معادل Swish با β=1.702 است. β را میتوان مانند هر ابرپارامتر دیگری تنظیم کرد یا حتی آن را قابل آموزش کرد تا gradient descent مقدارش را بیاموزد؛ این کار مانند PReLU ظرفیت مدل را افزایش میدهد اما احتمال overfitting را هم بیشتر میکند.
Mish در سال ۲۰۱۹ معرفی شد و تعریف آن چنین است:
mish(z) = z tanh(softplus(z)), softplus(z)=log(1+exp(z))
Mish نیز مانند GELU و Swish نسخهای هموار، nonconvex و nonmonotonic از ReLU است. در آزمایشهای ارائهشده، Mish در بسیاری از موارد اندکی بهتر از Swish و GELU عمل کرد. برای ورودیهای منفی منحنی آن تقریباً روی Swish و برای ورودیهای مثبت تقریباً روی GELU قرار میگیرد.
Keras از GELU و Swish مستقیماً پشتیبانی میکند:
tf.keras.layers.Dense(128, activation="gelu")
tf.keras.layers.Dense(128, activation="swish")
در نسخه مورد بحث کتاب، Mish و Swish تعمیمیافته بهصورت آماده وجود ندارند، اما فصل ۱۲ روش ساخت تابع فعالسازی و لایه سفارشی را آموزش میدهد.
Batch Normalization
He initialization همراه با ReLU یا مشتقهای آن خطر گرادیان محوشونده و انفجاری را در آغاز آموزش بسیار کم میکند، اما تضمین نمیکند که این مشکلات در ادامه آموزش بازنگردند. Sergey Ioffe و Christian Szegedy در سال ۲۰۱۵ روش Batch Normalization (BN) را برای مقابله با این مسئله پیشنهاد کردند.
BN معمولاً درست پیش یا پس از تابع فعالسازی هر لایه پنهان قرار میگیرد. این عملیات هر ورودی را zero-center و نرمال میکند و سپس با دو بردار پارامتر جدید، آن را scale و shift میدهد. به بیان دیگر، مدل میتواند مقیاس و میانگین بهینه هر ورودی لایه را یاد بگیرد. اگر BN در ابتدای شبکه قرار گیرد، در بسیاری از موارد حتی استانداردسازی جداگانه داده با StandardScaler یا Normalization ضروری نیست؛ هرچند BN فقط آمار mini-batch جاری را میبیند.
الگوریتم Batch Normalization
1. μB = (1/mB) Σ x(i)
2. σB2 = (1/mB) Σ (x(i) − μB)²
3. x̂(i) = (x(i) − μB) / √(σB² + ε)
4. z(i) = γ ⊗ x̂(i) + β
- μB بردار میانگین ورودیها در mini-batch است؛ برای هر ورودی یک میانگین.
- mB تعداد نمونههای mini-batch است.
- σB بردار انحراف معیار ورودیها در mini-batch است.
- x̂(i) بردار ورودی zero-centered و normalized برای نمونه i است.
- ε عددی بسیار کوچک، معمولاً حدود 10⁻⁵، برای جلوگیری از تقسیم بر صفر و مهار گرادیانهای بیش از حد بزرگ است.
- γ بردار پارامتر scale خروجی لایه و β بردار offset خروجی است.
- ⊗ ضرب عضو به عضو را نشان میدهد.
- z(i) خروجی نهایی BN پس از نرمالسازی، scale و shift است.
در زمان آموزش، BN از آمار batch جاری استفاده میکند. اما در زمان inference ممکن است فقط یک نمونه داشته باشیم یا batch خیلی کوچک و نامعتبر باشد. بنابراین بیشتر پیادهسازیها، از جمله Keras، در طول آموزش میانگین متحرک نمایی میانگین و انحراف معیار ورودیها را نگه میدارند. در نتیجه هر BN چهار بردار پارامتر دارد: γ و β با backpropagation یاد گرفته میشوند؛ μ و σ با moving average برآورد میشوند و در inference جای آمار batch را میگیرند.
در آزمایشهای اولیه BN، مشکل گرادیان محوشونده بهشدت کاهش یافت، حساسیت شبکه به مقداردهی اولیه وزنها کمتر شد و امکان استفاده از learning rateهای بزرگتر فراهم شد. روی مدلهای عمیق، این موضوع میتواند تعداد گامهای لازم برای رسیدن به همان دقت را بسیار کاهش دهد. BN همچنین اغلب اثر regularization ملایمی دارد و نیاز به برخی روشهای دیگر را کمتر میکند.
هزینه BN، افزایش پیچیدگی و محاسبات در هر لایه است. بااینحال پس از آموزش غالباً میتوان BN را با لایه قبلی فیوژ کرد. اگر لایه قبلی XW+b را تولید کند، BN بهطور تقریبی عبارت γ⊗(XW+b−μ)/σ+β را میسازد. با تعریف وزن و bias جدید:
W′ = γ ⊗ W / σ
b′ = γ ⊗ (b − μ) / σ + β
خروجی مستقیماً به XW′+b′ تبدیل میشود و لایه BN را میتوان در inference حذف کرد. مبدل TFLite این نوع بهینهسازی را خودکار انجام میدهد.
پیادهسازی BN در Keras
کافی است پیش یا پس از activation لایههای پنهان یک BatchNormalization اضافه شود. میتوان در ابتدای مدل نیز BN گذاشت، هرچند در آن نقطه یک Normalization معمولی اغلب همانقدر خوب است.
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[28, 28]),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Dense(300, activation="relu",
kernel_initializer="he_normal"),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Dense(100, activation="relu",
kernel_initializer="he_normal"),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Dense(10, activation="softmax")
])
در یک شبکه کوچک دو لایه اثر BN شاید چشمگیر نباشد، ولی با عمیقتر شدن شبکه تفاوت آن میتواند بسیار زیاد شود. هر BN برای هر ورودی چهار پارامتر اضافه میکند: γ، β، μ و σ. دو مورد اول trainable هستند؛ moving mean و moving variance توسط backpropagation تغییر نمیکنند و Keras آنها را non-trainable مینامد.
[(var.name, var.trainable) for var in model.layers[1].variables]
# [('batch_normalization/gamma:0', True),
# ('batch_normalization/beta:0', True),
# ('batch_normalization/moving_mean:0', False),
# ('batch_normalization/moving_variance:0', False)]
BN پیش از تابع فعالسازی یا پس از آن؟
مقاله اصلی BN قرار دادن آن پیش از activation را پیشنهاد میکند، اما نتیجه میتواند به مسئله بستگی داشته باشد و بهتر است هر دو حالت آزموده شوند. اگر BN پیش از activation قرار گیرد، activation باید از Dense حذف و بهصورت لایه جدا بعد از BN اضافه شود. چون BN خودش offset دارد، bias لایه Dense قبلی نیز معمولاً لازم نیست و میتوان use_bias=False گذاشت:
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[28, 28]),
tf.keras.layers.Dense(300, kernel_initializer="he_normal", use_bias=False),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation("relu"),
tf.keras.layers.Dense(100, kernel_initializer="he_normal", use_bias=False),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation("relu"),
tf.keras.layers.Dense(10, activation="softmax")
])
ابرپارامترهای BatchNormalization
مقادیر پیشفرض معمولاً مناسباند، اما گاهی momentum نیاز به تنظیم دارد. با دریافت مقدار آماری جدید v از batch جاری، میانگین جاری تقریباً چنین بهروزرسانی میشود:
v̄ ← v̄ × momentum + v × (1 − momentum)
momentum معمولاً نزدیک ۱ است، مانند ۰٫۹، ۰٫۹۹ یا ۰٫۹۹۹؛ برای dataset بزرگتر و mini-batch کوچکتر معمولاً تعداد ۹های بیشتر مناسب است.
axis مشخص میکند کدام محور نرمال شود و مقدار پیشفرض آن -1 است؛ یعنی محور آخر. برای ورودی دوبعدی با شکل [batch size, features]، هر ویژگی بر اساس میانگین و انحراف معیار نمونههای batch نرمال میشود. اگر BN را پیش از Flatten روی تصویرهای [batch, height, width] قرار دهیم، با axis پیشفرض برای هر ستون پیکسل یک مجموعه آمار میسازد. اگر هدف رفتار مستقل برای هر ۷۸۴ پیکسل باشد، میتوان axis=[1, 2] را تنظیم کرد.
BN در شبکههای عمیق، مخصوصاً CNNهای فصل ۱۴، چنان رایج است که در بعضی نمودارهای معماری اصلاً رسم نمیشود و حضور آن پس از هر لایه ضمنی فرض میشود.
Gradient Clipping
راه دیگری برای کاهش exploding gradients این است که در backpropagation گرادیانها را طوری محدود کنیم که از یک آستانه بالاتر نروند. این روش gradient clipping نام دارد و بهویژه در RNNها که استفاده از BN دشوارتر است کاربرد زیادی دارد.
optimizer = tf.keras.optimizers.SGD(clipvalue=1.0)
model.compile([...], optimizer=optimizer)
با clipvalue=1.0 هر مؤلفه گرادیان در بازه −۱ تا ۱ محدود میشود. این روش میتواند جهت بردار گرادیان را تغییر دهد؛ مثلاً [0.9, 100] به [0.9, 1] تبدیل میشود.
اگر حفظ جهت مهم باشد، از clipnorm استفاده میشود. در این حالت کل بردار فقط وقتی norm آن از آستانه بیشتر باشد scale میشود. مثلاً با clipnorm=1.0 بردار [0.9, 100] تقریباً به [0.00899964, 0.9999595] میرسد و جهت خود را حفظ میکند. اگر با TensorBoard انفجار گرادیان مشاهده شد، clip-by-value و clip-by-norm را با آستانههای مختلف روی validation set مقایسه کنید.
آغاز یادگیری انتقالی: استفاده مجدد از لایههای pretrained
برای یک DNN بسیار بزرگ معمولاً بهتر است پیش از آموزش از صفر بررسی کنیم آیا شبکهای برای وظیفهای مشابه از قبل آموزش دیده است یا نه. اگر چنین مدلی وجود داشته باشد، اغلب میتوان بیشتر لایههای آن، بهجز لایههای بالایی، را دوباره استفاده کرد. این تکنیک Transfer Learning است و علاوه بر سرعت بسیار بیشتر، داده آموزشی بسیار کمتری نیاز دارد.
فرض کنید مدلی تصاویر را در ۱۰۰ دسته، از حیوان و گیاه تا وسیله نقلیه و اشیای روزمره، طبقهبندی میکند و حالا میخواهیم مدلی برای تشخیص انواع خاص خودرو آموزش دهیم. این دو مسئله مشابه و تا حدی همپوشاناند، بنابراین reuse کردن لایههای شبکه اول انتخاب مناسبی است. اگر اندازه تصاویر ورودی مسئله جدید با مدل اصلی متفاوت باشد باید مرحلهای برای resize اضافه شود. بهطور کلی transfer learning زمانی بهتر جواب میدهد که ورودی دو مسئله ویژگیهای سطح پایین مشابه داشته باشند.
این ترجمه برای استفادهٔ آموزشی تهیه شده است. انتشار عمومی، فروش یا بازنشر آن باید با رعایت حقوق مؤلف و ناشر و اخذ مجوزهای لازم انجام شود.