機(jī)器學(xué)習(xí)(六)——支持向量機(jī)

一、支持向量機(jī)的一般原理

支持向量機(jī)跟邏輯回歸比較像??梢哉f,支持向量機(jī)是邏輯回歸的一種優(yōu)化或者擴(kuò)展。因此,雖然說支持向量機(jī)既可以處理分類問題,也可以處理回歸問題,但是它一般還是主要用于分類問題。而且其本質(zhì)是處理二元分類問題。
支持向量機(jī)的理念也非常簡單,從它的別名“大間距分類器”我們也能窺知一二。簡單點(diǎn)說,支持向量機(jī)就是找到這么一個(決策)邊界(或者理解成分界線,專業(yè)點(diǎn)說,叫超平面),既能將兩個分類進(jìn)行完美的區(qū)分,又能使得該邊界距離兩個分類之間的間距達(dá)到最大。(因?yàn)橹挥虚g距最大,才更具有魯棒性,分類器的效果才最好)。


那么如何找到這個“超平面”呢?

現(xiàn)在我們先回到邏輯回歸。在邏輯回歸中,因?yàn)閿?shù)據(jù)要分成兩類,我們是用0,1來進(jìn)行的兩類區(qū)分。而且為了最后使得h_\theta(x)的值能落在0,1之間,我們引入了邏輯函數(shù)g(z)=\frac{1}{1+e^{-z}},并且給g(z)賦予了一個意義,即他代表了分類結(jié)果為1時的概率。函數(shù)值越靠近1,代表分類結(jié)果為1的可能性越大。函數(shù)值越靠近0,代表分類結(jié)果為0的可能性越大。此外,h_\theta(x)只和\theta^Tx有關(guān),\theta^Tx>0,那么h_\theta(x)>0.5,而g(z)只是用來映射,真實(shí)的類別決定權(quán)還是在于\theta^Tx。再者,當(dāng)\theta^Tx>>0時,h_\theta(x)=1,反之h_\theta(x)=0。如果我們只從\theta^Tx出發(fā),希望模型達(dá)到的目標(biāo)就是讓訓(xùn)練數(shù)據(jù)中y=1的特征\theta^Tx>>0,而是y=0的特征\theta^Tx<<0。Logistic回歸就是要學(xué)習(xí)得到\theta,使得正例的特征遠(yuǎn)大于0,負(fù)例的特征遠(yuǎn)小于0,而且要在全部訓(xùn)練實(shí)例上達(dá)到這個目標(biāo)。

在svm中,我們對上述邏輯回歸稍微做一點(diǎn)點(diǎn)調(diào)整。首先,我們還是講數(shù)據(jù)分為兩類,但是我們用-1,1來進(jìn)行區(qū)分。因此,我們希望引入一個新的g(z),能夠使得映射后的值落在[-1,1] 之間。即:g(z)=\left\{\begin{array}\\1\qquad z >0 \\-1\qquad z<0 \\ \end{array}\right. 。注意,這個新的映射函數(shù)g(z) 是一個分段函數(shù),而不是之前在邏輯回歸中的連續(xù)函數(shù)。換言之,在進(jìn)行分類的時候,遇到一個新的數(shù)據(jù)點(diǎn)x,將x代入f(x) 中,如果f(x)小于0則將x的類別賦為-1,如果f(x)大于0則將x的類別賦為1。

現(xiàn)在,我們假設(shè)已經(jīng)找到了這個超平面,該超平面我們用 f(x)=w^Tx+b=0來表示。根據(jù)前面的介紹,我們知道一定會滿足如下幾點(diǎn):
1)在分類為1的所有樣本中,一定存在一個點(diǎn),其距離超平面的距離最近。我們設(shè)為x_0;
2)在分類為-1的所有樣本中,一定存在一個點(diǎn),其距離超平面的距離最近。我們設(shè)為x_1;
3)將x_0帶入到上面的式子中,得到的f(x_0),一定大于0。且其他所有分類為1的樣本點(diǎn),都有f(x_n) \ge f(x_0);
4)同理,將x_1帶入到上面的式子中,得到的f(x_1),一定小于0。且其他所有分類為-1的樣本點(diǎn),都有f(x_m) \leq f(x_1);
5)對于x_0或者x_1到超平面f(x) 的函數(shù)距離|f(x_0)|,|f(x_1)|,無論是多少,都不會影響超平面f(x)=0的存在。即,如果成比例的改變w和b,函數(shù)距離|f(x_0)|,|f(x_1)|也會成比例改變,但是,對于我們要求解的超平面是不會有影響的;
6)根據(jù)點(diǎn)到直線的距離公式,我們可以知道x_0到超平面f(x) 的幾何距離為:\frac{|f(x_0)|}{||w||}。(x_1是一樣的),其中||w||=\sqrt{w_1^2+w_2^2+...+w_n^2};

綜合以上,
1)為了去掉絕對值,我們可以將x_0或者x_1到超平面f(x) 的函數(shù)距離|f(x_0)|,|f(x_1)|改寫為:y_i*f(x_i)。
2)根據(jù)5)我們可以知道,無論y_i*f(x_i)為多少,都不會影響超平面的優(yōu)化,因此,為了后面便于計算,我們可以假定y_0*f(x_0)=1。(當(dāng)然,你也可以假定為任何值,但是如上所述,都不會影響超平面的優(yōu)化。既然如此,為啥我們不簡單點(diǎn),假定為1呢?)
3)根據(jù)svm的思想,我們最終得到svm的目標(biāo)函數(shù):即,求解 \frac{|f(x_0)|}{||w||} = \frac{y_0*f(x_0)}{||w||}=\frac{1}{||w||} 的最大值,也就等價于最小化\frac{1}{2}||w||^2(1/2只是為了后面求導(dǎo)方便,對結(jié)果沒有影響)。同時還需要滿足一個約束,即y_i*f(x_i) \ge y_0*f(x_0)=1

所以,SVM的目標(biāo)函數(shù)為: min \ \ \frac{1}{2}||w||^2 \qquad s.t. \ \ y_i(w^Tx_i+b) \ge 1

關(guān)于如何求解上述目標(biāo)函數(shù),細(xì)節(jié)就不在這里談了??梢曰仡櫼幌逻\(yùn)籌學(xué)中所教的方法。網(wǎng)上也有相關(guān)的文章介紹,有興趣的同學(xué)可以去參考。

這里在啰嗦幾句:
我們知道:兩個向量的內(nèi)積,就是一個向量在另外一個向量上的投影長度,乘以另外那個向量的長度(范式)。(不知道的同學(xué),直接記住就好)。
根據(jù)此特點(diǎn),w^Tx_i = P_i * ||w||,其中,P_i就是x_i在w上的投影長度。那么原來的約束就可以變?yōu)椋?img class="math-inline" src="https://math.jianshu.com/math?formula=P_i*%7C%7Cw%7C%7C%20%5Cge%201%20%5C%20%5C%20if%20%5C%20%5C%20y_i%20%3D%201" alt="P_i*||w|| \ge 1 \ \ if \ \ y_i = 1" mathimg="1">,或則P_i*||w|| \leq -1 \ \ if \ \ y_i = -1。 注意,這里我們省略了b,不影響實(shí)際的求解結(jié)果。
我們要求||w||的最小值,我們就需要讓P_i越大越好。我們通過旋轉(zhuǎn)超平面的角度,直到找到一個超平面,能夠滿足P_i最大,也就是x_i在w上的投影長度最大,就是我們要找的超平面。 這從幾何意義上也解釋了上述目標(biāo)函數(shù)的由來。

二、軟間隔

在實(shí)際應(yīng)用中,完全線性可分的樣本是很少的,如果遇到了不能夠完全線性可分的樣本,我們應(yīng)該怎么辦?比如下面這個:


于是我們就有了軟間隔,相比于硬間隔的苛刻條件,我們允許個別樣本點(diǎn)出現(xiàn)在間隔帶里面,比如:

我們允許部分樣本點(diǎn)不滿足約束條件:y_i(w^Tx_i+b) \ge 1
根據(jù)我們在運(yùn)籌學(xué)中學(xué)到的方法,我們可以引入一個松弛變量\xi_i,令\xi_i \ge 0,即y_i(w^Tx_i+b)+\xi_i \ge 1

增加軟間隔后我們的優(yōu)化目標(biāo)變成了:min \ \ \frac{1}{2}||w||^2+C\sum_{i=1}^m\xi_i \qquad s.t. \ \ y_i(w^Tx_i+b)+\xi_i \ge 1
這里的C,就跟之前過擬合中的\lambda一樣,是一個懲罰因子。如果C設(shè)的很大,\xi 就會很小,就相當(dāng)于是硬間隔,要求完全線形可分。反之,如果C設(shè)得比較小,\xi 就會相對較大,此時將會允許有跟多的樣本點(diǎn)不滿足約束條件。

三、核函數(shù)

還有一些數(shù)據(jù)沒法直接用一條直線分開,這類數(shù)據(jù)叫做非線性可分?jǐn)?shù)據(jù)。比如下面這個圖,一維數(shù)據(jù)紅點(diǎn)和藍(lán)點(diǎn)位于同一條線上,我們是找不到分界點(diǎn)將紅點(diǎn)和藍(lán)點(diǎn)分開的。


對于這種問題,我們也是有辦法解決的,如果我們用函數(shù)f(x)=x^2來映射這幾個數(shù)據(jù),增加一個維度,將一維數(shù)據(jù)轉(zhuǎn)換為二維數(shù)據(jù),紅點(diǎn)就變成(-1, 1)和(1, 1),而藍(lán)點(diǎn)變成(-3, 9)和(3, 9)。這樣紅點(diǎn)和藍(lán)點(diǎn)就變成線性可分的,可以很容易地找到?jīng)Q策邊界。

所以可以將低維空間中的非線性可分離數(shù)據(jù),映射到高維空間,就可能得到線性可分離數(shù)據(jù)。
這就如同我們在邏輯回歸中提到的一樣,我們可以使用高級數(shù)的多項(xiàng)式模型來解決無法用直線進(jìn)行分隔的分類問題:

為了獲得上圖所示的判定邊界,我們的模型可能是\theta_0+\theta_1x_1+\theta_2x_2+\theta_3x_1x_2+\theta_4x_1^2+\theta_5x_2^2+...的形式。

但是這里有個問題:原來的特征是低維的,我們?nèi)藶榈臉?gòu)造了一些特征,讓其變成高維的了。那到底要如何構(gòu)造特征,才能確保我們到了高維后,一定線形可分呢? 換句話說,按上面的例子,你如何知道要變成f(x)=x^2,就可以線形可分了?為什么不是其他的形式?

注意,這里跟我們之前學(xué)的PCA恰好相反。pca是降維,而我們這里是要通過對特征的一些組合變換,構(gòu)造出一些新的特征。通過人為的增加特征的維度來讓原來線形不可分變?yōu)榫€形可分。

我們知道,如果我們要引入高階特征,我們的維度將會呈指數(shù)增加,而且我們也不能保證我們引入了高階特征后,一定會線形可分。那如何構(gòu)造新的特征呢?這里我們介紹一個新的構(gòu)造特征的思路:即引入某種函數(shù),這些函數(shù)用來計算當(dāng)前訓(xùn)練樣本與給定標(biāo)記之間的相似度。如果我們給定了5個標(biāo)記,那么就會構(gòu)造出5個新的特征。這些變換過的特征,代表當(dāng)前所有訓(xùn)練樣本,與這5個標(biāo)記之間的相似程度。這就是核函數(shù)的思想。

那么,如何選定標(biāo)記呢? 一個最簡單的做法,即是將訓(xùn)練集的所有樣本作為標(biāo)記。一條樣本代表一個標(biāo)記,有m個樣本,就有m個標(biāo)記。那么轉(zhuǎn)換后的特征就有m個(實(shí)際是m+1個,我們這里不考慮截距項(xiàng))。我們要做的事情,就是用交叉驗(yàn)證或者測試集或者直接再用訓(xùn)練集來計算出m個特征的參數(shù)\theta

例如,對于高斯核函數(shù),假設(shè)我們選定了m個特征,有n個訓(xùn)練樣本,那么高斯核函數(shù)就是用下面這個公式來計算相似度:
K(x^{(n)},x^{(m)}) = exp(-\frac{||x^{(n)}-x^{(m)}||^2}{2\sigma^2})

當(dāng)然,計算相似度有很多種方法,你也可以使用線形核函數(shù):
K(x^{(n)},x^{(m)}) = x^{(n)}*x^{(m)}

或者多項(xiàng)式核函數(shù):
K(x^{(n)},x^{(m)}) = (\gamma x^{(n)}*x^{(m)}+c)^d

其原理都是類似,只是計算的方法不同而已。

從數(shù)學(xué)角度上來說,回顧上面內(nèi)容,我們的任務(wù)是找出合適的參數(shù)w,b,使得分割超平面間距最大,且能正確對數(shù)據(jù)進(jìn)行分類。間距最大是我們的優(yōu)化目標(biāo)。正確對數(shù)據(jù)進(jìn)行分類是約束條件。即在滿足約束條件y_i(w^Tx_i+b) \ge 1的前提下,求解||w||^2的最小值。

為了去掉約束條件,便于我們求解,我們使用拉格朗日乘子法。其方法是引入非負(fù)系數(shù)α來作為約束條件的權(quán)重:
L(w,b,\lambda)=\frac{1}{2}||w||^2+\sum_{i=1}^m \lambda_i(1-y_i(w^Tx_i+b))
對這個函數(shù)求偏導(dǎo),并令偏導(dǎo)數(shù)為0得到:
對w求偏導(dǎo):w-\sum_{i=1}^m \lambda_iy_ix_i=0 => w=\sum_{i=1}^m \lambda_iy_ix_i
對b求偏導(dǎo):-\sum_{i=1}^m \lambda_iy_i=0 => \sum_{i=1}^m \lambda_iy_i=0
w=\sum_{i=1}^m \lambda_iy_ix_i代入拉格朗日函數(shù),并考慮約束條件\sum_{i=1}^m \lambda_iy_i=0,求對偶問題,得到:
max \ \ \sum_{i=1}^m\lambda_i-\frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\lambda_i\lambda_jy_iy_jx_i^Tx_j \qquad s.t. \ \ \sum_{i=1}^m\lambda_iy_i=0
求出\lambda后,帶入目標(biāo)函數(shù),即可求出w和b。
在上面的式子中,y_i、\lambda_i 都是實(shí)數(shù),x_i^Tx_j 是特征向量。這種向量的點(diǎn)積運(yùn)算,我們就定義為核函數(shù)。它的物理含義是衡量兩個向量的相似性。即:K(x_i,x_j)=\phi(x_i)^T\phi(x_j)\phi(x)就是相似性函數(shù)。核函數(shù)的作用就是接收低維空間的輸入向量,能夠計算出在高維空間里的向量點(diǎn)積。

四、支持向量機(jī)的R的實(shí)現(xiàn)

在R中進(jìn)行支持向量機(jī)的模型預(yù)測比較簡單,可以直接使用e1071包的svm函數(shù)即可實(shí)現(xiàn)。

#導(dǎo)入數(shù)據(jù),預(yù)測化學(xué)品的生物降解
bdf <- read.table("biodeg.csv", sep=";", quote="\"")
head(bdf, n = 3) #查看數(shù)據(jù),有41個特征。第42列包含輸出,不可生物降解為NRB,可生物降解為RB
bdf$V42 <- factor(bdf$V42) #將第42列因子化
levels(bdf$V42)<-c(0,1)  #將第42列變?yōu)?,1


#區(qū)分測試集和訓(xùn)練集
library(caret)
library(tidyverse)
set.seed(23419002)
bdf_sampling_vector <- createDataPartition(bdf$V42, p = 0.80, list = FALSE)
bdf_train <- bdf[bdf_sampling_vector,]
bdf_test <- bdf[-bdf_sampling_vector,]

#訓(xùn)練模型。支持向量機(jī)的包為e1071
library(e1071)
#用支持向量機(jī)來進(jìn)行模型構(gòu)建。kernel核函數(shù),此處為線性核函數(shù);cost代表損失函數(shù)的值,即我們之前講到的C
model_lin<-svm(V42~.,data=bdf_train,kernel="linear",cost=10)
model_lin$fitted #查看訓(xùn)練后的結(jié)果

mean(bdf_train[,42] == model_lin$fitted) #計算訓(xùn)練后的模型準(zhǔn)確度,可以看到,在訓(xùn)練集上有87.9%的準(zhǔn)確率。
table(actual = bdf_train[,42],predictions = model_lin$fitted) #查看混淆矩陣

test_predictions <- predict(model_lin,bdf_test[,1:41])  #在測試集上進(jìn)行預(yù)測
mean(bdf_test[,42] == test_predictions) #查看測試集的準(zhǔn)確度。可以看到測試集上的準(zhǔn)確度達(dá)到了91.9%

這里我們有個問題。cost到底取多少算合適呢?所以我們面臨一個參數(shù)調(diào)優(yōu)的問題。參數(shù)調(diào)優(yōu),可以手工來進(jìn)行,也可也自動來進(jìn)行。
先看手工方式:

#定義一函數(shù),用于計算不同cost下的準(zhǔn)確度
getAccuraciesOfLinearSVM<-function(cost) {
  #根據(jù)給定參數(shù)cost,來擬合模型
  model_lin<-svm(V42~.,data=bdf_train,kernel="linear",cost=cost)
  #計算訓(xùn)練集的準(zhǔn)確度。signif用于數(shù)據(jù)截取函數(shù),digits表示保留3位小數(shù) 
  train_accuracy <- signif(mean(bdf_train[,42] == model_lin$fitted), digits = 3)
  test_predictions <- predict(model_lin,bdf_test[,1:41]) #預(yù)測測試集
  test_accuracy <- signif(mean(bdf_test[,42] == test_predictions), digits = 3) #計算測試集準(zhǔn)確度
  return(list(training=train_accuracy, test=test_accuracy))
}

#手工取c為0.01, 0.1, 1, 10, 100, 1000這幾個數(shù),觀察效果
cost <- c(0.01, 0.1, 1, 10, 100, 1000)
linearPerformances <- sapply(cost, getAccuraciesOfLinearSVM)
colnames(linearPerformances) <- cost #給輸出結(jié)果取個列名
linearPerformances

當(dāng)然,我們還可以使用caret包中的tune來自動完成上述工作。

#讀取數(shù)據(jù)(信貸數(shù)據(jù))
german_raw <- read.table("german.data", quote="\"")

#重新命名列名
names(german_raw) <- c("checking", "duration", "creditHistory",
                       "purpose", "credit", "savings", "employment",
                       "installmentRate", "personal", "debtors", "presentResidence", "property", "age", "otherPlans", "housing", "existingBankCredits", "job", "dependents", "telephone", "foreign", "risk")
library(caret)
#將一些因子變量變?yōu)樘摂M變量,同時將risk因子化,且變?yōu)?和1(之前是1和2)
dummies <- dummyVars(risk ~ ., data = german_raw)
german<- data.frame(predict(dummies, newdata = german_raw), risk=factor((german_raw$risk-1)))

set.seed(977)
german_sampling_vector <- createDataPartition(german$risk, p = 0.80, list = FALSE)
german_train <- german[german_sampling_vector,]
german_test <- german[-german_sampling_vector,]

#這里增加了權(quán)重。在信貸問題上,如果把一個高風(fēng)險客戶識別為低風(fēng)險客戶,對銀行有可能造成較大損失
#反之則不然。因此這里要對分類結(jié)果增加權(quán)重。這里我們把分類為低風(fēng)險客戶設(shè)置為5
#分類為高風(fēng)險客戶設(shè)置為1,因此分到低風(fēng)險客戶的懲罰項(xiàng)是高風(fēng)險客戶的5倍
#因此我們結(jié)果會偏向于盡可能的被分為高風(fēng)險客戶
class_weights <- c(1,5)
names(class_weights) <- c("0","1")
class_weights

set.seed(2423)
#使用tune進(jìn)行自動調(diào)參
german_radial_tune <- tune(svm,risk~.,data=german_train, kernel="radial", 
                           ranges = list(cost=c(0.01,0.1,1,10,100), 
                                         gamma=c(0.01,0.05,0.1,0.5,1)),
                           class.weights = class_weights)
#查看調(diào)參后的最優(yōu)參數(shù)組合
german_radial_tune$best.parameters
#在最優(yōu)參數(shù)組合下的性能
german_radial_tune$best.performance

#根據(jù)調(diào)參得到的最優(yōu)模型在測試集上進(jìn)行預(yù)測,可以看到,在測試集上有75%的準(zhǔn)確率
german_model <- german_radial_tune$best.model
test_predictions <- predict(german_model,german_test[,1:61])
mean(test_predictions == german_test[,62])
table(predicted = test_predictions, actual = german_test[,62])

set.seed(2423)
#不設(shè)權(quán)重的結(jié)果
german_radial_tune_unbiased <- tune(svm,risk~.,data=german_train, kernel="radial", 
                                    ranges = list(cost=c(0.01,0.1,1,10,100), 
                                                  gamma=c(0.01,0.05,0.1,0.5,1)))
german_radial_tune_unbiased$best.parameters
german_radial_tune_unbiased$best.performance

【參考文獻(xiàn)】
支持向量機(jī)通俗導(dǎo)論
支持向量機(jī)(SVM)是什么意思?
【機(jī)器學(xué)習(xí)】支持向量機(jī) SVM
支持向量機(jī)(SVM)——原理篇
支持向量機(jī)(SVM)介紹
SVM支持向量機(jī)原理及核函數(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容