日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

经典算法解读:一文看懂支持向量机以及推导

發布時間:2025/3/8 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 经典算法解读:一文看懂支持向量机以及推导 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文是吳恩達老師的機器學習課程[1]和《統計學習方法》[2]的筆記和代碼復現部分(支持向量機)。

作者:黃海廣[3]

備注:筆記和作業(含數據、原始作業文件)、視頻都在 github 中下載。

我將陸續將課程筆記和課程代碼發布在公眾號“機器學習初學者”,敬請關注。這個是第三部分:支持向量機,是原教程的第七周,包含了筆記和作業代碼(原課程作業是 OCTAVE 的,這里是復現的 python 代碼)

已完成

  • 第一部分:回歸代碼

  • 第二部分:邏輯回歸

本文資源都在 github:

  • https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes

  • https://github.com/fengdu78/lihang-code

筆記部分:

十二、支持向量機(Support Vector Machines)

12.1 優化目標

參考視頻: 12 - 1 - Optimization Objective (15 min).mkv

到目前為止,你已經見過一系列不同的學習算法。在監督學習中,許多學習算法的性能都非常類似,因此,重要的不是你該選擇使用學習算法A還是學習算法B,而更重要的是,應用這些算法時,所創建的大量數據在應用這些算法時,表現情況通常依賴于你的水平。比如:你為學習算法所設計的特征量的選擇,以及如何選擇正則化參數,諸如此類的事。還有一個更加強大的算法廣泛的應用于工業界和學術界,它被稱為支持向量機(Support Vector Machine)。與邏輯回歸和神經網絡相比,支持向量機,或者簡稱SVM,在學習復雜的非線性方程時提供了一種更為清晰,更加強大的方式。因此,在接下來的視頻中,我會探討這一算法。在稍后的課程中,我也會對監督學習算法進行簡要的總結。當然,僅僅是作簡要描述。但對于支持向量機,鑒于該算法的強大和受歡迎度,在本課中,我會花許多時間來講解它。它也是我們所介紹的最后一個監督學習算法。

正如我們之前開發的學習算法,我們從優化目標開始。那么,我們開始學習這個算法。為了描述支持向量機,事實上,我將會從邏輯回歸開始展示我們如何一點一點修改來得到本質上的支持向量機。

那么,在邏輯回歸中我們已經熟悉了這里的假設函數形式,和右邊的 S 型激勵函數。然而,為了解釋一些數學知識.我將用?表示。

現在考慮下我們想要邏輯回歸做什么:如果有一個?的樣本,我的意思是不管是在訓練集中或是在測試集中,又或者在交叉驗證集中,總之是?,現在我們希望?趨近 1。因為我們想要正確地將此樣本分類,這就意味著當?趨近于 1 時,?應當遠大于 0,這里的意思是遠遠大于 0。這是因為由于??表示?,當?遠大于 0 時,即到了該圖的右邊,你不難發現此時邏輯回歸的輸出將趨近于 1。相反地,如果我們有另一個樣本,即。我們希望假設函數的輸出值將趨近于 0,這對應于,或者就是??會遠小于 0,因為對應的假設函數的輸出值趨近 0。

如果你進一步觀察邏輯回歸的代價函數,你會發現每個樣本?都會為總代價函數,增加這里的一項,因此,對于總代價函數通常會有對所有的訓練樣本求和,并且這里還有一個項,但是,在邏輯回歸中,這里的這一項就是表示一個訓練樣本所對應的表達式。現在,如果我將完整定義的假設函數代入這里。那么,我們就會得到每一個訓練樣本都影響這一項。

現在,先忽略??這一項,但是這一項是影響整個總代價函數中的這一項的。

現在,一起來考慮兩種情況:

一種是等于 1 的情況;另一種是??等于 0 的情況。

在第一種情況中,假設??,此時在目標函數中只需有第一項起作用,因為時,項將等于 0。因此,當在??的樣本中時,即在?中 ,我們得到??這樣一項,這里同上一張幻燈片一致。

我用??表示,即:?。當然,在代價函數中,?前面有負號。我們只是這樣表示,如果??代價函數中,這一項也等于 1。這樣做是為了簡化此處的表達式。如果畫出關于?的函數,你會看到左下角的這條曲線,我們同樣可以看到,當?增大時,也就是相當于增大時,?對應的值會變的非常小。對整個代價函數而言,影響也非常小。這也就解釋了,為什么邏輯回歸在觀察到正樣本時,試圖將設置得非常大。因為,在代價函數中的這一項會變的非常小。

現在開始建立支持向量機,我們從這里開始:

我們會從這個代價函數開始,也就是一點一點修改,讓我取這里的?點,我先畫出將要用的代價函數。

新的代價函數將會水平的從這里到右邊(圖外),然后我再畫一條同邏輯回歸非常相似的直線,但是,在這里是一條直線,也就是我用紫紅色畫的曲線,就是這條紫紅色的曲線。那么,到了這里已經非常接近邏輯回歸中使用的代價函數了。只是這里是由兩條線段組成,即位于右邊的水平部分和位于左邊的直線部分,先別過多的考慮左邊直線部分的斜率,這并不是很重要。但是,這里我們將使用的新的代價函數,是在的前提下的。你也許能想到,這應該能做同邏輯回歸中類似的事情,但事實上,在之后的優化問題中,這會變得更堅定,并且為支持向量機,帶來計算上的優勢。例如,更容易計算股票交易的問題等等。

目前,我們只是討論了的情況,另外一種情況是當時,此時如果你仔細觀察代價函數只留下了第二項,因為第一項被消除了。如果當時,那么這一項也就是 0 了。所以上述表達式只留下了第二項。因此,這個樣本的代價或是代價函數的貢獻。將會由這一項表示。并且,如果你將這一項作為的函數,那么,這里就會得到橫軸。現在,你完成了支持向量機中的部分內容,同樣地,我們要替代這一條藍色的線,用相似的方法。

如果我們用一個新的代價函數來代替,即這條從 0 點開始的水平直線,然后是一條斜線,像上圖。那么,現在讓我給這兩個方程命名,左邊的函數,我稱之為,同時,右邊函數我稱它為。這里的下標是指在代價函數中,對應的??和??的情況,擁有了這些定義后,現在,我們就開始構建支持向量機。

這是我們在邏輯回歸中使用代價函數。也許這個方程看起來不是非常熟悉。這是因為之前有個負號在方程外面,但是,這里我所做的是,將負號移到了表達式的里面,這樣做使得方程看起來有些不同。對于支持向量機而言,實質上我們要將這替換為,也就是,同樣地,我也將這一項替換為,也就是代價。這里的代價函數,就是之前所提到的那條線。此外,代價函數,也是上面所介紹過的那條線。因此,對于支持向量機,我們得到了這里的最小化問題,即:

然后,再加上正則化參數。現在,按照支持向量機的慣例,事實上,我們的書寫會稍微有些不同,代價函數的參數表示也會稍微有些不同。

首先,我們要除去這一項,當然,這僅僅是由于人們使用支持向量機時,對比于邏輯回歸而言,不同的習慣所致,但這里我所說的意思是:你知道,我將要做的是僅僅除去這一項,但是,這也會得出同樣的??最優值,好的,因為?僅是個常量,因此,你知道在這個最小化問題中,無論前面是否有?這一項,最終我所得到的最優值都是一樣的。這里我的意思是,先給你舉一個樣本,假定有一最小化問題:即要求當取得最小值時的值,這時最小值為:當時取得最小值。

現在,如果我們想要將這個目標函數乘上常數 10,這里我的最小化問題就變成了:求使得最小的值,然而,使得這里最小的值仍為 5。因此將一些常數乘以你的最小化項,這并不會改變最小化該方程時得到值。因此,這里我所做的是刪去常量。也相同的,我將目標函數乘上一個常量,并不會改變取得最小值時的值。

第二點概念上的變化,我們只是指在使用支持向量機時,一些如下的標準慣例,而不是邏輯回歸。因此,對于邏輯回歸,在目標函數中,我們有兩項:第一個是訓練樣本的代價,第二個是我們的正則化項,我們不得不去用這一項來平衡。這就相當于我們想要最小化加上正則化參數,然后乘以其他項對吧?這里的表示這里的第一項,同時我用B表示第二項,但不包括,我們不是優化這里的。我們所做的是通過設置不同正則參數達到優化目的。這樣,我們就能夠權衡對應的項,是使得訓練樣本擬合的更好。即最小化。還是保證正則參數足夠小,也即是對于B項而言,但對于支持向量機,按照慣例,我們將使用一個不同的參數替換這里使用的來權衡這兩項。你知道,就是第一項和第二項我們依照慣例使用一個不同的參數稱為,同時改為優化目標,。因此,在邏輯回歸中,如果給定,一個非常大的值,意味著給予更大的權重。而這里,就對應于將?設定為非常小的值,那么,相應的將會給比給更大的權重。因此,這只是一種不同的方式來控制這種權衡或者一種不同的方法,即用參數來決定是更關心第一項的優化,還是更關心第二項的優化。當然你也可以把這里的參數?考慮成,同?所扮演的角色相同,并且這兩個方程或這兩個表達式并不相同,因為,但是也并不全是這樣,如果當時,這兩個優化目標應當得到相同的值,相同的最優值?。因此,就用它們來代替。那么,我現在刪掉這里的,并且用常數來代替。因此,這就得到了在支持向量機中我們的整個優化目標函數。然后最小化這個目標函數,得到SVM 學習到的參數。

最后有別于邏輯回歸輸出的概率。在這里,我們的代價函數,當最小化代價函數,獲得參數時,支持向量機所做的是它來直接預測的值等于 1,還是等于 0。因此,這個假設函數會預測 1。當大于或者等于 0 時,或者等于 0 時,所以學習參數就是支持向量機假設函數的形式。那么,這就是支持向量機數學上的定義。

在接下來的視頻中,讓我們再回去從直觀的角度看看優化目標,實際上是在做什么,以及 SVM 的假設函數將會學習什么,同時也會談談如何做些許修改,學習更加復雜、非線性的函數。

12.2 大邊界的直觀理解

參考視頻: 12 - 2 - Large Margin Intuition (11 min).mkv

人們有時將支持向量機看作是大間距分類器。在這一部分,我將介紹其中的含義,這有助于我們直觀理解SVM模型的假設是什么樣的。

這是我的支持向量機模型的代價函數,在左邊這里我畫出了關于的代價函數,此函數用于正樣本,而在右邊這里我畫出了關于的代價函數,橫軸表示,現在讓我們考慮一下,最小化這些代價函數的必要條件是什么。如果你有一個正樣本,,則只有在時,代價函數才等于 0。

換句話說,如果你有一個正樣本,我們會希望,反之,如果,我們觀察一下,函數,它只有在的區間里函數值為 0。這是支持向量機的一個有趣性質。事實上,如果你有一個正樣本,則其實我們僅僅要求大于等于 0,就能將該樣本恰當分出,這是因為如果>0 大的話,我們的模型代價函數值為 0,類似地,如果你有一個負樣本,則僅需要<=0 就會將負例正確分離,但是,支持向量機的要求更高,不僅僅要能正確分開輸入的樣本,即不僅僅要求>0,我們需要的是比 0 值大很多,比如大于等于 1,我也想這個比 0 小很多,比如我希望它小于等于-1,這就相當于在支持向量機中嵌入了一個額外的安全因子,或者說安全的間距因子。

當然,邏輯回歸做了類似的事情。但是讓我們看一下,在支持向量機中,這個因子會導致什么結果。具體而言,我接下來會考慮一個特例。我們將這個常數設置成一個非常大的值。比如我們假設的值為 100000 或者其它非常大的數,然后來觀察支持向量機會給出什么結果?

如果?非常大,則最小化代價函數的時候,我們將會很希望找到一個使第一項為 0 的最優解。因此,讓我們嘗試在代價項的第一項為 0 的情形下理解該優化問題。比如我們可以把設置成了非常大的常數,這將給我們一些關于支持向量機模型的直觀感受。

我們已經看到輸入一個訓練樣本標簽為,你想令第一項為 0,你需要做的是找到一個,使得,類似地,對于一個訓練樣本,標簽為,為了使?函數的值為 0,我們需要。因此,現在考慮我們的優化問題。選擇參數,使得第一項等于 0,就會導致下面的優化問題,因為我們將選擇參數使第一項為 0,因此這個函數的第一項為 0,因此是乘以 0 加上二分之一乘以第二項。這里第一項是乘以 0,因此可以將其刪去,因為我知道它是 0。

這將遵從以下的約束:,如果?是等于 1 的,,如果樣本是一個負樣本,這樣當你求解這個優化問題的時候,當你最小化這個關于變量的函數的時候,你會得到一個非常有趣的決策邊界。

具體而言,如果你考察這樣一個數據集,其中有正樣本,也有負樣本,可以看到這個數據集是線性可分的。我的意思是,存在一條直線把正負樣本分開。當然有多條不同的直線,可以把正樣本和負樣本完全分開。

比如,這就是一個決策邊界可以把正樣本和負樣本分開。但是多多少少這個看起來并不是非常自然是么?

或者我們可以畫一條更差的決策界,這是另一條決策邊界,可以將正樣本和負樣本分開,但僅僅是勉強分開,這些決策邊界看起來都不是特別好的選擇,支持向量機將會選擇這個黑色的決策邊界,相較于之前我用粉色或者綠色畫的決策界。這條黑色的看起來好得多,黑線看起來是更穩健的決策界。在分離正樣本和負樣本上它顯得的更好。數學上來講,這是什么意思呢?這條黑線有更大的距離,這個距離叫做間距(margin)。

當畫出這兩條額外的藍線,我們看到黑色的決策界和訓練樣本之間有更大的最短距離。然而粉線和藍線離訓練樣本就非常近,在分離樣本的時候就會比黑線表現差。因此,這個距離叫做支持向量機的間距,而這是支持向量機具有魯棒性的原因,因為它努力用一個最大間距來分離樣本。因此支持向量機有時被稱為大間距分類器,而這其實是求解上一頁幻燈片上優化問題的結果。

我知道你也許想知道求解上一頁幻燈片中的優化問題為什么會產生這個結果?它是如何產生這個大間距分類器的呢?我知道我還沒有解釋這一點。

我將會從直觀上略述為什么這個優化問題會產生大間距分類器。總之這個圖示有助于你理解支持向量機模型的做法,即努力將正樣本和負樣本用最大的間距分開。

在本節課中關于大間距分類器,我想講最后一點:我們將這個大間距分類器中的正則化因子常數設置的非常大,我記得我將其設置為了 100000,因此對這樣的一個數據集,也許我們將選擇這樣的決策界,從而最大間距地分離開正樣本和負樣本。那么在讓代價函數最小化的過程中,我們希望找出在和兩種情況下都使得代價函數中左邊的這一項盡量為零的參數。如果我們找到了這樣的參數,則我們的最小化問題便轉變成:

事實上,支持向量機現在要比這個大間距分類器所體現得更成熟,尤其是當你使用大間距分類器的時候,你的學習算法會受異常點(outlier) 的影響。比如我們加入一個額外的正樣本。

在這里,如果你加了這個樣本,為了將樣本用最大間距分開,也許我最終會得到一條類似這樣的決策界,對么?就是這條粉色的線,僅僅基于一個異常值,僅僅基于一個樣本,就將我的決策界從這條黑線變到這條粉線,這實在是不明智的。而如果正則化參數,設置的非常大,這事實上正是支持向量機將會做的。它將決策界,從黑線變到了粉線,但是如果?設置的小一點,**如果你將 C 設置的不要太大,則你最終會得到這條黑線,**當然數據如果不是線性可分的,如果你在這里有一些正樣本或者你在這里有一些負樣本,則支持向量機也會將它們恰當分開。因此,大間距分類器的描述,僅僅是從直觀上給出了正則化參數非常大的情形,同時,要提醒你的作用類似于,是我們之前使用過的正則化參數。這只是非常大的情形,或者等價地??非常小的情形。你最終會得到類似粉線這樣的決策界,但是實際上應用支持向量機的時候,**當不是非常非常大的時候,它可以忽略掉一些異常點的影響,得到更好的決策界。**甚至當你的數據不是線性可分的時候,支持向量機也可以給出好的結果。

回顧?,因此:

?較大時,相當于??較小,可能會導致過擬合,高方差。

?較小時,相當于較大,可能會導致低擬合,高偏差。

我們稍后會介紹支持向量機的偏差和方差,希望在那時候關于如何處理參數的這種平衡會變得更加清晰。我希望,這節課給出了一些關于為什么支持向量機被看做大間距分類器的直觀理解。它用最大間距將樣本區分開,盡管從技術上講,這只有當參數是非常大的時候是真的,但是它對于理解支持向量機是有益的。

本節課中我們略去了一步,那就是我們在幻燈片中給出的優化問題。為什么會是這樣的?它是如何得出大間距分類器的?我在本節中沒有講解,在下一節課中,我將略述這些問題背后的數學原理,來解釋這個優化問題是如何得到一個大間距分類器的。

12.3 大邊界分類背后的數學(選修)

參考視頻: 12 - 3 - Mathematics Behind Large Margin Classification (Optional) (20 min).mkv

在本節課中,我將介紹一些大間隔分類背后的數學原理。本節為選修部分,你完全可以跳過它,但是聽聽這節課可能讓你對支持向量機中的優化問題,以及如何得到大間距分類器,產生更好的直觀理解。

首先,讓我來給大家復習一下關于向量內積的知識。假設我有兩個向量,和,我將它們寫在這里。兩個都是二維向量,我們看一下,的結果。也叫做向量和之間的內積。由于是二維向量,我可以將它們畫在這個圖上。我們說,這就是向量即在橫軸上,取值為某個,而在縱軸上,高度是某個作為的第二個分量。現在,很容易計算的一個量就是向量的范數。表示的范數,即的長度,即向量的歐幾里得長度。根據畢達哥拉斯定理,,這是向量的長度,它是一個實數。現在你知道了這個的長度是多少了。我剛剛畫的這個向量的長度就知道了。

現在讓我們回頭來看向量?,因為我們想計算內積。是另一個向量,它的兩個分量和是已知的。向量可以畫在這里,現在讓我們來看看如何計算和之間的內積。這就是具體做法,我們將向量投影到向量上,我們做一個直角投影,或者說一個 90 度投影將其投影到上,接下來我度量這條紅線的長度。我稱這條紅線的長度為,因此就是長度,或者說是向量投影到向量上的量,我將它寫下來,是投影到向量上的長度,因此可以將,或者說的長度。這是計算內積的一種方法。如果你從幾何上畫出的值,同時畫出的范數,你也會同樣地計算出內積,答案是一樣的。另一個計算公式是:就是?這個一行兩列的矩陣乘以。因此可以得到。根據線性代數的知識,這兩個公式會給出同樣的結果。順便說一句,。因此如果你將和交換位置,將投影到上,而不是將投影到上,然后做同樣地計算,只是把和的位置交換一下,你事實上可以得到同樣的結果。申明一點,在這個等式中的范數是一個實數,也是一個實數,因此就是兩個實數正常相乘。

最后一點,需要注意的就是值,事實上是有符號的,即它可能是正值,也可能是負值。我的意思是說,如果是一個類似這樣的向量,是一個類似這樣的向量,和之間的夾角大于 90 度,則如果將投影到上,會得到這樣的一個投影,這是的長度,在這個情形下我們仍然有是等于乘以的范數。唯一一點不同的是在這里是負的。在內積計算中,如果和之間的夾角小于 90 度,那么那條紅線的長度是正值。然而如果這個夾角大于 90 度,則將會是負的。就是這個小線段的長度是負的。如果它們之間的夾角大于 90 度,兩個向量之間的內積也是負的。這就是關于向量內積的知識。我們接下來將會使用這些關于向量內積的性質試圖來理解支持向量機中的目標函數。

這就是我們先前給出的支持向量機模型中的目標函數。為了講解方便,我做一點簡化,僅僅是為了讓目標函數更容易被分析。

我接下來忽略掉截距,令,這樣更容易畫示意圖。我將特征數置為 2,因此我們僅有兩個特征,現在我們來看一下目標函數,支持向量機的優化目標函數。當我們僅有兩個特征,即時,這個式子可以寫作:,我們只有兩個參數。你可能注意到括號里面的這一項是向量的范數,或者說是向量的長度。我的意思是如果我們將向量寫出來,那么我剛剛畫紅線的這一項就是向量的長度或范數。這里我們用的是之前學過的向量范數的定義,事實上這就等于向量的長度。

當然你可以將其寫作,如果,那就是的長度。在這里我將忽略,這樣來寫的范數,它僅僅和有關。但是,數學上不管你是否包含,其實并沒有差別,因此在我們接下來的推導中去掉不會有影響這意味著我們的目標函數是等于。因此支持向量機做的全部事情,就是極小化參數向量范數的平方,或者說長度的平方

現在我將要看看這些項:更深入地理解它們的含義。給定參數向量給定一個樣本,這等于什么呢?在前一頁幻燈片上,我們畫出了在不同情形下,的示意圖,我們將會使用這些概念,和就類似于和?。

讓我們看一下示意圖:我們考察一個單一的訓練樣本,我有一個正樣本在這里,用一個叉來表示這個樣本,意思是在水平軸上取值為,在豎直軸上取值為。這就是我畫出的訓練樣本。盡管我沒有將其真的看做向量。它事實上就是一個始于原點,終點位置在這個訓練樣本點的向量。現在,我們有一個參數向量我會將它也畫成向量。我將畫在橫軸這里,將?畫在縱軸這里,那么內積?將會是什么呢?

使用我們之前的方法,我們計算的方式就是我將訓練樣本投影到參數向量,然后我來看一看這個線段的長度,我將它畫成紅色。我將它稱為用來表示這是第?個訓練樣本在參數向量上的投影。根據我們之前幻燈片的內容,我們知道的是將會等于?乘以向量??的長度或范數。這就等于。這兩種方式是等價的,都可以用來計算和之間的內積。

這告訴了我們什么呢?這里表達的意思是:這個?或者的,約束是可以被這個約束所代替的。因為?,將其寫入我們的優化目標。我們將會得到沒有了約束,而變成了。

需要提醒一點,我們之前曾講過這個優化目標函數可以被寫成等于。

現在讓我們考慮下面這里的訓練樣本。現在,繼續使用之前的簡化,即,我們來看一下支持向量機會選擇什么樣的決策界。這是一種選擇,我們假設支持向量機會選擇這個決策邊界。這不是一個非常好的選擇,因為它的間距很小。這個決策界離訓練樣本的距離很近。我們來看一下為什么支持向量機不會選擇它。

對于這樣選擇的參數,可以看到參數向量事實上是和決策界是 90 度正交的,因此這個綠色的決策界對應著一個參數向量這個方向,順便提一句的簡化僅僅意味著決策界必須通過原點。現在讓我們看一下這對于優化目標函數意味著什么。

比如這個樣本,我們假設它是我的第一個樣本,如果我考察這個樣本到參數的投影,投影是這個短的紅線段,就等于,它非常短。類似地,這個樣本如果它恰好是,我的第二個訓練樣本,則它到的投影在這里。我將它畫成粉色,這個短的粉色線段是,即第二個樣本到我的參數向量的投影。因此,這個投影非常短。事實上是一個負值,是在相反的方向,這個向量和參數向量的夾角大于 90 度,的值小于 0。

我們會發現這些將會是非常小的數,因此當我們考察優化目標函數的時候,對于正樣本而言,我們需要,但是如果?在這里非常小,那就意味著我們需要的范數非常大.因為如果??很小,而我們希望,令其實現的唯一的辦法就是這兩個數較大。如果??小,我們就希望的范數大。類似地,對于負樣本而言我們需要。我們已經在這個樣本中看到會是一個非常小的數,因此唯一的辦法就是的范數變大。但是我們的目標函數是希望找到一個參數,它的范數是小的。因此,這看起來不像是一個好的參數向量的選擇。

相反的,來看一個不同的決策邊界。比如說,支持向量機選擇了這個決策界,現在狀況會有很大不同。如果這是決策界,這就是相對應的參數的方向,因此,在這個決策界之下,垂直線是決策界。使用線性代數的知識,可以說明,這個綠色的決策界有一個垂直于它的向量。現在如果你考察你的數據在橫軸上的投影,比如這個我之前提到的樣本,我的樣本,當我將它投影到橫軸上,或說投影到上,就會得到這樣。它的長度是,另一個樣本,那個樣本是。我做同樣的投影,我會發現,的長度是負值。你會注意到現在?和這些投影長度是長多了。如果我們仍然要滿足這些約束,>1,則因為變大了,的范數就可以變小了。因此這意味著通過選擇右邊的決策界,而不是左邊的那個,支持向量機可以使參數的范數變小很多。因此,如果我們想令的范數變小,從而令范數的平方變小,就能讓支持向量機選擇右邊的決策界。這就是支持向量機如何能有效地產生大間距分類的原因。

看這條綠線,這個綠色的決策界。我們希望正樣本和負樣本投影到的值大。要做到這一點的唯一方式就是選擇這條綠線做決策界。這是大間距決策界來區分開正樣本和負樣本這個間距的值。這個間距的值就是等等的值。通過讓間距變大,即通過這些等等的值,支持向量機最終可以找到一個較小的范數。這正是支持向量機中最小化目標函數的目的。

以上就是為什么支持向量機最終會找到大間距分類器的原因。因為它試圖極大化這些的范數,它們是訓練樣本到決策邊界的距離。最后一點,我們的推導自始至終使用了這個簡化假設,就是參數。

就像我之前提到的。這個的作用是:的意思是我們讓決策界通過原點。如果你令不是 0 的話,含義就是你希望決策界不通過原點。我將不會做全部的推導。實際上,支持向量機產生大間距分類器的結論,會被證明同樣成立,證明方式是非常類似的,是我們剛剛做的證明的推廣。

之前視頻中說過,即便不等于 0,支持向量機要做的事情都是優化這個目標函數對應著值非常大的情況,但是可以說明的是,即便不等于 0,支持向量機仍然會找到正樣本和負樣本之間的大間距分隔。

總之,我們解釋了為什么支持向量機是一個大間距分類器。在下一節我們,將開始討論如何利用支持向量機的原理,應用它們建立一個復雜的非線性分類器。

12.4 核函數 1

參考視頻: 12 - 4 - Kernels I (16 min).mkv

回顧我們之前討論過可以使用高級數的多項式模型來解決無法用直線進行分隔的分類問題:

為了獲得上圖所示的判定邊界,我們的模型可能是的形式。

我們可以用一系列的新的特征來替換模型中的每一項。例如令:?

...得到。然而,除了對原有的特征進行組合以外,有沒有更好的方法來構造?我們可以利用核函數來計算出新的特征。

給定一個訓練樣本,我們利用的各個特征與我們預先選定的地標(landmarks)的近似程度來選取新的特征。

例如:

其中:,為實例中所有特征與地標之間的距離的和。上例中的就是核函數,具體而言,這里是一個高斯核函數(Gaussian Kernel)。注:這個函數與正態分布沒什么實際上的關系,只是看上去像而已。

這些地標的作用是什么?如果一個訓練樣本與地標之間的距離近似于 0,則新特征?近似于,如果訓練樣本與地標之間距離較遠,則近似于一個較大的數。

假設我們的訓練樣本含有兩個特征[?],給定地標與不同的值,見下圖:

圖中水平面的坐標為?,而垂直坐標軸代表。可以看出,只有當與重合時才具有最大值。隨著的改變值改變的速率受到的控制。

在下圖中,當樣本處于洋紅色的點位置處,因為其離更近,但是離和較遠,因此接近 1,而,接近 0。因此,因此預測。同理可以求出,對于離較近的綠色點,也預測,但是對于藍綠色的點,因為其離三個地標都較遠,預測。

這樣,圖中紅色的封閉曲線所表示的范圍,便是我們依據一個單一的訓練樣本和我們選取的地標所得出的判定邊界,在預測時,我們采用的特征不是訓練樣本本身的特征,而是通過核函數計算出的新特征。

12.5 核函數 2

參考視頻: 12 - 5 - Kernels II (16 min).mkv

在上一節視頻里,我們討論了核函數這個想法,以及怎樣利用它去實現支持向量機的一些新特性。在這一節視頻中,我將補充一些缺失的細節,并簡單的介紹一下怎么在實際中使用應用這些想法。

如何選擇地標?

我們通常是根據訓練集的數量選擇地標的數量,即如果訓練集中有個樣本,則我們選取個地標,并且令:。這樣做的好處在于:現在我們得到的新特征是建立在原有特征與訓練集中所有其他特征之間距離的基礎之上的,即:

下面我們將核函數運用到支持向量機中,修改我們的支持向量機假設為:

? 給定,計算新特征,當?時,預測?,否則反之。

相應地修改代價函數為:,

?在具體實施過程中,我們還需要對最后的正則化項進行些微調整,在計算時,我們用代替,其中是根據我們選擇的核函數而不同的一個矩陣。這樣做的原因是為了簡化計算。

理論上講,我們也可以在邏輯回歸中使用核函數,但是上面使用?來簡化計算的方法不適用與邏輯回歸,因此計算將非常耗費時間。

在此,我們不介紹最小化支持向量機的代價函數的方法,你可以使用現有的軟件包(如liblinear,libsvm等)。在使用這些軟件包最小化我們的代價函數之前,我們通常需要編寫核函數,并且如果我們使用高斯核函數,那么在使用之前進行特征縮放是非常必要的。

另外,支持向量機也可以不使用核函數,不使用核函數又稱為線性核函數(linear kernel),當我們不采用非常復雜的函數,或者我們的訓練集特征非常多而樣本非常少的時候,可以采用這種不帶核函數的支持向量機。

下面是支持向量機的兩個參數和的影響:

?較大時,相當于較小,可能會導致過擬合,高方差;

?較小時,相當于較大,可能會導致低擬合,高偏差;

較大時,可能會導致低方差,高偏差;

較小時,可能會導致低偏差,高方差。

如果你看了本周的編程作業,你就能親自實現這些想法,并親眼看到這些效果。這就是利用核函數的支持向量機算法,希望這些關于偏差和方差的討論,能給你一些對于算法結果預期的直觀印象。

12.6 使用支持向量機

參考視頻: 12 - 6 - Using An SVM (21 min).mkv

目前為止,我們已經討論了SVM比較抽象的層面,在這個視頻中我將要討論到為了運行或者運用SVM。你實際上所需要的一些東西:支持向量機算法,提出了一個特別優化的問題。但是就如在之前的視頻中我簡單提到的,我真的不建議你自己寫軟件來求解參數,因此由于今天我們中的很少人,或者其實沒有人考慮過自己寫代碼來轉換矩陣,或求一個數的平方根等我們只是知道如何去調用庫函數來實現這些功能。同樣的,用以解決SVM最優化問題的軟件很復雜,且已經有研究者做了很多年數值優化了。因此你提出好的軟件庫和好的軟件包來做這樣一些事兒。然后強烈建議使用高優化軟件庫中的一個,而不是嘗試自己落實一些數據。有許多好的軟件庫,我正好用得最多的兩個是liblinearlibsvm,但是真的有很多軟件庫可以用來做這件事兒。你可以連接許多你可能會用來編寫學習算法的主要編程語言。

在高斯核函數之外我們還有其他一些選擇,如:

多項式核函數(Polynomial Kernel)

字符串核函數(String kernel

卡方核函數( chi-square kernel

直方圖交集核函數(histogram interp kernel

等等...

這些核函數的目標也都是根據訓練集和地標之間的距離來構建新特征,這些核函數需要滿足 Mercer's 定理,才能被支持向量機的優化軟件正確處理。

多類分類問題

假設我們利用之前介紹的一對多方法來解決一個多類分類問題。如果一共有個類,則我們需要個模型,以及個參數向量。我們同樣也可以訓練個支持向量機來解決多類分類問題。但是大多數支持向量機軟件包都有內置的多類分類功能,我們只要直接使用即可。

盡管你不去寫你自己的SVM的優化軟件,但是你也需要做幾件事:

1、是提出參數的選擇。我們在之前的視頻中討論過誤差/方差在這方面的性質。

2、你也需要選擇內核參數或你想要使用的相似函數,其中一個選擇是:我們選擇不需要任何內核參數,沒有內核參數的理念,也叫線性核函數。因此,如果有人說他使用了線性核的SVM(支持向量機),這就意味這他使用了不帶有核函數的SVM(支持向量機)。

從邏輯回歸模型,我們得到了支持向量機模型,在兩者之間,我們應該如何選擇呢?

下面是一些普遍使用的準則:

為特征數,為訓練樣本數。

(1)如果相較于而言,要大許多,即訓練集數據量不夠支持我們訓練一個復雜的非線性模型,我們選用邏輯回歸模型或者不帶核函數的支持向量機。

(2)如果較小,而且大小中等,例如在 1-1000 之間,而在 10-10000 之間,使用高斯核函數的支持向量機。

(3)如果較小,而較大,例如在 1-1000 之間,而大于 50000,則使用支持向量機會非常慢,解決方案是創造、增加更多的特征,然后使用邏輯回歸或不帶核函數的支持向量機。

值得一提的是,神經網絡在以上三種情況下都可能會有較好的表現,但是訓練神經網絡可能非常慢,選擇支持向量機的原因主要在于它的代價函數是凸函數,不存在局部最小值。

今天的SVM包會工作得很好,但是它們仍然會有一些慢。當你有非常非常大的訓練集,且用高斯核函數是在這種情況下,我經常會做的是嘗試手動地創建,擁有更多的特征變量,然后用邏輯回歸或者不帶核函數的支持向量機。如果你看到這個幻燈片,看到了邏輯回歸,或者不帶核函數的支持向量機。在這個兩個地方,我把它們放在一起是有原因的。原因是:邏輯回歸和不帶核函數的支持向量機它們都是非常相似的算法,不管是邏輯回歸還是不帶核函數的SVM,通常都會做相似的事情,并給出相似的結果。但是根據你實現的情況,其中一個可能會比另一個更加有效。但是在其中一個算法應用的地方,邏輯回歸或不帶核函數的SVM另一個也很有可能很有效。但是隨著SVM的復雜度增加,當你使用不同的內核函數來學習復雜的非線性函數時,這個體系,你知道的,當你有多達 1 萬(10,000)的樣本時,也可能是 5 萬(50,000),你的特征變量的數量這是相當大的。那是一個非常常見的體系,也許在這個體系里,不帶核函數的支持向量機就會表現得相當突出。你可以做比這困難得多需要邏輯回歸的事情。

最后,神經網絡使用于什么時候呢?對于所有的這些問題,對于所有的這些不同體系一個設計得很好的神經網絡也很有可能會非常有效。有一個缺點是,或者說是有時可能不會使用神經網絡的原因是:對于許多這樣的問題,神經網絡訓練起來可能會特別慢,但是如果你有一個非常好的SVM實現包,它可能會運行得比較快比神經網絡快很多,盡管我們在此之前沒有展示,但是事實證明,SVM具有的優化問題,是一種凸優化問題。因此,好的SVM優化軟件包總是會找到全局最小值,或者接近它的值。對于SVM你不需要擔心局部最優。在實際應用中,局部最優不是神經網絡所需要解決的一個重大問題,所以這是你在使用SVM的時候不需要太去擔心的一個問題。根據你的問題,神經網絡可能會比SVM慢,尤其是在這樣一個體系中,至于這里給出的參考,看上去有些模糊,如果你在考慮一些問題,這些參考會有一些模糊,但是我仍然不能完全確定,我是該用這個算法還是改用那個算法,這個沒有太大關系,當我遇到機器學習問題的時候,有時它確實不清楚這是否是最好的算法,但是就如在之前的視頻中看到的算法確實很重要。但是通常更加重要的是:你有多少數據,你有多熟練是否擅長做誤差分析和排除學習算法,指出如何設定新的特征變量和找出其他能決定你學習算法的變量等方面,通常這些方面會比你使用邏輯回歸還是SVM這方面更加重要。但是,已經說過了,SVM仍然被廣泛認為是一種最強大的學習算法,這是一個體系,包含了什么時候一個有效的方法去學習復雜的非線性函數。因此,實際上與邏輯回歸、神經網絡、SVM一起使用這些方法來提高學習算法,我認為你會很好地建立很有技術的狀態。(編者注:當時GPU計算比較慢,神經網絡還不流行。)

機器學習系統對于一個寬泛的應用領域來說,這是另一個在你軍械庫里非常強大的工具,你可以把它應用到很多地方,如硅谷、在工業、學術等領域建立許多高性能的機器學習系統。

代碼部分

機器學習練習 3 - 支持向量機

代碼修改并注釋:黃海廣,haiguang2000@qq.com

在本練習中,我們將使用支持向量機(SVM)來構建垃圾郵件分類器。我們將從一些簡單的 2D 數據集開始使用 SVM 來查看它們的工作原理。然后,我們將對一組原始電子郵件進行一些預處理工作,并使用 SVM 在處理的電子郵件上構建分類器,以確定它們是否為垃圾郵件。

我們要做的第一件事是看一個簡單的二維數據集,看看線性 SVM 如何對數據集進行不同的 C 值(類似于線性/邏輯回歸中的正則化項)。

import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sb from scipy.io import loadmatraw_data = loadmat('data/ex6data1.mat')

我們將其用散點圖表示,其中類標簽由符號表示(+表示正類,o 表示負類)。

data = pd.DataFrame(raw_data['X'], columns=['X1', 'X2']) data['y'] = raw_data['y']positive = data[data['y'].isin([1])] negative = data[data['y'].isin([0])]fig, ax = plt.subplots(figsize=(12, 8)) ax.scatter(positive['X1'], positive['X2'], s=50, marker='x', label='Positive') ax.scatter(negative['X1'], negative['X2'], s=50, marker='o', label='Negative') ax.legend() plt.show()

請注意,還有一個異常的正例在其他樣本之外。這些類仍然是線性分離的,但它非常緊湊。我們要訓練線性支持向量機來學習類邊界。在這個練習中,我們沒有從頭開始執行 SVM 的任務,所以我要用 scikit-learn。

from sklearn import svm svc = svm.LinearSVC(C=1, loss='hinge', max_iter=1000) svc LinearSVC(C=1, class_weight=None, dual=True, fit_intercept=True,intercept_scaling=1, loss='hinge', max_iter=1000, multi_class='ovr',penalty='l2', random_state=None, tol=0.0001, verbose=0)

首先,我們使用 C=1 看下結果如何。

svc.fit(data[['X1', 'X2']], data['y']) svc.score(data[['X1', 'X2']], data['y']) 0.9803921568627451

其次,讓我們看看如果 C 的值越大,會發生什么

svc2 = svm.LinearSVC(C=100, loss='hinge', max_iter=1000) svc2.fit(data[['X1', 'X2']], data['y']) svc2.score(data[['X1', 'X2']], data['y']) 1.0

這次我們得到了訓練數據的完美分類,但是通過增加 C 的值,我們創建了一個不再適合數據的決策邊界。我們可以通過查看每個類別預測的置信水平來看出這一點,這是該點與超平面距離的函數。

data['SVM 1 Confidence'] = svc.decision_function(data[['X1', 'X2']])fig, ax = plt.subplots(figsize=(12,8)) ax.scatter(data['X1'], data['X2'], s=50, c=data['SVM 1 Confidence'], cmap='seismic') ax.set_title('SVM (C=1) Decision Confidence') plt.show() data['SVM 2 Confidence'] = svc2.decision_function(data[['X1', 'X2']])fig, ax = plt.subplots(figsize=(12,8)) ax.scatter(data['X1'], data['X2'], s=50, c=data['SVM 2 Confidence'], cmap='seismic') ax.set_title('SVM (C=100) Decision Confidence') plt.show()

可以看看靠近邊界的點的顏色,區別是有點微妙。如果您在練習文本中,則會出現繪圖,其中決策邊界在圖上顯示為一條線,有助于使差異更清晰。

現在我們將從線性 SVM 轉移到能夠使用內核進行非線性分類的 SVM。我們首先負責實現一個高斯核函數。雖然 scikit-learn 具有內置的高斯內核,但為了實現更清楚,我們將從頭開始實現。

def gaussian_kernel(x1, x2, sigma):return np.exp(-(np.sum((x1 - x2)**2) / (2 * (sigma**2)))) x1 = np.array([1.0, 2.0, 1.0]) x2 = np.array([0.0, 4.0, -1.0]) sigma = 2 gaussian_kernel(x1, x2, sigma) 0.32465246735834974

該結果與練習中的預期值相符。接下來,我們將檢查另一個數據集,這次用非線性決策邊界。

raw_data = loadmat('data/ex6data2.mat')data = pd.DataFrame(raw_data['X'], columns=['X1', 'X2']) data['y'] = raw_data['y']positive = data[data['y'].isin([1])] negative = data[data['y'].isin([0])]fig, ax = plt.subplots(figsize=(12, 8)) ax.scatter(positive['X1'], positive['X2'], s=30, marker='x', label='Positive') ax.scatter(negative['X1'], negative['X2'], s=30, marker='o', label='Negative') ax.legend() plt.show()

對于該數據集,我們將使用內置的 RBF 內核構建支持向量機分類器,并檢查其對訓練數據的準確性。為了可視化決策邊界,這一次我們將根據實例具有負類標簽的預測概率來對點做陰影。從結果可以看出,它們大部分是正確的。

svc = svm.SVC(C=100, gamma=10, probability=True) svc SVC(C=100, cache_size=200, class_weight=None, coef0=0.0,decision_function_shape='ovr', degree=3, gamma=10, kernel='rbf',max_iter=-1, probability=True, random_state=None, shrinking=True,tol=0.001, verbose=False) svc.fit(data[['X1', 'X2']], data['y']) svc.score(data[['X1', 'X2']], data['y']) 0.9698725376593279 data['Probability'] = svc.predict_proba(data[['X1', 'X2']])[:,0] fig,?ax?=?plt.subplots(figsize=(12,8)) ax.scatter(data['X1'], data['X2'], s=30, c=data['Probability'], cmap='Reds') plt.show()

對于第三個數據集,我們給出了訓練和驗證集,并且基于驗證集性能為 SVM 模型找到最優超參數。雖然我們可以使用 scikit-learn 的內置網格搜索來做到這一點,但是本著遵循練習的目的,我們將從頭開始實現一個簡單的網格搜索。

raw_data = loadmat('data/ex6data3.mat') X?=?raw_data['X'] Xval = raw_data['Xval'] y = raw_data['y'].ravel() yval = raw_data['yval'].ravel()C_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100] gamma_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]best_score = 0 best_params = {'C': None, 'gamma': None}for C in C_values:for gamma in gamma_values:svc = svm.SVC(C=C, gamma=gamma)svc.fit(X, y)score = svc.score(Xval, yval)if score > best_score:best_score = scorebest_params['C'] = Cbest_params['gamma'] = gammabest_score, best_params (0.965, {'C': 0.3, 'gamma': 100})

大間隔分類器

from sklearn.svm import SVC from sklearn import datasets import matplotlib as mpl import matplotlib.pyplot as plt mpl.rc('axes', labelsize=14) mpl.rc('xtick', labelsize=12) mpl.rc('ytick', labelsize=12) iris = datasets.load_iris() X = iris["data"][:, (2, 3)] # petal length, petal width y = iris["target"]setosa_or_versicolor = (y == 0) | (y == 1) X = X[setosa_or_versicolor] y = y[setosa_or_versicolor]# SVM Classifier model svm_clf = SVC(kernel="linear", C=float("inf")) svm_clf.fit(X, y) SVC(C=inf, cache_size=200, class_weight=None, coef0=0.0,decision_function_shape='ovr', degree=3, gamma='auto_deprecated',kernel='linear', max_iter=-1, probability=False, random_state=None,shrinking=True, tol=0.001, verbose=False) # Bad models x0 = np.linspace(0, 5.5, 200) pred_1 = 5*x0 - 20 pred_2 = x0 - 1.8 pred_3 = 0.1 * x0 + 0.5 def plot_svc_decision_boundary(svm_clf, xmin, xmax):w = svm_clf.coef_[0]b = svm_clf.intercept_[0]# At the decision boundary, w0*x0 + w1*x1 + b = 0# => x1 = -w0/w1 * x0 - b/w1x0 = np.linspace(xmin, xmax, 200)decision_boundary = -w[0]/w[1] * x0 - b/w[1]margin = 1/w[1]gutter_up = decision_boundary + margingutter_down = decision_boundary - marginsvs = svm_clf.support_vectors_plt.scatter(svs[:, 0], svs[:, 1], s=180, facecolors='#FFAAAA')plt.plot(x0, decision_boundary, "k-", linewidth=2)plt.plot(x0, gutter_up, "k--", linewidth=2)plt.plot(x0, gutter_down, "k--", linewidth=2)plt.figure(figsize=(12,2.7))plt.subplot(121) plt.plot(x0, pred_1, "g--", linewidth=2) plt.plot(x0, pred_2, "m-", linewidth=2) plt.plot(x0, pred_3, "r-", linewidth=2) plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs", label="Iris-Versicolor") plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo", label="Iris-Setosa") plt.xlabel("Petal length", fontsize=14) plt.ylabel("Petal width", fontsize=14) plt.legend(loc="upper left", fontsize=14) plt.axis([0, 5.5, 0, 2])plt.subplot(122) plot_svc_decision_boundary(svm_clf, 0, 5.5) plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs") plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo") plt.xlabel("Petal length", fontsize=14) plt.axis([0, 5.5, 0, 2])plt.show()

特征縮放的敏感性

Xs = np.array([[1, 50], [5, 20], [3, 80], [5, 60]]).astype(np.float64) ys = np.array([0, 0, 1, 1]) svm_clf = SVC(kernel="linear", C=100) svm_clf.fit(Xs, ys)plt.figure(figsize=(12,3.2)) plt.subplot(121) plt.plot(Xs[:, 0][ys==1], Xs[:, 1][ys==1], "bo") plt.plot(Xs[:, 0][ys==0], Xs[:, 1][ys==0], "ms") plot_svc_decision_boundary(svm_clf, 0, 6) plt.xlabel("$x_0$", fontsize=20) plt.ylabel("$x_1$ ", fontsize=20, rotation=0) plt.title("Unscaled", fontsize=16) plt.axis([0, 6, 0, 90])from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(Xs) svm_clf.fit(X_scaled, ys)plt.subplot(122) plt.plot(X_scaled[:, 0][ys==1], X_scaled[:, 1][ys==1], "bo") plt.plot(X_scaled[:, 0][ys==0], X_scaled[:, 1][ys==0], "ms") plot_svc_decision_boundary(svm_clf, -2, 2) plt.xlabel("$x_0$", fontsize=20) plt.title("Scaled", fontsize=16) plt.axis([-2, 2, -2, 2]) plt.show()

硬間隔和軟間隔分類

X_outliers = np.array([[3.4, 1.3], [3.2, 0.8]]) y_outliers = np.array([0, 0]) Xo1 = np.concatenate([X, X_outliers[:1]], axis=0) yo1 = np.concatenate([y, y_outliers[:1]], axis=0) Xo2 = np.concatenate([X, X_outliers[1:]], axis=0) yo2 = np.concatenate([y, y_outliers[1:]], axis=0)svm_clf2 = SVC(kernel="linear", C=10**9) svm_clf2.fit(Xo2, yo2)plt.figure(figsize=(12, 2.7))plt.subplot(121) plt.plot(Xo1[:, 0][yo1 == 1], Xo1[:, 1][yo1 == 1], "bs") plt.plot(Xo1[:, 0][yo1 == 0], Xo1[:, 1][yo1 == 0], "yo") plt.text(0.3, 1.0, "Impossible!", fontsize=24, color="red") plt.xlabel("Petal length", fontsize=14) plt.ylabel("Petal width", fontsize=14) plt.annotate("Outlier",xy=(X_outliers[0][0], X_outliers[0][1]),xytext=(2.5, 1.7),ha="center",arrowprops=dict(facecolor='black', shrink=0.1),fontsize=16, ) plt.axis([0, 5.5, 0, 2])plt.subplot(122) plt.plot(Xo2[:, 0][yo2 == 1], Xo2[:, 1][yo2 == 1], "bs") plt.plot(Xo2[:, 0][yo2 == 0], Xo2[:, 1][yo2 == 0], "yo") plot_svc_decision_boundary(svm_clf2, 0, 5.5) plt.xlabel("Petal length", fontsize=14) plt.annotate("Outlier",xy=(X_outliers[1][0], X_outliers[1][1]),xytext=(3.2, 0.08),ha="center",arrowprops=dict(facecolor='black', shrink=0.1),fontsize=16, ) plt.axis([0, 5.5, 0, 2])plt.show() from sklearn.pipeline import Pipeline from sklearn.datasets import make_moons X, y = make_moons(n_samples=100, noise=0.15, random_state=42) def plot_predictions(clf, axes):x0s = np.linspace(axes[0], axes[1], 100)x1s = np.linspace(axes[2], axes[3], 100)x0, x1 = np.meshgrid(x0s, x1s)X = np.c_[x0.ravel(), x1.ravel()]y_pred = clf.predict(X).reshape(x0.shape)y_decision = clf.decision_function(X).reshape(x0.shape)plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)plt.contourf(x0, x1, y_decision, cmap=plt.cm.brg, alpha=0.1) def plot_dataset(X, y, axes):plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], "bs")plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], "g^")plt.axis(axes)plt.grid(True, which='both')plt.xlabel(r"$x_1$", fontsize=20)plt.ylabel(r"$x_2$", fontsize=20, rotation=0) from sklearn.svm import SVC gamma1, gamma2 = 0.1, 5 C1, C2 = 0.001, 1000 hyperparams = (gamma1, C1), (gamma1, C2), (gamma2, C1), (gamma2, C2)svm_clfs = [] for gamma, C in hyperparams:rbf_kernel_svm_clf = Pipeline([("scaler", StandardScaler()),("svm_clf",SVC(kernel="rbf", gamma=gamma, C=C))])rbf_kernel_svm_clf.fit(X, y)svm_clfs.append(rbf_kernel_svm_clf)plt.figure(figsize=(12, 7))for i, svm_clf in enumerate(svm_clfs):plt.subplot(221 + i)plot_predictions(svm_clf, [-1.5, 2.5, -1, 1.5])plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])gamma, C = hyperparams[i]plt.title(r"$\gamma = {}, C = {}$".format(gamma, C), fontsize=12)plt.show()

svm 推導(統計學習方法)

1.支持向量機最簡單的情況是線性可分支持向量機,或硬間隔支持向量機。構建它的條件是訓練數據線性可分。其學習策略是最大間隔法。可以表示為凸二次規劃問題,其原始最優化問題為

求得最優化問題的解為,,得到線性可分支持向量機,分離超平面是

分類決策函數是

最大間隔法中,函數間隔與幾何間隔是重要的概念。

線性可分支持向量機的最優解存在且唯一。位于間隔邊界上的實例點為支持向量。最優分離超平面由支持向量完全決定。 二次規劃問題的對偶問題是

通常,通過求解對偶問題學習線性可分支持向量機,即首先求解對偶問題的最優值

,然后求最優值和,得出分離超平面和分類決策函數。

2.現實中訓練數據是線性可分的情形較少,訓練數據往往是近似線性可分的,這時使用線性支持向量機,或軟間隔支持向量機。線性支持向量機是最基本的支持向量機。

對于噪聲或例外,通過引入松弛變量,使其“可分”,得到線性支持向量機學習的凸二次規劃問題,其原始最優化問題是

求解原始最優化問題的解和,得到線性支持向量機,其分離超平面為

分類決策函數為

線性可分支持向量機的解唯一但不唯一。對偶問題是

線性支持向量機的對偶學習算法,首先求解對偶問題得到最優解,然后求原始問題最優解和,得出分離超平面和分類決策函數。

對偶問題的解中滿的實例點稱為支持向量。支持向量可在間隔邊界上,也可在間隔邊界與分離超平面之間,或者在分離超平面誤分一側。最優分離超平面由支持向量完全決定。

線性支持向量機學習等價于最小化二階范數正則化的合頁函數

3.非線性支持向量機

對于輸入空間中的非線性分類問題,可以通過非線性變換將它轉化為某個高維特征空間中的線性分類問題,在高維特征空間中學習線性支持向量機。由于在線性支持向量機學習的對偶問題里,目標函數和分類決策函數都只涉及實例與實例之間的內積,所以不需要顯式地指定非線性變換,而是用核函數來替換當中的內積。核函數表示,通過一個非線性轉換后的兩個實例間的內積。具體地,是一個核函數,或正定核,意味著存在一個從輸入空間 x 到特征空間的映射,對任意,有

對稱函數

,任意正整數,對稱函數對應的 Gram 矩陣是半正定的。

所以,在線性支持向量機學習的對偶問題中,用核函數替代內積,求解得到的就是非線性支持向量機

4.SMO 算法

SMO 算法是支持向量機學習的一種快速算法,其特點是不斷地將原二次規劃問題分解為只有兩個變量的二次規劃子問題,并對子問題進行解析求解,直到所有變量滿足 KKT 條件為止。這樣通過啟發式的方法得到原二次規劃問題的最優解。因為子問題有解析解,所以每次計算子問題都很快,雖然計算子問題次數很多,但在總體上還是高效的。

分離超平面:

點到直線距離:

為 2-范數:

直線為超平面,樣本可表示為:

margin:

函數間隔

幾何間隔:,當數據被正確分類時,幾何間隔就是點到超平面的距離

為了求幾何間隔最大,SVM 基本問題可以轉化為求解:(為幾何間隔,(為函數間隔)

分類點幾何間隔最大,同時被正確分類。但這個方程并非凸函數求解,所以要先 ① 將方程轉化為凸函數,② 用拉格朗日乘子法和 KKT 條件求解對偶問題。

① 轉化為凸函數:

先令,方便計算(參照衡量,不影響評價結果)

再將轉化成求解凸函數,1/2 是為了求導之后方便計算。

② 用拉格朗日乘子法和 KKT 條件求解最優值:

整合成:

推導:

根據 KKT 條件:

代入

再把 max 問題轉成 min 問題:

以上為 SVM 對偶問題的對偶形式


kernel

在低維空間計算獲得高維空間的計算結果,也就是說計算結果滿足高維(滿足高維,才能說明高維下線性可分)。

soft margin & slack variable

引入松弛變量,對應數據點允許偏離的 functional margin 的量。

目標函數:

對偶問題:


Sequential Minimal Optimization

首先定義特征到結果的輸出函數:.

因為

import?numpy?as?np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt %matplotlib inline # data def create_data():iris = load_iris()df = pd.DataFrame(iris.data, columns=iris.feature_names)df['label'] = iris.targetdf.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']data = np.array(df.iloc[:100, [0, 1, -1]])for i in range(len(data)):if data[i,-1] == 0:data[i,-1] = -1# print(data)return data[:,:2], data[:,-1] X, y = create_data() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25) plt.scatter(X[:50,0],X[:50,1], label='0') plt.scatter(X[50:,0],X[50:,1], label='1') plt.legend() class SVM:def __init__(self, max_iter=100, kernel='linear'):self.max_iter = max_iterself._kernel = kerneldef init_args(self, features, labels):self.m, self.n = features.shapeself.X = featuresself.Y = labelsself.b = 0.0# 將Ei保存在一個列表里self.alpha = np.ones(self.m)self.E = [self._E(i) for i in range(self.m)]# 松弛變量self.C = 1.0def _KKT(self, i):y_g = self._g(i) * self.Y[i]if self.alpha[i] == 0:return y_g >= 1elif 0 < self.alpha[i] < self.C:return y_g == 1else:return y_g <= 1# g(x)預測值,輸入xi(X[i])def _g(self, i):r = self.bfor j in range(self.m):r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])return r# 核函數def kernel(self, x1, x2):if self._kernel == 'linear':return sum([x1[k] * x2[k] for k in range(self.n)])elif self._kernel == 'poly':return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2return 0# E(x)為g(x)對輸入x的預測值和y的差def _E(self, i):return self._g(i) - self.Y[i]def _init_alpha(self):# 外層循環首先遍歷所有滿足0<a<C的樣本點,檢驗是否滿足KKTindex_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C]# 否則遍歷整個訓練集non_satisfy_list = [i for i in range(self.m) if i not in index_list]index_list.extend(non_satisfy_list)for i in index_list:if self._KKT(i):continueE1 = self.E[i]# 如果E2是+,選擇最小的;如果E2是負的,選擇最大的if E1 >= 0:j = min(range(self.m), key=lambda x: self.E[x])else:j = max(range(self.m), key=lambda x: self.E[x])return i, jdef _compare(self, _alpha, L, H):if _alpha > H:return Helif _alpha < L:return Lelse:return _alphadef fit(self, features, labels):self.init_args(features, labels)for t in range(self.max_iter):# traini1, i2 = self._init_alpha()# 邊界if self.Y[i1] == self.Y[i2]:L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)H = min(self.C, self.alpha[i1] + self.alpha[i2])else:L = max(0, self.alpha[i2] - self.alpha[i1])H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])E1 = self.E[i1]E2 = self.E[i2]# eta=K11+K22-2K12eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(self.X[i2],self.X[i2]) - 2 * self.kernel(self.X[i1], self.X[i2])if eta <= 0:# print('eta <= 0')continuealpha2_new_unc = self.alpha[i2] + self.Y[i2] * (E1 - E2) / eta #此處有修改,根據書上應該是E1 - E2,書上130-131頁alpha2_new = self._compare(alpha2_new_unc, L, H)alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (self.alpha[i2] - alpha2_new)b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(self.X[i2],self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.bb2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(self.X[i2],self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.bif 0 < alpha1_new < self.C:b_new = b1_newelif 0 < alpha2_new < self.C:b_new = b2_newelse:# 選擇中點b_new = (b1_new + b2_new) / 2# 更新參數self.alpha[i1] = alpha1_newself.alpha[i2] = alpha2_newself.b = b_newself.E[i1] = self._E(i1)self.E[i2] = self._E(i2)return 'train done!'def predict(self, data):r = self.bfor i in range(self.m):r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])return 1 if r > 0 else -1def score(self, X_test, y_test):right_count = 0for i in range(len(X_test)):result = self.predict(X_test[i])if result == y_test[i]:right_count += 1return right_count / len(X_test)def _weight(self):# linear modelyx = self.Y.reshape(-1, 1) * self.Xself.w = np.dot(yx.T, self.alpha)return self.w svm = SVM(max_iter=100) svm.fit(X_train, y_train) 'train done!' svm.score(X_test, y_test) 0.48

參考資料

[1]機器學習課程:?https://www.coursera.org/course/ml

[2]《統計學習方法》:?https://www.coursera.org/course/ml
[3]黃海廣:?https://github.com/fengdu78

[4]:[Lagrange Multiplier and KKT: http://blog.csdn.net/xianlingmao/article/details/7919597
[5]:[推導SVM: https://my.oschina.net/dfsj66011/blog/517766
[6]:[機器學習算法實踐-支持向量機(SVM)算法原理: http://pytlab.org/2017/08/15/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AE%97%E6%B3%95%E5%AE%9E%E8%B7%B5-%E6%94%AF%E6%8C%81%E5%90%91%E9%87%8F%E6%9C%BA-SVM-%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86/
[7]?:[Python實現SVM: http://blog.csdn.net/wds2006sdo/article/details/53156589

關于本站

“機器學習初學者”公眾號由是黃海廣博士創建,黃博個人知乎粉絲23000+,github排名全球前100名(32000+)。本公眾號致力于人工智能方向的科普性文章,為初學者提供學習路線和基礎資料。原創作品有:吳恩達機器學習個人筆記、吳恩達深度學習筆記等。

往期精彩回顧

  • 那些年做的學術公益-你不是一個人在戰斗

  • 適合初學者入門人工智能的路線及資料下載

  • 吳恩達機器學習課程筆記及資源(github標星12000+,提供百度云鏡像)

  • 吳恩達深度學習筆記及視頻等資源(github標星8500+,提供百度云鏡像)

  • 《統計學習方法》的python代碼實現(github標星7200+)

備注:加入本站微信群或者qq群,請回復“加群

加入知識星球(4300+用戶,ID:92416895),請回復“知識星球


總結

以上是生活随笔為你收集整理的经典算法解读:一文看懂支持向量机以及推导的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。