اگر لایههای یک شبکهی عصبی را صرفاً با ضرب ماتریسی روی هم بچینید، حتی یک مدل 100لایهای هم توانایی یادگیری الگوهای پیچیده و غیرخطی مثل تفکیک دادههای XOR یا بازشناسی تصویر را پیدا نمیکند؛ چون چند تبدیل خطی پیاپی در نهایت به یک تبدیل خطی تقلیل مییابند. توابع فعالساز همان ابزاری هستند که این خطی بودن را در هم میشکنند و مرزهای تصمیمگیری شبکه را منعطف میسازند.
انتخاب ناآگاهانهی تابع فعالساز میتواند آموزش مدل را پیش از شروع متوقف کند؛ مشکلی که معمولاً خود را در قالب محو شدگی گرادیان در لایههای ابتدایی یا از کار افتادن و مرگ دائمی نورونها نشان میدهد. شناخت رفتار ریاضی و مشتق هر تابع—از Sigmoid و Tanh گرفته تا گزینههای مدرنی مثل ReLU و GELU—مهارتی ضروری برای مهندسان یادگیری ماشین است تا در معماریهایی مانند ترنسفورمرها و CNNها دست به انتخاب درست بزنند.
کدها و آزمایشهای این درس سبک طراحی شدهاند و پیادهسازی آنها به پردازندهی ابری یا تجهیزات خاصی وابسته نیست؛ بنابراین میتوانید تمام مراحل را بهراحتی روی پردازندهی استاندارد (CPU) سیستم محلی خود اجرا و بررسی کنید.
بدون غیرخطی بودن، شبکهی 100لایهای شما صرفاً یک ضرب ماتریسی پرزرقوبرق است. توابع فعالساز همان درگاههایی هستند که به شبکههای عصبی امکان میدهند خمیده فکر کنند.
اهداف یادگیری
- پیادهسازی sigmoid ،tanh ،ReLU ،Leaky ReLU ،GELU ،Swish و softmax بههمراه مشتقهایشان از صفر
- ریشهیابی مسئلهی محو شدگی گرادیان (vanishing gradient) با اندازهگیری بزرگی مقادیر فعالسازی در بیش از 10 لایه با توابع فعالساز گوناگون
- شناسایی نورونهای مرده (dead neurons) در یک شبکهی ReLU و توضیح اینکه چرا GELU جلوی این مشکل را میگیرد
- انتخاب تابع فعالساز مناسب برای معماریهای مختلف (ترنسفورمر، CNN، RNN یا لایهی خروجی)
صورت مسئله
دو تبدیل خطی را پشت سر هم قرار دهید: . آن را بسط دهید: . نتیجه چیزی نیست جز ؛ یعنی فقط یک تبدیل خطی واحد. فرقی نمیکند چند لایهی خطی را روی هم بچینید، خروجی نهایی همیشه به یک ضرب ماتریسی تقلیل مییابد. شبکهی 100لایهای شما دقیقاً همان توانایی بازنمایی یک لایهی تکی را دارد.
این موضوع صرفاً یک بحث تئوری جذاب نیست؛ یعنی یک شبکهی عمیق خطی عملاً نمیتواند تابع XOR را یاد بگیرد، دادههای مارپیچی را دستهبندی کند یا چهرهای را تشخیص دهد. بدون توابع فعالساز، عمق شبکه سرابی بیش نیست.
توابع فعالساز خطی بودن را در هم میشکنند. آنها خروجی هر لایه را از یک تابع غیرخطی عبور میدهند و به شبکه توانایی میدهند تا مرزهای تصمیمگیری را خم کند، توابع دلخواه را تقریب بزند و واقعاً یاد بگیرد. اما اگر تابع فعالساز اشتباهی انتخاب کنید، گرادیانهایتان محو شده و به صفر میرسند (مانند sigmoid در شبکههای عمیق)، منفجر شده و به بینهایت میل میکنند (توابع فعالساز بیکران بدون مقداردهی اولیهی دقیق)، یا نورونهایتان برای همیشه خاموش میشوند (مانند ReLU با بایاسهای منفی بزرگ). انتخاب تابع فعالساز مستقیماً تعیین میکند که آیا شبکهی شما اصلاً چیزی یاد میگیرد یا نه.
مفاهیم پایه
چرا غیرخطی بودن ضروری است؟
ضرب ماتریسی خاصیت ترکیبپذیری دارد. ضرب یک بردار در ماتریس و سپس ماتریس ، دقیقاً مثل ضرب آن در ماتریس است. یعنی چیدن ده لایهی خطی روی هم، از نظر ریاضی دقیقاً معادل یک لایهی خطی با یک ماتریس بزرگ است. تمام آن پارامترها و همهی آن عمق عملاً هدر رفتهاند. به چیزی نیاز دارید که این زنجیره را بشکند؛ و این دقیقاً کاری است که توابع فعالساز انجام میدهند.
اثبات ریاضی آن ساده است. یک لایهی خطی عبارت را محاسبه میکند. دو لایه را روی هم بگذارید:
جایگذاری کنید:
دوباره همان یک لایه شد. حالا یک تابع فعالساز غیرخطی بین لایهها قرار دهید:
اکنون زنجیرهی جایگذاری از هم میپاشد. عبارت را نمیتوان به یک تبدیل خطی واحد ساده کرد. حالا شبکه میتواند توابع غیرخطی را بازنمایی کند. با افزودن هر لایهی جدید مجهز به تابع فعالساز، ظرفیت بازنمایی مدل بیشتر میشود.
تابع Sigmoid
نخستین تابع فعالساز در شبکههای عصبی.
بازهی خروجی: (0, 1). نرم، مشتقپذیر و نگاشتکنندهی هر عدد حقیقی به مقداری شبیه احتمال.
مشتق آن:
بیشترین مقدار این مشتق 0.25 است که در رخ میدهد. در پسانتشار، گرادیانها لایهبهلایه در یکدیگر ضرب میشوند. ده لایه با تابع sigmoid یعنی گرادیان حداکثر ده بار در 0.25 ضرب میشود:
کمتر از یکمیلیونیوم سیگنال اولیه! این همان مسئلهی محو شدگی گرادیان است. گرادیانها در لایههای ابتدایی آنقدر ناچیز میشوند که وزنها عملاً بهروزرسانی نمیشوند. در ظاهر به نظر میرسد شبکه یاد میگیرد—چون تابع زیان (loss) در لایههای آخر کم میشود—اما لایههای نخست کاملاً قفل شدهاند. شبکههای عمیق مبتنی بر sigmoid اصلاً آموزشپذیر نیستند.
مشکل دیگر: خروجیهای sigmoid همیشه مثبت هستند (بین 0 تا 1)، یعنی علامت گرادیانهای وزنها همواره یکسان میماند. این مسئله حین گرادیان کاهشی (gradient descent) نوسانهای زیگزاگی شدیدی ایجاد میکند.
تابع Tanh
نسخهی مرکزِ صفر شدهی تابع sigmoid.
بازهی خروجی: (-1, 1). با مرکزیت صفر (Zero-centered)، که مشکل حرکات زیگزاگی را از بین میبرد.
مشتق آن:
بیشترین مقدار مشتق در نقطهی برابر 1.0 است؛ یعنی چهار برابر بهتر از sigmoid. با این حال مسئلهی محو شدگی گرادیان همچنان پابرجاست. برای ورودیهای مثبت یا منفی خیلی بزرگ، مشتق به سمت صفر میل میکند. ده لایه پشت سر هم باز هم گرادیان را ناچیز میکند، فقط کمی ملایمتر.
تابع ReLU: نقطهی عطف بزرگ
واحد خطی یکسوساز (Rectified Linear Unit). این تابع که در سال 2010 توسط نایر و هینتون برای یادگیری عمیق فراگیر شد (ریشهی خود تابع به کارهای فوکوشیما در سال 1969 برمیگردد)، همهچیز را دگرگون کرد.
بازهی خروجی: . مشتق آن بهطور شگفتانگیزی ساده است:
برای ورودیهای مثبت هیچ محو شدگی گرادیانی اتفاق نمیافتد. گرادیان دقیقاً عدد 1 است و مستقیماً عبور میکند. به همین دلیل آموزش شبکههای عمیق ممکن شد؛ چرا که ReLU بزرگی گرادیان را در میان لایهها حفظ میکند.
اما یک حالت شکست وجود دارد: مسئلهی نورونهای مرده. اگر ورودی وزندار یک نورون همیشه منفی بماند (مثلاً به دلیل بایاس منفی بزرگ یا مقداردهی اولیهی نامناسب وزنها)، خروجیاش همیشه صفر، گرادیانش همواره صفر و دیگر هرگز بهروزرسانی نخواهد شد؛ یعنی برای همیشه میمیرد. در عمل، ممکن است 10 تا 40 درصد از نورونهای یک شبکهی ReLU حین آموزش از کار بیفتند.
تابع Leaky ReLU
سادهترین راهحل برای مشکل نورونهای مرده.
در اینجا عددی ثابت و کوچک است که معمولاً 0.01 در نظر گرفته میشود. سمت منفی به جای صفر، شیب اندکی دارد؛ در نتیجه نورونهای مرده همچنان سیگنال گرادیان دریافت میکنند و میتوانند احیا شوند.
تابع GELU: گزینهی پیشفرض امروزی
واحد خطی خطای گوسی (Gaussian Error Linear Unit). این تابع در سال 2016 توسط هندریکس و گیمپل معرفی شد و اکنون تابع فعالساز پیشفرض در BERT ،GPT و بیشتر ترنسفورمرهای مدرن است.
که در آن تابع توزیع تجمعی توزیع نرمال استاندارد است. تقریبی که در عمل استفاده میشود به این صورت است:
تابع GELU در تمام نقاط نرم است، برخلاف ReLU که مقادیر منفی را کاملاً قطع و صفر میکند اجازه میدهد مقادیر منفی کوچک عبور کنند و تفسیری احتمالاتی دارد: هر ورودی را بر اساس احتمال مثبت بودنش تحت یک توزیع گوسی وزندهی میکند. این گیتبندی نرم در معماریهای ترنسفورمر عملکرد بهتری نسبت به ReLU دارد؛ زیرا جریان گرادیان بهتری ایجاد میکند و مسئلهی نورونهای مرده را هم بهکلی از بین میبرد.
تابع Swish / SiLU
تابع فعالساز خودگیتبندیشده (Self-gated) که راماشاندران و همکارانش در سال 2017 از طریق جستوجوی خودکار کشف کردند.
فرمول رسمی Swish به صورت است. گوگل آن را با جستوجوی خودکار در فضای توابع فعالساز پیدا کرد؛ شبکهی عصبیای که قطعات شبکههای عصبی دیگر را طراحی میکند!
این تابع هم مانند GELU نرم و غیریکنواخت است و مقادیر منفی کوچک را نگه میدارد. تفاوت بسیار ظریف است: Swish از sigmoid برای گیتبندی استفاده میکند، در حالی که GELU از تابع توزیع تجمعی گوسی بهره میبرد. در عمل بازدهی این دو تقریباً یکسان است. Swish بیشتر در EfficientNet و برخی مدلهای بینایی ماشین استفاده میشود، اما GELU در مدلهای زبانی حاکم است.
تابع Softmax: فعالساز لایهی خروجی
این تابع در لایههای پنهان کاربردی ندارد. سافتمکس برداری از امتیازهای خام (لاجیتها) را به یک توزیع احتمال تبدیل میکند.
همهی خروجیها بین 0 و 1 قرار میگیرند و جمع کل خروجیها برابر 1 میشود. همین ویژگی آن را به گزینهی استاندارد نهایی برای دستهبندی چندکلاسه تبدیل کرده است. بزرگترین لاجیت بیشترین احتمال را میگیرد، اما برخلاف argmax، سافتمکس مشتقپذیر است و اطلاعات مربوط به میزان اطمینان نسبی به هر کلاس را نگه میدارد.
مقایسهی شکل توابع
مقایسهی جریان گرادیان
چه زمانی از کدام فعالساز استفاده کنیم؟
پیادهسازی گامبهگام
گام 1: پیادهسازی همهی توابع فعالساز و مشتق آنها
هر تابع یک عدد اعشاری تک میگیرد و یک عدد اعشاری برمیگرداند. توابع مشتق هم همان ورودی را میگیرند و گرادیان را تحویل میدهند.
import math
def sigmoid(x):
x = max(-500, min(500, x))
return 1.0 / (1.0 + math.exp(-x))
def sigmoid_derivative(x):
s = sigmoid(x)
return s * (1 - s)
def tanh_act(x):
return math.tanh(x)
def tanh_derivative(x):
t = math.tanh(x)
return 1 - t * t
def relu(x):
return max(0.0, x)
def relu_derivative(x):
return 1.0 if x > 0 else 0.0
def leaky_relu(x, alpha=0.01):
return x if x > 0 else alpha * x
def leaky_relu_derivative(x, alpha=0.01):
return 1.0 if x > 0 else alpha
def gelu(x):
return 0.5 * x * (1 + math.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * x ** 3)))
def gelu_derivative(x):
phi = 0.5 * (1 + math.erf(x / math.sqrt(2)))
pdf = math.exp(-0.5 * x * x) / math.sqrt(2 * math.pi)
return phi + x * pdf
def swish(x):
return x * sigmoid(x)
def swish_derivative(x):
s = sigmoid(x)
return s + x * s * (1 - s)
def softmax(xs):
max_x = max(xs)
exps = [math.exp(x - max_x) for x in xs]
total = sum(exps)
return [e / total for e in exps]
گام 2: مصورسازی نقاط مرگ گرادیان
گرادیان را در 100 نقطهی همفاصله در بازهی 5- تا 5 محاسبه کنید. یک هیستوگرام متنی چاپ کنید تا مشخص شود گرادیان هر تابع در چه نقاطی نزدیک به صفر است.
def gradient_scan(name, derivative_fn, start=-5, end=5, n=100):
step = (end - start) / n
near_zero = 0
healthy = 0
for i in range(n):
x = start + i * step
g = derivative_fn(x)
if abs(g) < 0.01:
near_zero += 1
else:
healthy += 1
pct_dead = near_zero / n * 100
print(f"{name:15s}: {healthy:3d} healthy, {near_zero:3d} near-zero ({pct_dead:.0f}% dead zone)")
gradient_scan("Sigmoid", sigmoid_derivative)
gradient_scan("Tanh", tanh_derivative)
gradient_scan("ReLU", relu_derivative)
gradient_scan("Leaky ReLU", leaky_relu_derivative)
gradient_scan("GELU", gelu_derivative)
gradient_scan("Swish", swish_derivative)
گام 3: آزمایش پدیدهی محو شدگی گرادیان
سیگنالی را در یک گذر رفت (forward-pass) از N لایه با تابع sigmoid در برابر ReLU عبور دهید و بررسی کنید بزرگی مقادیر فعالسازی چگونه تغییر میکند.
import random
def vanishing_gradient_experiment(activation_fn, name, n_layers=10, n_inputs=5):
random.seed(42)
values = [random.gauss(0, 1) for _ in range(n_inputs)]
print(f"\n{name} through {n_layers} layers:")
for layer in range(n_layers):
weights = [random.gauss(0, 1) for _ in range(n_inputs)]
z = sum(w * v for w, v in zip(weights, values))
activated = activation_fn(z)
magnitude = abs(activated)
bar = "#" * int(magnitude * 20)
print(f" Layer {layer+1:2d}: magnitude = {magnitude:.6f} {bar}")
values = [activated] * n_inputs
vanishing_gradient_experiment(sigmoid, "Sigmoid")
vanishing_gradient_experiment(relu, "ReLU")
vanishing_gradient_experiment(gelu, "GELU")
گام 4: شناساگر نورونهای مرده
یک شبکهی ReLU بسازید، ورودیهای تصادفی به آن بدهید و تعداد نورونهایی را که هرگز فعال نمیشوند بشمارید.
def dead_neuron_detector(n_inputs=5, hidden_size=20, n_samples=1000):
random.seed(0)
weights = [[random.gauss(0, 1) for _ in range(n_inputs)] for _ in range(hidden_size)]
biases = [random.gauss(0, 1) for _ in range(hidden_size)]
fire_counts = [0] * hidden_size
for _ in range(n_samples):
inputs = [random.gauss(0, 1) for _ in range(n_inputs)]
for neuron_idx in range(hidden_size):
z = sum(w * x for w, x in zip(weights[neuron_idx], inputs)) + biases[neuron_idx]
if relu(z) > 0:
fire_counts[neuron_idx] += 1
dead = sum(1 for c in fire_counts if c == 0)
rarely_fire = sum(1 for c in fire_counts if 0 < c < n_samples * 0.05)
healthy = hidden_size - dead - rarely_fire
print(f"\nDead Neuron Report ({hidden_size} neurons, {n_samples} samples):")
print(f" Dead (never fired): {dead}")
print(f" Barely alive (<5%): {rarely_fire}")
print(f" Healthy: {healthy}")
print(f" Dead neuron rate: {dead/hidden_size*100:.1f}%")
for i, c in enumerate(fire_counts):
status = "DEAD" if c == 0 else "WEAK" if c < n_samples * 0.05 else "OK"
bar = "#" * (c * 40 // n_samples)
print(f" Neuron {i:2d}: {c:4d}/{n_samples} fires [{status:4s}] {bar}")
dead_neuron_detector()
گام 5: مقایسهی آموزش — Sigmoid در برابر ReLU و GELU
یک شبکهی دولایهی یکسان را روی مجموعهدادهی دایرهها (نقاط داخل دایره = کلاس 1، نقاط بیرون = کلاس 0) با سه تابع فعالساز مختلف آموزش دهید و سرعت همگرایی آنها را با هم مقایسه کنید.
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 ActivationNetwork:
def __init__(self, activation_fn, activation_deriv, hidden_size=8, lr=0.1):
random.seed(0)
self.act = activation_fn
self.act_d = activation_deriv
self.lr = lr
self.hidden_size = hidden_size
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):
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(self.act(z))
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):
error = self.out - target
d_out = error * self.out * (1 - self.out)
for i in range(self.hidden_size):
d_h = d_out * self.w2[i] * self.act_d(self.z1[i])
self.w2[i] -= self.lr * d_out * self.h[i]
for j in range(2):
self.w1[i][j] -= self.lr * d_h * self.x[j]
self.b1[i] -= self.lr * d_h
self.b2 -= self.lr * d_out
def train(self, data, epochs=200):
losses = []
for epoch in range(epochs):
total_loss = 0
correct = 0
for x, y in data:
pred = self.forward(x)
self.backward(y)
total_loss += (pred - y) ** 2
if (pred >= 0.5) == (y >= 0.5):
correct += 1
avg_loss = total_loss / len(data)
accuracy = correct / len(data) * 100
losses.append(avg_loss)
if epoch % 50 == 0 or epoch == epochs - 1:
print(f" Epoch {epoch:3d}: loss={avg_loss:.4f}, accuracy={accuracy:.1f}%")
return losses
data = make_circle_data()
configs = [
("Sigmoid", sigmoid, sigmoid_derivative),
("ReLU", relu, relu_derivative),
("GELU", gelu, gelu_derivative),
]
results = {}
for name, act_fn, act_d_fn in configs:
print(f"\n=== Training with {name} ===")
net = ActivationNetwork(act_fn, act_d_fn, hidden_size=8, lr=0.1)
losses = net.train(data, epochs=200)
results[name] = losses
print("\n=== Final Loss Comparison ===")
for name, losses in results.items():
print(f" {name:10s}: start={losses[0]:.4f} -> end={losses[-1]:.4f} (improvement: {(1 - losses[-1]/losses[0])*100:.1f}%)")
نحوهی استفادهی کاربردی
کتابخانهی PyTorch همهی این موارد را هم در قالب توابع و هم در قالب ماژول ارائه میدهد:
import torch
import torch.nn as nn
import torch.nn.functional as F
x = torch.randn(4, 10)
relu_out = F.relu(x)
gelu_out = F.gelu(x)
sigmoid_out = torch.sigmoid(x)
swish_out = F.silu(x)
logits = torch.randn(4, 5)
probs = F.softmax(logits, dim=1)
model = nn.Sequential(
nn.Linear(10, 64),
nn.GELU(),
nn.Linear(64, 32),
nn.GELU(),
nn.Linear(32, 5),
)
لایههای پنهان در ترنسفورمر: GELU. لایههای پنهان در CNN: تابع ReLU. لایهی خروجی برای دستهبندی: softmax. لایهی خروجی برای رگرسیون: بدون تابع (خطی). لایهی خروجی برای خروجیهای احتمالی: sigmoid. اصل کار همین است. کار را با همین پیشفرضها شروع کنید و فقط وقتی تغییرشان دهید که شواهد (evidence) کافی داشته باشید.
شبکههای RNN و LSTM از tanh برای وضعیت پنهان (hidden state) و از sigmoid برای گیتها استفاده میکنند، اما اگر امروز سیستمی را از صفر طراحی میکنید احتمالاً سراغ RNN نمیروید. اگر نورونها در شبکهی ReLU شما از کار میافتند، سراغ GELU بروید. بیدلیل به سمت Leaky ReLU نروید—GELU مسئلهی نورونهای مرده را کاملاً حل میکند و جریان گرادیان بهتری هم دارد.
نتیجه و خروجی
خروجی این درس:
- فایل
outputs/prompt-activation-selector.md— یک پرامپت با قابلیت استفادهی مجدد برای انتخاب تابع فعالساز درست متناسب با هر معماری
تمرینها
-
تابع Parametric ReLU (PReLU) را که در آن شیب منفی یک پارامتر یادگرفتنی است پیادهسازی کنید. آن را روی مجموعهدادهی دایره آموزش دهید و با Leaky ReLU مقایسه کنید.
-
آزمایش محو شدگی گرادیان را این بار به جای 10 لایه با 50 لایه اجرا کنید. بزرگی سیگنال را در هر لایه برای توابع sigmoid ،tanh ،ReLU و GELU رسم کنید. سیگنال هر تابع در کدام لایه عملاً به صفر میرسد؟
-
تابع ELU (واحد خطی نمایی) را پیادهسازی کنید: اگر بود ، و اگر بود . نرخ نورونهای مردهی آن را با ReLU روی یک شبکهی یکسان مقایسه کنید.
-
یک «پایشگر سلامت گرادیان» بنویسید که حین آموزش اجرا شود: در هر دوره (epoch)، میانگین بزرگی گرادیان را در هر لایه محاسبه کند. اگر گرادیان لایهای کمتر از 0.001 یا بیشتر از 100 شد، هشدار چاپ کند.
-
مقایسهی آموزشی را طوری تغییر دهید که به جای دادههای دایره، از مجموعهدادهی XOR درس 01 استفاده کند. کدام تابع سریعتر از بقیه روی دادههای XOR همگرا میشود؟ چرا نتیجه با نتایج دادههای دایره فرق دارد؟
اصطلاحات کلیدی
| اصطلاح | در گفتوگوهای عامیانه | معنای دقیق فنی |
|---|---|---|
| تابع فعالساز (Activation function) | «بخش غیرخطی مدل» | تابعی که روی خروجی هر نورون اعمال میشود تا خطی بودن را بشکند و به شبکه امکان یادگیری نگاشتهای غیرخطی را بدهد |
| محو شدگی گرادیان (Vanishing gradient) | «از بین رفتن گرادیانها در شبکههای عمیق» | افت نمایی گرادیانها در طول لایهها در شرایطی که مشتق تابع فعالساز کمتر از 1 باشد؛ اتفاقی که آموزش لایههای اولیه را غیرممکن میکند |
| انفجار گرادیان (Exploding gradient) | «سر به فلک کشیدن گرادیانها» | رشد نمایی گرادیانها در طول لایهها وقتی ضریب مؤثر از 1 بزرگتر شود؛ پدیدهای که پایداری آموزش را به هم میزند |
| نورون مرده (Dead neuron) | «نورونی که یادگیری را متوقف کرده» | نورونی مبتنی بر ReLU که ورودیاش برای همیشه منفی شده، خروجی صفر میدهد و گرادیانش هم صفر است |
| سیگموئید (Sigmoid) | «فشردن مقادیر بین 0 تا 1» | تابع لجستیک ، دارای اهمیت تاریخی اما عامل اصلی محو شدگی گرادیان در شبکههای عمیق |
| رلو (ReLU) | «صفر کردن مقادیر منفی» | تابع ؛ فعالسازی که با حفظ بزرگی گرادیان، یادگیری عمیق کاربردی را محقق کرد |
| گلو (GELU) | «فعالساز ترنسفورمرها» | واحد خطی خطای گوسی؛ تابعی هموار که ورودیها را براساس احتمال مثبت بودنشان وزندهی میکند |
| سوئیش (Swish/SiLU) | «ReLU خودگیتبندیشده» | تابع که با جستوجوی خودکار کشف شد و در EfficientNet به کار میرود |
| سافتمکس (Softmax) | «تبدیل امتیازها به احتمال» | بردار لاجیتها را نرمالسازی کرده و به توزیع احتمالی تبدیل میکند که همهی مقادیر در بازهی (0,1) باشند و مجموعشان 1 شود |
| لیکی رلو (Leaky ReLU) | «ReLU نامیرا» | تابع که در آن مقداری کوچک است (0.01) و با ایجاد گرادیان منفی کوچک جلوی مرگ نورونها را میگیرد |
| اشباع (Saturation) | «قسمت صاف تابع sigmoid» | بخشهایی از تابع که مشتق در آنها به صفر میل میکند و مانع جریان گرادیان میشود |
| لاجیت (Logit) | «امتیاز خام پیش از softmax» | خروجی نرمالنشدهی لایهی پایانی، درست قبل از اعمال سافتمکس یا sigmoid |
برای مطالعهی بیشتر
- مقالهی Nair & Hinton، با عنوان «Rectified Linear Units Improve Restricted Boltzmann Machines» (سال 2010) — مقالهای که ReLU را معرفی کرد و امکان آموزش شبکههای عمیق را به وجود آورد
- مقالهی Hendrycks & Gimpel، با عنوان «Gaussian Error Linear Units (GELUs)» (سال 2016) — مقالهای که تابع فعالساز پیشفرض ترنسفورمرها را معرفی کرد
- مقالهی Ramachandran et al.، با عنوان «Searching for Activation Functions» (سال 2017) — استفاده از جستوجوی خودکار برای کشف Swish، نشاندهندهی اینکه طراحی توابع فعالساز میتواند خودکار انجام شود
- مقالهی Glorot & Bengio، با عنوان «Understanding the difficulty of training deep feedforward neural networks» (سال 2010) — مقالهای که مسئلهی محو شدگی و انفجار گرادیان را ریشهیابی کرد و مقداردهی اولیهی خاویر (Xavier initialization) را پیشنهاد داد
- کتاب «یادگیری عمیق» اثر Goodfellow ،Bengio و Courville، فصل 6.3 (https://www.deeplearningbook.org/) — بررسی دقیق و ریاضیاتی واحدهای پنهان و توابع فعالساز
