如何學習SVM(支持向量機)以及改進實現SVM算法程序 - 雷鋒網

2020-12-14 雷鋒網

雷鋒網 AI 科技評論按,本文為韋易笑在知乎問題如何學習SVM(支持向量機)以及改進實現SVM算法程序下面的回覆,雷鋒網 AI 科技評論獲其授權轉載。以下為正文:

學習 SVM 的最好方法是實現一個 SVM,可講理論的很多,講實現的太少了。

假設你已經讀懂了 SVM 的原理,並了解公式怎麼推導出來的,比如到這裡:

SVM 的問題就變成:求解一系列滿足約束的 alpha 值,使得上面那個函數可以取到最小值。然後記錄下這些非零的 alpha 值和對應樣本中的 x 值和 y 值,就完成學習了,然後預測的時候用:

上面的公式計算出 f(x) ,如果返回值 > 0 那麼是 +1 類別,否則是 -1 類別,先把這一步怎麼來的,為什麼這麼來找篇文章讀懂,不然你會做的一頭霧水。

那麼剩下的 SVM 實現問題就是如何求解這個函數的極值。方法有很多,我們先找個起點,比如 Platt 的 SMO算法,它後面有偽代碼描述怎麼快速求解 SVM 的各個係數。

第一步:實現傳統的 SMO 算法

現在大部分的 SVM 開源實現,源頭都是 platt 的 smo 算法,讀完他的文章和推導,然後照著偽代碼寫就行了,核心代碼沒幾行:

target = desired output vectorpoint = training point matrixprocedure takeStep(i1,i2)if (i1 == i2) return 0 alph1 = Lagrange multiplier for i1 y1 = target[i1] E1 = SVM output on point[i1] – y1 (check in error cache) s = y1*y2 Compute L, H via equations (13) and (14) if (L == H) return 0 k11 = kernel(point[i1],point[i1]) k12 = kernel(point[i1],point[i2]) k22 = kernel(point[i2],point[i2]) eta = k11+k22-2*k12 if (eta > 0) { a2 = alph2 + y2*(E1-E2)/eta if (a2 < L) a2 = L else if (a2 > H) a2 = H } else { Lobj = objective function at a2=L Hobj = objective function at a2=H if (Lobj < Hobj-eps) a2 = L else if (Lobj > Hobj+eps) a2 = H else a2 = alph2 } if (|a2-alph2| < eps*(a2+alph2+eps)) return 0 a1 = alph1+s*(alph2-a2) Update threshold to reflect change in Lagrange multipliers Update weight vector to reflect change in a1 & a2, if SVM is linear Update error cache using new Lagrange multipliers Store a1 in the alpha array Store a2 in the alpha array return 1endprocedure

核心代碼很緊湊,就是給定兩個 ai, aj,然後迭代出新的 ai, aj 出來,還有一層循環會不停的選擇最需要被優化的係數 ai, aj,然後調用這個函數。如何更新權重和 b 變量(threshold)文章裡面都有說,再多調試一下,可以用 python 先調試,再換成 C/C++,保證得到一個正確可用的 SVM 程序,這是後面的基礎。

第二步:實現核函數緩存

觀察下上面的偽代碼,開銷最大的就是計算核函數 K(xi, xj),有些計算又反覆用到,一個 100 個樣本的數據集求解,假設總共要調用核函數 20 萬次,但是 xi, xj 的組和只有 100x100=1 萬種,有緩存的話你的效率可以提升 20 倍。

樣本太大時,如果你想存儲所有核函數的組和,需要 N*N * sizeof(double) 的空間,如果訓練集有 10 萬個樣本,那麼需要 76 GB 的內存,顯然是不可能實現的,所以核函數緩存是一個有限空間的 LRU 緩存,SVM 的 SMO 求解過程中其實會反覆用到特定的幾個有限的核函數求解,所以命中率不用擔心。

有了這個核函數緩存,你的 SVM 求解程序能瞬間快幾十倍。

第三步:優化誤差值求解

注意看上面的偽代碼,裡面需要計算一個估計值和真實值的誤差 Ei 和 Ej,他們的求解方法是:

E(i) = f(xi) - yi

這就是目前為止 SMO 這段為代碼裡代價最高的函數,因為回顧下上面的公式,計算一遍 f(x) 需要 for 循環做乘法加法。

platt 的文章建議是做一個 E 函數的緩存,方便後面選擇 i, j 時比較,我看到很多入門版本 SVM 實現都是這麼做。其實這是有問題的,後面我們會說到。最好的方式是定義一個 g(x) 令其等於:

也就是 f(x) 公式除了 b 以外前面的最費時的計算,那麼我們隨時可以計算誤差:

E(j) = g(xj) + b - yj

所以最好的辦法是對 g(x) 進行緩存,platt 的方法裡因為所有 alpha 值初始化成了 0,所以 g(x) 一開始就可以全部設置成 0,稍微觀察一下 g(x) 的公式,你就會發現,因為去掉了 b 的幹擾,而每次 SMO 迭代更新 ai, aj 參數時,這兩個值都是線性變化的,所以我們可以給 g(x) 求關於 a 的偏導,假設 ai,aj 變化了步長 delta,那麼所有樣本對應的 g(x) 加上 delta 乘以針對 ai, aj 的偏導數就行了,具體代碼類似:

double Kik = kernel(i, k);double Kjk = kernel(j, k);G[k] += delta_alpha_i * Kik * y[i] + delta_alpha_j * Kjk * y[j];

把這段代碼放在 takeStep 後面,每次成功更新一對 ai, aj 以後,更新所有樣本對應的 g(x) 緩存,這樣通過每次迭代更新 g(x) 避免了大量的重複計算。

這其實是很直白的一種優化方式,我查了一下,有人專門發論文就講了個類似的方法。

第四步:實現冷熱數據分離

Platt 的文章裡也證明過一旦某個 alpha 出於邊界(0 或者 C)的時候,就很不容易變動,而且偽代碼也是優先在工作集裡尋找 > 0 and < C 的 alpha 值進行優化,找不到了,再對工作集整體的 alpha 值進行迭代。

那麼我們勢必就可以把工作集分成兩個部分,熱數據在前(大於 0 小於 C 的 alpha 值),冷數據在後(小於等於 0 或者大於等於 C 的 alpha)。

隨著迭代加深,會發現大部分時候只需要在熱數據裡求解,並且熱數據的大小會逐步不停的收縮,所以區分了冷熱以後 SVM 大部分都在針對有限的熱數據迭代,偶爾不行了,再全部迭代一次,然後又回到冷熱迭代,性能又能提高不少。

第五步:支持 Ensemble

大家都知道,通過 Ensemble 可以讓多個不同的弱模型組和成一個強模型,而傳統 SVM 實現並不能適應一些類似 AdaBoost 的集成方法,所以我們需要做一些改動。可以讓外面針對某一個分類傳入一個「權重」過來,修正 SVM 的識別結果。

最傳統的修改方式就是將不等式約束 C 分為 Cp 和 Cn 兩個針對 +1 分類的 C 及針對 -1 分類的 C。修改方式是直接用原始的 C 乘以各自分類的權重,得到 Cp 和 Cn,然後迭代時,不同的樣本根據它的 y 值符號,用不同的 C 值帶入計算。

這樣 SVM 就能用各種集成方法同其他模型一起組成更為強大精準的模型了。

實現到這一步你就得到了功能上和性能上同 libsvm 類似的東西,接下來我們繼續優化。

第六步:繼續優化核函數計算

核函數緩存非常消耗內存,libsvm 數學上已經沒得挑了,但是工程方面還有很大改進餘地,比如它的核緩存實現。

由於標準 SVM 核函數用的是兩個高維矢量的內積,根據內積的幾個條件,SVM 的核函數又是一個正定核,即 K(xi, xj) = K(xj, xi),那麼我們同樣的內存還能再多存一倍的核函數,性能又能有所提升。

針對核函數的計算和存儲有很多優化方式,比如有人對 NxN 的核函數矩陣進行採樣,只計算有限的幾個核函數,然後通過插值的方式求解出中間的值。還有人用 float 存儲核函數值,又降低了一倍空間需求。

第七步:支持稀疏向量和非稀疏向量

對於高維樣本,比如文字這些,可能有上千維,每個樣本的非零特徵可能就那麼幾個,所以稀疏向量會比較高效,libsvm 也是用的稀疏向量。

但是還有很多時候樣本是密集向量,比如一共 200 個特徵,大部分樣本都有 100個以上的非零特徵,用稀疏向量存儲的話就非常低效了,openCV 的 SVM 實現就是非稀疏向量。

非稀疏向量直接是用數組保存樣本每個特徵的值,在工程方面就有很多優化方式了,比如用的最多的求核函數的時候,直接上 SIMD 指令或者 CUDA,就能獲得更好的計算性能。

所以最好的方式是同時支持稀疏和非稀疏,兼顧時間和空間效率,對不同的數據選擇最適合的方式。

第八步:針對線性核進行優化

傳統的 SMO 方法,是 SVM 的通用求解方法,然而針對線性核,就是:

K(xi, xj) = xi . xj

還有很多更高效的求解思路,比如 Pegasos 算法就用了一種類似隨機梯度下降的方法,快速求 SVM 的解權重 w,如果你的樣本適合線性核,使用一些針對性的非 SMO 算法可以極大的優化 SVM 求解,並且能處理更加龐大的數據集,LIBLINEAR 就是做這件事情的。

同時這類算法也適合 online 訓練和並行訓練,可以逐步更新增量訓練新的樣本,還可以用到多核和分布式計算來訓練模型,這是 SMO 算法做不到的地方。

但是如果碰到非線性核,權重 w 處於高維核空間裡(有可能無限維),你沒法梯度下降迭代 w,並且 pegasos 的 pdf 裡面也沒有提到如何用到非線性核上,LIBLINEAR 也沒有辦法處理非線性核。

或許哪天出個數學家又找到一種更好的方法,可以用類似 pegasos 的方式求解非線性核,那麼 SVM 就能有比較大的進展了。

後話

上面八條,你如果實現前三條,基本就能深入理解 SVM 的原理了,如果實現一大半,就可以得到一個類似 libsvm 的東西,全部實現,你就能得到一個比 libsvm 更好用的 SVM 庫了。

上面就是如何實現一個相對成熟的 SVM 模型的思路,以及配套優化方法,再往後還有興趣,可以接著實現支持向量回歸,也是一個很有用的東西。

相關焦點

  • 如何學習SVM(支持向量機)以及改進實現SVM算法程序?
    SVM(支持向量機)以及改進實現SVM算法程序?如何更新權重和 b 變量(threshold)文章裡面都有說,再多調試一下,可以用 python 先調試過了,再換成 C/C++,保證得到一個正確可用的 svm 程序,這是後面的基礎。
  • 如何學習SVM?怎麼改進實現SVM算法程序?答案來了
    SVM 算法,以及如何利用 matlab 編程實現 SVM 算法,同時在理解原有的程序包以及在原有的程序包基礎上進行改進有什麼好的方法?本文為知乎問題「如何學習 SVM(支持向量機)以及改進實現 SVM 算法程序?」下的兩個答案。
  • 支持向量機(SVM)算法總結
    ,同時兼有數度快,支持數據量級大(相對經典機器學習算法)等特點使其在工程實踐中的得到了廣泛的應用。但很多算法工程師以外的人對這一算法了解不多。今天我們就聊一聊支持向量機算法。要理解svm我們需要先理解什麼是間隔最大化,首先從簡單的線性二分類開始開始說起。要想對不用的樣本空間分開來,如下如所示,需要找出一條線將不同分類的樣本隔離開。
  • 【典型算法】SVM算法
    小編說:機器學習算法眾多,全部掌握,一則不可能,二則沒必要。如何靈活掌握和應用機器學習算法呢?
  • 機器學習算法筆記-SVM支持向量機簡介及JAVA小實現
    前言最近開始學習一些機器學習裡的主要算法,SVM支持向量機是我目前產品裡用到的核心算法,想在這裡把我學到的一些東西記錄下來。
  • 支持向量機SVM原理(參數解讀和python腳本)
    網易雲課堂報名入口(微信二維碼掃一掃)支持向量機SVM這是線性支持向量機,LSVM支持向量缺點:1.如果數據特徵(維度)大於樣本量,支持向量機表現很差2.支持向量機不提供概率區間估計支持向量機,在sklearn裡面,有兩種,SVC支持向量分類,用於分類問題,SVR,支持向量回歸,用於回歸問題。
  • 動手寫機器學習算法:SVM支持向量機(附代碼)
    我們在學習過程中最容易犯的一個錯誤就是,其中:訓練集:(未完待續)作者:lawlite19https://github.com/lawlite19/MachineLearning_Python#相關文章:用Python實現機器學習算法
  • 關於支持向量機(SVM)的原理,你了解多少?(萬字長文 速收)
    其實Coursera的課堂上Andrew Ng講過支持向量機,但顯然他沒有把這作為重點,加上Ng講支持向量機的方法我一時半會難以完全消化,所以聽的也是一知半解。真正開始了解支持向量機就是看的這篇「三重境界」,之後才對這個算法有了大概的概念,以至如何去使用,再到其中的原理為何,再到支持向量機的證明等。總之,這篇文章開啟了我長達數月的研究支持向量機階段,直到今日。」
  • 從零推導支持向量機 (SVM) | 雷鋒網
    該文為其給雷鋒網(公眾號:雷鋒網) AI 科技評論的獨家供稿,未經許可禁止轉載。摘要支持向量機 (SVM) 是一個非常經典且高效的分類模型。但是,支持向量機中涉及許多複雜的數學推導,並需要比較強的凸優化基礎,使得有些初學者雖下大量時間和精力研讀,但仍一頭霧水,最終對其望而卻步。
  • 支持向量機SVM來預測晶片未知信息
    本文是用支持向量機SVM的方法去預測未知樣本的信息。
  • 經典算法解讀:一文看懂支持向量機以及推導
    在監督學習中,許多學習算法的性能都非常類似,因此,重要的不是你該選擇使用學習算法A還是學習算法B,而更重要的是,應用這些算法時,所創建的大量數據在應用這些算法時,表現情況通常依賴於你的水平。比如:你為學習算法所設計的特徵量的選擇,以及如何選擇正則化參數,諸如此類的事。
  • SVM 支持向量機算法-實戰篇
    上一篇介紹了 SVM 的原理和一些基本概念,本篇來介紹如何用 SVM 處理實際問題
  • 支持向量機(SVM)原理剖析
    SVM的的學習策略就是間隔最大化,可形式化為一個求解凸二次規劃的問題,也等價於正則化的合頁損失函數的最小化問題。SVM的的學習算法就是求解凸二次規劃的最優化算法。1.1 線性SVM算法原理SVM學習的基本想法是求解能夠正確劃分訓練數據集並且幾何間隔最大的分離超平面。
  • 8步學習SVM(支持向量機)(附改進實現SVM算法代碼)
    那麼剩下的 SVM 實現問題就是如何求解這個函數的極值。方法有很多,我們先找個起點,比如 Platt 的SMO 算法(https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-98-14.pdf),它後面有偽代碼描述怎麼快速求解 SVM 的各個係數。
  • 機器學習算法入門之「支持向量機」
    在機器學習領域,支持向量機(Support Vector Machine,簡稱SVM)屬於一種監督學習算法,可以用來解決分類和回歸問題,其中,在分類問題中的應用更加廣泛。我們可以導入sklearn.svm模塊中的SVC類實現支持向量機算法,先利用訓練集數據構建模型,再用測試集數據進行預測。
  • 【機器學習基礎】數學推導+純Python實現機器學習算法8-9:線性可分支持向量機和線性支持向量機
    為了處理非線性的情況,在感知機模型的基礎上有了兩個方向,一個就是上一講說到的神經網絡,大家也看到了,現在深度學習大放異彩,各種網絡功能強大。但實際上在神經網絡興起之前,基於感知機的另一種模型——支持向量機,同樣可以解決非線性問題。     支持向量機一般來說有三種任務類型:線性可分情況,近似線性可分情況以及線性不可分情況。
  • Python機器學習筆記:SVM-sklearn實現 (4)FlyAI
    上面將SVM再贅述了一下,下面學習sklearn中的SVM方法,sklearn中SVM的算法庫分為兩類,一類是分類的算法庫,主要包含LinearSVC,NuSVC和SVC三個類,另一類是回歸算法庫,包含SVR,NuSVR和LinearSVR三個類,相關模塊都包裹在sklearn.svm模塊中。
  • 機器學習的分類算法之SVM(支持向量機)
    引言SVM(Support Vector Machine,支持向量機)是機器學習中的有監督線性分類算法
  • 一個簡單的案例帶你了解支持向量機算法(Python代碼)
    相反,「支持向量機」就像一把鋒利的刀—它適用於較小的數據集,但它可以再這些小的數據集上面構建更加強大的模型。現在,我希望你現在已經掌握了隨機森林,樸素貝葉斯算法和模型融合的算法基礎。如果沒有,我希望你先抽出一部分時間來了解一下他們,因為在本文中,我將指導你了解認識機器學習算法中關鍵的高級算法,也就是支持向量機的基礎知識。
  • 零基礎也教你玩轉SVM
    一 SVM簡介    支持向量機(support vector machines, SVM)是一種二分類模型,它的基本模型是定義在特徵空間上的間隔最大的線性分類器,間隔最大使它有別於感知機;它分類的思想是,給定給一個包含正例和反例的樣本集合,SVM的目的是尋找一個超平面來對樣本根據正例和反例進行分割。