這是生信技能樹 一文 系列推文,前面的目錄:
一文學會WGCNA分析
一文看懂主成分分析
支持向量機,因其英文名為support vector machine,故一般簡稱SVM,就是一種二類分類模型,其基本模型定義為特徵空間上的間隔最大的線性分類器,其學習策略便是間隔最大化,最終可轉化為一個凸二次規劃問題的求解。
看起來這個定義是不是專有名詞太多呀!其實還有要完全理解SVM原理及算法,還需要理解 線性回歸,最小二乘法,邏輯回歸,線性分類器,線性可分,核函數,損失函數,
但是不要怕,不具體理解SVM原理及算法,我們仍然是可以使用它,左右不過是一個分類器罷了,就是根據一堆自變量來預測因變量,所以就是變量預測,值得一提的是,SVM通常應用於二元分類變量預測,但是經過一些改進也可以勉強對多元分類變量預測,同時基於SVM的SVR也可以預測連續變量。
通俗的理解,我們想根據年收入來預測某家庭是貧窮還是富有,可以簡單的按照年收入50萬來進行分類,這個時候就只有一個自變量,就是收入的金額這個數值,因變量也很簡單,就是二元分類情況。只不過通常我們要使用SVM的場景,因變量肯定不止一個,閾值也沒有那麼簡單找到。
SVM示例二元分類變量預測毫無疑問,生物學領域最經典的二元分類變量就是病人的生死問題啦!
load('~/Documents/Rdata/TCGA-LUAD-survival_input.Rdata')
#
# 首先你會有一個表達矩陣如下,每個病人的每個基因都有表達量。
exprSet[1:4,1:2]
#
#
#
#
#
# 然後你會有這些病人的臨床信息
head(phe)
#
#
#
#
#
#
#
#
#
#
#
#
#
#
# 當然,我這裡舉例就只關心 生死這個情況。
table(phe$event)
#
#
#
# 125個病人去世了,有388活著的。
#
exprSet=mean(colSums(exprSet))*exprSet/colSums(exprSet)
exprSet=log2(exprSet+1)
data=cbind(t(exprSet),phe$event)
colnames(data)[ncol(data)]='event'
data=as.data.frame(data)
data$event=as.factor(data$event)
# 最後得到的data這個變量就可以進入SVM啦。
我們可以先把上面的TCGA-LUAD的miRNA數據集分成50%的訓練集(Train),50%的測試集(Test):
#
require("mlbench")
#
library(e1071)
#
smp.size = floor(0.5*nrow(data))
set.seed(123456789)
train.ind = sample(seq_len(nrow(data)), smp.size)
train = data[train.ind, ] # 50%
test = data[-train.ind, ] # 50%
table(test$event)
#
#
#
table(train$event)
#
#
#
然後就可以使用svm()訓練分類模型
model = svm(formula = event ~ ., # 這裡的待預測的變量event是二分類變量,生與死。
data = train,kernel = "linear")
#
#
summary(model)
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
共有9 種核函數,常用的為其中的前四個:linear,Polynomial,RBF,Sigmoid 其中 RBF 適用於因變量比較少,而 linear適用於因變量非常多,也就是本例子裡面的基因非常多,所以我們現在linear。
訓練好的模型,就可以使用predict()函數去測試集裡面看看效果。
train.pred = predict(model, train)
test.pred = predict(model, test)
#
table(real=train$event, predict=train.pred)
#
#
#
#
confus.matrix = table(real=train$event, predict=train.pred)
sum(diag(confus.matrix))/sum(confus.matrix)
#
# 可以很明顯的看到過擬合現象,在訓練集預測100%。
#
table(real=test$event, predict=test.pred)
#
#
#
#
confus.matrix = table(real=test$event, predict=test.pred)
sum(diag(confus.matrix))/sum(confus.matrix)
#
# 可以看到準確率並不高。
關於模型的各種其它檢驗指標,建議直接閱讀我在生信技能樹分享的文章:https://vip.biotrainee.com/d/812-
多元分類變量預測SVM算法最初是為二值分類問題設計的,當處理多類問題時,就需要構造合適的多類分類器。目前,構造SVM多類分類器的方法主要有兩類:一類是直接法,直接在目標函數上進行修改,將多個分類面的參數求解合併到一個最優化問題中,通過求解該最優化問題「一次性」實現多類分類。這種方法看似簡單,但其計算複雜度比較高,實現起來比較困難,只適合用於小型問題中;另一類是間接法,主要是通過組合多個二分類器來實現多分類器的構造,常見的方法有one-against-one和one-against-all兩種。
a.一對多法(one-versus-rest,簡稱1-v-r SVMs)。訓練時依次把某個類別的樣本歸為一類,其他剩餘的樣本歸為另一類,這樣k個類別的樣本就構造出了k個SVM。分類時將未知樣本分類為具有最大分類函數值的那類。
b.一對一法(one-versus-one,簡稱1-v-1 SVMs)。其做法是在任意兩類樣本之間設計一個SVM,因此k個類別的樣本就需要設計k(k-1)/2個SVM。當對一個未知樣本進行分類時,最後得 票最多的類別即為該未知樣本的類別。Libsvm中的多類分類就是根據這個方法實現的。
c.層次支持向量機(H-SVMs)。層次分類法首先將所有類別分成兩個子類,再將子類進一步劃分成兩個次級子類,如此循環,直到得到一個單獨的類別為止。
參考:http://www.matlabsky.com/thread-9471-1-1.html
對於多元分類變量預測例子,我們可以使用mlbench中的數據集Glass來簡單演示,數據集裡面的type變量是6個分類,看下面代碼及輸出結果。
簡單探索一下數據集Glass,裡面記錄著214個觀測值,10個變量。
require("mlbench")
library(e1071)
data(Glass, package="mlbench")
data = Glass
在e1071的包裡面,我們可以直接使用svm()這個函數建模。
同樣的我們可以先把Glass數據集分成80%的訓練集(Train),20%的測試集(Test):
smp.size = floor(0.8*nrow(data))
set.seed(123456789)
train.ind = sample(seq_len(nrow(data)), smp.size)
train = data[train.ind, ]
test = data[-train.ind, ]
然後就可以使用svm()訓練分類模型
model = svm(formula = Type ~ ., # 這裡的待預測的變量Type必須是Factor
data = train)
#
summary(model)
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
訓練好的模型,就可以使用predict()函數去測試集裡面看看效果。
train.pred = predict(model, train)
test.pred = predict(model, test)
#
table(real=train$Type, predict=train.pred)
#
#
#
#
#
#
#
#
confus.matrix = table(real=train$Type, predict=train.pred)
sum(diag(confus.matrix))/sum(confus.matrix)
#
#
table(real=test$Type, predict=test.pred)
#
#
#
#
#
#
#
#
confus.matrix = table(real=test$Type, predict=test.pred)
sum(diag(confus.matrix))/sum(confus.matrix)
#
這裡直接摘抄:https://rpubs.com/skydome20/R-Note14-SVM-SVR 的筆記,他講解的非常好!
SVR是本來SVM的延伸型態,能夠處理連續的預測問題。
在e1071套件裡面,並沒有一個函式叫做svr(),而是一樣用svm()。
差別只在於:
這裡簡單手動建立一筆資料:
data = data.frame(x=1:20,
y=c(3,4,8,2,6,10,12,13,15,14,17,18,20,17,21,22,25,30,29,31))
plot(data$x, data$y, pch=16, xlab="X", ylab="Y")
我們可以先拉一條簡單的線性迴歸:
model <- lm(y ~ x , data)
lm.pred = predict(model, data)
plot(data$x, data$y, pch=16, xlab="X", ylab="Y")
points(lm.pred, pch=2, col="red")
abline(model, col="red")
我們可以直接用SVR來建模、預測:
model <- svm(y ~ x , data)
svr.pred = predict(model, data)
plot(data$x, data$y, pch=16, xlab="X", ylab="Y")
points(svr.pred, pch=4, col="blue")
最後比較一下線性迴歸和SVR的表現,同時計算RMSE(root mean square error):
plot(data$x, data$y, pch=16, xlab="X", ylab="Y")
points(lm.pred, pch=2, col="red")
points(svr.pred, pch=4, col="blue")
c(sqrt(mean((data$y - lm.pred)^2)),
sqrt(mean((data$y - svr.pred)^2))
)
可以發現到,在這個例子,SVR比lm的效果還要好一些些(1.79 < 1.91)。
其實還有交叉驗證,以及AUV等,不過它們已經算是單獨知識點,就不混在SVM裡面一起講解了。
如果你看到這裡,會發現,使用SVM在你自己的數據是一件很簡單的事情啦,只需要給一個矩陣和一個變量即可,這個變量可以是二元分類或者是多分類,甚至可以是連續變量,其餘的代碼都不需要怎麼修改就能跟著我完成svm的應用。
歡迎大家提出自己的想法,或者發郵件跟作者交流