گرادیان نزولی چیست؟ آموزش Gradient Descent با پایتون و PyTorch

گرادیان نزولی روش اصلی بهینه‌سازی بسیاری از مدل‌های یادگیری ماشین است. در این راهنما نحوه کار Gradient Descent، تفاوت Batch و SGD و Mini-batch، نرخ یادگیری، Adam، پیاده‌سازی با پایتون و PyTorch و خطاهای رایج را می‌آموزید.

Share
گرادیان نزولی چیست؟ آموزش Gradient Descent با پایتون و PyTorch

گرادیان نزولی یا Gradient Descent یکی از مهم‌ترین روش‌های بهینه‌سازی در یادگیری ماشین و یادگیری عمیق است. بسیاری از مدل‌ها برای یادگیری پارامترهای مناسب، از گرادیان نزولی یا نسخه‌های توسعه‌یافته آن استفاده می‌کنند.

وقتی یک مدل پیش‌بینی اشتباهی انجام می‌دهد، باید مشخص شود پارامترهای آن در چه جهتی تغییر کنند تا خطا کمتر شود. گرادیان نزولی همین مسیر اصلاح را مشخص می‌کند.

از این روش در آموزش مدل‌های مختلف استفاده می‌شود:

  • رگرسیون خطی
  • رگرسیون لجستیک
  • ماشین بردار پشتیبان خطی
  • شبکه‌های عصبی
  • مدل‌های بینایی ماشین
  • مدل‌های پردازش زبان طبیعی
  • مدل‌های Transformer
  • مدل‌های تولید متن، تصویر و صوت

در این مقاله، Gradient Descent را بدون فرمول‌های ریاضی توضیح می‌دهیم و آن را با پایتون، NumPy، Scikit-learn و PyTorch پیاده‌سازی می‌کنیم.

گرادیان نزولی چیست؟

گرادیان نزولی روشی برای پیدا کردن پارامترهایی است که خطای مدل را کاهش می‌دهند.

فرض کنید یک مدل ساده می‌خواهد قیمت محصول را بر اساس چند ویژگی پیش‌بینی کند. در ابتدای آموزش، پارامترهای مدل هنوز مناسب نیستند و خروجی آن با مقدار واقعی اختلاف زیادی دارد.

فرایند یادگیری به‌صورت تکراری انجام می‌شود:

۱. مدل با پارامترهای فعلی پیش‌بینی می‌کند.

۲. مقدار خطا محاسبه می‌شود.

۳. مشخص می‌شود هر پارامتر در چه جهتی باید تغییر کند.

۴. پارامترها کمی اصلاح می‌شوند.

۵. مدل دوباره پیش‌بینی می‌کند.

۶. این مراحل تا رسیدن به نتیجه مناسب ادامه پیدا می‌کنند.

هدف گرادیان نزولی رسیدن به نقطه‌ای است که مقدار تابع خطا تا حد ممکن کم باشد.

یک مثال ساده برای درک Gradient Descent

تصور کنید روی یک تپه مه‌آلود ایستاده‌اید و می‌خواهید به پایین‌ترین نقطه دره برسید. به دلیل مه، کل مسیر را نمی‌بینید؛ اما می‌توانید شیب زمین زیر پای خود را بررسی کنید.

در هر مرحله:

  • جهت سرازیری را پیدا می‌کنید.
  • یک قدم در همان جهت برمی‌دارید.
  • دوباره شیب را بررسی می‌کنید.
  • این کار را تا رسیدن به بخش کم‌شیب ادامه می‌دهید.

در این مثال:

  • موقعیت شما مانند پارامترهای مدل است.
  • ارتفاع زمین مانند میزان خطاست.
  • جهت سرازیری مانند جهت گرادیان است.
  • اندازه هر قدم مانند نرخ یادگیری است.
  • رسیدن به پایین دره مانند پیدا کردن پارامترهای مناسب است.

این تشبیه تمام جزئیات فنی را پوشش نمی‌دهد، اما ایده اصلی Gradient Descent را به‌خوبی نشان می‌دهد.

تابع خطا چیست؟

تابع خطا یا Loss Function مشخص می‌کند پیش‌بینی مدل چقدر با خروجی واقعی تفاوت دارد.

مدل‌های مختلف از توابع خطای متفاوتی استفاده می‌کنند:

نوع مسئلهنمونه تابع خطا
رگرسیونمیانگین مربعات خطا
طبقه‌بندی دودسته‌ایLog Loss
طبقه‌بندی چندکلاسهCross-Entropy
مدل مقاوم‌تر به داده پرتHuber Loss
مدل‌های رتبه‌بندیRanking Loss
مدل‌های زبانیCross-Entropy توکن‌ها

Gradient Descent مستقیماً هدف کسب‌وکار را نمی‌فهمد. فقط تلاش می‌کند تابع خطایی را که توسعه‌دهنده انتخاب کرده است، کاهش دهد.

اگر تابع خطا با هدف واقعی پروژه هماهنگ نباشد، کاهش آن الزاماً به بهبود ارزش کسب‌وکار منجر نمی‌شود.

گرادیان چیست؟

گرادیان نشان می‌دهد تغییر کوچک هر پارامتر چه اثری بر خطای مدل دارد.

برای هر پارامتر، گرادیان دو نوع اطلاعات فراهم می‌کند:

  • جهت تغییر
  • شدت اثر تغییر

اگر تغییر یک پارامتر باعث افزایش خطا شود، الگوریتم آن را در جهت مخالف اصلاح می‌کند. اگر یک پارامتر اثر زیادی بر خطا داشته باشد، مقدار اصلاح آن نیز می‌تواند بیشتر باشد.

در شبکه‌های عصبی، تعداد پارامترها ممکن است بسیار زیاد باشد. محاسبه این اثرها با فرایندی به نام Backpropagation انجام می‌شود.

تفاوت Gradient Descent و Backpropagation

این دو مفهوم به یکدیگر مرتبط‌اند، اما یکسان نیستند.

Backpropagation

پس‌انتشار یا Backpropagation گرادیان پارامترهای شبکه عصبی را محاسبه می‌کند. این مرحله مشخص می‌کند هر پارامتر چه سهمی در خطای نهایی داشته است.

Gradient Descent

گرادیان نزولی از گرادیان‌های محاسبه‌شده برای به‌روزرسانی پارامترهای مدل استفاده می‌کند.

به زبان ساده:

  • Backpropagation می‌گوید پارامترها چگونه باید تغییر کنند.
  • Optimizer تصمیم می‌گیرد تغییر دقیق چگونه اعمال شود.
  • Gradient Descent خانواده‌ای از روش‌های انجام این به‌روزرسانی است.

در PyTorch، متد loss.backward() گرادیان‌ها را محاسبه می‌کند و متد optimizer.step() پارامترها را به‌روزرسانی می‌کند. مستندات رسمی PyTorch نیز حلقه بهینه‌سازی را شامل پاک‌کردن گرادیان‌های قبلی، محاسبه گرادیان با Backpropagation و اجرای مرحله Optimizer معرفی می‌کند.

پارامترهای مدل چه هستند؟

پارامترها مقادیری هستند که مدل در فرایند آموزش یاد می‌گیرد.

در یک رگرسیون خطی، پارامترها شامل ضرایب ویژگی‌ها و مقدار پایه هستند. در شبکه عصبی، وزن‌ها و Biasهای لایه‌ها پارامتر محسوب می‌شوند.

پارامتر را نباید با Hyperparameter اشتباه گرفت.

پارامتر

مدل آن را از داده یاد می‌گیرد:

  • وزن‌ها
  • Biasها
  • ضرایب

Hyperparameter

پیش از آموزش یا در فرایند تنظیم مدل توسط توسعه‌دهنده انتخاب می‌شود:

  • نرخ یادگیری
  • اندازه Batch
  • تعداد Epoch
  • نوع Optimizer
  • مقدار Weight Decay
  • تعداد لایه‌ها

نرخ یادگیری چیست؟

نرخ یادگیری یا Learning Rate اندازه هر قدم در مسیر بهینه‌سازی را تعیین می‌کند.

نرخ یادگیری بسیار کوچک

اگر Learning Rate بیش از حد کوچک باشد:

  • آموزش بسیار کند می‌شود.
  • مدل به تعداد Epoch بیشتری نیاز دارد.
  • هزینه محاسباتی افزایش پیدا می‌کند.
  • مدل ممکن است پیش از رسیدن به نتیجه مناسب متوقف شود.

نرخ یادگیری بسیار بزرگ

اگر Learning Rate بیش از حد بزرگ باشد:

  • مدل از نقطه مناسب عبور می‌کند.
  • مقدار خطا نوسان می‌کند.
  • آموزش ناپایدار می‌شود.
  • مقدار خطا ممکن است افزایش پیدا کند.
  • پارامترها ممکن است به مقادیر نامعتبر برسند.

نرخ یادگیری مناسب

Learning Rate مناسب باعث می‌شود مدل با سرعت قابل قبول و بدون نوسان شدید به سمت نتیجه بهتر حرکت کند.

نرخ یادگیری یکی از مهم‌ترین Hyperparameterهای آموزش مدل است و معمولاً باید با آزمایش کنترل‌شده انتخاب شود.

Epoch چیست؟

یک Epoch زمانی کامل می‌شود که مدل تمام نمونه‌های داده آموزشی را یک بار مشاهده کرده باشد.

اگر مجموعه آموزشی شامل ۱۰۰ هزار نمونه باشد، پایان یک Epoch یعنی همه این نمونه‌ها یک بار در فرایند آموزش استفاده شده‌اند.

آموزش معمولاً چند Epoch ادامه پیدا می‌کند. تعداد بسیار کم Epoch می‌تواند باعث کم‌برازش شود. تعداد بسیار زیاد نیز ممکن است مدل را دچار بیش‌برازش کند.

Batch چیست؟

Batch گروهی از نمونه‌های آموزشی است که در یک مرحله پردازش می‌شوند.

برای مثال، اگر ۱۰ هزار نمونه و Batch Size برابر با ۱۰۰ داشته باشیم، هر Epoch شامل ۱۰۰ مرحله به‌روزرسانی خواهد بود.

اندازه Batch بر موارد زیر اثر می‌گذارد:

  • مصرف حافظه
  • سرعت آموزش
  • پایداری گرادیان
  • تعداد به‌روزرسانی‌ها
  • کیفیت تعمیم مدل
  • بهره‌برداری از GPU

انواع Gradient Descent

سه نوع اصلی گرادیان نزولی بر اساس میزان داده استفاده‌شده در هر مرحله وجود دارد.

Batch Gradient Descent

در Batch Gradient Descent، تمام داده‌های آموزشی برای محاسبه یک مرحله به‌روزرسانی استفاده می‌شوند.

مزایا

  • جهت به‌روزرسانی نسبتاً پایدار است.
  • نمودار خطا معمولاً هموارتر است.
  • پیاده‌سازی مفهومی ساده‌ای دارد.

محدودیت‌ها

  • برای داده‌های بزرگ کند است.
  • حافظه زیادی مصرف می‌کند.
  • تا پردازش کامل داده، به‌روزرسانی انجام نمی‌شود.
  • برای آموزش مدل‌های بزرگ عملی نیست.

Batch Gradient Descent بیشتر برای آموزش مفهومی یا مجموعه‌داده‌های کوچک مناسب است.

Stochastic Gradient Descent یا SGD

در SGD، هر مرحله به‌روزرسانی با یک نمونه آموزشی انجام می‌شود.

Scikit-learn توضیح می‌دهد که SGD یک روش بهینه‌سازی است، نه یک خانواده مستقل از مدل‌ها. برای مثال، می‌توان رگرسیون لجستیک یا مدل خطی SVM را با SGD آموزش داد.

مزایا

  • به‌روزرسانی‌ها سریع آغاز می‌شوند.
  • برای داده‌های بزرگ مناسب است.
  • امکان آموزش تدریجی وجود دارد.
  • نوسان‌ها گاهی به عبور از نقاط نامناسب کمک می‌کنند.

محدودیت‌ها

  • مسیر آموزش نوسان زیادی دارد.
  • خطا در هر مرحله الزاماً کاهش پیدا نمی‌کند.
  • به نرخ یادگیری حساس است.
  • ترتیب داده‌ها می‌تواند بر نتیجه اثر بگذارد.

Scikit-learn توصیه می‌کند داده آموزشی قبل از استفاده از SGD جابه‌جا شود و ویژگی‌های عددی نیز در بیشتر کاربردها استاندارد شوند.

Mini-batch Gradient Descent

در Mini-batch Gradient Descent، هر مرحله روی گروه کوچکی از نمونه‌ها انجام می‌شود.

این روش رایج‌ترین انتخاب برای آموزش شبکه‌های عصبی است؛ زیرا تعادلی میان پایداری Batch Gradient Descent و سرعت SGD ایجاد می‌کند.

مزایا

  • استفاده مؤثر از GPU
  • مصرف حافظه کنترل‌شده
  • به‌روزرسانی‌های سریع‌تر
  • نوسان کمتر از SGD تک‌نمونه‌ای
  • مناسب برای مجموعه‌داده‌های بزرگ

محدودیت‌ها

  • اندازه Batch باید تنظیم شود.
  • Batch بسیار بزرگ به حافظه بیشتری نیاز دارد.
  • Batch بسیار کوچک می‌تواند آموزش را پرنوسان کند.

مقایسه انواع گرادیان نزولی

روشداده در هر مرحلهپایداریسرعت هر به‌روزرسانیکاربرد
Batchتمام دادهزیادکمداده کوچک
SGDیک نمونهکمزیادآموزش آنلاین
Mini-batchگروهی از نمونه‌هامتوسطمناسبشبکه عصبی و GPU

در عمل، اصطلاح SGD گاهی برای روش Mini-batch نیز به کار می‌رود؛ به‌خصوص در کتابخانه‌های یادگیری عمیق.

پیاده‌سازی ساده Gradient Descent با پایتون

در این مثال، می‌خواهیم یک رابطه خطی ساده را از داده یاد بگیریم.

import numpy as np


X = np.array(
    [
        1.0,
        2.0,
        3.0,
        4.0,
        5.0,
    ]
)

y = np.array(
    [
        5.0,
        8.0,
        11.0,
        14.0,
        17.0,
    ]
)

weight = 0.0
bias = 0.0

learning_rate = 0.01
epochs = 1000

sample_count = len(X)

for epoch in range(epochs):
    predictions = (
        weight * X + bias
    )

    errors = predictions - y

    weight_gradient = (
        2
        * np.mean(
            errors * X
        )
    )

    bias_gradient = (
        2
        * np.mean(errors)
    )

    weight -= (
        learning_rate
        * weight_gradient
    )

    bias -= (
        learning_rate
        * bias_gradient
    )

    if epoch % 100 == 0:
        loss = np.mean(
            errors ** 2
        )

        print(
            f"Epoch {epoch}: "
            f"loss={loss:.4f}"
        )

print("Weight:", weight)
print("Bias:", bias)

این مثال نشان می‌دهد مدل چگونه در چند مرحله پارامترهای خود را اصلاح می‌کند.

عبارت‌های محاسباتی داخل کد بخشی از پیاده‌سازی هستند و برای اجرای برنامه ضرورت دارند؛ اما برای درک مفهوم، کافی است بدانید مدل جهت کاهش خطا را پیدا می‌کند و پارامترها را کمی در همان جهت تغییر می‌دهد.

نمایش روند کاهش خطا

import matplotlib.pyplot as plt
import numpy as np


weight = 0.0
bias = 0.0

learning_rate = 0.01
epochs = 500

loss_history = []

for epoch in range(epochs):
    predictions = (
        weight * X + bias
    )

    errors = predictions - y

    loss = np.mean(
        errors ** 2
    )

    loss_history.append(loss)

    weight_gradient = (
        2
        * np.mean(
            errors * X
        )
    )

    bias_gradient = (
        2
        * np.mean(errors)
    )

    weight -= (
        learning_rate
        * weight_gradient
    )

    bias -= (
        learning_rate
        * bias_gradient
    )

plt.plot(loss_history)

plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title(
    "Training Loss"
)

plt.show()

نمودار مناسب معمولاً در ابتدا کاهش سریع‌تری دارد و سپس به‌تدریج کم‌شیب می‌شود.

اگر نمودار خطا افزایش پیدا کند یا نوسان شدید داشته باشد، Learning Rate می‌تواند بیش از حد بزرگ باشد.

آموزش رگرسیون با SGDRegressor

Scikit-learn کلاس SGDRegressor را برای آموزش مدل‌های رگرسیون خطی با SGD ارائه می‌کند.

from sklearn.datasets import (
    make_regression,
)
from sklearn.linear_model import (
    SGDRegressor,
)
from sklearn.metrics import (
    mean_absolute_error,
    r2_score,
)
from sklearn.model_selection import (
    train_test_split,
)
from sklearn.pipeline import (
    make_pipeline,
)
from sklearn.preprocessing import (
    StandardScaler,
)


X, y = make_regression(
    n_samples=5000,
    n_features=20,
    noise=15,
    random_state=42,
)

X_train, X_test, y_train, y_test = (
    train_test_split(
        X,
        y,
        test_size=0.2,
        random_state=42,
    )
)

model = make_pipeline(
    StandardScaler(),
    SGDRegressor(
        loss="squared_error",
        penalty="l2",
        max_iter=2000,
        tol=1e-4,
        early_stopping=True,
        validation_fraction=0.1,
        n_iter_no_change=10,
        random_state=42,
    ),
)

model.fit(
    X_train,
    y_train,
)

predictions = model.predict(
    X_test
)

print(
    "MAE:",
    mean_absolute_error(
        y_test,
        predictions,
    ),
)

print(
    "R-squared:",
    r2_score(
        y_test,
        predictions,
    ),
)

Scikit-learn، SGDRegressor را برای مسائل رگرسیون دارای تعداد زیاد نمونه مناسب می‌داند و امکان انتخاب تابع خطا، Regularization و توقف زودهنگام را فراهم می‌کند.

آموزش طبقه‌بندی متن با SGDClassifier

SGD برای داده متنی پُربعد و Sparse نیز مناسب است.

import pandas as pd

from sklearn.feature_extraction.text import (
    TfidfVectorizer,
)
from sklearn.linear_model import (
    SGDClassifier,
)
from sklearn.metrics import (
    classification_report,
)
from sklearn.model_selection import (
    train_test_split,
)
from sklearn.pipeline import Pipeline


data = pd.read_csv(
    "persian_messages.csv"
)

X_train, X_test, y_train, y_test = (
    train_test_split(
        data["text"],
        data["label"],
        test_size=0.2,
        random_state=42,
        stratify=data["label"],
    )
)

pipeline = Pipeline(
    steps=[
        (
            "tfidf",
            TfidfVectorizer(
                ngram_range=(1, 2),
                min_df=2,
                sublinear_tf=True,
            ),
        ),
        (
            "classifier",
            SGDClassifier(
                loss="log_loss",
                penalty="elasticnet",
                alpha=0.0001,
                max_iter=2000,
                early_stopping=True,
                validation_fraction=0.1,
                n_iter_no_change=10,
                random_state=42,
            ),
        ),
    ]
)

pipeline.fit(
    X_train,
    y_train,
)

predictions = pipeline.predict(
    X_test
)

print(
    classification_report(
        y_test,
        predictions,
    )
)

با loss="log_loss"، مدل رفتاری مشابه رگرسیون لجستیک دارد؛ اما با روش SGD آموزش می‌بیند. با loss="hinge" نیز می‌توان یک مدل خطی مشابه SVM ایجاد کرد.

آموزش مدل با PyTorch

در PyTorch، حلقه آموزش معمولاً شامل مراحل زیر است:

۱. داده وارد مدل می‌شود.

۲. مدل خروجی را تولید می‌کند.

۳. Loss محاسبه می‌شود.

۴. گرادیان‌های قبلی پاک می‌شوند.

۵. Backpropagation گرادیان‌های جدید را محاسبه می‌کند.

۶. Optimizer پارامترها را به‌روزرسانی می‌کند.

نمونه کامل:

import torch

from torch import nn
from torch.utils.data import (
    DataLoader,
    TensorDataset,
)


torch.manual_seed(42)

X = torch.randn(
    2000,
    10,
)

true_weights = torch.tensor(
    [
        2.0,
        -1.5,
        0.8,
        0.0,
        3.2,
        -2.4,
        1.1,
        0.5,
        -0.7,
        1.8,
    ]
)

noise = (
    torch.randn(2000)
    * 0.5
)

y = (
    X @ true_weights
    + 4.0
    + noise
)

dataset = TensorDataset(
    X,
    y.unsqueeze(1),
)

loader = DataLoader(
    dataset,
    batch_size=64,
    shuffle=True,
)

model = nn.Linear(
    in_features=10,
    out_features=1,
)

loss_function = nn.MSELoss()

optimizer = torch.optim.SGD(
    model.parameters(),
    lr=0.01,
    momentum=0.9,
)

epochs = 50

for epoch in range(epochs):
    model.train()

    total_loss = 0.0

    for features, targets in loader:
        predictions = model(
            features
        )

        loss = loss_function(
            predictions,
            targets,
        )

        optimizer.zero_grad()

        loss.backward()

        optimizer.step()

        total_loss += (
            loss.item()
            * features.size(0)
        )

    average_loss = (
        total_loss
        / len(dataset)
    )

    if epoch % 5 == 0:
        print(
            f"Epoch {epoch}: "
            f"loss={average_loss:.4f}"
        )

طبق آموزش رسمی PyTorch، گرادیان‌ها به‌صورت پیش‌فرض روی هم جمع می‌شوند. بنابراین باید پیش از محاسبه مرحله جدید، با optimizer.zero_grad() پاک شوند. سپس loss.backward() گرادیان‌ها را محاسبه و optimizer.step() پارامترها را اصلاح می‌کند.

Momentum چیست؟

SGD ساده ممکن است در مسیر بهینه‌سازی مدام تغییر جهت دهد. Momentum از جهت حرکت مرحله‌های قبلی استفاده می‌کند تا حرکت مدل پایدارتر شود.

تشبیه مناسب، حرکت یک توپ روی سطح شیب‌دار است. توپ فقط بر اساس شیب لحظه فعلی حرکت نمی‌کند؛ بلکه بخشی از سرعت قبلی خود را نیز حفظ می‌کند.

مزایای احتمالی Momentum:

  • کاهش نوسان
  • عبور سریع‌تر از بخش‌های کم‌شیب
  • همگرایی سریع‌تر
  • حرکت پایدارتر در بعضی مسائل

در PyTorch می‌توان Momentum را هنگام ساخت Optimizer تعیین کرد:

optimizer = torch.optim.SGD(
    model.parameters(),
    lr=0.01,
    momentum=0.9,
)

پیاده‌سازی SGD در PyTorch از Momentum و گزینه‌های دیگری مانند Weight Decay و Nesterov پشتیبانی می‌کند.

Adam چیست؟

Adam یکی از Optimizerهای پرکاربرد در یادگیری عمیق است.

این روش برای هر پارامتر، روند گرادیان‌ها و شدت تغییرات اخیر را در نظر می‌گیرد و اندازه به‌روزرسانی را به‌صورت تطبیقی تنظیم می‌کند.

مزایای رایج Adam:

  • شروع مناسب برای بسیاری از شبکه‌های عصبی
  • نیاز کمتر به تنظیم دستی نسبت به SGD ساده
  • عملکرد مناسب روی گرادیان‌های Sparse
  • سازگاری خوب با مسائل پیچیده
  • همگرایی سریع در بسیاری از پروژه‌ها

استفاده در PyTorch:

optimizer = torch.optim.Adam(
    model.parameters(),
    lr=0.001,
)

PyTorch علاوه بر SGD و Adam، Optimizerهای متنوعی مانند AdamW، RMSprop، Adagrad و Adafactor ارائه می‌کند.

AdamW چیست؟

AdamW نسخه‌ای از Adam است که Weight Decay را به شکل جداگانه‌تری از به‌روزرسانی اصلی پارامترها اعمال می‌کند.

این Optimizer در آموزش و Fine-tuning بسیاری از مدل‌های مدرن، به‌ویژه مدل‌های Transformer، استفاده می‌شود.

optimizer = torch.optim.AdamW(
    model.parameters(),
    lr=0.0001,
    weight_decay=0.01,
)

انتخاب AdamW به این معنا نیست که تنظیم Learning Rate، Batch Size و Scheduler دیگر اهمیتی ندارند. این پارامترها همچنان باید بر اساس داده و معماری مدل انتخاب شوند.

مقایسه SGD، Adam و AdamW

Optimizerویژگی اصلیکاربرد متداول
SGDساده و کم‌حافظهمدل‌های خطی و بعضی شبکه‌های عصبی
SGD با Momentumحرکت پایدارتربینایی ماشین و شبکه‌های عمیق
Adamنرخ یادگیری تطبیقیشروع سریع پروژه‌های یادگیری عمیق
AdamWWeight Decay جداشدهTransformer و Fine-tuning
RMSpropتنظیم تطبیقی قدم‌هابعضی مدل‌های ترتیبی
Adagradمناسب ویژگی‌های Sparseبرخی مسائل متن و داده Sparse

بهترین Optimizer به نوع مدل، داده، Batch Size و هدف پروژه وابسته است. نتیجه باید با آزمایش کنترل‌شده ارزیابی شود.

Learning Rate Scheduler چیست؟

Learning Rate Scheduler نرخ یادگیری را در طول آموزش تغییر می‌دهد.

یک روش رایج این است که آموزش با Learning Rate نسبتاً بزرگ آغاز شود و سپس مقدار آن کاهش یابد. در ابتدا مدل سریع‌تر حرکت می‌کند و در مراحل بعد با قدم‌های کوچک‌تر تنظیم دقیق‌تری انجام می‌دهد.

نمونه در PyTorch:

optimizer = torch.optim.AdamW(
    model.parameters(),
    lr=0.001,
)

scheduler = (
    torch.optim.lr_scheduler
    .ReduceLROnPlateau(
        optimizer,
        mode="min",
        factor=0.5,
        patience=3,
    )
)

در پایان هر Epoch:

scheduler.step(
    validation_loss
)

روش‌های متداول زمان‌بندی نرخ یادگیری عبارت‌اند از:

  • کاهش مرحله‌ای
  • کاهش نمایی
  • کاهش بر اساس توقف بهبود
  • Cosine Annealing
  • One Cycle
  • Warmup همراه با کاهش تدریجی

Warmup چیست؟

در Warmup، آموزش با Learning Rate بسیار کوچک شروع می‌شود و طی چند مرحله به مقدار اصلی می‌رسد.

این روش در آموزش مدل‌های بزرگ و Transformerها کاربرد دارد؛ زیرا تغییر شدید پارامترها در ابتدای آموزش می‌تواند باعث ناپایداری شود.

پس از پایان Warmup، Scheduler معمولاً Learning Rate را به‌تدریج کاهش می‌دهد.

Early Stopping چیست؟

Early Stopping آموزش را زمانی متوقف می‌کند که عملکرد مدل روی داده اعتبارسنجی دیگر بهتر نمی‌شود.

این روش دو مزیت اصلی دارد:

  • کاهش زمان آموزش
  • کاهش احتمال بیش‌برازش

Scikit-learn در SGDClassifier و SGDRegressor امکان توقف بر اساس عملکرد داده اعتبارسنجی را فراهم می‌کند. اگر نتیجه برای چند Epoch بهبود پیدا نکند، آموزش متوقف می‌شود.

نمونه تنظیم:

model = SGDClassifier(
    early_stopping=True,
    validation_fraction=0.1,
    n_iter_no_change=10,
    max_iter=3000,
    random_state=42,
)

پیاده‌سازی Early Stopping در PyTorch

best_validation_loss = float(
    "inf"
)

patience = 5
epochs_without_improvement = 0

for epoch in range(100):
    train_one_epoch(
        model,
        train_loader,
        optimizer,
        loss_function,
    )

    validation_loss = evaluate(
        model,
        validation_loader,
        loss_function,
    )

    if (
        validation_loss
        < best_validation_loss
    ):
        best_validation_loss = (
            validation_loss
        )

        epochs_without_improvement = 0

        torch.save(
            model.state_dict(),
            "best_model.pt",
        )
    else:
        epochs_without_improvement += 1

    if (
        epochs_without_improvement
        >= patience
    ):
        print(
            "Early stopping"
        )
        break

مدل نهایی باید از بهترین Checkpoint اعتبارسنجی بارگذاری شود، نه الزاماً آخرین Epoch.

چرا استانداردسازی داده مهم است؟

اگر مقیاس ویژگی‌ها بسیار متفاوت باشد، مسیر Gradient Descent نامتعادل می‌شود.

برای مثال:

  • سن بین ۱۸ تا ۷۰
  • درآمد بین چند میلیون تا چند میلیارد
  • تعداد خرید بین صفر تا ۱۰۰

ویژگی درآمد می‌تواند به دلیل مقیاس بزرگ‌تر اثر نامتناسبی بر حرکت Optimizer داشته باشد.

Scikit-learn تأکید می‌کند که SGD نسبت به مقیاس ویژگی‌ها حساس است و استفاده از StandardScaler در Pipeline را توصیه می‌کند.

from sklearn.pipeline import (
    make_pipeline,
)
from sklearn.preprocessing import (
    StandardScaler,
)
from sklearn.linear_model import (
    SGDClassifier,
)


model = make_pipeline(
    StandardScaler(),
    SGDClassifier(
        random_state=42
    ),
)

Shuffle کردن داده‌ها

اگر داده‌ها بر اساس کلاس یا زمان مرتب شده باشند، Batchهای متوالی می‌توانند نماینده مناسبی از کل داده نباشند.

برای مثال، اگر تمام پیام‌های پرداخت در ابتدای فایل و تمام پیام‌های ارسال در انتهای آن باشند، مدل برای مدتی فقط یک کلاس را مشاهده می‌کند.

در آموزش عمومی باید داده‌های آموزشی را Shuffle کرد:

loader = DataLoader(
    dataset,
    batch_size=64,
    shuffle=True,
)

اما در تقسیم داده زمانی نباید آینده را به گذشته مخلوط کرد. ابتدا Train و Validation باید بر اساس زمان جدا شوند و سپس فقط داده آموزشی Shuffle شود.

مشکل Vanishing Gradient چیست؟

در بعضی شبکه‌های عمیق، گرادیان هنگام عبور از لایه‌های متعدد بسیار کوچک می‌شود. در نتیجه، لایه‌های ابتدایی تقریباً به‌روزرسانی نمی‌شوند.

نشانه‌های احتمالی:

  • Loss به‌کندی کاهش می‌یابد.
  • وزن لایه‌های ابتدایی تقریباً ثابت می‌ماند.
  • مدل با افزایش عمق بهتر نمی‌شود.

راهکارهای رایج:

  • استفاده از تابع فعال‌سازی مناسب
  • مقداردهی اولیه مناسب وزن‌ها
  • Normalization
  • اتصال‌های Residual
  • معماری مناسب‌تر
  • بررسی Learning Rate

مشکل Exploding Gradient چیست؟

گاهی گرادیان‌ها بیش از حد بزرگ می‌شوند و به‌روزرسانی پارامترها ناپایدار می‌شود.

نشانه‌ها:

  • افزایش ناگهانی Loss
  • ایجاد مقدار NaN
  • تغییر بسیار بزرگ وزن‌ها
  • شکست آموزش پس از چند مرحله

یکی از راهکارها Gradient Clipping است:

optimizer.zero_grad()

loss.backward()

torch.nn.utils.clip_grad_norm_(
    model.parameters(),
    max_norm=1.0,
)

optimizer.step()

Gradient Clipping باید همراه با بررسی علت اصلی مشکل استفاده شود. Learning Rate نامناسب، داده خراب یا معماری ناپایدار نیز می‌توانند عامل باشند.

Local Minimum و Saddle Point

سطح خطای یک مدل پیچیده می‌تواند بخش‌های متعددی داشته باشد:

  • نقاط کمینه محلی
  • نقاط زینی
  • نواحی تقریباً مسطح
  • دره‌های باریک
  • جهت‌های دارای انحنای متفاوت

در مدل‌های بزرگ، مشکل همیشه گیرکردن در یک Local Minimum نامناسب نیست. نقاط زینی و نواحی مسطح نیز می‌توانند سرعت آموزش را کاهش دهند.

Momentum و Optimizerهای تطبیقی می‌توانند عبور از بعضی از این نواحی را ساده‌تر کنند.

Regularization چه ارتباطی با Gradient Descent دارد؟

Regularization مدل را از یادگیری وزن‌های بیش از حد بزرگ یا الگوهای بسیار پیچیده بازمی‌دارد.

روش‌های رایج:

  • L1
  • L2
  • Elastic Net
  • Weight Decay
  • Dropout
  • Early Stopping

در Scikit-learn، مدل‌های مبتنی بر SGD از تنظیمات l1، l2 و elasticnet پشتیبانی می‌کنند.

model = SGDClassifier(
    loss="log_loss",
    penalty="elasticnet",
    alpha=0.0001,
    l1_ratio=0.15,
    random_state=42,
)

تنظیم پارامترهای SGD با GridSearchCV

from sklearn.model_selection import (
    GridSearchCV,
)


parameter_grid = {
    "sgdclassifier__alpha": [
        0.00001,
        0.0001,
        0.001,
    ],
    "sgdclassifier__penalty": [
        "l2",
        "l1",
        "elasticnet",
    ],
    "sgdclassifier__loss": [
        "log_loss",
        "hinge",
    ],
}

search = GridSearchCV(
    estimator=model,
    param_grid=parameter_grid,
    scoring="f1_macro",
    cv=5,
    n_jobs=-1,
)

search.fit(
    X_train,
    y_train,
)

print(
    search.best_params_
)

اگر Pipeline دارای نام‌های متفاوتی است، کلیدهای پارامتر باید با نام مرحله آن هماهنگ شوند.

آموزش آنلاین با Partial Fit

SGD می‌تواند داده‌ها را به‌صورت تدریجی دریافت کند. این قابلیت برای داده‌های جریانی یا مجموعه‌هایی که در حافظه جا نمی‌شوند مفید است.

import numpy as np

from sklearn.linear_model import (
    SGDClassifier,
)


classes = np.array(
    [
        "payment",
        "shipping",
        "technical",
        "other",
    ]
)

model = SGDClassifier(
    loss="log_loss",
    random_state=42,
)

for X_batch, y_batch in (
    training_batches
):
    model.partial_fit(
        X_batch,
        y_batch,
        classes=classes,
    )

در نخستین فراخوانی باید تمام کلاس‌های ممکن مشخص شوند. همچنین لازم است تغییر توزیع داده، کیفیت نسخه جدید و احتمال فراموشی الگوهای قبلی پایش شود.

چگونه متوجه شویم آموزش درست پیش می‌رود؟

در طول آموزش موارد زیر را ثبت کنید:

  • Training Loss
  • Validation Loss
  • معیار اصلی کسب‌وکار
  • Learning Rate
  • اندازه گرادیان
  • زمان هر Epoch
  • مصرف حافظه
  • تعداد نمونه پردازش‌شده
  • بهترین Checkpoint
  • دلیل توقف آموزش

حالت مناسب

Training Loss و Validation Loss تا حدی کاهش می‌یابند و معیار اصلی روی داده اعتبارسنجی بهتر می‌شود.

بیش‌برازش

Training Loss کاهش پیدا می‌کند، اما Validation Loss افزایش می‌یابد.

کم‌برازش

هر دو Loss بالا باقی می‌مانند و مدل الگوی داده را یاد نمی‌گیرد.

ناپایداری

Loss نوسان شدید دارد یا به مقدار نامعتبر تبدیل می‌شود.

خطاهای رایج هنگام استفاده از Gradient Descent

انتخاب Learning Rate بسیار بزرگ

این کار باعث نوسان یا شکست آموزش می‌شود.

انتخاب Learning Rate بسیار کوچک

مدل بسیار کند آموزش می‌بیند و ممکن است پیش از رسیدن به نتیجه مناسب متوقف شود.

استاندارد نکردن ویژگی‌ها

در مدل‌های حساس به مقیاس، حرکت Optimizer نامتعادل می‌شود.

فراموش‌کردن پاک‌سازی گرادیان‌ها

در PyTorch، گرادیان‌ها به‌صورت پیش‌فرض جمع می‌شوند. اگر این رفتار موردنظر نیست، باید پیش از هر مرحله پاک شوند.

ارزیابی فقط با Training Loss

کاهش خطای آموزش به‌تنهایی نشان‌دهنده عملکرد مناسب روی داده جدید نیست.

استفاده از Test Set برای تنظیم مدل

Test Set باید برای ارزیابی نهایی نگه داشته شود. تنظیم Hyperparameter با Validation Set انجام می‌شود.

Shuffle نکردن داده آموزشی

ترتیب نامناسب داده می‌تواند جهت به‌روزرسانی‌ها را منحرف کند.

ذخیره‌کردن آخرین مدل به‌جای بهترین مدل

بهترین عملکرد اعتبارسنجی ممکن است چند Epoch پیش از پایان آموزش اتفاق افتاده باشد.

نادیده‌گرفتن داده خراب

وجود مقدارهای نامعتبر، برچسب اشتباه یا نمونه‌های پرت می‌تواند Gradient را ناپایدار کند.

تغییر هم‌زمان چند پارامتر

اگر Learning Rate، Batch Size، Optimizer و معماری را هم‌زمان تغییر دهید، تشخیص علت بهبود یا افت دشوار می‌شود.

Gradient Descent در آموزش مدل‌های زبانی

مدل‌های زبانی بزرگ نیز با Optimizerهای مبتنی بر گرادیان آموزش می‌بینند. در هر مرحله، مدل تلاش می‌کند توکن بعدی یا خروجی موردنظر را بهتر پیش‌بینی کند و پارامترهای آن بر اساس خطا اصلاح می‌شوند.

آموزش یک مدل زبانی بزرگ از ابتدا به موارد زیر نیاز دارد:

  • داده بسیار زیاد
  • GPU یا شتاب‌دهنده‌های متعدد
  • حافظه بالا
  • آموزش توزیع‌شده
  • Checkpointing
  • مدیریت خطا و بازیابی
  • ارزیابی گسترده
  • هزینه محاسباتی قابل توجه

برای بیشتر کسب‌وکارها، آموزش مدل پایه از ابتدا منطقی نیست. استفاده از یک مدل آماده از طریق API، RAG، Prompt Engineering یا Fine-tuning محدود معمولاً مسیر عملی‌تری است.

چه ارتباطی میان Gradient Descent و API درواره وجود دارد؟

کاربر API معمولاً Gradient Descent را مستقیماً اجرا نمی‌کند. مدل‌های موجود قبلاً با فرایندهای بهینه‌سازی آموزش دیده‌اند و از طریق API برای Inference در دسترس قرار می‌گیرند.

آدرس پایه API درواره:

https://api.darvareh.ir/v1

یک درخواست ساده:

import os

from openai import OpenAI


client = OpenAI(
    api_key=os.environ[
        "DARVAREH_API_KEY"
    ],
    base_url=(
        "https://api.darvareh.ir/v1"
    ),
)

response = (
    client.chat.completions.create(
        model=os.environ[
            "DARVAREH_MODEL"
        ],
        messages=[
            {
                "role": "user",
                "content": (
                    "گرادیان نزولی را "
                    "با یک مثال ساده "
                    "توضیح بده."
                ),
            }
        ],
    )
)

print(
    response
    .choices[0]
    .message
    .content
)

استفاده از API به این معناست که کسب‌وکار می‌تواند از قابلیت مدل آموزش‌دیده استفاده کند، بدون اینکه زیرساخت آموزش آن را مدیریت کند.

چه زمانی مدل را آموزش دهیم و چه زمانی از API استفاده کنیم؟

نیازمسیر مناسب
پیش‌بینی عددی با داده داخلی ساختاریافتهآموزش مدل سبک محلی
طبقه‌بندی ساده متن با داده کافیمدل محلی مانند SGDClassifier
تولید متن و تحلیل پیچیدهمدل آماده از طریق API
پاسخ بر اساس اسناد اختصاصیRAG همراه با مدل API
تغییر سبک و قالب خروجیPrompt یا Fine-tuning محدود
ساخت مدل پایه از ابتدافقط با داده، سرمایه و تیم تخصصی کافی
نمونه اولیه سریعاستفاده از API درواره

در بسیاری از محصولات، معماری ترکیبی بهترین انتخاب است. مدل‌های کوچک و قطعی برای وظایف پرتکرار اجرا می‌شوند و مدل‌های زبانی از طریق API برای مسائل پیچیده‌تر استفاده می‌شوند.

پایش فرایند آموزش در Production

برای آموزش‌های تکرارشونده، اطلاعات زیر را ثبت کنید:

  • نسخه داده
  • نسخه کد
  • تنظیمات Optimizer
  • Learning Rate
  • Batch Size
  • تعداد Epoch
  • مقدار اولیه Seed
  • معیارهای Train و Validation
  • بهترین Checkpoint
  • زمان و هزینه آموزش
  • سخت‌افزار استفاده‌شده
  • نسخه کتابخانه‌ها
  • نتیجه ارزیابی نهایی

ابزارهای رایج برای ثبت آزمایش‌ها عبارت‌اند از:

  • MLflow
  • Weights & Biases
  • TensorBoard
  • ClearML
  • Aim
  • DVC

ثبت آزمایش‌ها کمک می‌کند نتایج قابل تکرار باشند و تیم بتواند دلیل تفاوت نسخه‌های مدل را پیدا کند.

چه زمانی Gradient Descent انتخاب مناسبی است؟

Gradient Descent زمانی مناسب است که:

  • تابع خطا نسبت به پارامترها قابل بهینه‌سازی باشد.
  • تعداد پارامترها زیاد باشد.
  • داده در حجم بالا وجود داشته باشد.
  • محاسبه مستقیم بهترین پارامترها دشوار باشد.
  • مدل باید به‌صورت مرحله‌ای آموزش ببیند.
  • از شبکه عصبی یا مدل خطی بزرگ استفاده شود.

چه زمانی بهینه‌ساز دیگری لازم است؟

Gradient Descent یک خانواده کلی از روش‌ها است و نسخه مناسب آن به مسئله بستگی دارد.

ممکن است نیاز باشد موارد زیر را بررسی کنید:

  • SGD با Momentum
  • Adam
  • AdamW
  • RMSprop
  • Adagrad
  • Adafactor
  • روش‌های بدون گرادیان
  • بهینه‌سازی بیزی
  • الگوریتم ژنتیک
  • Particle Swarm Optimization

برای تنظیم Hyperparameterهایی مانند Learning Rate، Batch Size یا تعداد لایه‌ها، خود Gradient Descent همیشه کافی نیست. روش‌هایی مانند Grid Search، Random Search و Bayesian Optimization می‌توانند مناسب‌تر باشند.

پرسش‌های متداول

Gradient Descent چیست؟

Gradient Descent روشی برای کاهش خطای مدل از طریق اصلاح مرحله‌ای پارامترها در جهت مناسب است.

Gradient به چه معنا است؟

Gradient جهت و شدت تغییر خطا نسبت به پارامترهای مدل را نشان می‌دهد.

Learning Rate چیست؟

Learning Rate اندازه تغییر پارامترها در هر مرحله آموزش را کنترل می‌کند.

تفاوت SGD و Gradient Descent چیست؟

در Batch Gradient Descent تمام داده برای یک به‌روزرسانی استفاده می‌شود، اما SGD از یک نمونه و Mini-batch از گروه کوچکی از نمونه‌ها استفاده می‌کند.

Mini-batch چیست؟

Mini-batch بخشی از داده آموزشی است که در یک مرحله وارد مدل می‌شود و برای محاسبه گرادیان استفاده می‌شود.

Epoch چیست؟

یک Epoch یعنی مدل تمام داده آموزشی را یک بار مشاهده کرده است.

تفاوت Gradient Descent و Backpropagation چیست؟

Backpropagation گرادیان‌ها را محاسبه می‌کند و Gradient Descent یا Optimizer از آن‌ها برای اصلاح پارامترها استفاده می‌کند.

تفاوت SGD و Adam چیست؟

SGD از روش ساده‌تری برای به‌روزرسانی استفاده می‌کند. Adam اندازه به‌روزرسانی هر پارامتر را با توجه به سابقه گرادیان‌ها تطبیق می‌دهد.

بهترین Learning Rate چقدر است؟

مقدار ثابتی برای همه مدل‌ها وجود ندارد. این مقدار به Optimizer، معماری، Batch Size و داده بستگی دارد و باید با آزمایش انتخاب شود.

چرا Loss به NaN تبدیل می‌شود؟

Learning Rate بزرگ، داده نامعتبر، گرادیان انفجاری، عملیات عددی ناپایدار یا مقداردهی اولیه نامناسب می‌توانند عامل باشند.

آیا برای استفاده از API هوش مصنوعی باید Gradient Descent بدانیم؟

برای ارسال درخواست به API لازم نیست، اما شناخت آن به درک آموزش، Fine-tuning، رفتار مدل و تنظیم مدل‌های محلی کمک می‌کند.

جمع‌بندی

گرادیان نزولی یکی از پایه‌های آموزش مدل‌های یادگیری ماشین و شبکه‌های عصبی است. این روش با محاسبه جهت کاهش خطا، پارامترهای مدل را به‌صورت مرحله‌ای اصلاح می‌کند.

برای اجرای موفق Gradient Descent:

۱. تابع خطای متناسب با مسئله انتخاب کنید.

۲. Learning Rate را با دقت تنظیم کنید.

۳. داده‌های عددی را در صورت نیاز استاندارد کنید.

۴. داده آموزشی را پس از تقسیم مناسب Shuffle کنید.

۵. Batch Size را با توجه به حافظه و پایداری انتخاب کنید.

۶. Training Loss و Validation Loss را هم‌زمان پایش کنید.

۷. از Early Stopping و ذخیره بهترین Checkpoint استفاده کنید.

۸. برای گرادیان‌های ناپایدار، Gradient Clipping را بررسی کنید.

۹. SGD، Momentum، Adam و AdamW را روی داده واقعی مقایسه کنید.

۱۰. نسخه داده، کد و تنظیمات آموزش را ثبت کنید.

اگر هدف شما ساخت یک محصول مبتنی بر هوش مصنوعی است، همیشه لازم نیست مدل بزرگی را از ابتدا آموزش دهید. می‌توانید مدل‌های سبک اختصاصی را برای وظایف محدود آموزش دهید و قابلیت‌های پیشرفته تولید و تحلیل را از طریق API درواره به نرم‌افزار خود اضافه کنید.

مقالات مرتبط

منابع

این مقاله صرفاً با هدف آموزش و اطلاع‌رسانی تهیه شده است. پیش از استفاده عملی، مستندات رسمی سرویس‌ها و صفحه سلب مسئولیت را مطالعه کنید.

Read more