干貨 | 詳解支持向量機(jī)(附學(xué)習(xí)資源)
支持向量機(jī)(SVM)已經(jīng)成為一種非常流行的算法。本文將嘗試解釋支持向量機(jī)的原理,并列舉幾個(gè)使用 Python Scikits 庫(kù)的例子。本文的所有代碼已經(jīng)上傳 Github。有關(guān)使用 Scikits 和 Sklearn 的細(xì)節(jié),我將在另一篇文章中詳細(xì)介紹。
什么是 支持向量機(jī)(SVM)?
SVM 是一種有監(jiān)督的機(jī)器學(xué)習(xí)算法,可用于分類(lèi)或回歸問(wèn)題。它使用一種稱(chēng)為核函數(shù)(kernel)的技術(shù)來(lái)變換數(shù)據(jù),然后基于這種變換,算法找到預(yù)測(cè)可能的兩種分類(lèi)之間的最佳邊界(optimal boundary)。簡(jiǎn)單地說(shuō),它做了一些非常復(fù)雜的數(shù)據(jù)變換,然后根據(jù)定義的標(biāo)簽找出區(qū)分?jǐn)?shù)據(jù)的方法。
為什么這種算法很強(qiáng)大?
在上面我們說(shuō) SVM 能夠做分類(lèi)和回歸。在這篇文章中,我將重點(diǎn)講述如何使用 SVM 進(jìn)行分類(lèi)。特別的是,本文的例子使用了非線性 SVM 或非線性核函數(shù)的 SVM。非線性 SVM 意味著算法計(jì)算的邊界不再是直線。它的優(yōu)點(diǎn)是可以捕獲數(shù)據(jù)之間更復(fù)雜的關(guān)系,而無(wú)需人為地進(jìn)行困難的數(shù)據(jù)轉(zhuǎn)換;缺點(diǎn)是訓(xùn)練時(shí)間長(zhǎng)得多,因?yàn)樗挠?jì)算量更大。
牛和狼的分類(lèi)問(wèn)題
什么是核函數(shù)技術(shù)?
核函數(shù)技術(shù)可以變換數(shù)據(jù)。它具備一些好用的分類(lèi)器的特點(diǎn),然后輸出一些你無(wú)需再進(jìn)行識(shí)別的數(shù)據(jù)。它的工作方式有點(diǎn)像解開(kāi)一條 DNA 鏈。從傳入數(shù)據(jù)向量開(kāi)始,通過(guò)核函數(shù),它解開(kāi)并組合數(shù)據(jù),直到形成更大且無(wú)法通過(guò)電子表格查看的數(shù)據(jù)集。該算法的神奇之處在于,在擴(kuò)展數(shù)據(jù)集的過(guò)程中,能發(fā)現(xiàn)類(lèi)與類(lèi)之間更明顯的邊界,使得 SVM 算法能夠計(jì)算更為優(yōu)化的超平面。
現(xiàn)在假裝你是一個(gè)農(nóng)夫,那么你就有一個(gè)問(wèn)題——需要建立一個(gè)籬笆,以保護(hù)你的牛不被狼攻擊。但是在哪里筑籬笆合適呢?如果你真的是一個(gè)用數(shù)據(jù)說(shuō)話的農(nóng)夫,一種方法是基于牛和狼在你的牧場(chǎng)的位置,建立一個(gè)分類(lèi)器。通過(guò)對(duì)下圖中幾種不同類(lèi)型的分類(lèi)器進(jìn)行比較,我們看到 SVM 能很好地區(qū)分牛群和狼群。我認(rèn)為這些圖很好地說(shuō)明了使用非線性分類(lèi)器的好處,可以看到邏輯回歸和決策樹(shù)模型的分類(lèi)邊界都是直線。
重現(xiàn)分析過(guò)程
想自己繪出這些圖嗎?你可以在你的終端或你選擇的 IDE 中運(yùn)行代碼,在這里我建議使用 Rodeo(Python 數(shù)據(jù)科學(xué)專(zhuān)用 IDE 項(xiàng)目)。它有彈出制圖的功能,可以很方便地進(jìn)行這種類(lèi)型的分析。它也附帶了針對(duì) Windows 操作系統(tǒng)的 Python 內(nèi)核。此外,感謝 TakenPilot(一位編程者 https://github.com/TakenPilot)的辛勤工作,使得 Rodeo 現(xiàn)在運(yùn)行閃電般快速。
下載 Rodeo 之后,從我的 github 頁(yè)面中下載 cows_and_wolves.txt 原始數(shù)據(jù)文件。并確保將你的工作目錄設(shè)置為保存文件的位置。
Rodeo 下載地址:https://www.yhat.com/products/rodeo
好了,現(xiàn)在只需將下面的代碼復(fù)制并粘貼到 Rodeo 中,然后運(yùn)行每行代碼或整個(gè)腳本。不要忘了,你可以彈出繪圖選項(xiàng)卡、移動(dòng)或調(diào)整它們的大小。
- # Data driven farmer goes to the Rodeoimport numpy as npimport pylab as plfrom sklearn import svmfrom sklearn import linear_modelfrom sklearn import treeimport pandas as pddef plot_results_with_hyperplane(clf, clf_name, df, plt_nmbr):
- x_min, x_max = df.x.min() - .5, df.x.max() + .5
- y_min, y_max = df.y.min() - .5, df.y.max() + .5
- # step between points. i.e. [0, 0.02, 0.04, ...]
- step = .02
- # to plot the boundary, we're going to create a matrix of every possible point
- # then label each point as a wolf or cow using our classifier
- xx, yy = np.meshgrid(np.arange(x_min, x_max, step), np.arange(y_min, y_max, step))
- Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
- # this gets our predictions back into a matrix
- Z = Z.reshape(xx.shape)
- # create a subplot (we're going to have more than 1 plot on a given image)
- pl.subplot(2, 2, plt_nmbr)
- # plot the boundaries
- pl.pcolormesh(xx, yy, Z, cmap=pl.cm.Paired)
- # plot the wolves and cows
- for animal in df.animal.unique():
- pl.scatter(df[df.animal==animal].x,
- df[df.animal==animal].y,
- marker=animal,
- label="cows" if animal=="x" else "wolves",
- color='black')
- pl.title(clf_name)
- pl.legend(loc="best")data = open("cows_and_wolves.txt").read()data = [row.split('\t') for row in data.strip().split('\n')]animals = []for y, row in enumerate(data):
- for x, item in enumerate(row):
- # x's are cows, o's are wolves
- if item in ['o', 'x']:
- animals.append([x, y, item])df = pd.DataFrame(animals, columns=["x", "y", "animal"])df['animal_type'] = df.animal.apply(lambda x: 0 if x=="x" else 1)# train using the x and y position coordiantestrain_cols = ["x", "y"]clfs = {
- "SVM": svm.SVC(),
- "Logistic" : linear_model.LogisticRegression(),
- "Decision Tree": tree.DecisionTreeClassifier(),}plt_nmbr = 1for clf_name, clf in clfs.iteritems():
- clf.fit(df[train_cols], df.animal_type)
- plot_results_with_hyperplane(clf, clf_name, df, plt_nmbr)
- plt_nmbr += 1pl.show()
SVM 解決難題
在因變量和自變量之間的關(guān)系是非線性的情況下,帶有核函數(shù)的 SVM 算法會(huì)得到更精確的結(jié)果。在這里,轉(zhuǎn)換變量(log(x),(x ^ 2))就變得不那么重要了,因?yàn)樗惴▋?nèi)在地包含了轉(zhuǎn)換變量的過(guò)程。如果你思考這個(gè)過(guò)程仍然有些不清楚,那么看看下面的例子能否讓你更清楚地理解。
假設(shè)我們有一個(gè)由綠色和紅色點(diǎn)組成的數(shù)據(jù)集。當(dāng)根據(jù)它們的坐標(biāo)繪制散點(diǎn)圖時(shí),點(diǎn)形成具有綠色輪廓的紅色圓形(看起來(lái)很像孟加拉國(guó)的旗子)。
如果我們丟失了 1/3 的數(shù)據(jù),那么會(huì)發(fā)生什么?如果無(wú)法恢復(fù)這些數(shù)據(jù),我們需要找到一種方法來(lái)估計(jì)丟失的 1/3 數(shù)據(jù)。
那么,我們?nèi)绾闻迦笔У?1/3 數(shù)據(jù)看起來(lái)像什么?一種方法是使用我們所擁有的 80%數(shù)據(jù)作為訓(xùn)練集來(lái)構(gòu)建模型。但是使用什么模型呢?讓我們?cè)囋囅旅娴哪P停?/p>
- 邏輯回歸模型
- 決策樹(shù)
- 支持向量機(jī)
對(duì)每個(gè)模型進(jìn)行訓(xùn)練,然后用這些模型來(lái)預(yù)測(cè)丟失的 1/3 數(shù)據(jù)。下面是每個(gè)模型的預(yù)測(cè)結(jié)果:
模型算法比較的實(shí)現(xiàn)
下面是比較 logistic 模型、決策樹(shù)和 SVM 的代碼。
- import numpy as npimport pylab as plimport pandas as pdfrom sklearn import svmfrom sklearn import linear_modelfrom sklearn import treefrom sklearn.metrics import confusion_matrix
- x_min, x_max = 0, 15y_min, y_max = 0, 10step = .1# to plot the boundary, we're going to create a matrix of every possible point# then label each point as a wolf or cow using our classifierxx, yy = np.meshgrid(np.arange(x_min, x_max, step), np.arange(y_min, y_max, step))df = pd.DataFrame(data={'x': xx.ravel(), 'y': yy.ravel()})df['color_gauge'] = (df.x-7.5)**2 + (df.y-5)**2df['color'] = df.color_gauge.apply(lambda x: "red" if x <= 15 else "green")df['color_as_int'] = df.color.apply(lambda x: 0 if x=="red" else 1)print "Points on flag:"print df.groupby('color').size()printfigure = 1# plot a figure for the entire datasetfor color in df.color.unique():
- idx = df.color==color
- pl.subplot(2, 2, figure)
- pl.scatter(df[idx].x, df[idx].y, color=color)
- pl.title('Actual')train_idx = df.x < 10train = df[train_idx]test = df[-train_idx]print "Training Set Size: %d" % len(train)print "Test Set Size: %d" % len(test)# train using the x and y position coordiantescols = ["x", "y"]clfs = {
- "SVM": svm.SVC(degree=0.5),
- "Logistic" : linear_model.LogisticRegression(),
- "Decision Tree": tree.DecisionTreeClassifier()}# racehorse different classifiers and plot the resultsfor clf_name, clf in clfs.iteritems():
- figure += 1
- # train the classifier
- clf.fit(train[cols], train.color_as_int)
- # get the predicted values from the test set
- test['predicted_color_as_int'] = clf.predict(test[cols])
- test['pred_color'] = test.predicted_color_as_int.apply(lambda x: "red" if x==0 else "green")
- # create a new subplot on the plot
- pl.subplot(2, 2, figure)
- # plot each predicted color
- for color in test.pred_color.unique():
- # plot only rows where pred_color is equal to color
- idx = test.pred_color==color
- pl.scatter(test[idx].x, test[idx].y, color=color)
- # plot the training set as well
- for color in train.color.unique():
- idx = train.color==color
- pl.scatter(train[idx].x, train[idx].y, color=color)
- # add a dotted line to show the boundary between the training and test set
- # (everything to the right of the line is in the test set)
- #this plots a vertical line
- train_line_y = np.linspace(y_min, y_max) #evenly spaced array from 0 to 10
- train_line_x = np.repeat(10, len(train_line_y)) #repeat 10 (threshold for traininset) n times
- # add a black, dotted line to the subplot
- pl.plot(train_line_x, train_line_y, 'k--', color="black")
- pl.title(clf_name)
- print "Confusion Matrix for %s:" % clf_name print confusion_matrix(test.color, test.pred_color)pl.show()
在 Rodeo 中復(fù)制和運(yùn)行上面的代碼。
結(jié)果
從這些圖中可以清楚地看出 SVM 更好。為什么呢?如果觀察決策樹(shù)和 GLM(廣義線性模型,這里指 logistic 回歸)模型的預(yù)測(cè)形狀,你會(huì)看到預(yù)測(cè)給出的直邊界。因?yàn)樗鼈兊妮斎肽P蜎](méi)有任何變換來(lái)解釋 x、y 以及顏色之間的非線性關(guān)系。給定一組特定的變換,我們絕對(duì)可以使 GLM 和 DT(決策樹(shù))得出更好的結(jié)果,但尋找合適的變換將浪費(fèi)大量時(shí)間。在沒(méi)有復(fù)雜的變換或特征縮放的情況下,SVM 算法 5000 數(shù)據(jù)點(diǎn)只錯(cuò)誤地分類(lèi)了 117 點(diǎn)(98%的精度,而 DT 精確度為 51%,GLM 精確度為 12%)。由于所有錯(cuò)誤分類(lèi)的點(diǎn)是紅色,所以預(yù)測(cè)的結(jié)果形狀有輕微的凸起。
不適用的場(chǎng)合
那為什么不是所有問(wèn)題都使用 SVM?很遺憾,SVM 的魅力也是它最大的缺點(diǎn)。復(fù)雜數(shù)據(jù)變換以及得到的決策邊界平面是很難解釋的。這就是為什么它通常被稱(chēng)為「黑箱」的原因。GLM 和決策樹(shù)恰恰相反,它們的算法實(shí)現(xiàn)過(guò)程及怎樣減少成本函數(shù)得到優(yōu)良結(jié)果都很容易理解。
更多學(xué)習(xí)資源
想了解更多關(guān)于 SVM 的知識(shí)?以下是我收藏的一些好資源:
初級(jí)——SVM 教程:基礎(chǔ)教程,作者是 MIT 的 Zoya Gavrilov
鏈接地址:http://web.mit.edu/zoya/www/SVM.pdf
初級(jí)——SVM 算法原理:Youtube 視頻,作者是 Thales SehnKörting
鏈接地址:https://youtu.be/1NxnPkZM9bc
中級(jí)——支持向量機(jī)在生物醫(yī)學(xué)中的簡(jiǎn)要介紹:紐約大學(xué) & 范德堡大學(xué)提供的課件
鏈接地址:https://www.med.nyu.edu/chibi/sites/default/files/chibi/Final.pdf
高級(jí)——模式識(shí)別下的支持向量機(jī)教程:作者是貝爾實(shí)驗(yàn)室(Bell Labs)的 Christopher Burges
鏈接地址:http://research.microsoft.com/en-us/um/people/cburges/papers/SVMTutorial.pdf
【本文是51CTO專(zhuān)欄機(jī)構(gòu)機(jī)器之心的原創(chuàng)譯文,微信公眾號(hào)“機(jī)器之心( id: almosthuman2014)”】