本文將首先簡要概述支持向量機(jī)及其訓(xùn)練和推理方程,然后將其轉(zhuǎn)換為代碼以開發(fā)支持向量機(jī)模型。之后然后將其擴(kuò)展成多分類的場景,并通過使用Sci-kit Learn測試我們的模型來結(jié)束。
SVM概述
支持向量機(jī)的目標(biāo)是擬合獲得最大邊緣的超平面(兩個(gè)類中最近點(diǎn)的距離)??梢灾庇^地表明,這樣的超平面(A)比沒有最大化邊際的超平面(B)具有更好的泛化特性和對噪聲的魯棒性。















Python實(shí)現(xiàn)
對于實(shí)現(xiàn),我們將使用下面這些庫:
import numpy as np # for basic operations over arrays
from scipy.spatial import distance # to compute the Gaussian kernel
import cvxopt # to solve the dual opt. problem
import copy # to copy numpy arrays
定義核和SVM超參數(shù),我們將實(shí)現(xiàn)常見的三個(gè)核函數(shù):

class SVM:
linear = lambda x, x? , c=0: x @ x?.T
polynomial = lambda x, x? , Q=5: (1 + x @ x?.T)**Q
rbf = lambda x, x?, γ=10: np.exp(-γ*distance.cdist(x, x?,'sqeuclidean'))
kernel_funs = {'linear': linear, 'polynomial': polynomial, 'rbf': rbf}
為了與其他核保持一致,線性核采用了一個(gè)額外的無用的超參數(shù)。kernel_funs接受核函數(shù)名稱的字符串,并返回相應(yīng)的內(nèi)核函數(shù)。
繼續(xù)定義構(gòu)造函數(shù):class SVM:
linear = lambda x, x? , c=0: x @ x?.T
polynomial = lambda x, x? , Q=5: (1 + x @ x?.T)**Q
rbf = lambda x, x?, γ=10: np.exp(-γ*distance.cdist(x, x?,'sqeuclidean'))
kernel_funs = {'linear': linear, 'polynomial': polynomial, 'rbf': rbf}
def __init__(self, kernel='rbf', C=1, k=2):
# set the hyperparameters
self.kernel_str = kernel
self.kernel = SVM.kernel_funs[kernel]
self.C = C # regularization parameter
self.k = k # kernel parameter
# training data and support vectors (set later)
self.X, y = None, None
self.αs = None
# for multi-class classification (set later)
self.multiclass = False
self.clfs = []
SVM有三個(gè)主要的超參數(shù),核(我們存儲給定的字符串和相應(yīng)的核函數(shù)),正則化參數(shù)C和核超參數(shù)(傳遞給核函數(shù));它表示多項(xiàng)式核的Q和RBF核的γ。
為了兼容sklearn的形式,我們需要使用fit和predict函數(shù)來擴(kuò)展這個(gè)類,定義以下函數(shù),并在稍后將其用作裝飾器:
擬合SVM對應(yīng)于通過求解對偶優(yōu)化問題找到每個(gè)點(diǎn)的支持向量α:







@SVMClass
def fit(self, X, y, eval_train=False):
# if more than two unique labels, call the multiclass version
if len(np.unique(y)) > 2:
self.multiclass = True
return self.multi_fit(X, y, eval_train)
# if labels given in {0,1} change it to {-1,1}
if set(np.unique(y)) == {0, 1}: y[y == 0] = -1
# ensure y is a Nx1 column vector (needed by CVXOPT)
self.y = y.reshape(-1, 1).astype(np.double) # Has to be a column vector
self.X = X
N = X.shape[0] # Number of points
# compute the kernel over all possible pairs of (x, x') in the data
# by Numpy's vectorization this yields the matrix K
self.K = self.kernel(X, X, self.k)
### Set up optimization parameters
# For 1/2 x^T P x + q^T x
P = cvxopt.matrix(self.y @ self.y.T * self.K)
q = cvxopt.matrix(-np.ones((N, 1)))
# For Ax = b
A = cvxopt.matrix(self.y.T)
b = cvxopt.matrix(np.zeros(1))
# For Gx <= h
G = cvxopt.matrix(np.vstack((-np.identity(N),
np.identity(N))))
h = cvxopt.matrix(np.vstack((np.zeros((N,1)),
np.ones((N,1)) * self.C)))
# Solve
cvxopt.solvers.options['show_progress'] = False
sol = cvxopt.solvers.qp(P, q, G, h, A, b)
self.αs = np.array(sol["x"]) # our solution
# a Boolean array that flags points which are support vectors
self.is_sv = ((self.αs-1e-3 > 0)&(self.αs <= self.C)).squeeze()
# an index of some margin support vector
self.margin_sv = np.argmax((0 < self.αs-1e-3)&(self.αs < self.C-1e-3))
if eval_train:
print(f"Finished training with accuracy{self.evaluate(X, y)}")
我們確保這是一個(gè)二進(jìn)制問題,并且二進(jìn)制標(biāo)簽按照支持向量機(jī)(±1)的假設(shè)設(shè)置,并且y是一個(gè)維數(shù)為(N,1)的列向量。然后求解求解(α?α?…α _n) 的優(yōu)化問題。
使用(α?α?…α _n) _來獲得在與支持向量對應(yīng)的任何索引處為1的標(biāo)志數(shù)組,然后可以通過僅對支持向量和(x?,y?)的邊界支持向量的索引求和來應(yīng)用預(yù)測方程。我們確實(shí)假設(shè)非支持向量可能不完全具有α=0,如果它的α≤1e-3,那么這是近似為零(CVXOPT結(jié)果可能不是最終精確的)。同樣假設(shè)非邊際支持向量可能不完全具有α=C。
下面就是預(yù)測的方法,預(yù)測方程為:

@SVMClass
def predict(self, X_t):
if self.multiclass: return self.multi_predict(X_t)
# compute (x?, y?)
x?, y? = self.X[self.margin_sv, np.newaxis], self.y[self.margin_sv]
# find support vectors
αs, y, X= self.αs[self.is_sv], self.y[self.is_sv], self.X[self.is_sv]
# compute the second term
b = y? - np.sum(αs * y * self.kernel(X, x?, self.k), axis=0)
# compute the score
score = np.sum(αs * y * self.kernel(X, X_t, self.k), axis=0) + b
return np.sign(score).astype(int), score
我們還可以實(shí)現(xiàn)一個(gè)評估方法來計(jì)算精度(在上面的fit中使用)。@SVMClass
def evaluate(self, X,y):
outputs, _ = self.predict(X)
accuracy = np.sum(outputs == y) / len(y)
return round(accuracy, 2)
最后測試我們的完整代碼:from sklearn.datasets import make_classification
import numpy as np
# Load the dataset
np.random.seed(1)
X, y = make_classification(n_samples=2500, n_features=5,
n_redundant=0, n_informative=5,
n_classes=2, class_sep=0.3)
# Test Implemented SVM
svm = SVM(kernel='rbf', k=1)
svm.fit(X, y, eval_train=True)
y_pred, _ = svm.predict(X)
print(f"Accuracy: {np.sum(y==y_pred)/y.shape[0]}") #0.9108
# Test with Scikit
from sklearn.svm import SVC
clf = SVC(kernel='rbf', C=1, gamma=1)
clf.fit(X, y)
y_pred = clf.predict(X)
print(f"Accuracy: {sum(y==y_pred)/y.shape[0]}") #0.9108
多分類SVM
我們都知道SVM的目標(biāo)是二元分類,如果要將模型推廣到多類則需要為每個(gè)類訓(xùn)練一個(gè)二元SVM分類器,然后對每個(gè)類進(jìn)行循環(huán),并將屬于它的點(diǎn)重新標(biāo)記為+1,并將所有其他類的點(diǎn)重新標(biāo)記為-1。 當(dāng)給定k個(gè)類時(shí),訓(xùn)練的結(jié)果是k個(gè)分類器,其中第i個(gè)分類器在數(shù)據(jù)上進(jìn)行訓(xùn)練,第i個(gè)分類器被標(biāo)記為+1,所有其他分類器被標(biāo)記為-1。
@SVMClass
def multi_fit(self, X, y, eval_train=False):
self.k = len(np.unique(y)) # number of classes
# for each pair of classes
for i in range(self.k):
# get the data for the pair
Xs, Ys = X, copy.copy(y)
# change the labels to -1 and 1
Ys[Ys!=i], Ys[Ys==i] = -1, +1
# fit the classifier
clf = SVM(kernel=self.kernel_str, C=self.C, k=self.k)
clf.fit(Xs, Ys)
# save the classifier
self.clfs.append(clf)
if eval_train:
print(f"Finished training with accuracy {self.evaluate(X, y)}")
然后,為了對新示例執(zhí)行預(yù)測,我們選擇相應(yīng)分類器最自信(得分最高)的類。@SVMClass
def multi_predict(self, X):
# get the predictions from all classifiers
N = X.shape[0]
preds = np.zeros((N, self.k))
for i, clf in enumerate(self.clfs):
_, preds[:, i] = clf.predict(X)
# get the argmax and the corresponding score
return np.argmax(preds, axis=1), np.max(preds, axis=1)
完整測試代碼:from sklearn.datasets import make_classification
import numpy as np
# Load the dataset
np.random.seed(1)
X, y = make_classification(n_samples=500, n_features=2,
n_redundant=0, n_informative=2,
n_classes=4, n_clusters_per_class=1,
class_sep=0.3)
# Test SVM
svm = SVM(kernel='rbf', k=4)
svm.fit(X, y, eval_train=True)
y_pred = svm.predict(X)
print(f"Accuracy: {np.sum(y==y_pred)/y.shape[0]}") # 0.65
# Test with Scikit
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC
clf = OneVsRestClassifier(SVC(kernel='rbf', C=1, gamma=4)).fit(X, y)
y_pred = clf.predict(X)
print(f"Accuracy: {sum(y==y_pred)/y.shape[0]}") # 0.65
繪制每個(gè)決策區(qū)域的圖示,得到以下圖:

總結(jié)
我們使用Python實(shí)現(xiàn)了支持向量機(jī)(SVM)學(xué)習(xí)算法,并且包括了軟邊界和常用的三個(gè)核函數(shù)。我們還將SVM擴(kuò)展到多分類的場景,并使用Sci-kit Learn驗(yàn)證了我們的實(shí)現(xiàn)。希望通過本文你可以更好的了解SVM。
-
算法
+關(guān)注
關(guān)注
23文章
4701瀏覽量
94853 -
向量機(jī)
+關(guān)注
關(guān)注
0文章
166瀏覽量
21162 -
SVM
+關(guān)注
關(guān)注
0文章
154瀏覽量
32966 -
python
+關(guān)注
關(guān)注
56文章
4825瀏覽量
86281
原文標(biāo)題:使用Python從零實(shí)現(xiàn)多分類SVM
文章出處:【微信號:vision263com,微信公眾號:新機(jī)器視覺】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
在stm32上實(shí)現(xiàn)svm實(shí)時(shí)訓(xùn)練與分類
如何在stm32上實(shí)現(xiàn)svm實(shí)時(shí)訓(xùn)練與分類?
集成學(xué)習(xí)的多分類器動態(tài)組合方法
基于SVM的兩級指紋分類研究
基于GA-SVM的帶鋼表面缺陷模式識別
基于優(yōu)化SVM模型的網(wǎng)絡(luò)負(fù)面信息分類方法研究
采用SVM的網(wǎng)頁分類方法研究
多分類孿生支持向量機(jī)研究進(jìn)展
支持向量機(jī)多分類的眼電輔助肌電的人機(jī)交互
SVM分類器實(shí)驗(yàn)的資料和工程文件資料免費(fèi)下載
可檢測網(wǎng)絡(luò)入侵的IL-SVM-KNN分類器

cifar10數(shù)據(jù)集介紹 knn和svm的圖像分類系統(tǒng)案例
怎樣使用Python從零實(shí)現(xiàn)多分類SVM呢?

評論