SVM的使用
爲什麼寫這個指南
我一直覺得 SVM 是個很有趣的東西,不過一直沒辦法去聽林智仁老師 的 Data mining與SVM的課,後來看了一些Internet上的文件,後來聽 kcwu 講了一下 libsvm 的用法後,就想整理一下,算是對於並不需要知道完整 SVM 理論的人提供使用 libsvm 的入門。 原始 libsvm 的README跟FAQ也是很好的文件, 不過你可能要先對 svm 跟流程有點了解後纔看得懂 (我在看時有這樣的感覺);這篇入門就是爲了從零開始的人而寫的。
不過請記得底下可能有些說法不一定對,但是對於只是想用 SVM 的人來說我覺得這樣說明會比較易懂。這篇入門原則上是給會寫基本程序的人看的,也是給我自己一個備忘, 不用太多數學底子,也不用對 SVM 有任何預備知識。
SVM:什麼是SVM,它能爲我們做什麼?
SVM, Support Vector Machine , 簡而言之它是個起源與人工神經網絡有點像的東西,現今最常拿來就是做分類。也就是說,如果我有一堆已經分好類的東西(可是分類的依據是未知的),那當收到新的東西時,SVM可以預測新的數據要分到哪一堆去。聽起來是很神奇的事(如果你覺得不神奇,請重想一想這句話代表什麼:分類的依據是未知的!,還是不神奇的話就請你寫個程序,解解上面的問題),不過 SVM 基於統計學習理論的,可以在合理的時間內漂亮的解決這個問題。
以圖形化的例子來說明,假定我在空間中標了一堆用顏色分類的點, 點的顏色就是它的類別, 位置就是它的數據, 那 SVM 就可以找出區隔這些點的程序, 依此就可以分出一個個的區域; 拿到新的點(數據) 時, 只要對照該位置在哪一區就可以找出它應該是哪一顏色(類別)了。當然 SVM 不是真的只有分區那麼簡單, 不過看上面的例子應該可以瞭解 SVM 大概在作什麼. 要對 SVM 再多懂一點點,可以參考 cjlin 在 data mining 課的 slides: pdf 或 ps 。我們可以把 SVM 當個黑盒子, 數據丟進去讓他處理然後我們再來用就好了.
哪裏得到SVM?
.zip 跟 .tar.gz 基本上是一樣的, 只是看你的操作系統; 習慣上 Windows 用 .zip 比較方便 (因爲有WinZIP, 也有WinRAR), UNIX 則是用 .tar.gz
編譯libsvm
解開來後, 假定是UNIX 系統, 直接打 make 就可以了; 編不出來的話請詳讀說明和運用常識. 因爲這是指南, 所以我不花時間細談, 而且編不出來的情形真是少之又少, 通常一定是你的系統有問題。 其他的子目錄可以不管, 只要 svm-train, svm-scale, svm-predict 三個執行文件有就可以了. Windows 的用戶要自己重編當然也是可以, 不過已經有編好的執行文件在裏面了: 請檢查 windows 子目錄, 應該會有 svmtrain.exe, svmscale.exe, svmpredict.exe, svmtoy.exe.
libsvm 有很多種用法, 這篇指南只打算講簡單的部分.
程序
svmtrain
訓練數據. 跑SVM被戲稱爲 "開火車" 也是由於這個程序名而來. train會接受特定格式的輸入, 產生一個 "Model" 文件. 這個 model 你可以想像成SVM的內部數據,因爲預測要model才能預測, 不能直接吃原始數據.想想也很合理,假定 train 本身是很耗時的動作, 而 train可以以某種形式存起內部數據,那下次要預測時直接把那些內部數據載入就快多了.
svmpredict
依照已經訓練好的 model, 再加上給定的輸入(新值), 輸出預測新值所對應的類別.
svmscale
掃描數據. 因爲原始數據可能範圍過大或過小, svmscale 可以先將數據重新 scale (縮放) 到適當範圍使訓練與預測速度更快。
文件格式要先交代一下. 你可以參考 libsvm 裏面附的 "heart_scale": 這是SVM 的輸入文件格式.
[label] [index1]:[value1] [index2]:[value2] ...
[label] [index1]:[value1] [index2]:[value2] ...
一行一條記錄數據,如:
+1 1:0.708 2:1 3:1 4:-0.320 5:-0.105 6:-1
label
或說是class, 就是你要分類的種類,通常是一些整數。
index
是有順序的索引,通常是連續的整數。
value
就是用來 train 的數據,通常是一堆實數。
每一行都是如上的結構, 意思就是: 我有一排數據, 分別是 value1, value2, .... value, (而且它們的順序已由 index 分別指定),這排數據的分類結果就是label。
或許你會不太懂,爲什麼會是 value1,value2,.... 這樣一排呢? 這牽涉到 SVM 的原理。你可以這樣想(我沒說這是正確的),它的名字就叫 Support "Vector" Machine,所以輸入的訓練數據是 "Vector"(向量), 也就是一排的 x1, x2, x3, ... 這些值就是 value,而 x[n] 的n就是由index 指定。這些東西又稱爲 "(屬性)attribute"。
真實的情況是,大部份時候我們給定的數據可能有很多 "特徵(feature)" 或說 "屬性(attribute)",所以輸入會是一組的。舉例來說,以前面點分區的例子 來說,我們不是每個點都有 X 跟 Y 的座標嗎?所以它就有兩種屬性。假定我有兩個點: (0,3) 跟 (5,8) 分別在 label(class) 1 跟 2 ,那就會寫成
1 1:0 2:3
2 1:5 2:8
同理,空間中的三維座標就等於有三組屬性。這種文件格式最大的好處就是可以使用稀疏矩陣(sparse matrix), 或說有些 數據的屬性可以有缺失。
運行libsvm
下來解釋一下libsvm 的程序怎麼用。 你可以先拿 libsvm 附的heart_scale 來做輸入,底下也以它爲例:
看到這裏你應該也瞭解,使用 SVM 的流程大概就是:
1. 準備數據並做成指定格式 (有必要時需 svmscale)
2. 用svmtrain 來訓練成 model
- 對新的輸入,使用 svmpredic來預測新數據的類別.
svmtrain
svmtrain 的語法大致就是:
svmtrain [options] training_set_file [model_file]
training_set_file 就是之前的格式,而 model_file 如果不給就會 叫 [training_set_file].model。 options 可以先不要給。
下列程序執行結果會產生 heart_scale.model 文件:(螢幕輸出不是很重要,沒有錯誤就好了)
./svmtrain heart_scale
optimization finished, #iter = 219
nu = 0.431030
obj = -100.877286, rho = 0.424632
nSV = 132, nBSV = 107
Total nSV = 132
svmpredict
svmpredict 的語法是 :
svmpredict test_file model_file output_file
test_file 就是我們要預測的數據。它的格式跟 svmtrain 的輸入,也就是 training_set_file 是一樣的, 不過每行最前面的 label 可以省略 (因爲預測就是要預測那個 label)。但如果 test_file 有 label 的值的話, predict 完會順便拿 predict 出來的值跟 test_file 裏面寫的值去做比對,這代表: test_file 寫的label是真正的分類結果,拿來跟我們預測的結果比對就可以知道預測的效果。所以,我們可以拿原 training set 當做 test_file再丟給 svmpredict 去預測(因爲格式一樣),看看正確率有多高,方便後面調參數。其它參數就很好理解了: model_file就是 svmtrain 出來的文件, output_file是存輸出結果的文件案。輸出的格式很簡單,每行一個 label,對應到你的 test_file 裏面的各行。下列程序執行結果會產生 heart_scale.out:
./svm-predict heart_scale heart_scale.model heart_scale.out
Accuracy = 86.6667% (234/270) (classification)
Mean squared error = 0.533333 (regression)
Squared correlation coefficient = 0.532639(regression)
我們把原輸入丟回去 predict,第一行的 Accuracy 就是預測的正確率了。如果輸入沒有label 的話,那就是真的預測了。看到這裏,基本上你應該已經可以利用 svm 來作事了:你只要寫程序輸出正確格式的數據,交給 svm 去 train,後來再 predict 並讀入結果即可。
Advanced Topics
後面可以說是一些稍微進階的部份,我可能不會講的很清楚,因爲我的重點是想表達一些觀念和解釋一些你看相關文件時很容易碰到的名詞。
Scaling
svm-scale 目前不太好用,不過它有其必要性。因爲適當的掃描有助於參數的選擇,還有解svm的速度。svmscale 會對每個屬性做掃描。範圍用 -l, -u 指定,通常是[0,1]或是[-1,1]。輸出在 stdout。另外要注意的(常常會忘記)是 testing data 和 training data要一起掃描。而 svm-scale 最難用的地方就是沒辦法指定 testing data/training data(不同文件) 然後一起掃描。
Arguments
前面提到,在train的時候可以使用一些參數。(直接執行 svm-train 不指定輸入文件與參數會列出所有參數及語法說明) 這些參數對應到原始 SVM 公式的一些參數,所以會影響預測的正確與否。
舉例來說,改個 c=10:
./svm-train -c 10 heart_scale
再來預測 ,正確率馬上變成 92.2% (249/270)。
Cross Validation
一般而言, SVM 使用的方式(在決定參數時)常是這樣:
1. 先有已分好類的一堆數據
2. 隨機拆成好幾組訓練集
- 用某組參數去訓練並預測別組,看正確率
- 正確率不夠的話,換參數再重複訓練/預測
等找到一組不錯的參數後,就拿這組參數來建model並用來做最後對未知數據的預測。這整個過程叫cross validation ,也就是交叉比對。在我們找參數的過程中,可以利用 svmtrain 的內建交叉比對功能來幫忙:
-v n: n-fold cross validation
n 就是要拆成幾組,像 n=3 就會拆成三組,然後先拿 1跟2來訓練並預測 3 以得到正確率; 再來拿 2跟 3 訓練並預測1,最後 1,3 訓練並預測2。其它以此類推。如果沒有交叉比對的話,很容易找到只在特定輸入時好的參數。像前面我們 c=10 得到 92.2%,不過拿 -v 5 來看看:
./svm-train -v 5 -c 10 heart_scale
Cross Validation Accuracy = 80.3704%
平均之後才只有 80.37%,比一開始的 86 還差。
What arguments rules?
通常而言,比較重要的參數是 gamma (-g) 跟 cost (-c) 。而交叉比對 (-v) 的參數常用 5。cost 預設值是 1, gamma 預設值是 1/k ,k 等於輸入數據條數。那我們怎麼知道要用多少來當參數呢?
TRY就是嘗試找比較好的參數值。 Try 參數的過程是用指數增長的方式來增加與減少參數的數值,也就是 2^n (2 的 n 次方)。因爲有兩組參數,所以等於要嘗試 n*n=n^2 次。這個過程是不連續的成長,所以可以想象成我們在一個 X-Y 平面上指定的範圍內找一羣格子點 (grid,如果你不太明白,想成方格紙或我們把平面上所有整數交點都打個點,就是那樣),每個格子點的 X 跟 Y 經過換算 (如 2^x, 2^y) 就拿去當 cost 跟 gamma 的值來交叉比對。所以現在你應該懂得 libsvm 的 python 子目錄下面有個 grid.py 是做啥的了:它把上面的過程自動化,在你給定的範圍內呼叫svm-train去嘗試所有的參數值。grid.py 還會把結果繪製出來,方便你尋找參數。 libsvm 有很多跟 python 結合的部份,由此可見 python 是強大方便的工具。很多神奇的功能,像自動登入多臺機器去平行跑 grid等等都是python幫忙的。不過 SVM 本身可以完全不需要python,只是會比較方便。跑 grid (基本上用 grid.py 跑當然是最方便,不過如果你不懂python而且覺得很難搞,那你要自己產生參數來跑也是可以的)通常好的範圍是 [c,g]=[2^-10,2^10]*[2^-10,2^10]另外其實 grid 用 [-8,8] 也很夠了。
Regression(衰減)
另一個值得一提的是regression(衰減)。簡單來說,前面都是拿 SVM 來做分類,所以label的值都是離散的數據、或說已知的固定值。而 regression 則是求連續的值、或說未知的值。你也可以說,一般是二分類問題, 而 regression是可以預測一個實數。
比如說我知道股市指數受到某些因素影響, 然後我想預測股市。股市的指數就是我們的 label, 那些因素量化以後變成屬性。以後收集那些屬性給 SVM 它就會預測出指數(可能是沒出現過的數字),這就要用 regression。那對於開獎的號碼呢?因爲都是固定已知的數字,很明顯我們應該用一般SVM的分類來預測 。 (註這是真實的例子 --llwang就寫過這樣的東西) 所以說 label 也要掃描, 用 svm-scale -y lower upper但是比較糟糕的情況是grid.py不支持regression,而且較差對比對 regression 也常常不是很有效。
總而言之,regression 是非常有趣的東西,不過也是比較高級的用法。在這裏我們不細談了,有興趣的人請再參考SVM與libsvm的其它文件。
尾聲
到此我已經簡單的說明了 libsvm 的使用方式, 更完整的用法請參考 libsvm 的說明跟 cjlin 的網站、。對於 SVM 的新手來說, libsvmtools 有很多好東西。像 SVM for dummies 就是很方便觀察 libsvm 流程的東西。