সরাসরি পর্বে যান
পর্ব ২৮ · ৭৭-এর মধ্যে · মডিউল ৬

Soft margin — hinge loss, C আর dual problem

Soft-Margin SVM — Slack Variables, Hinge Loss, the C Trade-off, Subgradient Descent and the Dual Problem

Taught in Bangla, this lesson relaxes the hard margin with slack variables and shows how C trades margin width against training errors. It rewrites the SVM as hinge loss plus L2, solves it by subgradient descent, derives the dual and reads the three groups of alpha from KKT. Real in-page runs match SVC's dual_coef_ and tune C with class weights.

এই পর্বে যা শিখবেন

  • Slack variable দিয়ে soft-margin primal লেখা, আর হাতে কয়েকটি বিন্দুর ξ ও লক্ষ্যের মান বের করা
  • C বদলালে margin, support vector-এর সংখ্যা আর test accuracy কীভাবে বদলায়, তা চোখে দেখা
  • Primal-কে hinge loss + L2 রূপে আনা, hinge, squared hinge আর log loss তুলনা, আর subgradient descent নিজে লেখা
  • Lagrange multiplier দিয়ে dual বের করা, α থেকে w আর b ফেরত আনা, আর KKT দিয়ে তিন ধরনের বিন্দু চেনা
  • scipy দিয়ে ছোট dual সমাধান করে scikit-learn-এর dual_coef_-এর সঙ্গে মেলানো, আর C বাছাইয়ে GridSearchCV ও class_weight

পর্ব ২৭ (Support Vector Machine — margin, support vector আর hard margin)-এ ধরে নেওয়া হয়েছে, দুই শ্রেণিকে একটি সরলরেখা বা hyperplane w·x + b = ০ দিয়ে পুরো আলাদা করা যায়। তখন লক্ষ্য ছিল ½‖w‖² সবচেয়ে ছোট করা, শর্ত yi(w·xi + b) ≥ ১. এখানে yi হলো +১ বা −১ লেবেল, w সীমারেখার দিক ঠিক করা ভেক্টর, b সরণ। Margin-এর চওড়া ২/‖w‖. কিন্তু একটিমাত্র গোলমেলে বিন্দু থাকলেই এই শর্ত পূরণ অসম্ভব হয়ে যায়। এই পর্ব সেই বাস্তব অবস্থার কথা। এখানে লাগবে পর্ব ০২-এর hinge loss, পর্ব ১৭-এর L2 regularization আর পর্ব ১৬-এর Lagrange, KKT আর duality. চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, scikit-learn 1.8.0); প্রথমবার scikit-learn নামতে প্রায় দশ সেকেন্ড লাগে।

১ · যখন সরলরেখায় আলাদা হয় না — slack variable আর soft-margin primal

শহরের দুই পাড়ার মাঝে একটি রাস্তা বানাতে হবে, যত চওড়া সম্ভব। মুশকিল হলো, দুই-একটি দোকান রাস্তার জায়গায় ঢুকে বসে আছে। সব দোকান সরাতে চাইলে রাস্তা হবে সরু গলি। বুদ্ধিমান পরিকল্পনা: রাস্তা চওড়াই রাখুন, আর যে দোকান রাস্তায় যত বেশি ঢুকেছে, তাকে তত জরিমানা ধরুন। জরিমানার হার কত, সেটি একটি নীতির সিদ্ধান্ত। Soft margin ঠিক এই হিসাব, আর জরিমানার হারের নাম C.

Corinna Cortes আর Vladimir Vapnik ১৯৯৫ সালে Machine Learning জার্নালের “Support-vector networks” প্রবন্ধে এই সমাধান দেন। প্রতিটি বিন্দুকে একটি অ-ঋণাত্মক slack variable ξi দেওয়া হয়, যা শর্তটিকে ঢিলা করে:

minw, b, ξ ½‖w‖² + C ∑i ξi   শর্ত: yi(w·xi + b) ≥ ১ − ξi,   ξi ≥ ০

প্রথম পদ margin চওড়া রাখতে চায়, দ্বিতীয় পদ মোট “ঢুকে পড়া”-র জরিমানা। C > ০ একটি hyperparameter: model নিজে শেখে না, আপনি বেছে দেন। f(x) = w·x + b হলো decision function, আর yif(xi)-কে বলা হয় বিন্দুটির functional margin: ধনাত্মক মানে ঠিক দিকে, ১ বা বেশি মানে margin-এর বাইরে নিরাপদ। সর্বোত্তম সমাধানে প্রতিটি ξi যত ছোট সম্ভব হয়, তাই ξi = max(০, ১ − yif(xi)). নিচে চারটি বিন্দুতে পুরো হিসাব:

w = (1, 1), b = −7+1 = ফেরত এসেছে

            
  1. ডেটা আর রেখা। চারটি অর্ডার: ডেলিভারির দিন আর দাম (হাজার টাকা)। প্রস্তাবিত রেখা w = (১, ১), b = −৭, অর্থাৎ দিন + দাম ৭-এর বেশি হলে “ফেরত আসবে”.
  2. Score. প্রতিটি বিন্দুর f(x): A-তে ২, B-তে ০.৫, C-তে ০, D-তে ১.৫. চিহ্ন বলে রেখার কোন পাশে।
  3. লেবেল দিয়ে গুণ। y·f ধনাত্মক মানে ঠিক দিকে। A আর B ঠিক দিকে, C ঠিক রেখার ওপরে (০), D ভুল দিকে (−১.৫)।
  4. Slack. ξ = max(০, ১ − y·f): A-র ০, B-র ০.৫, C-র ১, D-র ২.৫. y·f যত ১-এর নিচে, ξ তত বড়।
  5. চার ধরনের অবস্থান। ξ = ০ নিরাপদ; ০ থেকে ১-এর মাঝে margin-এর ভেতরে কিন্তু ঠিক শ্রেণি; ξ = ১ সীমারেখায়; ξ > ১ মানে ভুল শ্রেণি। তাই ∑ξi training ভুলের সংখ্যার একটি উপরিসীমা।
  6. লক্ষ্যের মান। ½‖w‖² = ১, ∑ξ = ৪. C = ১ হলে লক্ষ্য ৫, C = ১০ হলে ৪১: বড় C-তে ξ-এর ভার এত বেশি যে optimizer ‖w‖ বাড়িয়ে (margin সরু করে) ξ কমাতে চাইবে। Margin এখন ২/√২ ≈ ১.৪১.

Soft margin-এ বিন্দুর চার অবস্থান

Slack ξ measures how far a point violates its side of the margin

Soft-margin SVM: points outside, on, inside the margin and misclassified −1 0 +1 w·x + b-এর মান ক খ গ ঘ ξ ক · ξ = 0, α = 0 (margin-এর বাইরে) খ · ξ = 0, 0 < α < C (ঠিক margin-এ) গ · 0 < ξ < 1, α = C (ভেতরে, ঠিক দিকে) ঘ · ξ > 1, α = C (ভুল দিকে)
ছবি ২৮.১ — কালো দাগ দেওয়া বিন্দুগুলো support vector: যারা margin-এ, ভেতরে বা ভুল দিকে। কমলা তীরের দৈর্ঘ্য ξ (w-এর এককে): বিন্দুটিকে নিজের ±১ রেখায় পৌঁছাতে কতটা সরতে হতো। α-এর অর্থ অংশ ৬-এ আসবে।

২ · C — ভুলের দাম আর margin-এর চওড়া

C দুটি চাওয়ার মাঝে দাঁড়িপাল্লা। ছোট C: ξ সস্তা, তাই optimizer ‖w‖ ছোট রাখে, margin চওড়া হয়, অনেক বিন্দু margin-এর ভেতরে ঢোকে। Model সরল, কিছু training ভুল মেনে নেয়: বেশি bias, কম variance. বড় C: প্রতিটি ξ দামি, তাই সীমারেখা প্রতিটি গোলমেলে বিন্দুর দিকে বাঁকে, margin সরু হয়: কম bias, বেশি variance. C → ∞ হলে, ডেটা আলাদা করা গেলে, hard margin ফিরে আসে। এটি AI-এর ভিত্তি কোর্সের পর্ব ৬১-এর bias–variance trade-off, শুধু হাতলটি এখানে C.

একটি সূক্ষ্ম কথা: C গুণ হয় যোগফলের সঙ্গে, গড়ের সঙ্গে নয়। ডেটা দ্বিগুণ হলে ∑ξ-ও মোটামুটি দ্বিগুণ, তাই একই C-তে regularization কার্যত অর্ধেক হয়ে যায়। ছোট নমুনায় বাছা C বড় ডেটায় সরাসরি খাটে না।

এবার বাজারঘরের কাল্পনিক ডেটা: ২০০টি অর্ডার, seed দিয়ে বানানো, দুটি feature (ডেলিভারির দিন আর দাম), লেবেল ফেরত (+১) বা না-ফেরত (−১)। দুই শ্রেণি ইচ্ছা করে একে অন্যের ওপর ছড়ানো। প্রথম ১২০টি train, বাকি ৮০টি test. ব্লক ১, ২ আর ৪ এই module থেকে ডেটা নেয়।

# bazar_svm.py — বাজারঘরের কাল্পনিক অর্ডার: ফেরত আসবে কি না (seed দিয়ে বানানো)
import numpy as np

def make_orders(n=200, seed=28, ret_share=0.5):
    rng = np.random.default_rng(seed)
    y = np.where(rng.random(n) < ret_share, 1, -1)    # +1 = ফেরত এসেছে, -1 = আসেনি
    days = np.where(y == 1, 4.6, 3.0) + rng.normal(0, 1.0, n)    # ডেলিভারিতে দিন
    price = np.where(y == 1, 3.4, 2.2) + rng.normal(0, 0.9, n)   # দাম, হাজার টাকা
    X = np.column_stack([days, price]).round(2)
    return X, y

def split(X, y, n_train=120):                         # প্রথম ১২০টি train, বাকি test
    return X[:n_train], y[:n_train], X[n_train:], y[n_train:]

প্রথম ব্লক চারটি C-তে SVC(kernel="linear") শেখায়, আর প্রতিটির সীমারেখা, দুই margin-রেখা আর support vector আঁকে। scikit-learn-এর dual_coef_-এ থাকে yiαi; তার পরম মান C-এর সমান হলে বিন্দুটি margin-এর ভেতরে বা ভুল দিকে (অংশ ৬-এ কারণ)।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from bazar_svm import make_orders, split

X, y = make_orders()
Xtr, ytr, Xte, yte = split(X, y)
fig, axes = plt.subplots(2, 2, figsize=(8, 7.4), sharex=True, sharey=True)
xs = np.linspace(0, 8, 2)
print("C        #SV  at C  margin   train  test")
for ax, C in zip(axes.ravel(), [0.01, 0.1, 1, 100]):
    m = SVC(kernel="linear", C=C).fit(Xtr, ytr)
    w, b = m.coef_[0], m.intercept_[0]
    alpha = np.abs(m.dual_coef_[0])               # dual_coef_ = y·α
    at_C = np.sum(np.isclose(alpha, C))           # α = C: margin-এর ভেতরে বা ভুল দিকে
    print(f"{C:<7}  {len(m.support_):3d}  {at_C:4d}  {2/np.linalg.norm(w):6.2f}"
          f"   {m.score(Xtr, ytr):.3f}  {m.score(Xte, yte):.3f}")
    for lvl, ls in [(0, "-"), (1, "--"), (-1, "--")]:  # w·x + b = 0, +1, -1
        ax.plot(xs, (lvl - b - w[0] * xs) / w[1], "k" + ls, lw=1.2)
    ax.scatter(*Xtr[ytr == 1].T, s=14, c="tab:red", label="returned (+1)")
    ax.scatter(*Xtr[ytr == -1].T, s=14, c="tab:blue", label="kept (-1)")
    sv = Xtr[m.support_]
    ax.scatter(*sv.T, s=60, facecolors="none", edgecolors="k", lw=0.7, label="support vector")
    ax.set(title=f"C = {C}: {len(sv)} support vectors", xlim=(0, 8), ylim=(-0.5, 6))
for ax in axes[1]: ax.set_xlabel("delivery days")
for ax in axes[:, 0]: ax.set_ylabel("price (thousand taka)")
axes[0, 0].legend(fontsize=8, loc="upper left")

আসল আউটপুট (ব্রাউজারের Python), নিচে চারটি ছবি:

C        #SV  at C  margin   train  test
0.01      86    83    3.30   0.908  0.825
0.1       46    43    1.74   0.925  0.887
1         30    28    0.89   0.933  0.863
100       25    22    0.66   0.933  0.875

C = ০.০১-এ margin ৩.৩০ একক চওড়া (দিন আর হাজার টাকার মিশ্র মাপে), ১২০টির ৮৬টিই support vector, আর তাদের ৮৩টি α = C-তে আটকে: প্রায় সবাই margin-এর ভেতরে। ছবিতে রেখাটিও একটু হেলে আছে, কারণ এত ছাড়ের মধ্যে সে ডেটার আকার ঠিকমতো ধরেনি। C বাড়লে margin ১.৭৪, ০.৮৯, ০.৬৬-এ সরু হয়, support vector কমে ২৫-এ নামে, train accuracy ০.৯০৮ থেকে ০.৯৩৩. কিন্তু test accuracy সবচেয়ে ভালো C = ০.১-এ (০.৮৮৭)। বড় C train ভুল কমায়, নতুন ডেটায় সবসময় নয়। ৮০টি test বিন্দুতে একটি বিন্দু মানে ০.০১২৫, তাই ০.৮৬৩ বনাম ০.৮৭৫-এর পার্থক্য মাত্র এক বিন্দু; এক split দেখে C বাছা ঠিক নয়, তার জন্য অংশ ৮.

৩ · শর্ত ছাড়া একই সমস্যা — hinge loss + L2

অংশ ১-এ দেখেছেন, সর্বোত্তম ξi = max(০, ১ − yif(xi)). এটি primal-এ বসালে ξ আর শর্ত দুটিই উধাও:

minw, b ½‖w‖² + C ∑i max(০, ১ − yi(w·xi + b))

দ্বিতীয় পদটি hinge loss-এর যোগফল (পর্ব ০২), প্রথমটি L2 penalty (পর্ব ১৭)। পুরো লক্ষ্যকে nC দিয়ে ভাগ করলে সমাধান বদলায় না, আর চেনা রূপ আসে:

(λ/২)‖w‖² + (১/n) ∑i ℓhinge(yif(xi)),   λ = ১/(nC)

অর্থাৎ linear SVM আসলে “একটি loss + একটি regularizer”, যেমন ridge বা logistic regression; পার্থক্য শুধু loss-এ। b-তে কোনো penalty নেই, এটি মনে রাখুন, অংশ ৪-এ লাগবে। Hinge loss convex (পর্ব ১২), কিন্তু m = ১-এ একটি কোণা আছে যেখানে ঢাল নেই। তাই gradient-এর বদলে subgradient লাগে।

Hinge, squared hinge আর log loss

m = y·f(x) ধরে তিনটি loss পাশাপাশি। সবগুলোই ০-১ loss-এর (ভুল হলে ১, ঠিক হলে ০) convex surrogate, কারণ ০-১ loss সরাসরি কমানো কঠিন।

Lossসূত্রm ≥ ১ হলেবৈশিষ্ট্যকোথায়
Hingemax(০, ১ − m)ঠিক ০কোণাযুক্ত; দূরের বিন্দু কিছুই বলে না, তাই sparse support vectorSVC, SGDClassifier-এর default
Squared hingemax(০, ১ − m)²ঠিক ০মসৃণ; বড় ভুলে বর্গ শাস্তি, তাই outlier-এ বেশি স্পর্শকাতরLinearSVC-এর default
Log losslog(১ + e−m)ছোট, কিন্তু কখনো ০ নয়মসৃণ; প্রতিটি বিন্দু একটু টানে; সম্ভাবনা দেয়Logistic regression

চারটি margin-এ হাতে হিসাব করলে পার্থক্য স্পষ্ট হয় (log loss base ২-এ, যাতে m = ০-তে ১ হয়):

m = y·fHingeSquared hingeLog loss (base ২)
২ (নিরাপদ)০০০.১৮৩১
০.৫ (margin-এর ভেতরে)০.৫০.২৫০.৬৮৩৯
০ (সীমারেখায়)১১১
−১ (ভুল দিকে)২৪১.৮৯৪৬

m = −১-এ squared hinge ৪, অন্য দুটির প্রায় দ্বিগুণ: একটি ভুল লেবেল বা অস্বাভাবিক অর্ডার squared hinge-এর সীমারেখাকে বেশি টানে। আবার m = ০.৫-এ squared hinge সবচেয়ে কম শাস্তি দেয়, কারণ ১-এর কম সংখ্যার বর্গ আরও ছোট। m = ২-এ শুধু log loss শূন্য নয়: নিরাপদ বিন্দুও তাকে একটু টানে।

Hinge যেখানে শূন্য, সেখানে বিন্দুটি সমাধানে কোনো ভূমিকা রাখে না, এটিই support vector-এর sparsity-র উৎস। Log loss-এ সব বিন্দু সামান্য হলেও টানে, তাই logistic regression-এ “support vector” নেই। দুই model-এর সীমারেখা প্রায়ই কাছাকাছি হয়; বড় পার্থক্য হলো logistic regression সম্ভাবনা শেখে (AI-এর ভিত্তি কোর্সের পর্ব ৫৯), hinge শুধু চিহ্ন।

৪ · Subgradient descent — primal নিজে সমাধান

F(w, b) = (λ/২)‖w‖² + (১/n)∑max(০, ১ − yi(w·xi + b))-এর একটি subgradient সহজ: যে বিন্দুর yif(xi) < ১ (hinge “সক্রিয়”), শুধু সেগুলো ঢাল দেয়।

gw = λw − (১/n) ∑সক্রিয় yixi,   gb = −(১/n) ∑সক্রিয় yi

ধাপ: w ← w − ηgw, b ← b − ηgb, η হলো ধাপের আকার। ঠিক yif = ১-এ hinge-এর ঢাল ০ থেকে −yixi-এর মাঝে যেকোনো কিছু হতে পারে; আমরা ০ নিচ্ছি। নিচে তিনটি বিন্দুতে একটি পুরো ধাপ:

λ = 0.1, η = 0.5, n = 3শুরু: w = (0.5, 0), b = 0

            
  1. লক্ষ্য আর ডেটা। তিনটি বিন্দু, λ = ০.১, ধাপ η = ০.৫. শুরুর রেখা শুধু প্রথম feature দেখে: w = (০.৫, ০), b = ০.
  2. কে সক্রিয়। P1-এর y·f = ১.৫ ≥ ১, hinge ০, সে ঢালে কিছু দেয় না। P2 (০.৫) আর P3 (−১) সক্রিয়, hinge ০.৫ আর ২. লক্ষ্য F = ০.০১২৫ + ০.৮৩৩৩ = ০.৮৪৫৮.
  3. Subgradient. Regularizer-এর অংশ λw = (০.০৫, ০). সক্রিয় দুজনের yixi-এর যোগ (১, ২) − (২, ০) = (−১, ২), n = ৩ দিয়ে ভাগ করে বাদ দিলে gw = (০.৩৮৩৩, −০.৬৬৬৭)। একটি +১ আর একটি −১ সক্রিয়, তাই gb = ০.
  4. ধাপ। ঢালের উল্টো দিকে ০.৫ গুণ: w = (০.৩০৮৩, ০.৩৩৩৩)। এখন দ্বিতীয় feature-ও কাজে লাগছে, b বদলায়নি।
  5. ফল। P2 প্রায় margin-এ (০.৯৭৫), P3 এখনো ভুল দিকে কিন্তু কম (−০.৬১৬৭)। F কমে ০.৫৫৭৫. Subgradient ধাপ সবসময় F কমায় না; এখানে কমেছে, তবে সাধারণ নিয়মে ধাপ ছোট করতে থাকতে হয়।

Shai Shalev-Shwartz, Yoram Singer আর Nathan Srebro ২০০৭ সালের ICML-এ Pegasos প্রস্তাব করেন: প্রতিবার একটি বা কয়েকটি এলোমেলো বিন্দু নিয়ে এই subgradient ধাপ, ηt = ১/(λt). ব্লক ২ সেই ধাপের নিয়মই নেয়, তবে সহজতার জন্য প্রতিবার সব ১২০টি বিন্দু, আর b-ও শেখায় (মূল Pegasos-এ b ছিল না)। শেষ অর্ধেকের w-এর গড় নেওয়া হয়, কারণ subgradient-এর শেষ ধাপ লাফায়। ফল মেলানো হয় দুটি scikit-learn solver-এর সঙ্গে: SVC (LIBSVM, dual সমাধান করে) আর LinearSVC(loss="hinge") (LIBLINEAR).

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC, LinearSVC
from bazar_svm import make_orders, split

X, y, _, _ = split(*make_orders())
X = (X - X.mean(0)) / X.std(0)                     # standardize, নইলে b খুব ধীরে নড়ে
n, C = len(y), 1.0
J = lambda w, b: 0.5 * w @ w + C * np.maximum(0, 1 - y * (X @ w + b)).sum()

w, b, ws, hist = np.zeros(2), 0.0, [], []
lam = 1 / (n * C)                                   # λ = 1/(nC)
for t in range(1, 2001):
    act = y * (X @ w + b) < 1                       # hinge যেখানে সক্রিয়
    gw = lam * w - (y[act, None] * X[act]).sum(0) / n
    gb = -y[act].sum() / n
    eta = 1 / (lam * t)                             # Pegasos-ধাঁচের কমতে-থাকা ধাপ
    w, b = w - eta * gw, b - eta * gb
    ws.append(np.r_[w, b]); hist.append(J(w, b))
wa = np.mean(ws[1000:], axis=0)                     # শেষ অর্ধেকের গড়
svc = SVC(kernel="linear", C=C).fit(X, y)
lin = LinearSVC(C=C, loss="hinge", max_iter=100000).fit(X, y)
for name, wv, bv in [("subgradient (avg)", wa[:2], wa[2]), ("SVC (libsvm)", svc.coef_[0],
        svc.intercept_[0]), ("LinearSVC (liblinear)", lin.coef_[0], lin.intercept_[0])]:
    print(f"{name:22s} w = {np.round(wv, 3)}  b = {bv:6.3f}  J = {J(wv, bv):.3f}")

m = np.linspace(-2, 3, 200)
fig, (a1, a2) = plt.subplots(1, 2, figsize=(9, 3.8))
for curve, lab in [(m < 0, "0-1"), (np.maximum(0, 1 - m), "hinge"),
        (np.maximum(0, 1 - m) ** 2, "squared hinge"), (np.log2(1 + np.exp(-m)), "log loss (base 2)")]:
    a1.plot(m, curve, label=lab)
a1.set(xlabel="margin m = y f(x)", ylabel="loss", ylim=(0, 4)); a1.legend(fontsize=8)
a2.semilogy(hist, lw=1); a2.axhline(J(svc.coef_[0], svc.intercept_[0]), c="k", ls="--")
a2.set(xlabel="iteration t", ylabel="objective J (log scale)", title="dashed: SVC optimum")
fig.subplots_adjust(wspace=0.3, bottom=0.15)

আসল আউটপুট (ব্রাউজারের Python), নিচে loss-এর বক্ররেখা আর লক্ষ্যের পতন:

subgradient (avg)      w = [1.622 1.682]  b = -0.705  J = 27.798
SVC (libsvm)           w = [1.622 1.682]  b = -0.705  J = 27.798
LinearSVC (liblinear)  w = [1.616 1.664]  b = -0.700  J = 27.801

বিশ লাইনের নিজের লেখা loop আর LIBSVM একই w, b আর লক্ষ্য J = ২৭.৭৯৮ দিয়েছে, তিন দশমিক পর্যন্ত। ডান ছবিতে লক্ষ্য শুরুতে হাজারের ঘরে (প্রথম ধাপ η = ১২০, বিশাল), কয়েকশো ধাপে ড্যাশ রেখার কাছে আসে, তারপর চারপাশে কাঁপে; তাই গড় নেওয়া। বাঁ ছবিতে log loss (base ২) আর hinge দুটিই m = ০-তে ১ ছোঁয়, কিন্তু m > ১-এ hinge ঠিক ০, log loss নয়।

LinearSVC একটু আলাদা কেন: LIBLINEAR b-কে একটি বাড়তি feature-এর ওজন ধরে (মান intercept_scaling, default ১), তাই b-ও penalty পায়; ফলে তার J সামান্য বেশি (২৭.৮০১)। Standardize করা ডেটায় b ছোট, পার্থক্যও ছোট। একই ডেটা scale না করে চালালে পার্থক্য বড় হয়; ডেস্কটপে সত্যিকারের run (Python 3.14.4, scikit-learn 1.8.0, একটি run):

~/bazarghar-ml$ python intercept.py
scikit-learn 1.8.0
SVC        w = [1.337 1.803]  b = -10.190  J = 27.219
LinearSVC  w = [0.666 0.782]  b =  -4.689  J = 33.595

এখানে সঠিক b প্রায় −১০, তার penalty বড়, তাই LIBLINEAR b-কে −৪.৭-এ টেনে রাখে আর w-ও বদলে যায়। দুটি “linear SVM” আসলে একটু আলাদা সমস্যা সমাধান করছে। intercept_scaling বাড়ালে penalty-র প্রভাব কমে, যেমন scale না করা ডেটায় ১০ দিলে b ≈ −৯.৩৮; কিন্তু আমাদের পরীক্ষায় ১০০ দিলে LIBLINEAR converge না করার warning দিয়েছিল। সহজ পথ: feature standardize করা। SGDClassifier(loss="hinge") একই লক্ষ্যের stochastic রূপ, সেখানে alpha = λ = ১/(nC).

৫ · Dual problem — Lagrange থেকে α

এবার primal-কে পর্ব ১৬-এর পদ্ধতিতে উল্টে দেখা। প্রতিটি margin-শর্তের জন্য একটি Lagrange multiplier αi ≥ ০, আর প্রতিটি ξi ≥ ০ শর্তের জন্য μi ≥ ০:

L = ½‖w‖² + C∑ξi − ∑αi[yi(w·xi + b) − ১ + ξi] − ∑μiξi

w, b আর ξ-এর সাপেক্ষে L-এর ঢাল শূন্য করলে তিনটি সম্পর্ক:

  • ∂L/∂w = ০ ⇒ w = ∑αiyixi. সমাধানটি training বিন্দুগুলোর ওজনযুক্ত যোগফল।
  • ∂L/∂b = ০ ⇒ ∑αiyi = ০. দুই শ্রেণির মোট “টান” সমান।
  • ∂L/∂ξi = ০ ⇒ C − αi − μi = ০. যেহেতু μi ≥ ০, তাই αi ≤ C.

এগুলো L-এ ফেরত বসালে ξ আর μ পুরো কেটে যায়, থাকে শুধু α:

maxα ∑αi − ½ ∑i∑j αiαjyiyj(xi·xj)   শর্ত: ০ ≤ αi ≤ C,   ∑αiyi = ০

লক্ষ করুন, hard margin-এর dual থেকে পার্থক্য মাত্র একটি: α-এর ওপর উপরিসীমা C. তাই এটিকে বলে box constraint. এটি একটি quadratic program: লক্ষ্য দ্বিঘাত, শর্ত রৈখিক, আর Qij = yiyjxi·xj ম্যাট্রিক্সটি positive semidefinite (পর্ব ১০), তাই সমস্যাটি convex. Primal convex আর শর্তগুলো রৈখিক, তাই strong duality খাটে: দুই সমস্যার সর্বোত্তম মান সমান। আর একটি জিনিস চোখে রাখুন: ডেটা dual-এ আসে শুধু dot product xi·xj হিসেবে। পর্ব ২৯ (Kernel trick — RBF, polynomial আর Mercer-এর অন্তর্দৃষ্টি) ঠিক এখান থেকে শুরু হবে।

৬ · তিন ধরনের বিন্দু — KKT আর α

সর্বোত্তম বিন্দুতে KKT শর্ত খাটে। দুটি complementary slackness শর্ত এখানে মূল: αi[yif(xi) − ১ + ξi] = ০ আর μiξi = (C − αi)ξi = ০. এই দুটি থেকে প্রতিটি বিন্দু তিনটির একটি দলে পড়ে:

αiযুক্তিyif(xi)ξiঅবস্থান
০α = ০ ⇒ μ = C > ০ ⇒ ξ = ০≥ ১০margin-এর বাইরে; সমাধানে কোনো ভূমিকা নেই
০ < α < Cμ > ০ ⇒ ξ = ০; α > ০ ⇒ শর্ত সমতায়= ১০ঠিক margin-রেখায়; b বের করতে এরাই লাগে
Cα > ০ ⇒ y·f = ১ − ξ, আর ξ ≥ ০≤ ১১ − y·fভেতরে বা ভুল দিকে (ξ > ১ হলে ভুল শ্রেণি)

α > ০ যাদের, তারাই support vector; বাকিরা মুছে ফেললেও সমাধান একই থাকে। b বের করতে একটি “মুক্ত” support vector (০ < α < C) নিন: সেখানে yi(w·xi + b) = ১, আর yi² = ১ বলে b = yi − w·xi. সংখ্যার ভুল কমাতে LIBSVM-এর মতো solver এমন সব বিন্দুর গড় নেয়। কোনো মুক্ত support vector না থাকলে (সব α হয় ০, নয় C) b আর একটি সংখ্যা নয়, একটি পরিসর: α = ০ বিন্দুগুলো থেকে y·f ≥ ১, আর α = C বিন্দুগুলো থেকে y·f ≤ ১. LIBSVM তখন সেই পরিসরের মাঝখান নেয়। খুব ছোট C-তে এমন অবস্থার কাছাকাছি দেখা যায়: ব্লক ১-এর C = ০.০১-এ ৮৬টি support vector-এর ৮৩টিই C-তে আটকে, মুক্ত মাত্র ৩টি। নিচে পাঁচটি বিন্দুর একটি সম্পূর্ণ সমাধান থেকে w আর b ফেরত আনা:

Dual-এর সমাধান α দেওয়া আছেC = 0.5

            
  1. দেওয়া α. পাঁচটি বিন্দু আর dual-এর সমাধান। P1 আর P5 উপরিসীমা C = ০.৫-এ, P2 আর P3 মাঝে, P4 শূন্যে। প্রথমে শর্ত যাচাই: ∑αiyi = ০.
  2. w. শুধু α > ০ বিন্দুগুলো যোগ হয়: w = (০.২৫, ০.৭৫)। P4 বাদ দিলেও w একই থাকত।
  3. b. মুক্ত support vector P2 আর P3 ঠিক margin-এ, তাই y·f = ১ থেকে b = y − w·x. দুটিই −১ দেয়, ভালো লক্ষণ।
  4. KKT যাচাই। f(x) = ০.২৫x১ + ০.৭৫x২ − ১. P4-এর y·f = ১.২৫ ≥ ১ (α = ০)। P2, P3-এর ঠিক ১. P1 সীমারেখায় (০, ξ = ১), P5 margin-এর ভেতরে (০.২৫, ξ = ০.৭৫), দুজনেরই α = C. সব শর্ত মিলেছে।
  5. Strong duality. Primal-এর মান ১.১৮৭৫, dual-এর মানও ১.১৮৭৫. Duality gap শূন্য, তাই এই α সত্যিই সর্বোত্তম (একই উত্তর SVC(kernel="linear", C=0.5)-ও দেয়, ডেস্কটপে মেলানো)।

৭ · Dual নিজে সমাধান — scipy আর SVC-এর dual_coef_

বাস্তব solver এই QP সাধারণ optimizer দিয়ে সমাধান করে না। John Platt ১৯৯৮ সালে SMO (sequential minimal optimization) প্রস্তাব করেন: একবারে মাত্র দুটি α বদলানো, কারণ ∑αiyi = ০ রাখতে অন্তত দুটি একসঙ্গে নড়াতে হয়, আর দুটির সমস্যা হাতেই সমাধান হয়। LIBSVM (Chih-Chung Chang আর Chih-Jen Lin) এই ধারার একটি উন্নত রূপ ব্যবহার করে, আর scikit-learn-এর SVC ভেতরে LIBSVM চালায়। ছোট সমস্যায় অবশ্য সাধারণ optimizer যথেষ্ট, আর বোঝার জন্য সেটিই ভালো: ব্লক ৩ ১৬টি বিন্দুর dual scipy.optimize.minimize-এর SLSQP দিয়ে সমাধান করে, ০ ≤ α ≤ C সীমা আর ∑αy = ০ সমতা দিয়ে। তারপর SVC-এর সঙ্গে মেলায় আর প্রতিটি দলের y·f(x) ছাপে।

import numpy as np
from scipy.optimize import minimize
from sklearn.svm import SVC
from bazar_svm import make_orders

X, y = make_orders(n=16, seed=5)
X = (X - X.mean(0)) / X.std(0)
C = 1.0
Q = (y[:, None] * y[None, :]) * (X @ X.T)           # Q_ij = y_i y_j x_i·x_j
neg_dual = lambda a: 0.5 * a @ Q @ a - a.sum()      # dual-কে সর্বোচ্চ = এর ঋণাত্মককে সর্বনিম্ন
res = minimize(neg_dual, np.zeros(len(y)), jac=lambda a: Q @ a - 1, method="SLSQP",
               bounds=[(0, C)] * len(y),
               constraints={"type": "eq", "fun": lambda a: a @ y, "jac": lambda a: y * 1.0},
               options={"ftol": 1e-12, "maxiter": 500})
a = np.where(res.x < 1e-6, 0, np.where(res.x > C - 1e-6, C, res.x))
w = (a * y) @ X                                     # w = Σ α_i y_i x_i
free = (a > 0) & (a < C)                            # 0 < α < C: ঠিক margin-এর ওপরে
b = np.mean(y[free] - X[free] @ w)
print("scipy:", res.success, " w =", np.round(w, 4), " b =", round(b, 4))

svc = SVC(kernel="linear", C=C, tol=1e-6).fit(X, y)
a_svc = np.zeros(len(y)); a_svc[svc.support_] = np.abs(svc.dual_coef_[0])
print("SVC  :", "w =", np.round(svc.coef_[0], 4), " b =", round(svc.intercept_[0], 4))
print("max |alpha_scipy - alpha_svc| =", f"{np.abs(a - a_svc).max():.1e}")

yf = y * (X @ w + b)
for name, mask in [("alpha = 0", a == 0), ("0 < alpha < C", free), ("alpha = C", a == C)]:
    print(f"{name:14s} n={mask.sum():2d}  y*f(x): {np.round(np.sort(yf[mask]), 2)}")

আসল আউটপুট (ব্রাউজারের Python):

scipy: True  w = [1.7015 0.164 ]  b = -0.1079
SVC  : w = [1.7015 0.164 ]  b = -0.1079
max |alpha_scipy - alpha_svc| = 1.2e-09
alpha = 0      n= 9  y*f(x): [1.06 1.5  1.79 1.85 2.49 2.54 2.63 2.69 3.5 ]
0 < alpha < C  n= 2  y*f(x): [1. 1.]
alpha = C      n= 5  y*f(x): [-0.6   0.22  0.63  0.8   0.87]

নিজের লেখা dual আর LIBSVM-এর w, b চার দশমিক পর্যন্ত এক, আর ১৬টি α-এর সবচেয়ে বড় পার্থক্য প্রায় ১০−৯. SVC-এর dual_coef_-এ থাকে শুধু support vector-দের yiαi, আর support_-এ তাদের সূচক; তাই α ফেরত আনতে পরম মান নিয়ে ঠিক জায়গায় বসাতে হয়েছে। তিন দলেই অংশ ৬-এর টেবিল মিলেছে: α = ০-এর ৯টির y·f সব ১-এর বেশি; দুটি মুক্ত support vector ঠিক ১-এ; α = C-এর ৫টি ১-এর নিচে, তার একটি −০.৬ (ভুল শ্রেণি)। ছোট্ট একটি বিষয়: dual-এ ১০−৬-এর কম α-কে আমরা শূন্য ধরেছি; solver-এর সংখ্যা কখনো হুবহু শূন্য বা C হয় না, তাই এমন একটি সহনশীলতা সবসময় লাগে।

আরও গভীরে: Dual-এর sparsity থেকে একটি পুরোনো ফল আসে: leave-one-out cross-validation-এর ভুলের হার support vector-এর অনুপাত দিয়ে ওপর থেকে সীমিত (Vapnik-এর বইগুলোতে আলোচিত; soft margin-এ বিবরণ একটু জটিল)। তাই অনেক support vector প্রায়ই গোলমেলে ডেটা বা ছোট C-এর লক্ষণ, যেমন ব্লক ১-এ ৮৬টি। Primal-এর দিকে LIBLINEAR (Rong-En Fan ও সহকর্মীরা, JMLR ২০০৮) dual coordinate descent ব্যবহার করে (Cho-Jui Hsieh ও সহকর্মীরা, ICML ২০০৮), যা বড়, sparse টেক্সট ডেটায় kernel SVM-এর চেয়ে বহু গুণ দ্রুত হতে পারে; পর্ব ৩১ (বড় ডেটায় kernel — Nyström, random features আর কখন SVM নয়) এই দিকটি দেখবে।

৮ · C বাছাই — GridSearchCV, scaling আর class_weight

C ডেটা থেকে শেখা যায় না, তাই cross-validation দিয়ে বাছতে হয়: সাধারণত লগ-স্কেলে মান (০.০০১, ০.০১, … ১০০), প্রতিটিতে k-fold-এর গড় score, সেরাটি বেছে পুরো train-এ আবার শেখানো। এটি AI-এর ভিত্তি কোর্সের পর্ব ৬৬-এর grid search. দুটি নিয়ম অবশ্যই মানুন:

  • Scaling Pipeline-এর ভেতরে। Margin একটি দূরত্ব, তাই টাকায় মাপা দাম (হাজারের ঘরে) দিনের feature-কে ঢেকে ফেলে। StandardScaler Pipeline-এ রাখলে প্রতিটি fold-এ শুধু সেই fold-এর train অংশ থেকে গড় আর বিস্তার শেখা হয়, leakage হয় না (AI-এর ভিত্তি কোর্সের পর্ব ৬৪)।
  • অসম শ্রেণিতে class_weight. class_weight="balanced" প্রতিটি শ্রেণির C-কে n/(২ × সেই শ্রেণির সংখ্যা) দিয়ে গুণ করে, ফলে ছোট শ্রেণির প্রতিটি ξ বেশি দামি হয়। Primal-এ এটি ∑Ciξi, dual-এ প্রতিটি αi-এর আলাদা উপরিসীমা Ci.

Grid-এর পরিসর নিয়ে দুটি ব্যবহারিক কথা। এক, C-এর প্রভাব গুণিতকে, তাই মানগুলো লগ-স্কেলে সমান দূরত্বে রাখুন; ১, ২, ৩-এর মতো রৈখিক grid প্রায় কিছুই বদলায় না। দুই, সেরা মান grid-এর প্রান্তে পড়লে (যেমন সবচেয়ে বড় বা সবচেয়ে ছোট C) পরিসর বাড়িয়ে আবার চালান, কারণ আসল সেরা মান হয়তো বাইরে। অংশ ২-এর কথাটিও মনে রাখুন: train-এর আকার বদলালে, যেমন CV-র প্রতিটি fold-এ ডেটা ৮০%, একই C-এর কার্যকর regularization সামান্য বদলায়; সাধারণত এটি ছোট প্রভাব, কিন্তু খুব ছোট ডেটায় চোখে পড়তে পারে।

ব্লক ৪-এ ৬০০টি অর্ডার, ফেরত মাত্র প্রায় ১২%, আর দাম ইচ্ছা করে টাকায় (হাজারের ঘরে)। Score হিসেবে balanced accuracy, অর্থাৎ দুই শ্রেণির recall-এর গড়, কারণ সাধারণ accuracy-তে “কেউ ফেরত দেবে না” বললেই ৮৮%.

import numpy as np
from sklearn.metrics import precision_score, recall_score
from sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from bazar_svm import make_orders

X, y = make_orders(n=600, seed=280, ret_share=0.15)  # ফেরত অল্প
X[:, 1] *= 1000                                      # দাম টাকায়: scale খুব আলাদা
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, stratify=y, random_state=0)
print(f"train {len(ytr)}, returns {np.mean(ytr == 1):.3f}")

cv = StratifiedKFold(5, shuffle=True, random_state=0)
grid = {"svc__C": [0.001, 0.01, 0.1, 1, 10, 100]}
for cw in [None, "balanced"]:
    pipe = make_pipeline(StandardScaler(), SVC(kernel="linear", class_weight=cw))
    gs = GridSearchCV(pipe, grid, cv=cv, scoring="balanced_accuracy").fit(Xtr, ytr)
    cvs = np.round(gs.cv_results_["mean_test_score"], 3)
    pred = gs.predict(Xte)
    print(f"\nclass_weight={cw}\n  CV balanced acc by C: {cvs}")
    print(f"  best C = {gs.best_params_['svc__C']}, test: recall(return) = "
          f"{recall_score(yte, pred):.3f}, precision = {precision_score(yte, pred):.3f}")

আসল আউটপুট (ব্রাউজারের Python):

train 420, returns 0.117

class_weight=None
  CV balanced acc by C: [0.5   0.5   0.537 0.594 0.655 0.655]
  best C = 10, test: recall(return) = 0.476, precision = 0.714

class_weight=balanced
  CV balanced acc by C: [0.785 0.792 0.811 0.778 0.778 0.778]
  best C = 0.1, test: recall(return) = 0.810, precision = 0.354

Weight ছাড়া ছোট C-তে balanced accuracy ঠিক ০.৫: margin এত ঢিলা যে model সবাইকে “ফেরত নয়” বলছে, কারণ ৮৮% বিন্দুর পক্ষে থাকাই সস্তা। বড় C-তে কিছুটা উন্নতি, তবু test-এ ফেরতের মাত্র ৪৭.৬% ধরা পড়ে। Balanced weight-এ পুরো ছবি বদলায়: CV-তে সেরা ০.৮১১, C = ০.১-এ, আর test-এ ৮১% ফেরত ধরা পড়ে, কিন্তু precision ০.৩৫৪: যাদের “ফেরত আসবে” বলা হলো, তাদের প্রায় তিনজনে একজন সত্যিই ফেরত দেয়। কোনটি ভালো, তা নির্ভর করে একটি বাড়তি ফোনের খরচ আর একটি ফেরতের ক্ষতির ওপর। আর লক্ষ করুন, weight বদলালে সেরা C-ও বদলেছে: দুটি একসঙ্গে tune করতে হয়।

Pipeline থেকে StandardScaler বাদ দিলে কী হয়? ডেস্কটপে সত্যিকারের run (Python 3.14.4, scikit-learn 1.8.0, একই ডেটা, class_weight="balanced", একটি run; warning-এর ফাইল-পথ ছোট করা হয়েছে):

~/bazarghar-ml$ python noscale.py
…/sklearn/svm/_base.py:313: ConvergenceWarning: Solver terminated early (max_iter=1000000).  Consider pre-processing your data with StandardScaler or MinMaxScaler.
…/sklearn/svm/_base.py:313: ConvergenceWarning: Solver terminated early (max_iter=1000000).  Consider pre-processing your data with StandardScaler or MinMaxScaler.
scikit-learn 1.8.0
no scaler, C=0.01  balanced acc 0.810  w = [0.4066 0.001 ]  time 0.23 s
no scaler, C=1     balanced acc 0.715  w = [2.7319 0.0128]  time 0.58 s

দুবারই LIBSVM দশ লাখ ধাপেও শেষ করতে পারেনি, আর নিজেই StandardScaler-এর পরামর্শ দিয়েছে। C = ১-এ balanced accuracy ০.৭১৫, scale করা Pipeline-এর চেয়ে স্পষ্ট খারাপ। দামের ওজন ০.০০১-এর ঘরে, কারণ টাকার এককে সামান্য ওজনই বড় f দেয়; অর্থাৎ ‖w‖² penalty দুটি feature-কে মোটেই সমানভাবে দেখছে না।

SVM-এর score সম্ভাবনা নয়। decision_function সীমারেখা থেকে চিহ্নসহ দূরত্বের মতো একটি সংখ্যা; hinge loss-এর সেরা উত্তর শুধু চিহ্ন, সম্ভাবনা নয়। সম্ভাবনা লাগলে আলাদা ডেটায় Platt বা isotonic দিয়ে calibrate করুন (পর্ব ২৬)। SVC(probability=True) ভেতরে ৫-fold cross-validation চালিয়ে Platt scaling শেখে: সময় বেশি লাগে, ফল এলোমেলোতার ওপর নির্ভর করে, আর scikit-learn-এর docs সতর্ক করে যে তার predict_proba-র argmax কখনো predict-এর সঙ্গে না-ও মিলতে পারে।

পরীক্ষা ও ইন্টারভিউতে:

  • C বাড়ালে কী হয়? Margin সরু, support vector কম, training ভুল কম; overfitting-এর ঝুঁকি বাড়ে।
  • Soft-margin dual-এ hard margin থেকে কী বদলায়? শুধু ০ ≤ αi ≤ C; ξ dual-এ আসে না।
  • αi = C মানে কী? yif(xi) ≤ ১: বিন্দুটি margin-এর ভেতরে বা ভুল দিকে।
  • b কীভাবে পাবেন? একটি মুক্ত support vector (০ < α < C) থেকে b = yi − w·xi, বা সবগুলোর গড়।
  • Hinge loss-এর SVM আর SGDClassifier-এর alpha-র সম্পর্ক? alpha = λ = ১/(nC).

৯ · AI-এর যুগে — SVM কোড আর গণিত যাচাই

AI assistant SVM-এর কোড দ্রুত লেখে, আর dual-এর derivation-ও সুন্দর করে সাজায়। কিন্তু চিহ্নের একটি ভুল, বা পুরোনো argument, পুরো ফল বদলে দেয়। পরিচিত ভুলগুলো:

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
Scaling ছাড়া SVC, বা পুরো ডেটায় scaler fit করে তারপর splitMargin দূরত্ব-নির্ভর; দ্বিতীয়টি leakagePipeline-এ StandardScaler; ConvergenceWarning পড়া
dual_coef_-কে α বলেএতে yiαi, আর শুধু support vector-দেরপরম মান নিন, support_ দিয়ে জায়গা মেলান
Test set দেখে C বেছে সেই test score রিপোর্ট করেTest-এ tuning মানে আশাবাদী ফলGridSearchCV শুধু train-এ; test একবার, শেষে
decision_function-কে সম্ভাবনা বলে, বা probability=True না বুঝে চালায়Score সম্ভাবনা নয়; ভেতরের CV এলোমেলোআলাদা ডেটায় calibration, reliability diagram
Dual-এ α ≤ C ভুলে যায়, বা ∑αiyi = ০ বাদ দেয়প্রথমটি hard margin, দ্বিতীয়টি b-হীন model৩–৫টি বিন্দুতে হাতে derivation; scipy বনাম SVC মেলানো

AI-কে শুধু কোড নয়, যাচাইয়ের পথও চাইতে বলুন:

আমি বাজারঘরের অর্ডার ফেরত আসবে কি না, তার জন্য linear SVM বানাচ্ছি (scikit-learn 1.8.0). ডেটায় ফেরত প্রায় ১২%, feature-গুলোর একক আলাদা। ১. ডেটা আগে train আর test-এ ভাগ করো; test শেষে একবারই ছোঁবে। ২. StandardScaler আর SVC(kernel="linear") একটি Pipeline-এ রাখো; C-এর জন্য লগ-স্কেলের grid আর StratifiedKFold দিয়ে GridSearchCV করো, class_weight=None আর "balanced" দুটিই grid-এ রাখো। ৩. Score হিসেবে balanced accuracy নাও, আর test-এ ফেরত-শ্রেণির recall ও precision আলাদা করে দেখাও। ৪. সেরা model-এর support vector-এর সংখ্যা, কতগুলোর α = C, আর margin-এর চওড়া 2/‖w‖ ছাপো; dual_coef_-এর অর্থ এক লাইনে বলো। ৫. decision_function-কে সম্ভাবনা বলবে না; সম্ভাবনা লাগলে আলাদা ডেটায় calibration-এর পরিকল্পনা দাও। ৬. প্রতিটি class আর argument 1.8.0-এর docs-এ আছে কি না কীভাবে যাচাই করব বলো, সব seed নির্দিষ্ট রাখো, আর কোনো warning চাপা দিও না।

AI-এর দেওয়া SVM হাতে পেলে — যাচাইয়ের চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১Hard margin থেকে soft margin-এ যাওয়া কেন এত বড় পদক্ষেপ ছিল? ১৯৯০-এর দশকে SVM কীভাবে এত জনপ্রিয় হলো?

সর্বোচ্চ margin-এর সীমারেখার ধারণা পুরোনো; Vladimir Vapnik আর Alexey Chervonenkis ১৯৬০-এর দশকে সোভিয়েত ইউনিয়নে “optimal separating hyperplane” নিয়ে কাজ করেছিলেন। কিন্তু তা ছিল পুরোপুরি আলাদা করা যায় এমন ডেটার জন্য। ১৯৯২ সালে Bernhard Boser, Isabelle Guyon আর Vapnik (তখন AT&T Bell Labs-এ) COLT সম্মেলনে kernel-এর সঙ্গে maximum-margin classifier দেখান। তবু বাস্তব ডেটায় একটি ভুল লেবেল বা একটি অস্বাভাবিক বিন্দুই hard margin-কে অসম্ভব করে দিত, বা margin-কে এত সরু করত যে নতুন ডেটায় ফল খারাপ হতো।

Cortes আর Vapnik-এর ১৯৯৫ সালের প্রবন্ধ slack আর C যোগ করে এই বাধা সরায়, আর সমস্যাটি convex QP-ই থাকে, অর্থাৎ local minimum-এর ভয় নেই। সেই সময় neural network-এর training ছিল অস্থির, ফল নির্ভর করত শুরুর ওজন আর অনেক কৌশলের ওপর। SVM দিয়েছিল একটি পরিষ্কার লক্ষ্য, একটি নিশ্চিত সর্বোত্তম সমাধান, অল্প কয়েকটি hyperparameter (C আর kernel-এর), আর VC তত্ত্বের সঙ্গে একটি তাত্ত্বিক যোগসূত্র। হাতে লেখা অঙ্ক চেনা আর টেক্সট শ্রেণিবিভাগের মতো কাজে এটি সেই সময়ের সেরা পদ্ধতিগুলোর সমান বা ভালো ফল দেখায়; Thorsten Joachims ১৯৯৮ সালে টেক্সট শ্রেণিবিভাগে SVM-এর পক্ষে প্রভাবশালী ফল প্রকাশ করেন।

পরে LIBSVM আর SVMlight-এর মতো বিনামূল্যের সফটওয়্যার পদ্ধতিটিকে সবার হাতে পৌঁছে দেয়। ২০১০-এর দশকে বড় ডেটা আর GPU-তে deep learning অনেক ক্ষেত্রে এগিয়ে যায়, কিন্তু soft margin-এর মূল শিক্ষা থেকে যায়: নিখুঁত মিল চাওয়ার বদলে ভুলের একটি দাম ঠিক করা, আর সেই দাম cross-validation দিয়ে বাছা।

মূল উপলব্ধি: soft margin একটি সুন্দর তত্ত্বকে বাস্তব ডেটায় ব্যবহারযোগ্য করেছিল, convexity না হারিয়ে; এটিই SVM-কে প্রায় এক দশক ধরে অনেকের প্রথম পছন্দ বানিয়েছিল।

প্র ০২Linear SVM আর logistic regression প্রায় একই রকম ফল দেয়। তাহলে কোনটি কখন বেছে নেবেন?

অংশ ৩ দেখিয়েছে, দুটিই “loss + L2 penalty”, পার্থক্য শুধু loss-এ: hinge বনাম log loss. দুটির বক্ররেখা m বড় ঋণাত্মক হলে প্রায় সমান্তরাল, আর m = ০-এর কাছে কাছাকাছি। তাই অনেক ডেটায় দুটির সীমারেখা আর accuracy খুব কাছাকাছি হয়; Ji Zhu আর Trevor Hastie ২০০৫ সালে kernel logistic regression নিয়ে লিখতে গিয়ে দেখান যে এর classification ফল প্রায়ই SVM-এর মতোই, আর সঙ্গে সম্ভাবনাও দেয়।

পার্থক্য আসে অন্য জায়গায়। সম্ভাবনা: log loss একটি proper scoring rule, তাই logistic regression সম্ভাবনা শেখে; hinge-এর সেরা উত্তর শুধু চিহ্ন (Yi Lin, ২০০২)। ঝুঁকির হিসাব, খরচ-ভিত্তিক threshold বা বাজেটে logistic স্বাভাবিক পছন্দ। Sparsity: hinge-এ দূরের বিন্দু কিছুই বলে না, তাই kernel রূপে prediction শুধু support vector দিয়ে হয়; kernel logistic regression-এ সব বিন্দু লাগে। Outlier: দুটিরই loss ভুলের সঙ্গে রৈখিকভাবে বাড়ে, তাই দুটিই squared hinge বা squared loss-এর চেয়ে কম স্পর্শকাতর। Optimization: log loss মসৃণ, তাই L-BFGS বা Newton পদ্ধতি সরাসরি খাটে; hinge-এর কোণার জন্য বিশেষ solver লাগে।

বাজারঘরের ক্ষেত্রে: যদি শুধু “কোন অর্ডারে ফোন করব” ঠিক করতে হয় আর ঝুঁকির ক্রমই যথেষ্ট, দুটিই চলে; যদি প্রত্যাশিত ক্ষতির টাকা হিসাব করতে হয়, logistic regression বা calibrated SVM. বাস্তবে দুটিই একই cross-validation-এ চালিয়ে তুলনা করা সবচেয়ে সৎ পথ।

মূল উপলব্ধি: loss-এর পছন্দ শুধু accuracy নয়, model আপনাকে কী ধরনের উত্তর দেবে তা ঠিক করে: চিহ্ন ও sparsity, নাকি সম্ভাবনা।

প্র ০৩Primal না dual — কোনটি সমাধান করবেন? n আর feature-এর সংখ্যা এই সিদ্ধান্তে কী ভূমিকা রাখে?

Primal-এর চলক d + ১টি (w আর b), dual-এর চলক nটি (প্রতিটি উদাহরণের জন্য একটি α)। dual-এর QP-তে Q ম্যাট্রিক্স n × n, তাই n = এক লাখ হলে ১০১০টি ঘর, যা পুরোটা মেমরিতে রাখা কঠিন। SMO আর LIBSVM এটি সামলায় একবারে দুটি α বদলে আর Q-এর দরকারি সারি cache করে, তবু সময় সাধারণত n-এর সঙ্গে দ্বিঘাতের চেয়েও দ্রুত বাড়ে। অন্যদিকে primal-এর একটি subgradient ধাপের খরচ শুধু n × d.

তাই নিয়মটি মোটামুটি এমন। Kernel লাগলে dual, কারণ dual-এ ডেটা শুধু dot product হিসেবে আসে, আর সেখানে kernel বসানো যায় (পর্ব ২৯)। Linear SVM আর অনেক উদাহরণ হলে primal-ধাঁচের solver: Pegasos বা SGD, আর LIBLINEAR-এর dual coordinate descent, যা kernel ছাড়া dual-এর সুবিধা নেয় কিন্তু Q কখনো বানায় না। Pegasos প্রবন্ধের মূল দাবি ছিল, নির্দিষ্ট নির্ভুলতায় পৌঁছানোর সময় ডেটার আকারের ওপর সরাসরি নির্ভর করে না, যদিও তা λ-এর ওপর নির্ভর করে। d অনেক বড় আর ডেটা sparse হলে (TF-IDF টেক্সট), linear SVM এখনো একটি শক্ত baseline.

scikit-learn-এ এই পছন্দ ক্লাসের নামে লুকানো: SVC(kernel="linear") LIBSVM-এর dual, LinearSVC LIBLINEAR, SGDClassifier(loss="hinge") primal-এ stochastic ধাপ। তিনটির লক্ষ্য প্রায় এক হলেও বিবরণ আলাদা: intercept-এর penalty, default loss (LinearSVC-এ squared hinge), আর থামার নিয়ম। অংশ ৪-এর ডেস্কটপ run দেখিয়েছে, scale না করা ডেটায় এই ছোট পার্থক্য বড় হয়ে ওঠে।

মূল উপলব্ধি: dual kernel-এর দরজা খোলে কিন্তু n × n-এর দাম চায়; linear কাজে, বিশেষত বড় ডেটায়, primal-ধাঁচের solver প্রায়ই অনেক বেশি ব্যবহারিক।

প্র ০৪C বাছাই আর মূল্যায়ন একই cross-validation-এ করলে সমস্যা কী? বাজারঘরের মতো অসম ডেটায় পুরো পরিকল্পনা কেমন হবে?

GridSearchCV-এর best_score_ হলো অনেকগুলো C-এর মধ্যে সবচেয়ে ভালো CV গড়। সবচেয়ে ভালোটি বাছার কাজটিই একটি আশাবাদী পক্ষপাত আনে: কিছু C কাকতালীয়ভাবে ভালো fold পেয়েছে। Gavin Cawley আর Nicola Talbot ২০১০ সালে JMLR-এ দেখান যে model বাছাইয়ের এই overfitting তুলনার ফল পর্যন্ত বদলে দিতে পারে, বিশেষত ছোট ডেটায় আর অনেক hyperparameter-এ। তাই চূড়ান্ত সংখ্যা আসা উচিত আলাদা test থেকে, বা nested cross-validation থেকে (AI-এর ভিত্তি কোর্সের পর্ব ৬৬)।

অসম ডেটায় আরও তিনটি বিষয় যোগ হয়। এক, fold-গুলো stratified হতে হবে, নইলে কোনো fold-এ ফেরত-অর্ডার প্রায় থাকবেই না। দুই, score বাছাই: accuracy অসম ডেটায় বিভ্রান্তিকর, ব্লক ৪-এ ছোট C-এর model সবাইকে “ফেরত নয়” বলেও ৮৮% accuracy পেত। balanced accuracy, F1, বা খরচ-ভিত্তিক score বেছে নিন, আর কেন বেছেছেন তা লিখে রাখুন। তিন, class_weight আর C একসঙ্গে tune করুন, কারণ weight আসলে প্রতিটি শ্রেণির জন্য আলাদা C.

পুরো পরিকল্পনা: সময় ধরে ভাগ (পুরোনো মাস train, শেষ মাস test), কারণ ভবিষ্যতের অর্ডারেই model চলবে; train-এ stratified ৫-fold, Pipeline-এ scaler আর SVC, grid-এ C আর class_weight; score হিসেবে প্রত্যাশিত খরচ বা balanced accuracy; সেরা model test-এ একবার; সম্ভাবনা লাগলে আলাদা calibration ডেটা; আর logistic regression-এর baseline পাশে। ফলের পার্থক্য ছোট হলে পর্ব ২২-এর paired test দিয়ে দেখুন সত্যিই পার্থক্য আছে কি না।

মূল উপলব্ধি: C বাছাই নিজেই একটি শেখা, তাই তার মূল্যায়ন আলাদা ডেটায় হতে হয়; অসম ডেটায় score আর weight-এর পছন্দ C-এর চেয়েও বেশি গুরুত্বপূর্ণ হতে পারে।

প্র ০৫Deep learning-এর যুগে hinge loss আর margin-এর ধারণা কি হারিয়ে গেছে?

না, রূপ বদলে টিকে আছে। কয়েকটি উদাহরণ, প্রতিটির বিবরণ মূল প্রবন্ধে যাচাই করে নেবেন। Yichuan Tang ২০১৩ সালে (ICML-এর একটি workshop) neural network-এর শেষ স্তরে softmax-এর বদলে linear SVM-এর squared hinge loss ব্যবহার করে কিছু benchmark-এ সামান্য উন্নতির কথা জানান। মুখ চেনার কাজে Google-এর FaceNet (২০১৫) “triplet loss” ব্যবহার করে: একই মানুষের দুটি ছবির দূরত্ব অন্য মানুষের ছবির দূরত্বের চেয়ে অন্তত একটি margin কম হতে হবে, আর loss হলো max(০, … + margin), অর্থাৎ hinge-এর গঠন। পরে SphereFace, CosFace আর ArcFace (২০১৭–২০১৯)-এর মতো পদ্ধতি softmax-এর ভেতরে কোণের margin যোগ করে।

Ranking-এও একই ভাবনা: ভালো ফলাফলকে খারাপের চেয়ে অন্তত একটি margin বেশি score দিতে হবে, এই “pairwise hinge” অনেক search আর recommendation-এর শুরুর দিকের model-এ ব্যবহৃত হয়েছে। GAN-এর কিছু রূপে discriminator-এর loss-ও hinge আকারের। আর adversarial robustness-এর আলোচনায় margin আবার কেন্দ্রে: সীমারেখা থেকে বিন্দুর দূরত্ব যত বেশি, ছোট নড়চড়ে সিদ্ধান্ত বদলানো তত কঠিন।

যা মূলত হারিয়েছে, তা হলো dual আর kernel-এর পথ বড় ছবি বা টেক্সট ডেটায়, কারণ n × n ম্যাট্রিক্স আর হাতে বানানো feature-এর সীমা। কিন্তু ছোট ও মাঝারি tabular ডেটা, বা ভালো feature থাকলে, linear বা kernel SVM এখনো একটি যুক্তিসঙ্গত baseline. বাজারঘরের মতো দোকানে যেখানে অর্ডার কয়েক হাজার, সেখানে এক সেকেন্ডে শেখা একটি SVM অনেক সময় বড় model-এর সঙ্গে তুলনার প্রথম মাপকাঠি।

মূল উপলব্ধি: “নিরাপদ দূরত্ব রেখে ঠিক হও, তার কম হলে রৈখিক জরিমানা” এই hinge-এর ভাবনা SVM ছাড়িয়ে আধুনিক loss-এর একটি মৌলিক নকশা হয়ে আছে।

অনুশীলন

হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে অংশ ২, ৪, ৭ আর ৮-এর ব্লক বদলে যাচাই করুন।

  1. পূর্ণ numerical: একটি linear SVM-এ w = (২, −১), b = −১, C = ২. চারটি বিন্দু: P (৩, ২), y = +১; Q (১, ০), y = +১; R (০, −১), y = −১; S (১, ১), y = −১. (ক) প্রতিটির y·f আর ξ (খ) primal লক্ষ্যের মান (গ) margin-এর চওড়া (ঘ) কোন বিন্দু ভুল শ্রেণিতে?

    (ক) f = ২x১ − x২ − ১. P: ৬ − ২ − ১ = ৩, y·f = ৩, ξ = ০. Q: ২ − ০ − ১ = ১, y·f = ১, ξ = ০ (ঠিক margin-এ)। R: ০ + ১ − ১ = ০, y·f = ০, ξ = ১. S: ২ − ১ − ১ = ০, y·f = ০, ξ = ১.

    (খ) ‖w‖² = ৪ + ১ = ৫, ½‖w‖² = ২.৫; ∑ξ = ২; লক্ষ্য = ২.৫ + ২ × ২ = ৬.৫.

    (গ) ২/‖w‖ = ২/√৫ ≈ ০.৮৯৪.

    (ঘ) কোনোটিই স্পষ্ট ভুল দিকে নয় (y·f < ০ নেই); R আর S ঠিক সীমারেখায় (f = ০), তাই তাদের শ্রেণি অনির্ধারিত। এই w, b যদি সর্বোত্তম সমাধান হয়, তবে y·f < ১ বলে R আর S-এর α = C.

  2. সমালোচনা ও নকশা: একজন সহকর্মী লিখলেন: “পুরো ডেটায় StandardScaler().fit_transform করেছি, তারপর ৮০/২০ split. C = ০.০১, ০.১ … ১০০০ চালিয়ে test accuracy সবচেয়ে বেশি C = ১০০০-এ, ৯৪%. ফেরত মাত্র ৬%. Model-এর decision_function-কে sigmoid দিয়ে ফেরতের সম্ভাবনা বলে dashboard-এ দেখাব।” কী কী সমস্যা, আর সঠিক নকশা কী?

    সমস্যা: (১) Split-এর আগে পুরো ডেটায় scaler: test-এর গড় আর বিস্তার train-এ ঢুকেছে, ছোট হলেও leakage. (২) Test দেখে C বাছা: ৯৪% একটি আশাবাদী সংখ্যা, আর এখন কোনো নিরপেক্ষ test নেই। (৩) ফেরত ৬% হলে “সবাই ফেরত নয়” বললেই ৯৪%: accuracy কিছুই প্রমাণ করে না; হয়তো model ঠিক তাই করছে। (৪) C = ১০০০ সবচেয়ে সরু margin, অসম ডেটায় overfit বা অর্থহীন হতে পারে। (৫) Sigmoid(decision_function) সম্ভাবনা নয়।

    সঠিক নকশা: আগে split (সম্ভব হলে সময় ধরে), তারপর Pipeline(StandardScaler, SVC) আর stratified CV-তে C ও class_weight একসঙ্গে GridSearchCV, score balanced accuracy বা খরচ-ভিত্তিক। Test-এ একবার, recall আর precision আলাদা করে। Dashboard-এ সম্ভাবনা লাগলে আলাদা ডেটায় Platt বা isotonic calibration, আর reliability diagram দিয়ে যাচাই।

  3. আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে।
    import numpy as np
    from sklearn.svm import SVC
    X = np.array([[0], [1], [2], [3]])
    y = np.array([-1, -1, 1, 1])
    m = SVC(kernel="linear", C=10).fit(X, y)
    print(m.support_, m.dual_coef_, m.coef_, m.intercept_)
    [1 2] [[-2.  2.]] [[2.]] [-3.]

    ডেটা আলাদা করা যায়, আর সবচেয়ে কাছের দুটি বিন্দু x = ১ (−১) আর x = ২ (+১)। দুজনেই margin-এ: w + b = −১ আর ২w + b = +১, তাই w = ২, b = −৩. Dual: ∑αiyi = ০ থেকে α১ = α২ = α, আর w = −α × ১ + α × ২ = α = ২. dual_coef_-এ yiαi: −২ আর ২. α = ২ < C = ১০, তাই soft margin এখানে hard margin-এর সমান (ডেস্কটপে scikit-learn 1.8.0-এ মেলানো)। C = ০.৫ দিলে α ≤ ০.৫ সীমায় এই সমাধান আর সম্ভব নয়: তখন চারটি বিন্দুই support vector, w = ২/৩, b = −১ হয়।

  4. Dual আর λ, হাতে: (ক) একটি SVM-এর সমাধানে তিনটি support vector: x১ = (১, ২), y = +১, α = ০.৩; x২ = (২, ০), y = +১, α = ০.২; x৩ = (০, ০), y = −১, α = ০.৫; C = ০.৫. ∑αiyi = ০ মেলে কি? w কত? মুক্ত support vector কোনগুলো, আর তাদের থেকে b কত? (খ) n = ২,০০০, C = ০.০৫ হলে hinge + L2-এর গড় রূপে λ কত, আর SGDClassifier-এ alpha কত দেবেন?

    (ক) ∑αiyi = ০.৩ + ০.২ − ০.৫ = ০, মেলে। w = ০.৩(১, ২) + ০.২(২, ০) − ০.৫(০, ০) = (০.৩ + ০.৪, ০.৬) = (০.৭, ০.৬). মুক্ত: x১ আর x২ (০ < α < ০.৫); x৩-এর α = C, তাই তা থেকে b নেওয়া যাবে না। x১: b = ১ − (০.৭ + ১.২) = −০.৯; x২: b = ১ − ১.৪ = −০.৪. দুটি আলাদা! অর্থাৎ এই α-গুলো আসলে সর্বোত্তম সমাধান নয়: সত্যিকারের সমাধানে সব মুক্ত support vector একই b দেয়। এটিই যাচাইয়ের মূল্য; AI বা বইয়ের দেওয়া α-ও এভাবে পরীক্ষা করুন।

    (খ) λ = ১/(nC) = ১/(২০০০ × ০.০৫) = ০.০১, তাই alpha=0.01 (scikit-learn-এর SGD-তে লক্ষ্য (১/n)∑loss + alpha × ½‖w‖²)। তবে SGD-এর ধাপের নিয়ম আর intercept-এর ব্যবস্থা আলাদা, তাই ফল SVC-এর কাছাকাছি হবে, হুবহু নয়।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি linear soft-margin SVM শেখানো আর যাচাইয়ের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. ডেটা train আর test-এ ভাগ করা, test তালাবন্ধ রাখা
  2. StandardScaler আর SVC একটি Pipeline-এ রাখা
  3. Train-এ stratified CV দিয়ে C (আর class_weight) বাছা
  4. সেরা C-তে পুরো train-এ আবার শেখানো
  5. Support vector, α = C-এর সংখ্যা আর margin পরীক্ষা করা
  6. Test-এ একবার মূল্যায়ন, প্রতিটি শ্রেণির recall আর precision-সহ

w = (৩, ৪), b = −২ হলে একটি বিন্দু x = (১, ০), y = −১-এর slack ξ কত, আর margin-এর চওড়া কত?

  • ξ = ০, margin ০.৪
  • ξ = ২, margin ০.২
  • ξ = ২, margin ০.৪
  • ξ = ১, margin ০.৪

একই ডেটায় linear SVM-এর C ০.০১ থেকে ১০০-তে বাড়ালে সাধারণত কী ঘটে?

  • Margin চওড়া হয়, support vector বাড়ে
  • Margin সরু হয়, support vector কমে, training ভুল সাধারণত কমে
  • Test accuracy সবসময় বাড়ে
  • কিছুই বদলায় না, কারণ সীমারেখা ডেটা ঠিক করে

Soft-margin SVM-এর dual-এ hard-margin dual-এর তুলনায় কোন পরিবর্তন আসে?

  • Dual-এর লক্ষ্যে ∑ξi যোগ হয়
  • ∑αiyi = ০ শর্তটি উঠে যায়
  • w-এর সূত্র বদলে যায়
  • প্রতিটি αi-এর ওপর উপরিসীমা C আসে: ০ ≤ αi ≤ C

একটি soft-margin SVM-এর সর্বোত্তম সমাধানে একটি বিন্দুর αi = C. তার yif(xi) সম্পর্কে কী নিশ্চিত বলা যায়?

  • yif(xi) ≤ ১
  • yif(xi) = ১ ঠিক
  • yif(xi) < ০, বিন্দুটি অবশ্যই ভুল শ্রেণিতে
  • yif(xi) ≥ ১

½‖w‖² + C∑max(০, ১ − yif(xi)) লক্ষ্যটিকে (λ/২)‖w‖² + (১/n)∑max(০, ১ − yif(xi)) রূপে লিখলে λ কত?

  • λ = C
  • λ = ১/(nC)
  • λ = ১/C
  • λ = nC

একটি binary SVC শেখানোর পর dual_coef_ = [[−০.৫, −০.২, ০.৭]], আর C = ০.৫. কোন বিবৃতিটি ঠিক?

  • প্রথম দুটি support vector-এর α ঋণাত্মক, তাই তারা বাতিল
  • তিনটিই মুক্ত support vector
  • এই মানগুলো অসম্ভব, কারণ একটি α C-এর চেয়ে বড়
  • ∑αiyi ≠ ০, তাই সমাধানটি ভুল

ফেরত-অর্ডার মাত্র ১০%. Linear SVC-তে class_weight="balanced" দিলে মূলত কী বদলায়?

  • ছোট শ্রেণির প্রতিটি বিন্দুর কার্যকর C বড় হয়, তাই তার ভুল বেশি দামি
  • ছোট শ্রেণির বিন্দুগুলো নকল করে ডেটায় যোগ হয়
  • decision_function সঠিক সম্ভাবনা দিতে শুরু করে
  • সেরা C আর কখনো বদলায় না

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Primal: min ½‖w‖² + C∑ξi, শর্ত yi(w·xi + b) ≥ ১ − ξi, ξi ≥ ০. ξ = ০ বাইরে, ০–১ ভেতরে, > ১ ভুল দিকে; margin ২/‖w‖.
  2. C: ছোট C চওড়া margin, বেশি support vector, বেশি bias; বড় C সরু margin, বেশি variance. C যোগফলে গুণ হয়, তাই n বদলালে তার অর্থও বদলায়।
  3. Hinge রূপ: (λ/২)‖w‖² + (১/n)∑max(০, ১ − yif(xi)), λ = ১/(nC); subgradient-এ শুধু y·f < ১ বিন্দুগুলো ঢাল দেয়। LinearSVC-এ default squared hinge, আর b-ও penalty পায়।
  4. Dual: max ∑α − ½∑∑αiαjyiyjxi·xj, শর্ত ০ ≤ α ≤ C, ∑αy = ০; w = ∑αyx, b মুক্ত support vector থেকে। dual_coef_ = yiαi.
  5. অভ্যাস: Pipeline-এ scaler, train-এর CV-তে C (আর class_weight), test একবার; score সম্ভাবনা নয়, লাগলে আলাদা ডেটায় calibration.

পরের পর্বে: পর্ব ২৯ (Kernel trick — RBF, polynomial আর Mercer-এর অন্তর্দৃষ্টি)। অংশ ৫-এ দেখেছেন, dual-এ ডেটা আসে শুধু xi·xj হিসেবে। সেই dot product-কে একটি kernel দিয়ে বদলালে একই QP বাঁকা সীমারেখা শেখে, নতুন feature না বানিয়েই।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান