1. 程式人生 > >系統學習機器學習之SVM(三)--Liblinear,LibSVM使用整理,總結

系統學習機器學習之SVM(三)--Liblinear,LibSVM使用整理,總結

1.Libsvm與Liblinear區別,簡單原始碼剖析。

LIblinear,主要專門為百萬級別的資料和特徵實現的線性分類器。

他們兩個都是用來做分類的,相對來說Libsvm應用的範圍較廣, 而Liblinear主要用於處理大資料量的訓練過程。在什麼樣的情況下,該選擇Liblinear而不是Libsvm呢?作者給出幾點建議:
1. 當你面對海量的資料時,這裡的海量通常是百萬級別以上。海量資料分為兩個層次:樣本數量和特徵的數量。
2.使用線性和非線性對映訓練模型得到相近的效果。
3.對模型訓練的時間效率要求較高。

在這類情況下,建議你使用Liblinear,而不是libsvm

2.Liblinear使用,Java版本

3.Liblinear使用,官方翻譯。

在過去的十幾年裡,支援向量機(Support Vector Machines)應該算得上是機器學習領域影響力最大的演算法了。而在SVM演算法的各種實現工具中,由國立臺灣大學林智仁老師開發的工具包LIBSVM,又無疑是影響力最大的。2011年LIBSVM的系統介紹論文“LIBSVM: a library for support vector machines”發表在了期刊ACM TIST(ACM Transactions on Intelligent Systems and Technology)上。2011年的時候,這個期刊的影響因子還不到1,但到了2014年,它的影響因子居然達到了9.39,把TPAMI都甩出了一大截。這其中貢獻最大的當然是關於LIBSVM的這篇論文,在google scholar上,這篇文章的引用量居然已經接近20000,著實嚇人。仔細想來其實也並不奇怪,各種研究工作,只要涉及到分類,大部分都會用到SVM演算法或者與SVM的演算法進行對比,而此時LIBSVM往往是首選工具。其實不但是學術界,在工業界LIBSVM也有非常廣泛的應用。這一方面得益於演算法實現的穩定與高效,另一方面也是因為LIBSVM提供了豐富的介面與靈活的使用方式。一些非常有名的機器學習工具,如基於java的Weka和基於python的scikit-learn,其提供的SVM演算法在底層也是基於LIBSVM的實現。

幾年前參加過一個機器學習的學習班,主講人是CMU的Eric Xing老師。在提問環節,大家都在討論一些高深的理論問題,突然有一個女生舉手提問主講人如果要用SVM的話有什麼工具推薦使用。這個問題在當時的場景似乎有點不合時宜,Eric Xing老師回答說他們CMU從來不用別人的庫,所有的演算法程式碼都是自己實現的云云。後來那次講座上其他的東西我都不太記得了,印象最深的就變成了這個問答。現在想來,如果從演算法學習角度,自己實現這些演算法對於理論的理解和工程能力的提升都是大有好處。但是對於絕大部分人來說,自己動手實現的SVM演算法在效率和擴充套件性上,還是會和LIBSVM的演算法實現存在不小的差距,畢竟人家的程式是千錘百煉中產生的。如果單純為了使用的話,一個大家都認可的好工具絕不失為一個很好地選擇。但是,有一個好的工具並不等於我們可以對演算法與實現一無所知而把所有的事情都交給工具。相反,如果我們能夠很好地瞭解演算法與實現中的一些背景知識,可以更好地使用這些工具。此外,LIBSVM與他的姊妹工具LIBLINEAR提供了豐富的優化與引數選項,通過選擇適當的方法,也可以大大提高我們的工作效率。因此,下面總結一些在我之前的工作中遇到的一些與LIBSVM和LIBLINEAR相關的重要問題,希望能夠為讀者對這些工具的使用有所幫助。

LIBSVM與LIBLINEAR的關係

這是很多剛開始使用SVM的人容易弄混淆的問題。簡單來說,LIBSVM是一套完整的SVM模型實現。使用者可以在LIBSVM中使用和核函式來訓練非線性的分類器,當然也能使用更基礎的線性SVM。而LIBLINEAR是一個針對線性分類場景而設計的工具包,除了支援線性的SVM外,還可以支援線性的Logistic Regression等模型,但是無法通過定義核函式方式實現非線性的分類器。由於支援核函式的擴充套件,LIBSVM理論上具有比LIBLINEAR更強的分類能力,能夠處理更為複雜的問題。但是,很多人因此就只使用LIBSVM,甚至最簡單的線性分類器都是用LIBSVM來訓練和預測,這也是不可取的。因為LIBLINEAR設計的初衷就是為了提高線性分類的效率,其優化演算法與LIBSVM中的優化演算法有著根本的區別。雖然在進行線性分類時LIBSVM和LIBLINEAR都可以達到類似的結果,但是LIBLINEAR無論是在訓練上還是在預測上,都比LIBSVM高效得多。此外,受限於演算法,LIBSVM往往在樣本量過萬之後速度就比較慢了,如果樣本量再上升一個數量級,那麼通常的機器已經無法處理了。但使用LIBLINEAR,則完全不需要有這方面的擔憂,即便百萬千萬級別的資料,LIBLINEAR也可以輕鬆搞定,因為LIBLINEAR本身就是為了解決較大規模樣本的模型訓練而設計的。

雖然搞清楚了兩者主要的區別,但我在剛剛接觸這些工具時,一直很疑惑為什麼兩個緊密相關演算法,卻演化出兩個彼此獨立的工具包。而讓我更加不解的是,LIBSVM早在2000年就已經發布了,而LIBLINEAR直到2007年才釋出第一個版本。根據常識,應該是先有一個簡單的工具,然後再逐漸完善,但是功能更加強大的LIBSVM卻早於LIBLINEAR很久釋出。要回答這個問題,還得從機器學習以及SVM的歷史說起。

早期的機器學習分類演算法可以追溯到Perception(感知機)。Perception的基本思想和Logistic Regression類似,只不過是用線上學習的方法訓練出一個線性分類器。在UCI資料集 中可以看到很多80年代到90年代初期用於機器學習研究的資料。可以看出,這其中的很多問題都非常複雜,比如影象或者語音的識別。但是另一方面,受限於當時獲取資料與計算儲存能力的限制,這些資料集的規模通常非常的小,有的只有幾千甚至幾百個樣本。對於這樣相對複雜的問題,可以想到將特徵直接用簡單的線性分類器進行分類,肯定不會取得太好的效果。這個時候,機器學習領域一個里程碑Multi-Layer Neural Networks(多層神經網路)出現了。多層神經網路引入了Hidden Layer (隱含層),模型的表達能力大大增強,可以訓練出各種複雜的分類器。然而神經網路也有一個致命的弱點,由於模型本身的侷限性,非常容易過擬合,尤其是在訓練樣本量較少的情況下。而這時SVM應運而生,完美的解決了這個問題。一方面,SVM的目標函式是一個凸函式,可以保證得到問題的全域性最優解,避免了神經網路優化頻繁陷入區域性最優的困擾。另一方面,SVM的背後有一套結構化風險最小化的理論,給定了訓練樣本和訓練引數,是可以從理論上計算出模型在真實資料上誤差的bounds。在SVM中,通過對引數的調節和樣本量的選擇,可以在模型方差與訓練誤差之間方便的做出權衡。此外,SVM可以定義不同的核函式來構造非線性分類器,可以得到與神經網路方法大體相當的分類能力,從而適應不同的問題。因此,在上個世紀末到這個是基礎,SVM橫掃了各種分類的應用場景,成為了當時最炙手可熱的機器學習演算法。

然而,SVM也存在侷限性。首先,基於核函式的SVM求解相對比較複雜,需要儲存一個稠密的樣本間的Kernel矩陣,當樣本量很大時,儲存量相當可觀。而到目前為止,一直沒有一個非常有效的並行SVM訓練方法能夠從根本上提升SVM模型的訓練。在十幾年前,樣本量最多隻是上萬級別的時候,這個問題並不顯得重要。但是在十多年後,隨著網際網路的爆炸式發展,隨便一個模型的訓練樣本量都可能數以億計,這時SVM在大資料訓練上的不足就凸顯無疑了。SVM之所以效果好,主要是得益於非線性核函式的引入。但是新的問題不斷出現,而這些問題又涉及到的不同領域知識與業務場景,很多時候僅僅依靠常見的幾種kernel函式並不能解決問題。但是SVM本身過分依賴於核函式,而核函式又存在著很多的限制,其靈活性當然不如人工的特徵構造方法。另一方面,隨著資料量的不斷增加,即使這些樣不能直接被標註用於模型的訓練,但是可以很多機器學習方法可以從大量的樣本中進行特徵的自動學習。比如早年的流形學習,還有文字上的主題模型,影象上的稀疏編碼與字典學習等。通過這些非線性的方法學習的樣本特徵,往往已經是樣本的高層語義表達,有資料充足的情況下,只需要使用較為簡單的線性分類器,就可以達到比較好的效果。這時的主要矛盾變成了分類器必須有能力處理足夠大量的樣本,而在方法上,可以是簡單地現行方法。也就是在這個時候,LIBLINEAR應運而生。

在LIBSVM誕生的時代,SVM的核函式帶來的非線性模型是SVM的主要優勢之一,且當時的樣本量還不是瓶頸。因此LIBSVM的整體框架都是針對訓練kernel SVM模型來訓練的。但是如果只是需要訓練一個線性的SVM模型,那麼演算法可以簡單的多,也可以高效的多。因此LIBLINEAR在保持基本介面和呼叫方式一致的情況下,採用了新的訓練演算法,支援了線性SVM和Logistic Regression的訓練。LIBSVM和LIBLINEAR的作者林智仁老師在後來的很多演講中,都在大力的推廣LIBLINEAR,並且給出了很多實際的例子證明,人工構造特徵+線性模型的方式可以達到甚至超過kernel SVM的表現,同時大大降低訓練的時間和消耗的資源。

其實就在最近幾年,情況又有了新的變化。人工構造特徵+線性分類器的方式在很多問題上又遇到了瓶頸。與此同時,一方面可供使用資料量更大了,另一方面,計算機的計算能力又有了突飛猛進的增長。此時曾經被SVM狠狠壓在地上的神經網路又重新煥發了生機。與SVM相比,神經網路模型的優勢在於可以通過控制模型的層數和每一層函式的型別,設計出各種靈活的分類器。同時神經網路的優化演算法比kernel SVM更適合並行化。當時影響神經網路發展的主要問題是計算資源的限制和樣本量少引起的過擬合。但現在這兩項限制都幾乎不存在了。基於GPU的平行計算技術現在已經比較成熟,可以支援高速的平行計算。而造成過擬合的原因從根本上說,是因為訓練樣本的比真實樣本數少的太多,不能夠反應真實的資料情況。但是如果把我們擁有的所有樣本都作為訓練樣本,其實機會已經就是真實的樣本集了,因此過擬合的事實基礎就不存在了。雖然神經網路在理論上還有缺陷,但是通過計算能力和資料的增加,這些缺陷已經不是問題。正是因為上面的原因,這些年機器學習的熱點又重新回到了神經網路。

1995年,SVM的發明人Vapnik和他在Bell實驗室的老大Larry Jackel打過兩個賭,而見證人是當時也在Bell而最近因為Deep Learning而聲名鵲起的Yann Lecun。賭局的具體內容見下圖:bet-by-2000不管他們輸贏與否,我們都能感覺到這門學科的變化之快。在神經網路大行其道的時候,很難想到半路殺出一個SVM將神經網路演算法殺了一個體無完膚。而在SVM一統天下的時候,大部分人也不會相信神經網路居然還能最後等到逆襲的那一天。

仔細看每一次演算法的革新,其實真正的推動力都是具體的問題需求與當時的技術條件。因此,脫離具體的應用場景去單純比較演算法的優劣並沒有太多意義。對於使用者來說,最適合這個問題場景的演算法就是最好的演算法。具體到LIBSVM和LIBLINEAR,我嘗試總結下面幾個原則:

?凡是確定使用線性分類器的場景,一定使用LIBLINEAR而不是LIBSVM
?如果樣本量較大,比如達到10萬以上的規模,這時LIBSVM已經很難處理了。如果線性分類器的效果實在不好,只能採用人工構造特徵+LIBLINEAR的方式,或者採用其他的分類器,如神經網路,隨機森林等。
?對於高維稀疏資料,典型的如文字的向量空間表示,一般都採用線性的分類器。
?對於樣本量和維度都不算太大的問題,且沒有對預測的效率有過分的需求,都可以用LIBSVM嘗試一下kernel SVM的分類器,很多情況下用kernel SVM比直接用libear SVM還是能達到更高的精度。


模型與優化

LIBSVM和LIBLINEAR都提供了多種不同的模型供使用者選擇,不同的模型有各自適用的場景。下面分別介紹LIBSVM和LIBLINEAR所提供的各種模型。

LIBSVM

下面是LIBSVM幫助內容提供的介紹,給出了LIBSVM支援的5種模型。其中模型0和1對應的都是SVM的分類模型,2對應的是one-class分類器,也就是隻需要標註一個標籤,模型3和4對應的是SVM的迴歸模型。

1 -s svm_type : set type of SVM (default 0) 2 0 -- C-SVC (multi-class classification) 3 1 -- nu-SVC (multi-class classification) 4 2 -- one-class SVM 5 3 -- epsilon-SVR (regression) 6 4 -- nu-SVR (regression)

首先來看最基礎的C-SVC模型。SVM可以寫成如下的優化目標函式(這裡不詳細介紹推導演算法了):

argminw,b,ξ subjectto 12wTw+C∑i=1lξiyi(wT?(xi)?b)≥1?ξi,ξi≤0,i=1,…,l

當模型使用linear kernel,也就是?(x)=x時,上面的問題一個標準的二次凸優化問題,可以比較方便的對每一個變數進行求導。求解這樣的問題是有很多快速的優化方法的,這些方法在LIBLINEAR中都有應用。但是如果是引入kernel的SVM,情況就大不一樣了。因為很多時候我們既不能得到核函式的具體形式,又無法得到特徵在核空間中新的表達。這個時候,之前用線上性SVM上的的求解思路就完全不work了。為了解決這個問題,就必須採用標準的SVM求解思路,首先把原問題轉化為對偶問題,得到下面的目標函式(具體過程可以參考任何介紹SVM的資料):

argminα subjectto f(α)=12αTQα?eTα0≤αi≤C,i=1,…,l,yTα=0

通過對偶變化,上面的目標函式變成了一個關於變數α的二次型。很顯然,上面目標函式中最重要的常亮是矩陣Q,既訓練樣本的Kernel Matrix,滿足Qi.j=?(xi)T?(xj)。先看好的一方面,根據核函式的定義,能夠保證Q是一個正定的矩陣。也就是說,上面的目標函式還是一個凸函式,優化收斂後能保證得到的解是全域性最優解, 這也是SVM的重要優勢之一。但是問題也隨之而來,使用常用的核函式,只要任意給出兩個向量,總是能夠計算出一個非0的距離。這也就意味著矩陣Q將會是一個非常稠密的矩陣,如果訓練樣本足夠多,那麼矩陣Q的儲存和計算將成為很大的問題,這也是SVM的優化演算法中的最大挑戰。

由於矩陣Q過大,所以想一次性優化整個α是比較困難的。所以常用的方法都是先把Q大卸八塊,每次選擇一部分的Q,然後update與這部分Q相關的α的值。這其中最著名的演算法就是1998由John C. Platt提出的SMO演算法,而LIBSVM的優化過程也是基於SMO演算法進行的。SMO演算法的每一步迭代都選擇最小的優化單元,也就是固定其他的α,只挑選兩個α的值進行優化。之所以不選擇一個,是因為有yTα=0的約束,至少選擇兩個α的座標才有可能進行更新。本文主要目的是介紹LIBSVM,所以就不詳細討論SMO的細節了。至於LIBSVM中的具體演算法實現,在LIBSVM的官方論文中介紹的很詳細,這裡總結部分關鍵問題:
?Working Set,也就是需要優化的α部分的選取
?迭代停止條件的設定
?α的更新演算法,也就是每一步子問題的求解方法
?Shrinking,即移除一些已經滿足條件的α,加快收斂速度
?Cache,當Q矩陣過大時,需要對矩陣進行快取。

上面的每個問題,處理起來都不簡單。作為使用者,或許也沒有必要深諳裡面的所有細節。我覺得最需要認識的兩個問題是:1) SVM的目標函式看起來好像是一個標準的優化問題,但實際求解卻要複雜得多。為了提高求解的速度,既要做演算法上的優化,也需要做工程上的改進。如果只是簡簡單單按照教科書的方法,甚至直接呼叫一些優化的工具包來實現的SVM演算法,最多也就算個demo。要能夠真正寫一個高效穩定、能處理大規模資料的SVM工具還是非常不容易的。所以用LIBSVM還是比自己實現演算法要簡單靠譜不少。2)SVM的求解之所以要優化,就是因為這個問題本身計算和儲存比較麻煩。所以雖然做了這麼多的優化,整個演算法求解的效率仍然較低。所以我們在使用時還要注意各種程式的細節,提高執行的效率。另外,樣本量過大時,有時候為了充分利用資料,也不得不忍痛割愛,放棄kernel的使用。

除了標準的C-SVM,LIBSVM也提供了對其他一些SVM方法的支援。其中ν-SVM與C-SVM的演算法與應用場景基本是相同的,唯一的區別是原本的引數C變成了引數ν。C-SVM中引數C調整範圍在[0,+∞),而ν-SVM中與之對應的引數ν的調整範圍變成了 (0,1]。這樣的設定使得ν-SVM更具解釋性,有時在引數設定上也能提供一定的方便。但ν-SVM與C-SVM並不存在本質上的差別,通過引數的調節,兩者可以達到完全相同的效果。所以在使用LIBSVM處理分類問題是,選擇上面任何一種方法都是OK的,只需要遵循自己的習慣就好了。

One-Class SVM也是LIBSVM所支援的一種分類方法。顧名思義,使用One Class時,只需要提供一類樣本,演算法會學習一個儘量小的超球面包裹所有的訓練樣本。One-Class SVM看起來很有誘惑力,因為我們經常會遇到有一類樣本而需要學習分類器的情況。但事實上,一方面很多時候我們得到的正樣本在取樣過程中存在很大的偏差,導致學習出的One Class分類器不一定考慮到了所有正樣本的情形;另一方面,大部分問題還是存在很多構造人工負樣本的辦法。根據我的經驗,採用普通的SVM效果通常還是會好過One-Class SVM,而One-Class SVM在真實場景中的使用也並算不上多。因此在使用這個方法前也需要對問題進行更深入的研究。

最後,LIBSVM也支援基於SVM的迴歸模型,即SVR。與分類模型類似,SVR也分為C-SVR和ν-SVR。SVR的目標函式與SVM的分類模型稍有區別。由於迴歸問題預測值與目標值的偏差可大可小,因此SVR使用了兩個slack variable用來刻畫預測的誤差邊界。雖然存在這樣的差別,但是兩者的基本思路和優化演算法與還是基本一致的。

在LIBSVM的實現中,上面五種模型,即C-SVM,ν-SVM,One-class SVM,C-SVR,ν-SVR,最終都可以轉化為一個更通用的優化框架,然後用同樣的策略進行求解,這也是LIBSVM所實現的主要功能。在實際使用中,最常用到的方法還是C-SVM,這是最傳統的SVM分類模型。

LIBLINEAR

LIBLINEAR是在LIBSVM流行多年後才開發的,要解決的問題本質上也比LIBSVM更簡單,其優勢主要在於效率與scalablility。之所以存在這樣的優勢,是因為線性SVM的求解要比kernel SVM簡單許多。

還從上面的對偶問題說起,之前SVM的求解很大程度上受到yTα=0的困擾,因此每次必須選擇一組 α進行優化。如果對這一約束項追根述源,可以發現這一項是通過令模型的常數項b導數為0而得到的。而線上性模型中,我們可以通過一個簡單地trick,令x=[x,1]和w=[w,b],這樣,在模型中的常數項就不存在了。當然,這樣的trick只能在線性模型中才適用。沒有了上面的約束,優化的目標函式變成了:

argminα subjecttof(α)=12αTQα?eTα0≤αi≤C,i=1,…,l

這個時候,就可以每次只選擇一個αi進行優化,每一輪遍歷α的所有維度,多輪迭代,直至最後收斂。這樣的優化演算法叫做coordinate descent(座標下降法)。利用線性函式的特殊性,直接根據α就可以計算出w的向量表示,於是大大提高了演算法的效率。具體的優化演算法可以參考文獻 A Dual Coordinate Descent Method for Large-scale Linear SVM。

換一個看問題的角度,線性SVM的目標函式可以寫成下面的形式:

argminw12wTw+C∑i=1l(max(0,1?yiwTxi))

進一步對問題進行抽象,可以把一類分類問題寫成下面的形式:

argminwΩ(w)+C∑i=1l?(yi,wTxi)

其中的?作為誤差函式,用來度量預測值與目標值的損失。在上面的線性SVM的情形中,有

?(yi,wTxi)=max(0,1?yiwTxi)

這裡的?稱為Hinge Loss。

又如在Logistic Regression中,loss function ?被定義為

?(yi,wTxi)=log(1+e?yiwTixi)

Ω一般被稱為正則化項(Regularizer),最常使用的就是前面出現的?2-norm,寫作wTw,也可以寫作∥w∥22,即向量w中所有元素的平方和。除?2-norm之外,?1-norm也是經常使用regularizer,而且會帶來一些特別的效果(後面會進行討論)。大量的監督學習模型都可以寫成loss function + regularizer的形式,而引數C則控制了兩者在最終損失函式中所佔的比重。不同loss function與regularizer的選取以及兩者之間的平衡,是機器學習的最重要主題之一。

對於上面的問題,有很多成熟的演算法可以進行模型的求解,比如最速梯度法,牛頓法等,對於樣本量較大時,也可以採用隨機梯度的方法進行訓練。 一般來說,由於考慮了二階導數,牛頓法本身的優化效率要高於只考慮一階導數的最速梯度法。但由於牛頓法本身在計算量和收斂性上存在很多侷限性,所以很少直接使用,而是在牛頓法思想基礎上進行一定的改進。其中普遍使用的演算法有BFGS和L-BFGS等。具體到liblinear軟體包,作者採用的是Trust Region Newton (TRON) method對模型對傳統牛頓法進行了改進,該方法被證明比L-BFGS訓練更加高效。

LIBLINEAR中實現了基於TRON方法的L-2 SVM和Logistical Regression模型訓練。其中的L2-loss SVM是標準SVM的變種,loss function變成了:

?(yi,wTxi)=(max(0,1?yiwTxi))2

從實際效果來說,L2-loss SVM與標準的L1-loss SVM並沒有太大的區別。但是在計算上,前者的求導形式更加簡單,便於梯度的計算與優化。LIBLINEAR並沒有實現Trust Region Newton法的標準L1-loss SVM實現,一方面是因為直接對hinge loss求導需要分段討論比較複雜,另一方面L2-loss SVM基本可以直接替代L1-loss SVM。不過在其他的一些軟體包中,如SVMLIN中,則實現了L1-loss SVM的原問題求解,但使用的優化演算法是L-BGFS而不是TRON。

總結

前面介紹了LIBSVM和LIBLINEAR的優化演算法,下面簡單總結一下不同演算法的應用場景吧:
?所有線性問題都是用LIBLINEAR,而不要使用LIBSVM。
?LIBSVM中的不同演算法,如C-SVM和nu-SVM在模型和求解上並沒有本質的區別,只是做了一個引數的變換,所以選擇自己習慣的就好。
?LIBLINEAR的優化演算法主要分為兩大類,即求解原問題(primal problem)和對偶問題(dual problem)。求解原問題使用的是TRON的優化演算法,對偶問題使用的是Coordinate Descent優化演算法。總的來說,兩個演算法的優化效率都較高,但還是有各自更加擅長的場景。對於樣本量不大,但是維度特別高的場景,如文字分類,更適合對偶問題求解,因為由於樣本量小,計算出來的Kernel Matrix也不大,後面的優化也比較方便。而如果求解原問題,則求導的過程中要頻繁對高維的特徵矩陣進行計算,如果特徵比較稀疏的話,那麼就會多做很多無意義的計算,影響優化的效率。相反,當樣本數非常多,而特徵維度不高時,如果採用求解對偶問題,則由於Kernel Matrix過大,求解並不方便。反倒是求解原問題更加容易。

多分類問題

LIBSVM和LIBLINEAR都支援多分類(Multi-class classification)問題。所謂多分類問題,就是說每一個樣本的類別標籤可以超過2個,但是最終預測的結果只能是一個類別。比如經典的手寫數字識別問題,輸入是一幅影象,最後輸出的是0-9這十個數字中的某一個。

LIBSVM與LIBLINEAR但實現方式卻完全不同。LIBSVM採取的one vs one的策略,也就是所有的分類兩兩之間都要訓練一個分類器。這樣一來,如果存在k個class,理論上就需要訓練 k(k?1)/2個分類器。實際上,libsvm在這一步也進行了一定的優化,利用已有分類的關係,減少分類器的個數。儘管如此,LIBSVM在多分類問題上還是要多次訓練分類器。但是,考慮到前面說的LIBSVM的優化方法,隨著樣本數量的增加,訓練的複雜度會非線性的增加。而通過1VS1的策略,可以保證每一個子分類問題的樣本量不至於太多,其實反倒是方便了整個模型的訓練。

而LIBLINEAR則採取了另一種訓練策略,即one vs all。每一個class對應一個分類器,副樣本就是其他類別的所有樣本。由於LIBLINEAR能夠和需要處理的訓練規模比LIBSVM大得多,因此這種方式要比one vs one更加高效。此外,LIBLINEAR還實現了基於Crammer and Singer方法的SVM多分類演算法,在一個統一的目標函式中學習每一個class對應的分類器。

輸出檔案

一般來說,我們使用LIBLINEAR或者LIBSVM,可以直接呼叫系統的訓練與預測函式,不需要直接去接觸訓練得到的模型檔案。但有時候我們也可能需要在自己的平臺實現預測的演算法,這時候就不可避免的要對模型檔案進行解析。

由於LIBLINEAR與LIBSVM的訓練模型不同,因此他們對應的模型檔案格式也不同。LIBLINEAR訓練結果的格式相對簡單,例如:

 1 solver_type L2R_L2LOSS_SVC_DUAL  2 nr_class 3  3 label 0 1 2  4 nr_feature 5  5 bias -1  6 w  7 -0.4021097293855418 0.1002472498884907 -0.1619908595357437  8 0.008699468444669581 0.2310109611908343 -0.2295723940247394  9 -0.6814324057724231 0.4263611607497726 -0.4190714505083906 10 -0.1505088594898125 0.2709227166451816 -0.1929294695905781 11 2.14656708009991 -0.007495770268046003 -0.1880325536062815

上面的solver_type表示求解演算法,w以下表示求解得到的模型權重。其中每一列對應一個class的分類器,而每一行對應特徵的一個維度。其中nr_class表示求解的個數,nr_feature表示特徵的維度,bias表示模型的bias,可以人工設定權重。這裡容易產生誤解的是label這個欄位,表示的是每一個使用者訓練檔案中label對應w的列數。比如在上面的模型中,使用者指定編號為0的分類器對應w的第一列。但是上面的對應關係並不是一定存在的,比如在二分類場景中,用將整樣本標為1,負樣本標為0,但在模型訓練中,LIBLINEAR會按照自己的編號系統進行訓練,因而有可能出現負樣本在前,正樣本在後的情形。這時候,就必須要根據label 1 0將LIBLIENAR內部的編號體系與真實的使用者標籤進行對應。當然,後來LIBLINEAR和LIBSVM做了一些優化,在二分類時,如果正負樣本標籤分別是-1和+1,那麼可以始終保證正樣本出現在w的第一列。但是這個機制也不是完全靠譜,比如說在LIBLINEAR的spark實現程式碼中,就沒有實現這個特性,曾經把我整的很慘。因此在這裡還是需要十分注意。

LIBSVM的訓練結果格式就更復雜一些,例如:

 1 kernel_type rbf  2 gamma 0.0769231  3 nr_class 3  4 total_sv 140  5 rho -1.04496 0.315784 1.03037  6 label 1 0 -1  7 nr_sv 2 2 1  8 SV  9 0 1 1:0.583333 2:-1 3:0.333333 4:-0.603774 5:1 6:-1 7:1 8:0.358779 9:-1 10:-0.483871 12:-1 13:1 10 0 0.6416468628860974 1:0.125 2:1 3:0.333333 4:-0.320755 5:-0.406393 6:1 7:1 8:0.0839695 9:1 10:-0.806452 12:-0.333333 13:0.5 11 0 1 1:0.333333 2:1 3:-1 4:-0.245283 5:-0.506849 6:-1 7:-1 8:0.129771 9:-1 10:-0.16129 12:0.333333 13:-1 12 0.2685466895842373 0 1:0.583333 2:1 3:1 4:-0.509434 5:-0.52968 6:-1 7:1 8:-0.114504 9:1 10:-0.16129 12:0.333333 13:1 13 0 1 1:0.208333 2:1 3:0.333333 4:-0.660377 5:-0.525114 6:-1 7:1 8:0.435115 9:-1 10:-0.193548 12:-0.333333 13:1

上面引數的意義都比較直接,需要注意的是SV後面就是訓練出的模型引數,以支援向量的方式進行儲存。nr_sv給出了每一個支援向量所對應的模型,比如“2 2 1”就表示前兩行是標籤為1類的支援向量,其後面兩行是標籤為0類的支援向量,最後一行是標籤為-1類的支援向量。而具體每一行支援向量,在上面的模型中,由於存在三類,所以每一個支援向量有可能都會存在於兩個分類器中,所以前兩列的數分別對應了對剩下兩個分類作為支援向量時候的α值,後面才是真正的支援向量。

調節引數

LIBSVM和LIBLINEAR工具包都包含很多需要調節的引數,引數的調節既需要足夠的耐心,也有著很多的技巧。當然,還需要對引數本身的意義和對模型的影響了如指掌。下面主要討論一些對模型影響較大的引數

引數C

引數C是在LIBLINEAR和LIBSVM的求解中都要用到的一個引數。前面說到的各種模型,可以寫成統一的形式:


argminwΩ(?(w))+C∑i=1l?(yi,?(w)T?(xi))(1)

其中右邊的一項是模型的損失項,其大小表明了分類器對樣本的擬合程度。而左邊的一項,則是人為加上的損失,與訓練樣本無關,被稱作正則化項(Regularizer),反映了對訓練模型額外增加的一些約束。而引數C則負責調整兩者之間的權重。C越大,則要求模型能夠更好地擬合訓練樣本資料,反之,則要求模型更多的滿足正則化項的約束。以LIBLINEAR為例,下面先討論LIBLINEAR下?2norm的情況:


argminw∥w∥22+C∑i=1l?(yi,wTxi)(2)

之所以要增加正則化項,是因為在設計模型的時候,我們對於樣本的質量以及模型的泛化能力沒有充分的自信,認為在沒有其他約束的情況下,訓練得到的模型會因為過於遷就已有的樣本資料而無法對新的資料達到同樣的效果。在這個時候,就必須在模型中增加人類的一些經驗知識。比如上面對?(w)增加?2norm的約束就是如此。如果上面公式中的損失函式對應一個迴歸問題,那麼這個問題就被稱作Ridge Regression,中文叫做脊迴歸或者嶺迴歸。

我們可以站在不同的角度來理解?2norm正則化項的意義。如果把學習分類函式中w看作是一個引數估計的問題,那麼不帶正則化項的目標函式對應的就是對w進行最大似然估計的問題。為了使w的估計更加接近真實的情況,我們可以根據經驗對w制定一個先驗分佈。當我們假設w先驗分佈是一個多元高斯分佈,且不同維度之間是沒有關聯的(即協方差矩陣非對角線元素為0),而每一個維度特徵的方差為某一固定制,那麼推匯出來的最大後驗概率就是上面的帶正則化項的目標函式。而C與w先驗分佈的方差相關。C越大,就意味著正則化的效果偏弱,w的波動範圍可以更大,先驗的方差也更大;而C越小,則意味著正則化的效果更強,w的波動範圍變小,先驗的方差也變小。通過減小C的值,可以控制w的波動不至於過大,以免受一些資料的影響,造成模型的過擬合(overfitting)。 
另外也有一種更直觀的解釋,上面regularized形式的目標函式也可以根據KKT條件轉為constraint形式,也就是:


argminws.t.∑i=1l?(yi,wTxi)∥w∥22<s2(3)

通過引數s限制w的大小,而s與C也存在著一定正向相關的關係。因此,當C較小時,w的取值也被限制在的一個很小的範圍內。下面的圖給了一個非常直觀的解釋:

L2Norm

由於有了對w取值的限制,就出現了兩種情況。第一種是當s不夠大的時候,此時如果沿梯度下降的方向一直搜尋,找到全域性最優解,就已經找出圈外,不滿足下面的約束項。這個時候,只能在滿足約束的條件下找到儘量好的解。根據KKT條件,此時的最優解一定是劃定範圍的圓圈與目標函式等梯度線相切的位置,如上圖左邊所示。如果把梯度圖看成一座山的等高線,那邊最優解的位置一定是等高線中凸起的部分,類似於一座山上的山脊或者山嶺,這也是脊(嶺)迴歸的由來。另一種情況是當s足夠大的時候,這個時候,在由s所劃定的範圍內已經能夠達到全域性最優解,這個時候,下面的約束項其實並沒有起到作用,就如上圖右邊所示。

因此在調參過程中,如果資料量較少,或者我們對資料的質量信心不足,就應該減少C的大小,增加先驗的重要性,反之則可以增加C的大小,讓資料本身起更大的作用。而在優化過程中,C越大,需要搜尋的w的範圍也越大,計算的代價也會越高。而從前面的分析中可以看出,當C增加到一定程度後,模型已經能確保達到全域性最優,此時繼續增加C對提高演算法的表現已經沒有幫助。因此,在LIBLINEAR中,實際推薦的做法是將C的值從小向大來調,當C增加之後已經無法改變演算法的效果時,說明C對模型已經沒有影響,就沒有必要繼續調下去了。選擇較小的C反而可以提高模型收斂的速度。

?1norm的使用

在LIBLINEAR中,除了提供上面提到的?2norm正則化項之外,還提供了?1norm的選項。?1norm一般寫成∥w∥1,其實就是對向量w中的所有元素的絕對值進行求和。與?2norm相比,?1norm也具有對w本身大小的約束,使得w的某些維度值不至於過大而導致過擬合。這個特性在統計學上也稱作收縮”shrinkage”。而此外,?1norm還有另外一個非常有用的特徵,即能夠使學習到的w比較稀疏(sparse),也就是存在很多的0項,而且可以通過係數C控制0項的的個數。當C減小時,w的非0項就增多,當C無限小時,由於完全沒有擬合損失的壓力,w也可以變成全部是0了。

為什麼?1norm還有這樣的功能呢?說來話長,下次可以專門再寫部落格討論了。簡單來說,是由?1norm的特殊性質決定的。?1norm所對應的絕對值函式是連續的,但並非處處可導,因為在0點存在特殊的情況,即左右導數實不相等的。因此可以認為定義sub_gradient來描述這種特殊情況的導數,這樣的結果是在這個特殊點上,導數不是一個值,而是左右倒數中間的一個範圍。由於在0點導數取值非常靈活,使得在模型求解的過程中,很容易在這樣的點達到極值,也就會使得學習到的w儘量的稀疏。

基於?1的線性迴歸模型也被叫做LASSO。採用這個懲罰項的基本動機是認為只有少數特徵是與分類結果真正相關,而絕大多數特徵是無關的。這一假設確實存在一定的道理,但是在實際分類準確度上,?1norm正則化項對?2norm項並沒有絕對的優勢,這是因為?2norm雖然不能直接去除那些與分類結果無關的特徵,但是模型學習的結果也會讓這些特徵的權重很低,所以他們能起到的影響不大。但是?1norm的一個好處是可以作為一個特徵選擇的工具,從高維特徵空間中只選取少數一些與分類結果最為相關的特徵進行計算。這在處理大量高維資料或者實時計算問題是,還是非常有幫助的,可以大大減少儲存,提高計算的效率。

Kernel相關引數

如果使用LIBSVM的話,引數調節的工作就會更復雜一些。首先是kernel的使用。一般來說rbf kernel是被鼓勵優先使用的。如果使用rbf kernel效果都無法調到滿意,那麼採用poly或linear也無濟於事。在一些特殊場景下,可以考慮自定義kernel,這在LIBSVM中也是支援的。

rbf的全稱是Radial Basis Function,中文叫做徑向基函式。而一般在SVM使用的rbf是Gaussian RBF,也就是我們一般所說的高斯核函式。給定兩個點x1和x2,Gaussian RBF定義為:


K(x1,x2)=exp(?γ∥x1?x2∥2)(4)

可見,高斯核函式是對兩點之間的歐氏距離進行了一定的變換,且變換受到引數γ的控制。應該怎樣理解高斯核函式的意義與γ的作用的?


K(x1,x2)=exp(?γ∥x1?x2∥2)=exp(?γ(?∥x1∥2+2xT1x2?∥x2∥2))=exp(?γ∥x1∥2)exp(?γ∥x2∥2)exp(2γxT1x2)=exp(?γ∥x1∥2)exp(?γ∥x2∥2)∑n=0∞(2γxT1x2)nn!(5)

當γ較小,或者說在極端情況趨向於0的時候,可以有∑∞n=0(2γxT1x2)nn!≈2γxT1x2,也就是n>1以後的項遠小於n=1項。這個時候,rbf kernel的效果其實和linear kernel相差無幾。

相反,當γ增大時,n>1以後的項也產生作用,其基本思想和poly kernel差不多,只是rbf直接把維度從有限維度的多項式上升到了無窮維的多項式而已。當γ無限增大時,可以看到,除非∥x1?x2∥2為0,否則K(x_1, x_2) 都會無限趨近於0。也就是說,當γ趨近於無窮大的時候,每一個數據點除了和其自身外,和其他點得距離都為0。在這種情況下,模型訓練的結果只能是把所有點都作為支援向量,因此在訓練資料上精度可以達到100%。這樣的模型也可以看成KNN的特殊情況,此時的K等於1。

上面分別討論了問題的兩個極端情況。當γ無限小的時候,rbf核SVM和線性SVM效果類似,因此模型的複雜度,或者說VC維較低,不容易過擬合。而當γ值無限增大時,所有點都變成支援向量,模型複雜多或者說VC維最高,也最容易過擬合。而一般情況下,γ取一箇中間值,也就間距兩者的意義,相比於線性模型,可以選擇更多的支援向量,增加模型的複雜度與擬合能力。而相比於1-NN模型,也會適當降低模型的複雜度,避免過擬合的風險。此外,從上面的討論也可以看出,通過引數調整,rbf核基本上可以達到與線性核以及poly核差不多的效果。所以,在不考慮計算效率的情況下,為了達到最優模型,只需要針對rbf模型進行調參就可以了。

上面在引數調整的討論裡,都假設引數C是固定的。但在實際SVM的調參過程中,C和γ是同時變化的,這進一步增加了調參的複雜性。關於兩個變數之間的關係,也有很多理論上的分析與討論,這裡不過多進行討論,可以參考檔案:Asymptotic Behaviors of Support Vector Machines with Gaussian Kernel。

在實際的應用場景下,我們可以通過cross-validate的方法對引數進行遍歷,選擇最佳的引數組合。LIBSVM也提供了grid.py工具,用於SVM的調參。在一般的應用中,我們只需要設定引數的可變範圍,然後在訓練資料中對引數組合進行遍歷,選擇最優引數組合就可以了。

總結

最後簡單做下總結引數與kernel的選擇吧: * 線性模型選擇,選擇LIBLINEAR,主要調節引數C,從小到大調節,如果再增加C對結果改變不大時,就可以不用進行測試下去了,選擇較小的C就可以了。 * kernel選擇,如果需要使用kernel,對於一般問題,優先使用rbf kernel。LIBSVM提供的多項式和tanh核函式,都存在一些侷限性,一般來說,rbf是使用方便性和模型效果都比較穩定的核函數了。 * LIBSVM引數調節。如果使用rbf核,需要同時調節引數C和引數γ,問題要更復雜一些,最好的辦法是自動遍歷引數進行引數選擇,比如利用grid.py。

??

??