當前位置:
首頁 > 知識 > 【收藏】支持向量機原理詳解+案例+代碼!【點擊閱讀原文下載】

【收藏】支持向量機原理詳解+案例+代碼!【點擊閱讀原文下載】

或許你已經開始了自己的探索,聽說過線性可分、核心技巧、核函數等術語。支持向量機(SVM)演算法的核心理念非常簡單,而且將其應用到自然語言分類任務中也不需要大部分複雜的東西。

【數據+代碼下載請點擊閱讀原文】


(原理部分來自機器之心:


https://mp.weixin.qq.com/s/5tUQ9B5juP-Vg8z-gp60rg)




SVM 是如何工作的?




支持向量機的基礎概念可以通過一個簡單的例子來解釋。讓我們想像兩個類別:紅色和藍色,我們的數據有兩個特徵:x 和 y。我們想要一個分類器,給定一對(x,y)坐標,輸出僅限於紅色或藍色。我們將已標記的訓練數據列在下圖中:


支持向量機會接受這些數據點,並輸出一個超平面(在二維的圖中,就是一條線)以將兩類分割開來。這條線就是判定邊界:將紅色和藍色分割開。



但是,最好的超平面是什麼樣的?對於 SVM 來說,它是最大化兩個類別邊距的那種方式,換句話說:超平面(在本例中是一條線)對每個類別最近的元素距離最遠。





線性數據




上面的例子很簡單,因為那些數據是線性可分的——我們可以通過畫一條直線來簡單地分割紅色和藍色。然而,大多數情況下事情沒有那麼簡單。看看下面的例子:



很明顯,你無法找出一個線性決策邊界(一條直線分開兩個類別)。然而,兩種向量的位置分得很開,看起來應該可以輕易地分開它們。




這個時候我們需要引入第三個維度。迄今為止,我們有兩個維度:x 和 y。讓我們加入維度 z,並且讓它以直觀的方式出現:z = x2 + y2(沒錯,圓形的方程式)




於是我們就有了一個三維空間,看看這個空間,他就像這樣:



支持向量機將會如何區分它?很簡單:


太棒了!請注意,現在我們處於三維空間,超平面是 z 某個刻度上(比如 z=1)一個平行於 x 軸的平面。它在二維上的投影是這樣:



於是,我們的決策邊界就成了半徑為 1 的圓形,通過 SVM 我們將其成功分成了兩個類別。




核函數


在以上例子中,我們找到了一種通過將空間巧妙地映射到更高維度來分類非線性數據的方法。然而事實證明,這種轉換可能會帶來很大的計算成本:可能會出現很多新的維度,每一個都可能帶來複雜的計算。為數據集中的所有向量做這種操作會帶來大量的工作,所以尋找一個更簡單的方法非常重要。




還好,我們已經找到了訣竅:SVM 其實並不需要真正的向量,它可以用它們的數量積(點積)來進行分類。這意味著我們可以避免耗費計算資源的境地了。我們需要這樣做:






  • 想像一個我們需要的新空間:




  • z = x2 + y2




  • 找到新空間中點積的形式:




  • a · b = xa· xb + ya· yb + za· zb



  • a · b = xa· xb + ya· yb + (xa2 + ya2) · (xb2 + yb2)




  • 讓 SVM 處理新的點積結果——這就是核函數



這就是核函數的技巧,它可以減少大量的計算資源需求。通常,內核是線性的,所以我們得到了一個線性分類器。但如果使用非線性內核(如上例),我們可以在完全不改變數據的情況下得到一個非線性分類器:我們只需改變點積為我們想要的空間,SVM 就會對它忠實地進行分類。




注意,核函數技巧實際上並不是 SVM 的一部分。它可以與其他線性分類器共同使用,如邏輯回歸等。支持向量機只負責找到決策邊界。




支持向量機如何用於自然語言分類?



有了這個演算法,我們就可以在多維空間中對向量進行分類了。如何將它引入文本分類任務呢?首先你要做的就是把文本的片斷整合為一個數字向量,這樣才能使用 SVM 進行區分。換句話說,什麼屬性需要被拿來用作 SVM 分類的特徵呢?




最常見的答案是字頻,就像在樸素貝葉斯中所做的一樣。這意味著把文本看作是一個詞袋,對於詞袋中的每個單詞都存在一個特徵,特徵值就是這個詞出現的頻率。




這樣,問題就被簡化為:這個單詞出現了多少次,並把這個數字除以總字數。在句子「All monkeys are primates but not all primates are monkeys」中,單詞 mokey 出現的頻率是 2/10=0.2,而 but 的頻率是 1/10=0.1。




對於計算要求更高的問題,還有更好的方案,我們也可以用 TF-IDF。




現在我們做到了,數據集中的每個單詞都被幾千(或幾萬)維的向量所代表,每個向量都表示這個單詞在文本中出現的頻率。太棒了!現在我們可以把數據輸入 SVM 進行訓練了。我們還可以使用預處理技術來進一步改善它的效果,如詞幹提取、停用詞刪除以及 n-gram。




選擇核函數




現在我們有了特徵向量,唯一要做的事就是選擇模型適用的核函數了。每個任務都是不同的,核函數的選擇有關於數據本身。在我們的例子中,數據呈同心圓排列,所以我們需要選擇一個與之匹配的核函數。




既然需要如此考慮,那麼什麼是自然語言處理需要的核函數?我們需要費線性分類器嗎?亦或是數據線性分離?事實證明,最好堅持使用線性內核,為什麼?




回到我們的例子上,我們有兩種特徵。一些現實世界中 SVM 在其他領域裡的應用或許會用到數十,甚至數百個特徵值。同時自然語言處理分類用到了數千個特徵值,在最壞的情況下,每個詞都只在訓練集中出現過一次。這會讓問題稍有改變:非線性核心或許在其他情況下很好用,但特徵值過多的情況下可能會造成非線性核心數據過擬合。因此,最好堅持使用舊的線性核心,這樣才能在那些例子中獲得很好的結果。




Python實現


題目:


模式識別中著名的數據集。本實驗通過花萼(sepal)和花瓣(petal)的長和寬,建立SVM分類器來判斷樣本屬於山鳶尾(Iris Setosa)、變色鳶尾(Iris Versicolor)還是維吉尼亞鳶尾(Iris Virginica)。請按要求完成實驗。


數據集:


文件iris.txt為該實驗的數據集,包含150個樣本,對應數據集的每行數據。每行數據包含每個樣本的四個特徵(按順序分 鳶尾花數據集(Iris data set)是模別為花萼長度、花萼寬度、花瓣長度、花瓣寬度)和樣本的類別信息(Iris Setosa、Iris Versicolor、Iris Virginica中的一種)。




文件列表如下:(所有數據+代碼下載請點擊閱讀原文)


i

ris.txt 原始數據集


iris_train.txt 訓練數據集


iris_test.txt 測試數據集


SVM.py 未採用pca降維的SVM分類器


SVM_PCA.py 採用pca降維的SVM分類器



SVM.py代碼如下:

 1

#!/usr/bin/python


 2

#-*- coding: utf-8 -*-


 3

from

 numpy 

import

 *

 4

import

 matplotlib.pyplot 

as

 plt

 5

import

 matplotlib.animation 

as

 ai

 6

import

 numpy 

as

 np

 7

import

 time

 8


 9

def

 

loadData

()

:

    

#載入函數


10

    dataMat = []

11

    labelMat1 = []

12

    labelMat2 = []

13

    labelMat3 = []

14

    ylabel = []

15

    fr = open(

"iris_train.txt"

)

16

    

for

 line 

in

 fr.readlines():

17

        lineArr = line.strip().split(

","

)

18

        dataMat.append([float(lineArr[

0

]), float(lineArr[

1

]), float(lineArr[

2

]), float(lineArr[

3

])])

19

        

if

(lineArr[

4

]==

"Iris-setosa"

):

20

          labelMat1.append(float(

1

))

21

        

else

:

22

          labelMat1.append(float(

-1

))

23

        

if

(lineArr[

4

]==

"Iris-versicolor"

):

24

          labelMat2.append(float(

1

))

25

        

else

:

26

          labelMat2.append(float(

-1

))

27

        

if

(lineArr[

4

]==

"Iris-virginica"

):

28

          labelMat3.append(float(

1

))

29

        

else

:

30

          labelMat3.append(float(

-1

))

31

        ylabel.append(lineArr[

4

])

32

    

return

 dataMat,labelMat1,labelMat2,labelMat3,ylabel

33


34

def

 

loadData_test

()

:


35

    dataMat = []

36

    ylabel = []

37

    fr = open(

"iris_test.txt"

)

38

    

for

 line 

in

 fr.readlines():

39

        lineArr = line.strip().split(

","

)

40

        dataMat.append([float(lineArr[

0

]), float(lineArr[

1

]), float(lineArr[

2

]), float(lineArr[

3

])])

41

        ylabel.append(lineArr[

4

])

42

    

return

 dataMat,ylabel

43


44


45

def

 

pca

(dataMat, topNfeat)

:

  

46

    meanVals = mean(dataMat, axis = 

0

)   

#求平均值  


47

    meanRemoved = dataMat - meanVals 

#去平均值  


48

    covMat = cov(meanRemoved,rowvar=

0

#計算協防差矩陣  


49

    eigVals, eigVects = linalg.eig(mat(covMat))  

50

    eigValInd = argsort(eigVals)  

51

    

#從小到大對N個值排序  


52

    eigValInd = eigValInd[: -(topNfeat + 

1

) : 

-1

]  

53

    redEigVects = eigVects[:, eigValInd]

54

    

#將數據轉換到新空間  


55

    lowDDataMat = meanRemoved * redEigVects  

56

    

#reconMat = (lowDDataMat * redEigVects.T) + meanVals  


57

    

return

 lowDDataMat

58


59

def

 

selectJrand

(i,m)

:


60

    j=i             

#排除i


61

    

while

 (j==i):

62

          j = int(random.uniform(

0

,m))

63

    

return

 j

64


65

def

 

clipAlpha

(aj,H,L)

:


66

    

if

 aj > H:

67

       aj = H

68

    

if

 L > aj:

69

       aj = L

70

    

return

 aj

71


72

def

 

smoSimple

(dataMatrix, classLabels, C, toler, maxIter)

:


73

    labelMat = mat(classLabels).T

74

    b = 

-1

; m,n = shape(dataMatrix) 

75

    alphas = mat(zeros((m,

1

)))

76

    iter = 

0


77

    

while

 (iter < maxIter):

78

        alphaPairsChanged = 

0

   

#alpha是否已經進行了優化


79

        

for

 i 

in

 range(m):

80

            

#   w = alpha * y * x;  f(x_i) = w^T * x_i + b


81

            

# 預測的類別


82

            fXi = float(multiply(alphas,labelMat).T*dataMatrix*dataMatrix[i,:].T) + b    

83

            Ei = fXi - float(labelMat[i])   

#得到誤差,如果誤差太大,檢查是否可能被優化


84

            

#必須滿足約束


85

            

if

 ((labelMat[i]*Ei < -toler) 

and

 (alphas[i] < C)) 

or

 ((labelMat[i]*Ei > toler) 

and

 (alphas[i] > 

0

)): 

86

                j = selectJrand(i,m)

87

                fXj = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b

88

                Ej = fXj - float(labelMat[j])

89

                alphaIold = alphas[i].copy(); alphaJold = alphas[j].copy()                

90

                

if

 (labelMat[i] != labelMat[j]):                                          

91

                    L = max(

0

, alphas[j] - alphas[i])

92

                    H = min(C, C + alphas[j] - alphas[i])

93

                

else

:

94

                    L = max(

0

, alphas[j] + alphas[i] - C)

95

                    H = min(C, alphas[j] + alphas[i])

96

                

if

 L==H: 

#print "L==H"; 


97

                   

continue


98

                

# Eta = -(2 * K12 - K11 - K22),且Eta非負,此處eta = -Eta則非正


99

                eta = 

2.0

 * dataMatrix[i,:]*dataMatrix[j,:].T - dataMatrix[i,:]*dataMatrix[i,:].T - dataMatrix[j,:]*dataMatrix[j,:].T

100

                

if

 eta >= 

0

#print "eta>=0"; 


101

                   

continue


102

                alphas[j] -= labelMat[j]*(Ei - Ej)/eta

103

                alphas[j] = clipAlpha(alphas[j],H,L)

104

                  

#如果內層循環通過以上方法選擇的α_2不能使目標函數有足夠的下降,那麼放棄α_1


105

                

if

 (abs(alphas[j] - alphaJold) < 

0.00001

): 

#print "j not moving enough"; 


106

                    

continue


107

                alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])

108

                b1 = b - Ei- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[i,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[i,:]*dataMatrix[j,:].T

109

                b2 = b - Ej- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[j,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[j,:]*dataMatrix[j,:].T

110

                

if

 (

0

 < alphas[i]) 

and

 (C > alphas[i]): b = b1

111

                

elif

 (

0

 < alphas[j]) 

and

 (C > alphas[j]): b = b2

112

                

else

: b = (b1 + b2)/

2.0


113

                alphaPairsChanged += 

1


114

        

if

 (alphaPairsChanged == 

0

): iter += 

1


115

        

else

: iter = 

0


116

    

return

 b,alphas

117


118

def

 

calcWs

(alphas,dataMatrix, labelMat)

:


119

    m,n = shape(dataMatrix) 

120

    w = zeros((n,

1

))

121

    

for

 i 

in

 range(m):

122

        w += multiply(alphas[i]*labelMat[i],dataMatrix[i,:].T)

123

    

return

 w

124


125

def

 

pred

(dataMat, labelMat, w1, b1,w3,b3)

:


126

    dataMat = mat(dataMat)

127

    sum1 =

0

 

128

    m,n = shape(dataMat)

129

    

for

 i 

in

 range(m):

130

        

if

(dataMat[i]*w1 + b1 > 

0.0

 

and

 labelMat[i]==

"Iris-setosa"

):

131

           sum1 +=

1


132

        

elif

(dataMat[i]*w3 + b3 > 

0.0

 

and

 labelMat[i]==

"Iris-virginica"

):

133

           sum1 +=

1


134

        

elif

(dataMat[i]*w3 + b3 < 

0.0

 

and

 dataMat[i]*w1 + b1 < 

0.0

 

and

 labelMat[i]==

"Iris-versicolor"

):

135

           sum1 +=

1


136

    m = float(sum1)/float(m)*

100


137

    

print

 

"正確率為: "

 , m

138


139


140

xdata,ydata1,ydata2,ydata3,ylabe = loadData()

141

xdata_test, ylabe_test = loadData_test()

142

xdata = mat(xdata)

143

xdata_test = mat(xdata_test)

144

b1 , alphas1 = smoSimple(xdata,ydata1,

0.8

,

0.0001

,

40

)

145

#b2 , alphas2 = smoSimple(X,ydata2,0.8,0.0001,40)


146

b3 , alphas3 = smoSimple(xdata,ydata3,

0.8

,

0.0001

,

40

)

147

w1 = calcWs(alphas1,xdata,ydata1)

148

#w2 = calcWs(alphas2,X,ydata2)


149

w3 = calcWs(alphas3,xdata,ydata3)

150

pred(xdata_test, ylabe_test, w1, b1, w3, b3)




喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!

TAG: |