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

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

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

ماشین بردار پشتیبان یا Support Vector Machine که به‌اختصار SVM نامیده می‌شود، یکی از الگوریتم‌های شناخته‌شده یادگیری ماشین برای طبقه‌بندی، رگرسیون و تشخیص نقاط غیرعادی است.

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

SVM به‌خصوص در این مسائل کاربرد دارد:

  • طبقه‌بندی متن
  • دسته‌بندی پیام‌های پشتیبانی
  • تشخیص هرزنامه
  • تحلیل احساسات
  • تشخیص تصویر با ویژگی‌های استخراج‌شده
  • طبقه‌بندی داده‌های زیستی
  • پیش‌بینی مقدار عددی با SVR
  • تشخیص ناهنجاری با One-Class SVM
  • دسته‌بندی Embeddingهای متنی

در این مقاله ابتدا مفاهیم ریاضی SVM را به زبان ساده توضیح می‌دهیم، سپس مدل را با Scikit-learn پیاده‌سازی می‌کنیم و در پایان از Embeddingهای API درواره برای ساخت یک طبقه‌بند متن فارسی استفاده خواهیم کرد.

SVM چیست؟

SVM یک الگوریتم یادگیری نظارت‌شده است که تلاش می‌کند بهترین مرز تصمیم را میان کلاس‌ها پیدا کند.

فرض کنید دو گروه نقطه داریم:

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

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

این فاصله Margin نام دارد.

نمونه‌هایی که نزدیک‌ترین فاصله را با مرز تصمیم دارند، Support Vector یا بردار پشتیبان نامیده می‌شوند. این نقاط نقش تعیین‌کننده‌ای در موقعیت مرز دارند.

Scikit-learn خانواده SVM را برای طبقه‌بندی، رگرسیون و تشخیص نقاط غیرعادی ارائه می‌کند.

ابرصفحه یا Hyperplane چیست؟

در یک مسئله خطی دودویی، مرز تصمیم با رابطه زیر نمایش داده می‌شود:

wTx+b=0w^Tx+b=0

در این رابطه:

  • x بردار ویژگی‌های ورودی است.
  • w جهت و شیب مرز را تعیین می‌کند.
  • b مقدار جابه‌جایی مرز است.

مدل برای پیش‌بینی می‌تواند علامت عبارت زیر را بررسی کند:

f(x)=wTx+bf(x)=w^Tx+b

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

Margin چیست؟

Margin فاصله میان مرز تصمیم و نزدیک‌ترین نمونه‌های آموزشی است.

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

برای SVM خطی، عرض Margin با اندازه بردار w ارتباط معکوس دارد:

Margin∝1∥w∥Margin \propto \frac{1}{\|w\|}

بنابراین مسئله بهینه‌سازی SVM شامل کوچک‌کردن اندازه w در کنار طبقه‌بندی صحیح نمونه‌ها است.

Support Vector چیست؟

Support Vectorها نمونه‌هایی هستند که روی حاشیه یا داخل محدوده Margin قرار گرفته‌اند و موقعیت مرز تصمیم را تعیین می‌کنند.

اگر نمونه‌ای دور از مرز باشد، تغییر کوچک آن معمولاً مرز را تغییر نمی‌دهد. اما جابه‌جایی یک Support Vector می‌تواند موقعیت Hyperplane را تغییر دهد.

نام Support Vector Machine نیز از همین نمونه‌های پشتیبان گرفته شده است.

Hard Margin SVM چیست؟

در Hard Margin فرض می‌شود داده‌ها کاملاً خطی و بدون خطا قابل‌جداسازی هستند.

محدودیت مدل:

yi(wTxi+b)≥1y_i(w^Tx_i+b)\geq1

برای تمام نمونه‌ها باید این شرط برقرار باشد.

Hard Margin در داده واقعی معمولاً انتخاب مناسبی نیست؛ زیرا:

  • داده‌ها ممکن است نویز داشته باشند.
  • کلاس‌ها می‌توانند هم‌پوشانی داشته باشند.
  • وجود یک Outlier مرز را به‌شدت تغییر می‌دهد.
  • جداسازی کامل همیشه امکان‌پذیر نیست.

Soft Margin SVM چیست؟

Soft Margin اجازه می‌دهد بعضی نمونه‌ها داخل Margin قرار بگیرند یا اشتباه طبقه‌بندی شوند.

برای این منظور متغیرهای خطا یا Slack Variables اضافه می‌شوند:

yi(wTxi+b)≥1−ξiy_i(w^Tx_i+b)\geq1-\xi_i

تابع هدف:

min⁡w,b,ξ12∥w∥2+C∑iξi\min_{w,b,\xi} \frac{1}{2}\|w\|^2 + C\sum_i\xi_i

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

  • بزرگ‌بودن Margin
  • کاهش خطاهای آموزشی

پارامتر C در SVM چیست؟

C میزان جریمه خطا را تعیین می‌کند.

C بزرگ

مدل تلاش بیشتری برای طبقه‌بندی صحیح نمونه‌های آموزشی انجام می‌دهد:

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

C کوچک

مدل خطاهای بیشتری را می‌پذیرد:

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

مقدار مناسب C باید با Cross-validation و داده واقعی انتخاب شود.

اگر داده خطی جدا نشود چه می‌شود؟

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

برای مثال، ممکن است یک کلاس به‌شکل دایره‌ای داخل کلاس دیگر قرار گرفته باشد. در فضای دوبعدی هیچ خطی این دو گروه را به‌خوبی جدا نمی‌کند.

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

اما محاسبه صریح این تبدیل می‌تواند پرهزینه باشد. SVM از Kernel Trick استفاده می‌کند تا شباهت نمونه‌ها را در فضای جدید، بدون محاسبه مستقیم تمام مختصات آن فضا به دست آورد.

Kernel Trick چیست؟

Kernel تابعی است که شباهت دو نمونه را در یک فضای ویژگی، مستقیم یا غیرمستقیم، محاسبه می‌کند:

K(xi,xj)=ϕ(xi)Tϕ(xj)K(x_i,x_j)=\phi(x_i)^T\phi(x_j)

در این رابطه φ تبدیل داده به فضای جدید است.

با Kernel Trick لازم نیست همیشه مقدار φ(x) به‌طور صریح محاسبه شود.

Scikit-learn برای SVC کرنل‌هایی مانند linear، poly، rbf، sigmoid و Kernel ازپیش‌محاسبه‌شده را پشتیبانی می‌کند.

انواع Kernel در SVM

Linear Kernel

K(x,z)=xTzK(x,z)=x^Tz

کرنل خطی برای داده‌هایی مناسب است که مرز تقریباً خطی دارند.

کاربردهای متداول:

  • طبقه‌بندی متن با TF-IDF
  • داده‌های دارای ویژگی‌های زیاد
  • داده‌های Sparse
  • مجموعه داده‌های بزرگ‌تر

در مسائل متنی، تعداد ویژگی‌ها ممکن است ده‌ها هزار کلمه یا عبارت باشد. مدل خطی در چنین فضاهایی اغلب خط مبنای قدرتمندی ایجاد می‌کند.

Polynomial Kernel

K(x,z)=(γxTz+r)dK(x,z)=(\gamma x^Tz+r)^d

پارامترهای مهم:

  • degree: درجه چندجمله‌ای
  • gamma: مقیاس اثر نمونه‌ها
  • coef0: جمله مستقل

Polynomial Kernel می‌تواند تعامل‌های چندجمله‌ای میان ویژگی‌ها را مدل کند، اما تنظیم آن دشوارتر است.

RBF Kernel

K(x,z)=exp⁡(−γ∥x−z∥2)K(x,z)= \exp(-\gamma\|x-z\|^2)

RBF یا Gaussian Kernel یکی از رایج‌ترین گزینه‌ها برای مرزهای غیرخطی است.

این Kernel می‌تواند مرزهای انعطاف‌پذیری بسازد، اما به تنظیم صحیح C و gamma نیاز دارد. مستندات Scikit-learn نیز این دو پارامتر را برای SVM با کرنل RBF از تنظیمات اصلی معرفی می‌کند.

Sigmoid Kernel

K(x,z)=tanh⁡(γxTz+r)K(x,z)= \tanh(\gamma x^Tz+r)

رفتار این Kernel تا حدی به تابع فعال‌سازی Sigmoid در شبکه‌های عصبی شباهت دارد، اما معمولاً انتخاب پیش‌فرض نیست.

پارامتر gamma در SVM چیست؟

gamma میزان اثر هر نمونه آموزشی را در Kernelهایی مانند RBF کنترل می‌کند.

gamma کوچک

اثر هر نمونه محدوده وسیع‌تری دارد:

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

gamma بزرگ

اثر هر نمونه به ناحیه نزدیک خودش محدود می‌شود:

  • مرز تصمیم پیچیده‌تر می‌شود.
  • مدل جزئیات بیشتری یاد می‌گیرد.
  • احتمال بیش‌برازش افزایش پیدا می‌کند.

به‌صورت شهودی، gamma کوچک یعنی شعاع اثر بزرگ و gamma بزرگ یعنی شعاع اثر کوچک. مستندات Scikit-learn همین رابطه را در مثال پارامترهای RBF توضیح می‌دهد.

تعامل C و gamma

C و gamma باید هم‌زمان تنظیم شوند.

وضعیتنتیجه احتمالی
C کوچک و gamma کوچکمدل ساده و احتمال کم‌برازش
C بزرگ و gamma بزرگمرز پیچیده و احتمال بیش‌برازش
C بزرگ و gamma کوچکجریمه خطا زیاد، مرز نسبتاً نرم
C کوچک و gamma بزرگاثر محلی زیاد با تحمل خطا

بهترین ترکیب به توزیع داده وابسته است و باید با Cross-validation انتخاب شود.

چرا مقیاس‌بندی برای SVM مهم است؟

SVM بر فاصله، ضرب داخلی و اندازه ویژگی‌ها تکیه دارد.

فرض کنید دو ویژگی داریم:

سن: 18 تا 70
درآمد سالانه: 100,000,000 تا 5,000,000,000

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

روش رایج استفاده از StandardScaler است:

z=x−μσz=\frac{x-\mu}{\sigma}

Scaler باید فقط روی داده آموزش Fit شود. بهترین روش قرار دادن آن داخل Pipeline است.

SVM برای طبقه‌بندی دودویی

در طبقه‌بندی دودویی دو کلاس داریم:

مثبت / منفی
فروش / پشتیبانی
عادی / غیرعادی
ریزش / ماندگاری

SVM یک مرز تصمیم میان این دو کلاس یاد می‌گیرد.

خروجی پیش‌فرض معمولاً برچسب کلاس یا فاصله نمونه از مرز تصمیم است.

SVM برای طبقه‌بندی چندکلاسه

SVM در اصل یک روش دودویی است، اما می‌توان آن را برای چند کلاس توسعه داد.

راهبردهای رایج:

One-vs-Rest

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

اگر چهار کلاس داشته باشیم، چهار مدل خواهیم داشت.

One-vs-One

برای هر جفت کلاس یک مدل جداگانه ساخته می‌شود.

برای K کلاس:

K(K−1)2\frac{K(K-1)}{2}

مدل ساخته می‌شود.

در Scikit-learn، رفتار دقیق به Estimator انتخاب‌شده بستگی دارد. SVC به‌صورت داخلی از راهبرد چندکلاسه مبتنی بر مدل‌های جفتی استفاده می‌کند، درحالی‌که LinearSVC پیاده‌سازی متفاوت و مناسب‌تری برای بسیاری از داده‌های بزرگ و Sparse دارد.

SVC و LinearSVC چه تفاوتی دارند؟

SVC

کلاس SVC از Kernelهای مختلف پشتیبانی می‌کند:

from sklearn.svm import SVC

برای مرزهای غیرخطی و مجموعه داده‌های کوچک یا متوسط مناسب است.

آموزش SVC با افزایش تعداد نمونه‌ها می‌تواند پرهزینه شود. مستندات Scikit-learn هشدار می‌دهد که زمان Fit آن حداقل به‌صورت درجه دوم نسبت به تعداد نمونه‌ها رشد می‌کند و برای ده‌ها هزار نمونه یا بیشتر ممکن است عملی نباشد.

LinearSVC

from sklearn.svm import LinearSVC

برای مرز خطی، داده Sparse و مجموعه داده بزرگ‌تر مناسب‌تر است.

کاربرد متداول:

  • طبقه‌بندی متن
  • TF-IDF
  • داده با ابعاد زیاد
  • تعداد نمونه بیشتر

LinearSVC مستقیماً predict_proba ارائه نمی‌کند. اگر احتمال کالیبره‌شده لازم باشد، باید از روش‌هایی مانند CalibratedClassifierCV استفاده شود.

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

در این مثال از مجموعه داده Breast Cancer موجود در Scikit-learn استفاده می‌کنیم.

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

نصب کتابخانه‌ها

pip install scikit-learn pandas matplotlib

بارگذاری و تقسیم داده

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split


dataset = load_breast_cancer(
    as_frame=True,
)

X = dataset.data
y = dataset.target

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

ساخت Pipeline

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC


model = Pipeline(
    [
        (
            "scaler",
            StandardScaler(),
        ),
        (
            "svm",
            SVC(
                kernel="rbf",
                C=10.0,
                gamma="scale",
                class_weight="balanced",
                probability=True,
                random_state=42,
            ),
        ),
    ]
)

قرار دادن Scaler داخل Pipeline باعث می‌شود عملیات Fit فقط با داده آموزش انجام شود و هنگام Cross-validation نیز نشت داده رخ ندهد.

آموزش مدل

model.fit(
    X_train,
    y_train,
)

ارزیابی

from sklearn.metrics import (
    classification_report,
    confusion_matrix,
    roc_auc_score,
)


predictions = model.predict(X_test)

probabilities = model.predict_proba(
    X_test
)[:, 1]

print(
    classification_report(
        y_test,
        predictions,
        target_names=dataset.target_names,
    )
)

print(
    confusion_matrix(
        y_test,
        predictions,
    )
)

print(
    "ROC AUC:",
    roc_auc_score(
        y_test,
        probabilities,
    ),
)

چرا فقط Accuracy کافی نیست؟

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

معیارهای مهم:

  • Precision
  • Recall
  • F1 Score
  • ROC AUC
  • PR AUC
  • Confusion Matrix
  • هزینه False Positive
  • هزینه False Negative

معیار مناسب باید بر اساس مسئله کسب‌وکار انتخاب شود.

from sklearn.model_selection import (
    GridSearchCV,
    StratifiedKFold,
)


parameter_grid = {
    "svm__C": [
        0.01,
        0.1,
        1,
        10,
        100,
    ],
    "svm__gamma": [
        0.0001,
        0.001,
        0.01,
        0.1,
        1,
        "scale",
    ],
}

cross_validation = StratifiedKFold(
    n_splits=5,
    shuffle=True,
    random_state=42,
)

search = GridSearchCV(
    estimator=model,
    param_grid=parameter_grid,
    scoring="roc_auc",
    cv=cross_validation,
    n_jobs=-1,
    refit=True,
)

search.fit(
    X_train,
    y_train,
)

print("Best score:", search.best_score_)
print("Best parameters:", search.best_params_)

مقادیر C و gamma معمولاً باید روی مقیاس لگاریتمی جست‌وجو شوند.

برای فضای بزرگ‌تر می‌توان از Randomized Search یا Optuna استفاده کرد.

تنظیم SVM با Optuna

import optuna

from sklearn.model_selection import (
    cross_val_score,
)


def objective(trial):
    c_value = trial.suggest_float(
        "C",
        1e-3,
        1e3,
        log=True,
    )

    gamma_value = trial.suggest_float(
        "gamma",
        1e-6,
        1e0,
        log=True,
    )

    candidate = Pipeline(
        [
            (
                "scaler",
                StandardScaler(),
            ),
            (
                "svm",
                SVC(
                    kernel="rbf",
                    C=c_value,
                    gamma=gamma_value,
                    class_weight="balanced",
                ),
            ),
        ]
    )

    scores = cross_val_score(
        candidate,
        X_train,
        y_train,
        cv=cross_validation,
        scoring="roc_auc",
        n_jobs=-1,
    )

    return scores.mean()


study = optuna.create_study(
    direction="maximize",
)

study.optimize(
    objective,
    n_trials=60,
)

print(study.best_value)
print(study.best_params)

برای آموزش کامل این روش، مقاله بهینه‌سازی بیزی و Optuna را مطالعه کنید.

SVR چیست؟

Support Vector Regression یا SVR نسخه رگرسیونی SVM است.

در SVR هدف پیدا کردن تابعی است که بیشتر نمونه‌ها در یک لوله با عرض ε اطراف آن قرار بگیرند.

نقاط داخل این محدوده خطای مؤثری ندارند و نقاط خارج آن جریمه می‌شوند.

پیاده‌سازی:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVR


regression_model = Pipeline(
    [
        (
            "scaler",
            StandardScaler(),
        ),
        (
            "svr",
            SVR(
                kernel="rbf",
                C=10.0,
                gamma="scale",
                epsilon=0.1,
            ),
        ),
    ]
)

پارامترهای مهم SVR:

  • C: میزان جریمه خطا
  • gamma: شعاع اثر نمونه‌ها
  • epsilon: عرض محدوده بدون جریمه
  • kernel: نوع Kernel

One-Class SVM چیست؟

One-Class SVM برای تشخیص ناهنجاری یا Novelty Detection استفاده می‌شود.

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

from sklearn.svm import OneClassSVM


anomaly_model = OneClassSVM(
    kernel="rbf",
    gamma="scale",
    nu=0.05,
)

anomaly_model.fit(
    normal_training_data
)

predictions = anomaly_model.predict(
    new_data
)

خروجی معمول:

1  = عادی
-1 = غیرعادی

استفاده از One-Class SVM به معنی اثبات تقلب، خرابی یا مشکل نیست. خروجی فقط نشان می‌دهد نمونه با الگوهای آموزشی تفاوت دارد.

طبقه‌بندی متن با SVM و TF-IDF

یکی از کاربردهای مهم SVM، طبقه‌بندی متن است.

متن ابتدا باید به بردار عددی تبدیل شود. TF-IDF میزان اهمیت هر واژه را بر اساس فراوانی آن در یک سند و کل مجموعه اسناد محاسبه می‌کند.

مستندات Scikit-learn نمونه رسمی طبقه‌بندی اسناد را با ویژگی‌های Sparse و وزن‌دهی TF-IDF ارائه می‌کند.

فرض کنید فایل tickets.csv شامل دو ستون باشد:

text,category
"مبلغ از حساب کم شد ولی اعتبار اضافه نشد",billing
"چطور مدل را در کد تغییر بدهم؟",technical
"برای خرید سازمانی فاکتور می‌خواهم",sales

ساخت مدل طبقه‌بندی متن فارسی

import pandas as pd

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


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

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

text_classifier = Pipeline(
    [
        (
            "tfidf",
            TfidfVectorizer(
                ngram_range=(1, 2),
                min_df=2,
                max_df=0.95,
                sublinear_tf=True,
            ),
        ),
        (
            "classifier",
            LinearSVC(
                C=1.0,
                class_weight="balanced",
            ),
        ),
    ]
)

text_classifier.fit(
    X_train,
    y_train,
)

predictions = text_classifier.predict(
    X_test
)

print(
    classification_report(
        y_test,
        predictions,
    )
)

پیش‌بینی پیام جدید

messages = [
    "برای اتصال به API نمونه کد پایتون می‌خواهم",
    "پرداخت کردم ولی کیف پول شارژ نشده است",
]

categories = text_classifier.predict(
    messages
)

for message, category in zip(
    messages,
    categories,
):
    print(category, message)

محدودیت TF-IDF برای متن فارسی

TF-IDF بر واژه‌ها و الگوهای ظاهری متن تکیه دارد. مشکلات احتمالی:

  • تفاوت «می شود» و «می‌شود»
  • حروف عربی و فارسی مانند ي و ی
  • حروف ك و ک
  • نیم‌فاصله
  • غلط املایی
  • Pinglish
  • مترادف‌ها
  • وابستگی معنایی
  • جمله‌بندی متفاوت

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

نمونه ساده:

import re


def normalize_persian(text):
    text = str(text)

    text = text.replace("ي", "ی")
    text = text.replace("ك", "ک")

    text = re.sub(
        r"\s+",
        " ",
        text,
    )

    return text.strip()

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

طبقه‌بندی متن با Embedding و SVM

Embedding متن را به یک بردار عددی متراکم تبدیل می‌کند که بخشی از معنای آن را نمایش می‌دهد.

در این معماری:

متن فارسی
    ↓
مدل Embedding
    ↓
بردار عددی
    ↓
Linear SVM
    ↓
دسته‌بندی

مزیت احتمالی نسبت به TF-IDF این است که متن‌هایی با معنای نزدیک، حتی اگر کلمات دقیقاً یکسانی نداشته باشند، می‌توانند بردارهای نزدیک‌تری داشته باشند.

برای آشنایی بیشتر، مقاله Embedding چیست؟ را مطالعه کنید.

دریافت Embedding از API درواره

ابتدا کتابخانه‌ها را نصب کنید:

pip install openai scikit-learn pandas joblib

متغیرهای محیطی:

export DARVAREH_API_KEY="YOUR_API_KEY"
export DARVAREH_EMBEDDING_MODEL="YOUR_EMBEDDING_MODEL_ID"

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

ساخت Client:

import os

from openai import OpenAI


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

EMBEDDING_MODEL = os.environ[
    "DARVAREH_EMBEDDING_MODEL"
]

ساخت Embedding به‌صورت گروهی

def create_embeddings(
    texts,
    batch_size=64,
):
    vectors = []

    for start in range(
        0,
        len(texts),
        batch_size,
    ):
        batch = texts[
            start:start + batch_size
        ]

        response = client.embeddings.create(
            model=EMBEDDING_MODEL,
            input=batch,
        )

        ordered_items = sorted(
            response.data,
            key=lambda item: item.index,
        )

        vectors.extend(
            item.embedding
            for item in ordered_items
        )

    return vectors

مرتب‌سازی بر اساس index کمک می‌کند ترتیب بردارها با ترتیب متن‌های ورودی هماهنگ بماند.

آموزش SVM روی Embeddingهای فارسی

import numpy as np
import pandas as pd

from sklearn.metrics import (
    classification_report,
)
from sklearn.model_selection import (
    train_test_split,
)
from sklearn.preprocessing import (
    LabelEncoder,
    Normalizer,
)
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC


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

texts = [
    normalize_persian(text)
    for text in data["text"].tolist()
]

embeddings = np.asarray(
    create_embeddings(texts),
    dtype=np.float32,
)

label_encoder = LabelEncoder()

labels = label_encoder.fit_transform(
    data["category"]
)

(
    X_train,
    X_test,
    y_train,
    y_test,
) = train_test_split(
    embeddings,
    labels,
    test_size=0.2,
    stratify=labels,
    random_state=42,
)

embedding_classifier = Pipeline(
    [
        (
            "normalize",
            Normalizer(norm="l2"),
        ),
        (
            "classifier",
            LinearSVC(
                C=1.0,
                class_weight="balanced",
            ),
        ),
    ]
)

embedding_classifier.fit(
    X_train,
    y_train,
)

predictions = embedding_classifier.predict(
    X_test
)

print(
    classification_report(
        y_test,
        predictions,
        target_names=label_encoder.classes_,
    )
)

پیش‌بینی دسته پیام جدید

def classify_messages(messages):
    normalized_messages = [
        normalize_persian(message)
        for message in messages
    ]

    vectors = np.asarray(
        create_embeddings(
            normalized_messages
        ),
        dtype=np.float32,
    )

    encoded_predictions = (
        embedding_classifier.predict(vectors)
    )

    return label_encoder.inverse_transform(
        encoded_predictions
    )


messages = [
    "چطور API را به برنامه پایتون وصل کنم؟",
    "پرداخت انجام شده ولی موجودی اضافه نشده",
]

categories = classify_messages(messages)

for message, category in zip(
    messages,
    categories,
):
    print({
        "message": message,
        "category": category,
    })

ذخیره مدل آموزش‌دیده

import joblib


joblib.dump(
    {
        "classifier": embedding_classifier,
        "label_encoder": label_encoder,
        "embedding_model": EMBEDDING_MODEL,
    },
    "ticket_classifier.joblib",
)

بارگذاری:

artifacts = joblib.load(
    "ticket_classifier.joblib"
)

embedding_classifier = artifacts[
    "classifier"
]

label_encoder = artifacts[
    "label_encoder"
]

فایل‌های مدل را فقط از منبع مورداعتماد بارگذاری کنید؛ زیرا قالب‌هایی مانند Pickle و Joblib برای ورودی ناشناس ایمن نیستند.

چرا شناسه مدل Embedding باید ذخیره شود؟

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

همراه هر نسخه طبقه‌بند این موارد را ذخیره کنید:

  • شناسه دقیق مدل Embedding
  • نسخه داده آموزشی
  • نسخه نرمال‌ساز فارسی
  • نسخه Label Encoder
  • تاریخ آموزش
  • معیارهای ارزیابی
  • پارامترهای SVM
  • ابعاد بردار
  • نسخه کتابخانه‌ها

مقایسه TF-IDF و Embedding برای SVM

ویژگیTF-IDF + SVMEmbedding + SVM
نیاز به APIندارددر این مثال دارد
درک شباهت معناییمحدودمعمولاً بهتر
سرعت آموزش طبقه‌بندبالابالا
هزینه تولید ویژگیکموابسته به مدل
کار با واژه‌های دقیققویمناسب
ابعاد بردارمعمولاً بسیار زیادمتراکم و ثابت
سازگاری با متن جدیدوابسته به واژگانمعمولاً بهتر
استقرار کاملاً محلیسادهنیازمند مدل محلی یا API
مدیریت نسخهواژگان و Vectorizerمدل Embedding و طبقه‌بند

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

استفاده از SVM برای مسیریابی درخواست‌ها

طبقه‌بند SVM می‌تواند دسته پیام را پیش‌بینی کند:

sales
technical
billing
feedback
other

سپس نرم‌افزار می‌تواند برای هر دسته:

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

نمونه:

MODEL_BY_CATEGORY = {
    "sales": os.environ["DARVAREH_FAST_MODEL"],
    "technical": os.environ[
        "DARVAREH_QUALITY_MODEL"
    ],
    "billing": os.environ[
        "DARVAREH_FAST_MODEL"
    ],
    "feedback": os.environ[
        "DARVAREH_FAST_MODEL"
    ],
    "other": os.environ[
        "DARVAREH_QUALITY_MODEL"
    ],
}

انتخاب مدل:

def select_model_for_message(message):
    category = classify_messages(
        [message]
    )[0]

    return (
        category,
        MODEL_BY_CATEGORY[category],
    )

ارسال درخواست:

def answer_message(message):
    category, model = (
        select_model_for_message(message)
    )

    response = client.chat.completions.create(
        model=model,
        temperature=0.2,
        max_tokens=600,
        messages=[
            {
                "role": "system",
                "content": (
                    "به پیام کاربر به زبان فارسی، "
                    "دقیق و حرفه‌ای پاسخ بده."
                ),
            },
            {
                "role": "user",
                "content": message,
            },
        ],
    )

    return {
        "category": category,
        "model": model,
        "answer": (
            response.choices[0].message.content
            or ""
        ),
    }

کلید API و منطق مسیریابی باید در سمت سرور نگهداری شوند.

اطمینان مدل در LinearSVC

LinearSVC مقدار فاصله از مرز را از طریق decision_function ارائه می‌کند:

scores = (
    embedding_classifier.decision_function(
        new_vectors
    )
)

این مقادیر احتمال نیستند. برای دریافت احتمال کالیبره‌شده می‌توان مدل را با CalibratedClassifierCV ترکیب کرد:

from sklearn.calibration import (
    CalibratedClassifierCV,
)


calibrated_classifier = (
    CalibratedClassifierCV(
        estimator=LinearSVC(
            C=1.0,
            class_weight="balanced",
        ),
        method="sigmoid",
        cv=5,
    )
)

پس از آموزش:

probabilities = (
    calibrated_classifier.predict_proba(
        X_test
    )
)

کالیبراسیون باید روی داده مناسب انجام و جداگانه ارزیابی شود.

مدیریت کلاس‌های نامتوازن

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

راهکارها:

  • استفاده از class_weight="balanced"
  • جمع‌آوری نمونه بیشتر برای کلاس‌های کمیاب
  • استفاده از Macro F1
  • بررسی Recall هر کلاس
  • Stratified Split
  • ادغام کلاس‌های بسیار مبهم
  • تعریف دسته other
  • بازبینی خطاهای مدل
  • تنظیم Threshold پس از کالیبراسیون

فقط افزایش مصنوعی نمونه‌ها بدون بررسی کیفیت برچسب‌ها ممکن است نتیجه را بدتر کند.

Confusion Matrix برای تحلیل خطا

from sklearn.metrics import (
    ConfusionMatrixDisplay,
)
import matplotlib.pyplot as plt


ConfusionMatrixDisplay.from_predictions(
    y_test,
    predictions,
    display_labels=label_encoder.classes_,
    xticks_rotation=45,
)

plt.tight_layout()
plt.show()

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

برای مثال، اگر پیام‌های billing و sales مرتب اشتباه شوند، ممکن است:

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

داده چندبرچسبی

ممکن است یک پیام هم‌زمان چند دسته داشته باشد:

technical + billing
sales + technical

در این حالت مسئله Multi-label است، نه Multi-class.

می‌توان از روش One-vs-Rest استفاده کرد:

from sklearn.multiclass import (
    OneVsRestClassifier,
)


multi_label_model = OneVsRestClassifier(
    LinearSVC(
        C=1.0,
        class_weight="balanced",
    )
)

برچسب‌ها نیز باید با ساختاری مانند MultiLabelBinarizer تبدیل شوند.

مزایای SVM

  • عملکرد مناسب در بسیاری از مسائل طبقه‌بندی
  • مؤثر در فضای با ابعاد زیاد
  • مناسب برای متن و داده Sparse
  • امکان استفاده از Kernelهای غیرخطی
  • تمرکز روی نمونه‌های نزدیک مرز
  • کنترل منظم‌سازی با پارامتر C
  • کاربرد در طبقه‌بندی، رگرسیون و تشخیص ناهنجاری
  • عملکرد مناسب با داده کوچک یا متوسط و ویژگی‌های باکیفیت

محدودیت‌های SVM

  • حساسیت به مقیاس ویژگی‌ها
  • هزینه زیاد SVC غیرخطی روی داده بزرگ
  • تنظیم دشوار C و gamma
  • تفسیرپذیری محدود در Kernelهای غیرخطی
  • عدم ارائه احتمال مستقیم در بعضی پیاده‌سازی‌ها
  • حساسیت به نویز و Outlier در تنظیمات نامناسب
  • نیاز به تبدیل متن، تصویر یا صوت به بردار
  • کاهش کارایی در داده بسیار بزرگ یا بسیار پرنویز

تفاوت SVM و Logistic Regression

ویژگیSVMLogistic Regression
هدف اصلیبیشینه‌کردن Marginمدل‌کردن احتمال کلاس
احتمال خروجینیازمند کالیبراسیون در برخی مدل‌هاطبیعی‌تر
Kernelپشتیبانی می‌کندنسخه استاندارد خیر
داده SparseLinearSVC مناسببسیار مناسب
تفسیر ضرایبدر مدل خطی ممکنمعمولاً ساده‌تر
مرز غیرخطیبا Kernelنیازمند ساخت ویژگی
مقیاس‌بندیمهممعمولاً مهم

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

تفاوت SVM و درخت تصمیم

ویژگیSVMدرخت تصمیم
مرز تصمیمخطی یا Kernel-basedتقسیم‌های شرطی
نیاز به Scalingداردمعمولاً ندارد
تفسیرپذیریمحدودتربیشتر
داده با ابعاد زیادمناسبوابسته به مسئله
تعامل غیرخطیبا Kernelبه‌صورت طبیعی
احتمال بیش‌برازشبا C و gamma کنترل می‌شودبا عمق و هرس
طبقه‌بندی متنLinear SVM بسیار مناسبمعمولاً خط مبنای ضعیف‌تر

برای آشنایی بیشتر، مقاله درخت تصمیم و Random Forest را مطالعه کنید.

تفاوت SVM و شبکه عصبی

ویژگیSVMشبکه عصبی
داده کوچک و متوسطاغلب مناسبممکن است به داده بیشتر نیاز داشته باشد
داده خام تصویر و صوتمحدودبسیار مناسب‌تر
ویژگی‌سازیمعمولاً لازممی‌تواند ویژگی را یاد بگیرد
هزینه آموزشمتوسط تا زیادمعمولاً زیاد
مقیاس‌پذیریSVC محدودمعماری‌های بزرگ مقیاس‌پذیرتر
تنظیماتC، gamma و Kernelمعماری و پارامترهای متعدد

در معماری‌های مدرن می‌توان از مدل عمیق فقط برای تولید Embedding و از SVM برای طبقه‌بندی نهایی استفاده کرد.

چه زمانی از SVM استفاده کنیم؟

SVM انتخاب مناسبی است اگر:

  • مسئله طبقه‌بندی نظارت‌شده دارید.
  • داده کوچک یا متوسط است.
  • تعداد ویژگی‌ها زیاد است.
  • Embedding یا TF-IDF در اختیار دارید.
  • مرز تصمیم خطی یا قابل‌مدل‌سازی با Kernel است.
  • یک خط مبنای قوی برای متن می‌خواهید.
  • کیفیت ویژگی‌ها مناسب است.

احتمالاً انتخاب مناسبی نیست اگر:

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

اشتباهات رایج در استفاده از SVM

نرمال‌نکردن ویژگی‌ها

تفاوت مقیاس می‌تواند مرز تصمیم را منحرف کند.

Fit کردن Scaler روی کل داده

این کار باعث Data Leakage می‌شود. Scaler باید داخل Pipeline قرار گیرد.

استفاده از SVC روی داده بسیار بزرگ

کرنل غیرخطی می‌تواند زمان و حافظه زیادی مصرف کند. LinearSVC یا روش‌های خطی دیگر را بررسی کنید.

انتخاب C و gamma بدون Cross-validation

تنظیمات پیش‌فرض همیشه برای داده واقعی مناسب نیستند.

فعال‌کردن probability بدون نیاز

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

استفاده از Accuracy روی داده نامتوازن

عملکرد هر کلاس و معیارهایی مانند Macro F1 و PR AUC را بررسی کنید.

ارزیابی روی داده آموزش

امتیاز آموزش میزان تعمیم مدل را نشان نمی‌دهد.

استفاده از Embedding جدید با طبقه‌بند قدیمی

با تغییر مدل Embedding باید سازگاری بردارها بررسی و معمولاً طبقه‌بند دوباره آموزش داده شود.

نادیده‌گرفتن کلاس ناشناخته

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

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

SVM مخفف چیست؟

SVM مخفف Support Vector Machine و به معنای ماشین بردار پشتیبان است.

SVM به زبان ساده چگونه کار می‌کند؟

SVM مرزی میان کلاس‌ها پیدا می‌کند که بیشترین فاصله را از نزدیک‌ترین نمونه‌های دو طرف داشته باشد.

Support Vector چیست؟

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

Kernel در SVM چیست؟

Kernel روشی برای محاسبه شباهت نمونه‌ها در فضای ویژگی جدید است و امکان ساخت مرزهای غیرخطی را فراهم می‌کند.

تفاوت C و gamma چیست؟

C میزان جریمه خطا را کنترل می‌کند. gamma شعاع اثر نمونه‌ها را در Kernelهایی مانند RBF تعیین می‌کند.

آیا SVM برای متن فارسی مناسب است؟

بله. Linear SVM همراه با TF-IDF یا Embedding می‌تواند خط مبنای قدرتمندی برای دسته‌بندی متن فارسی باشد. کیفیت نتیجه باید روی داده واقعی فارسی سنجیده شود.

آیا SVM برای رگرسیون استفاده می‌شود؟

بله. نسخه رگرسیونی آن Support Vector Regression یا SVR نام دارد.

آیا SVM احتمال کلاس را برمی‌گرداند؟

SVC با probability=True می‌تواند احتمال ارائه کند. LinearSVC احتمال مستقیم ندارد و در صورت نیاز باید کالیبره شود.

آیا SVM برای داده بزرگ مناسب است؟

نسخه خطی می‌تواند برای داده‌های بزرگ‌تر و Sparse مناسب باشد، اما SVC با Kernel غیرخطی با افزایش تعداد نمونه‌ها پرهزینه می‌شود.

آیا می‌توان SVM را به API درواره متصل کرد؟

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

جمع‌بندی

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

مفاهیم اصلی آن عبارت‌اند از:

  1. Hyperplane
  2. Margin
  3. Support Vector
  4. Soft Margin
  5. پارامتر C
  6. Kernel Trick
  7. پارامتر gamma
  8. Scaling

SVM خطی برای داده‌های با ابعاد زیاد، TF-IDF و Embeddingها مناسب است. SVM با Kernel RBF می‌تواند مرزهای غیرخطی بسازد، اما تنظیم C و gamma و هزینه محاسباتی آن باید جدی گرفته شود.

برای استفاده صحیح از SVM باید:

  1. داده را به آموزش، اعتبارسنجی و آزمایش تقسیم کنید.
  2. Scaling را داخل Pipeline قرار دهید.
  3. معیار مناسب با مسئله انتخاب کنید.
  4. C و gamma را با Cross-validation تنظیم کنید.
  5. داده نامتوازن را مدیریت کنید.
  6. روی داده بزرگ، LinearSVC را بررسی کنید.
  7. احتمال خروجی را در صورت نیاز کالیبره کنید.
  8. مدل Embedding و طبقه‌بند را با هم نسخه‌بندی کنید.
  9. برای ورودی خارج از دامنه سازوکار مشخص داشته باشید.
  10. عملکرد مدل را پس از استقرار پایش کنید.

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

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

https://api.darvareh.ir/v1

API درواره با ساختار OpenAI سازگار است و امکان اتصال یکپارچه برنامه‌ها به مدل‌های مختلف را فراهم می‌کند.

مقالات مرتبط

منابع

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

Read more

الگوریتم KNN چیست؟ آموزش K-Nearest Neighbors با پایتون و کاربرد در Embedding

الگوریتم KNN چیست؟ آموزش K-Nearest Neighbors با پایتون و کاربرد در Embedding

الگوریتم KNN یکی از ساده‌ترین روش‌های یادگیری ماشین برای طبقه‌بندی و رگرسیون است. در این راهنما، K-Nearest Neighbors را با پایتون، تنظیم پارامترها و یک نمونه عملی طبقه‌بندی متن با Embedding و API درواره می‌آموزید.