現(xiàn)支持向量機(jī):西瓜書課后題6.2實(shí)戰(zhàn)與核函數(shù)調(diào)參)
剛把西瓜書第六章啃到支持向量機(jī)這一章時(shí)我一度懷疑自己是不是數(shù)學(xué)沒入門。對(duì)偶問題、KKT條件、核函數(shù)這些概念書上推導(dǎo)一行接一行但心里始終缺一個(gè)“哦原來(lái)跑起來(lái)是這樣”的畫面。后來(lái)強(qiáng)迫自己放下課本先把課后題6.2用LIBSVM跑通一遍再回頭看公式很多含糊的地方就突然通了。這篇筆記就是基于我自己實(shí)操后的復(fù)盤用Python調(diào)用LIBSVM實(shí)現(xiàn)SVM從數(shù)據(jù)準(zhǔn)備、訓(xùn)練、參數(shù)設(shè)置到結(jié)果解讀完整走一遍課后題6.2順便把踩過的坑和調(diào)參經(jīng)驗(yàn)一起寫下來(lái)。如果你也正在看西瓜書第六章或者剛接觸SVM、想把第一個(gè)SVM實(shí)驗(yàn)跑起來(lái)這篇應(yīng)該能幫你少走不少?gòu)澛贰?. 課后題6.2到底讓做什么1.1 先別急著寫代碼把題目要求拆開看課后題6.2的原題核心意思是讓你用LIBSVM這個(gè)現(xiàn)成工具在西瓜數(shù)據(jù)集3.0上訓(xùn)練SVM模型然后比較不同核函數(shù)下的表現(xiàn)。這里有個(gè)容易被忽略的點(diǎn)題目沒有讓你從零實(shí)現(xiàn)SMO算法而是希望你用成熟庫(kù)去獲得感性認(rèn)識(shí)。這樣設(shè)計(jì)是有道理的因?yàn)樵诘诹虑懊嬉呀?jīng)詳細(xì)推導(dǎo)了SVM的數(shù)學(xué)原理后面還會(huì)有手動(dòng)實(shí)現(xiàn)的題所以這一題的意義在于“先把工具用熟把結(jié)果看懂”。西瓜數(shù)據(jù)集3.0是書里自帶的一個(gè)小型二分類數(shù)據(jù)集特征主要是“密度”和“含糖率”標(biāo)簽是“好瓜”和“壞瓜”。為什么用這個(gè)數(shù)據(jù)集因?yàn)樗挥袃蓚€(gè)特征方便可視化也方便觀察支持向量在二維平面上的分布。數(shù)據(jù)規(guī)模不大訓(xùn)練極快很適合新手反復(fù)調(diào)整參數(shù)看效果。1.2 為什么偏偏是LIBSVM而不是自己寫個(gè)梯度下降LIBSVM是支持向量機(jī)領(lǐng)域最常用的開源庫(kù)之一底層用C實(shí)現(xiàn)接口簡(jiǎn)單輸出信息詳細(xì)。它支持線性核、多項(xiàng)式核、RBF核、sigmoid核也支持分類、回歸、交叉驗(yàn)證等功能。對(duì)新手來(lái)說最直觀的價(jià)值在于訓(xùn)練完成時(shí)它會(huì)在終端打印一串診斷信息比如迭代次數(shù)、目標(biāo)函數(shù)值、支持向量個(gè)數(shù)等。這些信息恰好能幫助你對(duì)應(yīng)書上那些抽象概念。熟悉Python的同學(xué)可能會(huì)問scikit-learn里的SVC不也能用嗎確實(shí)能而且sklearn的SVC底層也包含LIBSVM的實(shí)現(xiàn)接口更Pythonic。但既然題目點(diǎn)名LIBSVM我建議還是老老實(shí)實(shí)體驗(yàn)一下原始庫(kù)的輸出格式等你理解了那些指標(biāo)再用sklearn會(huì)更有底氣。這篇筆記里我會(huì)兩種方式都提到讓你可以按自己的環(huán)境選擇。1.3 一句話總結(jié)課后題6.2的核心目標(biāo)題目的訓(xùn)練過程可以拆成四步準(zhǔn)備符合LIBSVM格式的數(shù)據(jù)選擇合適的核函數(shù)和參數(shù)訓(xùn)練模型讀取并理解訓(xùn)練日志對(duì)比不同核函數(shù)下的支持向量數(shù)量、準(zhǔn)確率等指標(biāo)。做完之后再想想為什么不同核函數(shù)的結(jié)果會(huì)不一樣為什么有的模型復(fù)雜但準(zhǔn)確率并沒那么高這些問題想通了才算真正吃透這一題。2. 環(huán)境準(zhǔn)備與LIBSVM的兩種打開方式2.1 小白首選直接用scikit-learn的SVC接口如果你只是想先把實(shí)驗(yàn)跑通最省事的方法是用sklearn。安裝很簡(jiǎn)單pip install scikit-learn然后在Python里導(dǎo)入from sklearn import svm clf svm.SVC(kernelrbf, C1.0, gammascale) clf.fit(X_train, y_train)為什么建議這個(gè)作為第一種方式因?yàn)閟klearn的SVC接口把LIBSVM的細(xì)節(jié)封裝得很干凈fit之后直接拿模型去預(yù)測(cè)代碼量最少。但它不會(huì)在終端打印LIBSVM那種詳細(xì)日志所以很多人跑完sklearn之后對(duì)“支持向量個(gè)數(shù)”沒有直觀概念。2.2 想看到原始訓(xùn)練日志就裝原版LIBSVM的Python接口原版LIBSVM在官網(wǎng)下載后里面有一個(gè)python目錄里面有svmutil.py和svm.py兩個(gè)關(guān)鍵文件。你只需要把python目錄放到你的項(xiàng)目里然后import即可import sys sys.path.append(libsvm/python) # 換成你的實(shí)際路徑 from svmutil import *如果你不確定路徑也可以在命令行里進(jìn)入libsvm/python目錄直接運(yùn)行Python。這種方式相比sklearn麻煩一點(diǎn)但優(yōu)點(diǎn)是訓(xùn)練時(shí)能看到如下日志optimization finished, #iter 45 nu 0.215038 obj -1.234567, rho -0.345678 nSV 12, nBSV 3 Total nSV 12這一串信息對(duì)理解SVM原理非常有幫助后文我會(huì)逐行解釋。2.3 準(zhǔn)備數(shù)據(jù)把西瓜數(shù)據(jù)集變成LIBSVM格式LIBSVM的數(shù)據(jù)格式是這樣的label index1:value1 index2:value2 ...每行是一個(gè)樣本開頭是標(biāo)簽后面是特征對(duì)。比如西瓜數(shù)據(jù)集3.0里的一個(gè)樣本可能是1 1:0.697 2:0.460意思就是標(biāo)簽為1好瓜第1個(gè)特征密度為0.697第2個(gè)特征含糖率為0.460。如果標(biāo)簽是壞瓜可以寫成-1。一般來(lái)說我們會(huì)把原始CSV轉(zhuǎn)成這種格式。假設(shè)你手上有一個(gè)watermelon.csv列名是density, sugar, labellabel為1或0那么轉(zhuǎn)換代碼可以這樣寫def csv_to_libsvm(input_file, output_file): with open(input_file, r, encodingutf-8) as fin, \ open(output_file, w, encodingutf-8) as fout: for line in fin: parts line.strip().split(,) label 1 if parts[2] 1 else -1 density float(parts[0]) sugar float(parts[1]) fout.write(f{label} 1:{density} 2:{sugar}\n) csv_to_libsvm(watermelon.csv, watermelon.libsvm)實(shí)際操作中你還需要切分訓(xùn)練集和測(cè)試集。最簡(jiǎn)單的方法是把數(shù)據(jù)按比例隨機(jī)打亂后分成兩部分這個(gè)后面在代碼里會(huì)演示。如果你實(shí)在找不到西瓜數(shù)據(jù)集也可以用LIBSVM自帶的heart_scale文件來(lái)練手它是一個(gè)標(biāo)準(zhǔn)的心跳疾病數(shù)據(jù)集格式已經(jīng)符合要求直接調(diào)用svm_read_problem(heart_scale)即可。3. 手把手訓(xùn)練SVM線性核、多項(xiàng)式核、RBF核3.1 訓(xùn)練一個(gè)模型的核心代碼用原版LIBSVM的Python接口核心代碼非常短from svmutil import * y, x svm_read_problem(watermelon.libsvm) # 假設(shè)前70%做訓(xùn)練集后30%做測(cè)試集 train_count int(len(y) * 0.7) y_train, x_train y[:train_count], x[:train_count] y_test, x_test y[train_count:], x[train_count:] param svm_parameter(-t 2 -c 1.0 -g 0.5) model svm_train(y_train, x_train, param) p_label, p_acc, p_val svm_predict(y_test, x_test, model)這里最關(guān)鍵的是svm_parameter里的參數(shù)。-t 2表示用RBF核-c 1.0是懲罰系數(shù)C-g 0.5是gamma。如果你想用線性核改成-t 0用多項(xiàng)式核改成-t 1同時(shí)還要指定-d次數(shù)和-rcoef0。3.2 svm_parameter里的每個(gè)參數(shù)到底是什么很多小白直接抄參數(shù)卻完全不懂含義這樣一旦結(jié)果不對(duì)就兩眼一抹黑。我把常用參數(shù)整理成一張表參數(shù)命令行寫法含義典型值核函數(shù)類型-t0線性、1多項(xiàng)式、2RBF、3sigmoid2懲罰系數(shù)C-c對(duì)誤分類樣本的容忍度越大越嚴(yán)格1.0gamma-g核函數(shù)的寬度影響模型復(fù)雜度1/特征維度多項(xiàng)式核次數(shù)-d僅在多項(xiàng)式核時(shí)有效3coef0-r多項(xiàng)式/sigmoid核的常數(shù)項(xiàng)0是否輸出概率-b1表示輸出概率0不輸出0C是SVM里最核心的懲罰項(xiàng)。你可以把它理解成“班主任查紀(jì)律的嚴(yán)格程度”C越大模型就越不愿意放過任何一個(gè)訓(xùn)練集里的錯(cuò)誤點(diǎn)結(jié)果可能是訓(xùn)練集準(zhǔn)確率很高但測(cè)試集反而變差也就是過擬合。C太小模型又會(huì)太“佛系”連明顯的分類錯(cuò)誤都容忍導(dǎo)致欠擬合。所以C不是越大越好要和數(shù)據(jù)情況匹配。gamma只對(duì)RBF核有決定性影響。它定義了單個(gè)樣本的影響半徑gamma越大決策邊界越曲折模型越復(fù)雜gamma越小邊界越平滑。很多人上來(lái)就調(diào)C卻忽略gamma結(jié)果折騰半天還不如先固定C從不同量級(jí)的gamma里找找感覺。3.3 三種核函數(shù)放到循環(huán)里對(duì)比既然課后題6.2要求比較不同核函數(shù)最直接的辦法是寫一個(gè)循環(huán)把線性核、多項(xiàng)式核、RBF核都訓(xùn)練一遍然后打印各自的準(zhǔn)確率和支持向量數(shù)量kernels [ (Linear, -t 0 -c 1.0), (Poly, -t 1 -d 3 -r 0 -c 1.0), (RBF, -t 2 -c 1.0 -g 0.5), ] for name, opt in kernels: param svm_parameter(opt) model svm_train(y_train, x_train, param) p_label, p_acc, p_val svm_predict(y_test, x_test, model) nsv model.get_nr_sv() print(f{name}: accuracy{p_acc[0]}%, nSV{nsv})這樣跑下來(lái)你會(huì)發(fā)現(xiàn)線性核在這個(gè)二維小數(shù)據(jù)集上可能就已經(jīng)不錯(cuò)了RBF核則有可能取得更高的測(cè)試準(zhǔn)確率但支持向量數(shù)量也會(huì)變多。這正好呼應(yīng)了書上“核函數(shù)的選擇會(huì)影響模型復(fù)雜度”這句話。3.4 為什么訓(xùn)練前一定要做標(biāo)準(zhǔn)化SVM相關(guān)算法對(duì)特征的尺度非常敏感。因?yàn)楹撕瘮?shù)內(nèi)部要算樣本之間的距離或內(nèi)積如果一個(gè)特征值范圍是0到1另一個(gè)是100到1000那么大尺度的特征就會(huì)主導(dǎo)距離計(jì)算另一個(gè)特征幾乎白費(fèi)。西瓜數(shù)據(jù)集的密度和含糖率都在0到1附近量級(jí)差異不大所以很多教程里不標(biāo)準(zhǔn)化也能跑出不錯(cuò)結(jié)果。但真實(shí)項(xiàng)目中特征量級(jí)差異往往非常大。標(biāo)準(zhǔn)化最常用的是Z-score歸一化讓每個(gè)特征均值為0、標(biāo)準(zhǔn)差為1。在sklearn里是這樣寫的from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)注意fit_transform要用在訓(xùn)練集上transform用在測(cè)試集上千萬(wàn)不要對(duì)整個(gè)數(shù)據(jù)集直接fit_transform否則會(huì)把測(cè)試集的信息泄露到訓(xùn)練過程中導(dǎo)致結(jié)果虛高。這個(gè)錯(cuò)誤我在第一次做實(shí)驗(yàn)時(shí)就犯過后面你會(huì)看到后果。4. 結(jié)果怎么看支持向量、準(zhǔn)確率、決策邊界4.1 訓(xùn)練日志里的每一行都是什么意思用原版LIBSVM運(yùn)行后終端會(huì)輸出一段很“唬人”的日志。我拿一次實(shí)際訓(xùn)練的輸出舉例optimization finished, #iter 45 nu 0.215038 obj -1.234567, rho -0.345678 nSV 12, nBSV 3 Total nSV 12逐行拆解optimization finished, #iter 45SMO算法迭代了45次后收斂。迭代次數(shù)多不一定是壞事但有可能是C或者gamma設(shè)置得太極端。nu和C相關(guān)的一個(gè)參數(shù)用于控制支持向量的比例。數(shù)值越小說明模型越“簡(jiǎn)單”。obj目標(biāo)函數(shù)的最優(yōu)值。對(duì)SVM來(lái)說這是在約束條件下求解出來(lái)的對(duì)偶目標(biāo)函數(shù)值可以粗略理解為模型訓(xùn)練結(jié)束時(shí)的損失。rho決策邊界的偏置項(xiàng)也就是超平面方程里的b。nSV 12支持向量的個(gè)數(shù)。這是理解SVM的關(guān)鍵指標(biāo)。訓(xùn)練完成后模型只保留這些支持向量用于預(yù)測(cè)其他樣本都可以丟棄。nBSV 3邊界支持向量的個(gè)數(shù)。邊界支持向量通常位于間隔內(nèi)部或者分類錯(cuò)誤的一側(cè)它們的數(shù)量偏多往往意味著數(shù)據(jù)噪聲較大或者C設(shè)置過大。對(duì)新手來(lái)說最需要盯住的是nSV。如果一個(gè)模型訓(xùn)練準(zhǔn)確率很高但nSV占了訓(xùn)練集的一大半那這個(gè)模型基本處于過擬合狀態(tài)泛化能力堪憂。相反如果nSV非常少說明模型只靠很少幾個(gè)關(guān)鍵點(diǎn)就能劃分?jǐn)?shù)據(jù)通常泛化比較好。4.2 用準(zhǔn)確率判斷但別只看準(zhǔn)確率svm_predict運(yùn)行結(jié)束后會(huì)返回三個(gè)結(jié)果預(yù)測(cè)標(biāo)簽p_label、準(zhǔn)確率p_acc、決策值或概率p_val。其中p_acc本身是一個(gè)數(shù)組格式如下Accuracy 92.8571% (13/14) (classification) Mean squared error 0.0714 (regression) Squared correlation coefficient 0.9231 (regression)分類任務(wù)里我們最關(guān)心Accuracy 92.8571%但只看這個(gè)數(shù)字是有風(fēng)險(xiǎn)的。比如西瓜數(shù)據(jù)集里好瓜壞瓜比例如果接近1:1準(zhǔn)確率還有參考價(jià)值一旦出現(xiàn)七八成都是好瓜模型就算把所有樣本都預(yù)測(cè)成好瓜準(zhǔn)確率也會(huì)高達(dá)七八成這顯然不是我們想要的結(jié)果。所以我會(huì)習(xí)慣性打印混淆矩陣。用sklearn可以一行搞定from sklearn.metrics import confusion_matrix cm confusion_matrix(y_test, p_label) print(cm)混淆矩陣的四格分別代表真正例、假正例、真負(fù)例、假負(fù)例??赐曛竽悴拍芘袛嗄P褪前押霉先J(rèn)出來(lái)了但誤傷了很多壞瓜還是反過來(lái)。這個(gè)信息在調(diào)參時(shí)特別有用。4.3 把決策邊界畫出來(lái)一切變得直觀西瓜數(shù)據(jù)集只有兩個(gè)特征這給了我們一個(gè)絕佳的可視化機(jī)會(huì)。在二維平面上畫網(wǎng)格點(diǎn)讓訓(xùn)練好的模型逐點(diǎn)預(yù)測(cè)再按預(yù)測(cè)類別給網(wǎng)格上色就能看到?jīng)Q策邊界長(zhǎng)什么樣子。代碼如下import numpy as np import matplotlib.pyplot as plt def plot_decision_boundary(model, X_train, y_train): h 0.01 x_min, x_max X_train[:, 0].min() - 0.1, X_train[:, 0].max() 0.1 y_min, y_max X_train[:, 1].min() - 0.1, X_train[:, 1].max() 0.1 xx, yy np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) # 需要把網(wǎng)格坐標(biāo)轉(zhuǎn)成LIBSVM格式 grid_samples [] for px, py in zip(xx.ravel(), yy.ravel()): grid_samples.append({1: px, 2: py}) p_label, _, _ svm_predict([0]*len(grid_samples), grid_samples, model) p_label np.array(p_label).reshape(xx.shape) plt.contourf(xx, yy, p_label, alpha0.6, cmapplt.cm.coolwarm) plt.scatter(X_train[:, 0], X_train[:, 1], cy_train, edgecolorsk, cmapplt.cm.coolwarm) plt.xlabel(density) plt.ylabel(sugar) plt.show()注意這里svm_predict需要為每個(gè)網(wǎng)格點(diǎn)傳一個(gè)標(biāo)簽但因?yàn)橹皇穷A(yù)測(cè)標(biāo)簽值填什么都不會(huì)影響結(jié)果。畫出來(lái)之后你能清晰看到線性核的邊界是直線RBF核的邊界則是彎曲的支持向量往往就貼在這些邊界附近??吹竭@個(gè)圖再回頭看書上的KKT條件你會(huì)對(duì)“支持向量是距離超平面最近的點(diǎn)”這句話有完全不同的感受。5. 參數(shù)調(diào)優(yōu)的實(shí)戰(zhàn)心得5.1 核函數(shù)選型不要迷信RBF很多教程一上來(lái)就說RBF核最萬(wàn)能直接用它。但對(duì)小數(shù)據(jù)集、特征維度不高的時(shí)候線性核往往已經(jīng)夠用而且泛化更穩(wěn)。核函數(shù)選型可以簡(jiǎn)單按下面的經(jīng)驗(yàn)判斷特征維度非常高比如文本分類的幾萬(wàn)維優(yōu)先線性核因?yàn)楦呔S空間里線性超平面通常已經(jīng)足夠。特征維度低樣本量適中且數(shù)據(jù)分布有明顯非線性時(shí)優(yōu)先RBF。數(shù)據(jù)中有一定的特征交叉關(guān)系多項(xiàng)式核可能更直觀但次數(shù)太高容易過擬合。需要模型可解釋、能直接提取權(quán)重時(shí)線性核唯一可選。課后題6.2里三種核都試一遍是必要的但試完以后不要只選測(cè)試集準(zhǔn)確率最高的那個(gè)還要結(jié)合模型復(fù)雜度一起考慮。5.2 C和gamma怎么配合在RBF核下C和gamma是兩個(gè)需要聯(lián)合調(diào)優(yōu)的參數(shù)。最古典的方法是網(wǎng)格搜索C取2^-5到2^15gamma取2^-15到2^3每一個(gè)組合都做交叉驗(yàn)證。LIBSVM自帶交叉驗(yàn)證功能參數(shù)里加-v 5表示做5折交叉驗(yàn)證param svm_parameter(-t 2 -c 2 -g 0.5 -v 5) svm_train(y_train, x_train, param)注意加了-v之后svm_train返回的是交叉驗(yàn)證準(zhǔn)確率而不是一個(gè)模型對(duì)象。這是新手最容易看代碼跑完卻拿不到模型來(lái)預(yù)測(cè)的原因。之前我自己做小實(shí)驗(yàn)時(shí)粗略網(wǎng)格搜索的腳本大概是這樣的best_acc 0 best_param None for c in [0.1, 1, 10, 100]: for g in [0.01, 0.1, 1, 10]: param svm_parameter(f-t 2 -c {c} -g {g} -v 5) acc svm_train(y_train, x_train, param) if acc best_acc: best_acc acc best_param f-t 2 -c {c} -g {g}最后用best_param重新訓(xùn)練模型并測(cè)試。這樣雖然笨但在小數(shù)據(jù)上很快也能讓你直觀看到C和gamma對(duì)準(zhǔn)確率的影響。5.3 標(biāo)準(zhǔn)化和交叉驗(yàn)證一起做才靠譜我自己的經(jīng)驗(yàn)是先標(biāo)準(zhǔn)化再調(diào)參否則網(wǎng)格搜索選出來(lái)的參數(shù)會(huì)被特征的量級(jí)干擾。標(biāo)準(zhǔn)化以后RBF核的gamma才有一個(gè)穩(wěn)定的參考范圍。如果數(shù)據(jù)不標(biāo)準(zhǔn)化同樣的C和gamma在不同特征量級(jí)下表現(xiàn)會(huì)千差萬(wàn)別你甚至?xí)玫揭粋€(gè)看起來(lái)“準(zhǔn)確率接近100%”的結(jié)果但那只是被大尺度特征帶偏的假象。交叉驗(yàn)證也千萬(wàn)別省。尤其在數(shù)據(jù)量少的時(shí)候只用一次隨機(jī)劃分的測(cè)試集來(lái)判斷參數(shù)好壞偶然性很大。用5折或10折交叉驗(yàn)證選出參數(shù)后再用獨(dú)立的測(cè)試集做最終評(píng)估這個(gè)流程會(huì)讓你的結(jié)果可信得多。6. 踩坑記錄與結(jié)果解讀要點(diǎn)6.1 數(shù)據(jù)標(biāo)準(zhǔn)化前后結(jié)果差很多我第一次直接在原始西瓜數(shù)據(jù)上跑RBF核準(zhǔn)確率大概在85%左右。后來(lái)順手做了一次標(biāo)準(zhǔn)化同樣的參數(shù)組合直接跳到95%以上。原因就是密度和含糖率雖然量級(jí)都小于1但分布范圍不完全一致RBF核本身對(duì)特征尺度很敏感。這個(gè)坑在課后題里不一定會(huì)暴露因?yàn)閿?shù)據(jù)集太干凈但在任何真實(shí)項(xiàng)目里標(biāo)準(zhǔn)化應(yīng)該成為SVM流程的默認(rèn)動(dòng)作。記住對(duì)SVM來(lái)說標(biāo)準(zhǔn)化不是可選項(xiàng)而是必選項(xiàng)。6.2 類別不平衡會(huì)讓準(zhǔn)確率失真西瓜數(shù)據(jù)集做成了類別基本均衡的樣子但真實(shí)數(shù)據(jù)經(jīng)常不是。一個(gè)很典型的現(xiàn)象就是訓(xùn)練集里好瓜90個(gè)、壞瓜10個(gè)模型為了降低整體損失會(huì)把所有樣本都預(yù)測(cè)成好瓜最終準(zhǔn)確率90%聽起來(lái)很漂亮但壞瓜一個(gè)都沒找出來(lái)。這時(shí)候準(zhǔn)確率就是騙人的指標(biāo)。解決辦法有三個(gè)一是對(duì)少數(shù)類樣本提高懲罰權(quán)重在原版LIBSVM里可以用-w1和-w-1分別設(shè)置正類和負(fù)類的C權(quán)重二是對(duì)少數(shù)類進(jìn)行過采樣三是改用F1分?jǐn)?shù)或召回率來(lái)評(píng)估不要只看準(zhǔn)確率。實(shí)踐中最立竿見影的是第一種方法。6.3 LIBSVM輸出概率的坑如果你想用SVM輸出每個(gè)樣本屬于正類的概率而不是只給分類標(biāo)簽需要在訓(xùn)練參數(shù)里加上-b 1param svm_parameter(-t 2 -c 1.0 -g 0.5 -b 1) model svm_train(y_train, x_train, param) p_label, p_acc, p_val svm_predict(y_test, x_test, model)加了-b 1之后p_val才是概率值。如果不加p_val返回的是決策值而且很多情況下是空的。這個(gè)坑很隱蔽因?yàn)槟悴豢次臋n的話很難意識(shí)到p_val為什么空了。還有一點(diǎn)要注意加了-b 1之后訓(xùn)練會(huì)變慢因?yàn)樾枰~外做Platt縮放來(lái)擬合概率。在樣本量大的時(shí)候先評(píng)估是否需要概率輸出再?zèng)Q定加不加-b。6.4 一個(gè)容易被忽略的點(diǎn)隨機(jī)劃分會(huì)帶來(lái)方差如果你每次跑代碼的結(jié)果都不一樣先檢查一下訓(xùn)練集和測(cè)試集劃分的時(shí)候有沒有設(shè)置隨機(jī)種子。在劃分前加上random_state或seed可以保證結(jié)果可復(fù)現(xiàn)。當(dāng)然即使設(shè)置隨機(jī)種子網(wǎng)格搜索得到的“最優(yōu)參數(shù)”也只代表在這一份數(shù)據(jù)劃分下的最優(yōu)換一份數(shù)據(jù)劃分結(jié)果可能不同。所以我在做對(duì)比實(shí)驗(yàn)時(shí)會(huì)用cross_val_score多做幾次交叉驗(yàn)證并看平均分?jǐn)?shù)而不是只看一次測(cè)試集結(jié)果。這組實(shí)驗(yàn)做完我最大的感受是SVM的數(shù)學(xué)推導(dǎo)雖然難啃但它落地后的運(yùn)行邏輯其實(shí)非常簡(jiǎn)潔。模型訓(xùn)練完只保留支持向量預(yù)測(cè)過程就是計(jì)算新樣本和支持向量之間的關(guān)系核函數(shù)然后加權(quán)投票。有了LIBSVM輸出日志里那一串?dāng)?shù)字再回去翻書上的KKT條件和拉格朗日對(duì)偶很多符號(hào)就不是“空中樓閣”了。如果你剛做完6.2建議再往前走一步嘗試用Python手寫一個(gè)簡(jiǎn)單的SMO實(shí)現(xiàn)不需要高性能只要能在同樣的西瓜數(shù)據(jù)上跑出類似的支持向量這個(gè)過程會(huì)對(duì)SVM的理解有一個(gè)質(zhì)的提升。我已經(jīng)開始動(dòng)手了等做完再寫一篇筆記分享出來(lái)。