Support Vector Machine (SVM)
林朕陞
2021-5-28
概要:我要介紹機器學習演算法中具有強大分類功能之一的「支持向量機」,Support Vector Machine。SVM是一種著名的二元分類器(binary classifier),由俄國統計學家Vapnik於1963年基於統計學習理論的基礎提出的演算法,而後在1992年與其他學者發表應用核方法 (kernel method) 來加強「非線性」分類的能力 (Boser et al., 1992)。
簡單來說,SVM是一種「監督式」學習的演算法,試圖在兩類別之間建構一個決策超平面(hyperplane),將資料區分成兩個類別(2 classes),最後進行分類或預測,所以又被稱為二元分類器(binary classifier)。
從數學上來說,找到一個決策邊界使兩類別的邊界寬度(margin)極大化,邊界越寬,模型對於新數據會有更好的分類表現,而落在邊界上及邊界內的樣本點稱之為支持向量 (support vector)。若此超平面能夠將資料做完美的切割,稱之為線性可分(linear separability);反之,稱為非線性可分(non-linear separability)。實務上,後者較為常見。
在二維的空間中,超平面指的就是「一條直線」;三維空間中,則是「一個平面」,之所以會有一個「超」字,是因為實際資料往往不會只有二維或三維。在更高維度的空間中,我們無法觀察這個平面的形狀為何,於是就用「超平面(hyperplane)」一詞來概括所有情況。
一直以來,SVM就是資料採礦(data mining)中最受歡迎的分類演算法之一。無論是小樣本的運用(屬於統計學習範疇)、非線性可分(non-linear separability)、高維度模式識別問題(醫學、圖像辨識等),SVM都有相當不錯的表現。SVM是要處理依變數(dependent variable)為類別(factor)的分類問題,在R中對應的套件是e1071。
我會一步步教導同學操作SVM演算法,並使用「員工離職預測」的資料集。
資料說明
資料集:員工離職預測
資料來源:人工智慧共創平台
網址:https://aidea-web.tw/topic/926a5d08-c74f-453c-9e75-bad4586dd271
議題主旨:人才是企業最重要的資源,提早發現員工離職傾向並留任優秀人才,是企業持續成長的重要議題。員工離職預測是利用大數據與人工智慧,分析員工未來是否會有離職的風險,以利針對離職風險較高的優秀員工,及早啟動留才管理機制。 此議題中蒐集了多個可能會影響員工離職的因素,如:sex(性別)、rank(工作職等)、hierarchy(管理層級)、project(專案總數)、honor(榮譽數)、promotion(是否升遷)、promospeed(升遷速度)、bsinconc(出差集中度)、age(年齡層級)、marital(婚姻狀況)、experience(任職前工作平均年數)、family(眷屬量)、commutecost(通勤成本)等13個特徵變數。預測變數為resign,0 為未離職;1 為離職。公司的人資部門必須參考過去的經驗以及條件狀況,來判斷目前還在職的員工之離職傾向。在此議題中,我將使用機器學習的SVM演算法,建立模型來分析、預測未來員工是否會離職。
資料下載:本議題提供下載資料 data.zip 檔案(data.csv: 年資料,共14,392筆),其中離職樣本數796,未離職樣本數13,596。
內容:
- 進行SVM之前,我們先安裝套件:
# 安裝e1071套件 install.packages("e1071")
require(e1071)
## Loading required package: e1071
- 載入資料檔:
# 資料清理為剔除有NA的樣本點
# 將應變數(resign)轉為factor
data=read.csv("data.csv")
data=na.omit(data)
data[,14]= factor(data[,14])
- 我們先將data資料分成70%的訓練資料(Train),30%的測試資料(Test):
set.seed(22)
train.index = sample(x=1:nrow(data), size=ceiling(0.7*nrow(data) ))
train = data[train.index, ] # 70%
test = data[-train.index, ] # 30%
- 我們接著使用svm()訓練SVM的分類模型:
#執行SVR模型
model <- svm(resign ~ ., data=train, kernel ="radial", cost=25, scale=FALSE) # 依變數的類型必須是factor
#可以看到SVM預設的參數設定
summary(model)
##
## Call:
## svm(formula = resign ~ ., data = train, kernel = "radial", cost = 25,
## scale = FALSE)
##
##
## Parameters:
## SVM-Type: C-classification
## SVM-Kernel: radial
## cost: 25
##
## Number of Support Vectors: 2409
##
## ( 1854 555 )
##
##
## Number of Classes: 2
##
## Levels:
## 0 1
- 接下來要針對訓練集、測試集進行分類、預測,使用predict()函式:
# 預測
train.pred = predict(model, train)
test.pred = predict(model, test)
# 訓練集資料的混淆矩陣 (confusion matrix),可評估分類的準確率。
table(predict=train.pred, truth=train$resign)
## truth
## predict 0 1
## 0 9466 417
## 1 1 140
# 測試集資料的混淆矩陣 (confusion matrix),可評估分類的準確率。
table(predict=test.pred, truth=test$resign)
## truth
## predict 0 1
## 0 4031 232
## 1 28 4
# 計算訓練集分類的準確率 (accuracy)
confus.train = table(predict=train.pred, truth=train$resign)
confus.test = table(predict=test.pred, truth=test$resign)
c(sum(diag(confus.train))/sum(confus.train),sum(diag(confus.test))/sum(confus.test))
## [1] 0.9583001 0.9394645
結論:
在訓練及測試資料上都得到94%左右的準確率,效果還不錯,而且模型看起來並沒有發生overfitting的問題。而且這僅僅只是使用預設參數(default)所建立的SVM分類模型。只要我們懂得進一步調校參數,便可以讓模型的表現更上一層樓。
參數討論:
前面的SVM,我們都是用預設的參數來建模,並且都得到還不錯的結果,主要是我們的資料並算太複雜。但是在實際資料中,幾乎不可能發生。所以我們必須了解SVM裡面的參數,了解它們的意義,了解如何有效率地調校(tune)它們,讓模型既不會發生overfitting,同時表現合乎預期。
在調參數的階段,所使用的手法被稱為grid search,概念是針對每一種參數的組合,都會訓練出一個對應的模型,最後觀察模型的表現,挑選出表現最優的模型。 在訓練的過程中,R會自動引入cross validation(交叉驗證)的手法,確保模型的可靠度(robustness),讓tune出來的參數是可以採用的。在e1071的套件裡,有內建tune()的函式可以讓我們來調參數。在SVM,同時也是e1071套件裡的svm()裡面,有兩個最重要的參數值得關注:(cost, gamma)。
svm(...
type = 決定svR是要用來classification(類別)、還是regression(連續)。
scale = 將資料正規化成(平均值, 標準差) = (0,1) 的分佈。
kernel = 將資料由低維度映射到高維度特徵空間的kernel-function,用來處理「非線性可分」的問題。
cost = 懲罰參數,在Lagrange formulation中的大C,決定給被分錯的資料「多少」懲罰值。
epsilon = margin of tolerance。越大,表示在容忍範圍內被分錯的資料,不會被懲罰;反之,越接近0,每一個被分錯的資料都會被懲罰,此參數扮演極重要的角色。
gamma = 在kernel-function裡面的參數(linear-function除外)。
...
)
- 更多設定請參考R的官方e1071套件手冊PP.52~60 https://cran.r-project.org/web/packages/e1071/e1071.pdf
# tune cost and epsilon in SVM
tune.model = tune(svm, resign~., data=train, kernel ="radial", scale=FALSE, range=list(cost=2^(3:5), gamma = seq(0.5,1.5,0.5)),tunecontrol = tune.control(cross = 2) )
# 調校參數的最主要一行
tune.model
##
## Parameter tuning of 'svm':
##
## - sampling method: 2-fold cross validation
##
## - best parameters:
## cost gamma
## 8 1.5
##
## - best performance: 0.06115323
這裡有兩個重點:
- 在訓練的過程中,我們訓練的是一堆模型,包含cost=10-1,100,101,102,gamma=0.5,1,1.5,2,這兩種參數的排列組合,換句話說,會有4x4=16個SVM模型。
- 裡面的值是classification error,採用的是1-準確率(accuracy),評斷分類的好壞。
plot(tune.model)
同學可注意這張圖的分層很有意思,根據顏色的深淺,可以看得出來有一層一層的光譜出現在圖中,表示在特定範圍內的cost和gamma,會有互相配合和影響的情況,而我們會希望classification error越小越好,也就是圖中的顏色越深越好。隨著gamma越低,模型的表現越糟(顏色越淺)。同時它也希望cost愈低愈好(顏色越深),所以最後得出的最佳參數組合: cost=8, gamma=1.5。最後,要挑出表現最佳的模型,可以直接從tune()回傳的結果中取出($best.model)。
# Best model in set of tuning models
tune.model$best.model
##
## Call:
## best.tune(method = svm, train.x = resign ~ ., data = train, ranges = list(cost = 2^(3:5),
## gamma = seq(0.5, 1.5, 0.5)), tunecontrol = tune.control(cross = 2),
## kernel = "radial", scale = FALSE)
##
##
## Parameters:
## SVM-Type: C-classification
## SVM-Kernel: radial
## cost: 8
##
## Number of Support Vectors: 8737
- 我們使用剛才的最佳參數組合(cost=8, gamma=1.5),進行SVM的分類及預測:
#執行SVM模型
model = svm(resign ~., data=train, kernel ="radial", cost=8, gamma=1.5, scale=FALSE)
# 預測
train.pred = predict(model, train)
test.pred = predict(model, test)
# 訓練集資料的混淆矩陣 (confusion matrix),可評估分類的準確率。
table(predict=train.pred, truth=train$resign)
## truth
## predict 0 1
## 0 9464 106
## 1 3 451
# 測試集資料的混淆矩陣 (confusion matrix),可評估分類的準確率。
table(predict=test.pred, truth=test$resign)
## truth
## predict 0 1
## 0 4033 232
## 1 26 4
# 計算訓練集分類的準確率 (accuracy)
confus.train = table(predict=train.pred, truth=train$resign)
confus.test = table(predict=test.pred, truth=test$resign)
c(sum(diag(confus.train))/sum(confus.train),sum(diag(confus.test))/sum(confus.test))
## [1] 0.9891261 0.9399302
- 可以發現到,在本次的參數調校中,SVM在訓練集的準確率顯著上升(0.989>0.958),但在測試集則些微增加。
總結:
SVM是資料科學中最重要的演算法之一,它同時具有機器學習和統計理論的優點,在分類及預測的問題上都表現的很不錯。但要充分發揮SVM的長處,需要將其中的許多細節(參數)知道得十分清楚,同時具有對資料的敏銳度才行,包括kernel-function的選擇,都需要經驗累積,非常值得同學花時間好好靜下來學習。