我正在做一個關於SVM的小項目,在我執行驗證SVM訓練后的模型的時候,得到的report分數總是很高,無論是召回率(查全率)、精准度、還是f1-score都很高:
圖1 分類器分數report
但是,對於訓練的效果就非常差,差到連包含訓練集的測試集都無法正確分類,如下圖所示,左邊是原圖像,右邊是分類圖像,(我標注的標簽樣本是黃色區域與褐色區域),其中SVC的默認參數為rbf、C=1.0、gamma=“auto_deprecated”,LinearSVC的默認參數為:C=1.0、class_weight=none、dual=true、loss=“squard_hinge”:
a.原圖
b.SVC(default parameter)
c.LinearSVC(default parameter)
圖2. 默認分類效果
由上文可以發現,分類器分類的效果很不好,為了進一步驗證這個問題的原因,接下來我分別對LinearSVC和SVC進行參數調整:
1、LinearSVC參數調整
C:使用損失函數是用來對樣本的分類偏差進行描述,例如:
由上文可以發現,分類器分類的效果很不好,為了進一步驗證這個問題的原因,接下來我分別對LinearSVC和SVC進行參數調整:
1、LinearSVC參數調整
C:使用損失函數是用來對樣本的分類偏差進行描述,例如:
引入松弛變量后,優化問題可以寫為:
約束條件為:
對於不同的C值對於本實例中的分類影響為,如圖3所示:
圖3. LinearSVC在不同懲罰系數C下的表現
由此可以看出SVC在懲罰系數為0.3、4.0、300時能夠較准確根據顏色對圖像進行分類。但是作者發現,懲罰系數相同,重新訓練時,會有不同的效果展示,才疏學淺,尚未能解釋,如果有知道為什么的大神,敬請指點迷津。
2、SVC參數調整
SVC模型中有兩個非常重要的參數,即C與gamma,其中C是懲罰系數,這里的懲罰系數同LinearSVC中的懲罰系數意義相同,表示對誤差的寬容度,C越高,說明越是不能容忍誤差的出現,C越小,表示越容易欠擬合,泛化能力變差。而另一個非常重要的參數gamma,是在選擇RBF函數作為核函數后,才出現的,這個也是LinearSVC中所不包含的。高斯函數RBF中的gamma值有一個自帶的默認值:gamma='auto_deprecated';表示的是數據映射到新的特征空間后的分布,gamma值越大,支持向量就越少,gamma值越小,支持向量就越多。支持向量的個數影響訓練與預測的速度。
RBF公式里面的sigma和gamma的關系為:
gamma的物理意義是關於RBF的幅寬,它可以影響每個支持向量對應的高斯的作用范圍,進而影響分類器的泛化能力。在本例中我更改了多組c的值、gamma的值效果均是差之千里:
C=2、gamma=’auto’
C=default、gamma=1
C=default、gamma=10
C=default、gamma=100
圖4 SVC參數修改后分類效果
如此效果真的是讓人頭疼,不過有一點可以確定的是在樣本海量的情況下,LinearSVC表現的要更好一些,算法執行時間很快,模型也較小,同樣的樣本下SVC用到的時間大概是LinearSVC的60倍左右。但是我們不能就此而已,我們要為問題尋找思路,這便轉換成了一個尋優問題。
對於同時受到兩個參數影響的調參問題,我們可以使用一些尋優算法,常用的尋優算法可分為兩類:一類是在參數定義域空間內進行網格式搜索,例如專門針對SVC參數的GridSearch算法、交叉驗證算法,這類算法雖然穩定性高、參數估計准確,但是算法時間復雜度較高,計算量較大;另一類是采用啟發式優化,例如gaSVMForClass遺傳算法參數尋優、psoSVMForClass粒子群優化算法參數尋優等,這類算法能夠更快的尋找到最優參數組合。
本文先從GridSearch入手,畢竟它是sklearn模塊中的子模塊,導入方法也較簡單:直接from sklearn.model_selection import GridSearchCV就ok。
GridSearch網格搜索是一種調參手段,它的基本原理是暴力求解,即嘗試每一種可能性,將表現最好的認為是結果。使用的方法是窮舉搜索,在所有的候選參數中,通過循環遍歷,嘗試每一種可能性。對於要調整兩個參數c和g的高斯核函數來說,加入c的可能值有5種,g的可能值有6種,那么要遍歷所有的c與g的組合參數值,就有5*6種可能,也就是可以列一個表格包含5列6行的表格,然后依次對表格內容進行遍歷便可以實現遍歷所有的30種可能了。
例如:遍歷gamma在[0.001, 0.01, 0.1, 10, 100, 1000]范圍,C在[0.001, 0.01, 0.1, 10, 100, 1000]范圍內的最優值,便可以執行以下程序:
best_score = 0 for gamma in [0.001, 0.01, 0.1, 10, 100, 1000]: for C in [0.001, 0.01, 0.1, 10, 100, 1000]: svm = SVC(gamma=gamma, C=C) svm.fit(X_train, y_train) score = svm.score(X_test, y_test) if score > best_score: best_score = score best_parameters = {'gamma':gamma, 'C':C} print("best score:{:.2f}".format(best_score)) print("best parameters:{}".format(best_parameters))
最終結果便是:
best score:0.95
best parameters:{'gamma': 0.01, 'C': 10}
由此可以看出,其實這個GridSearch方法就是一個代替我們去進行嘗試的machine,它無法自己尋求合理的c與gamma的取值范圍,且比較耗時,你設置的C值和gamma值越多,尋優時間就會翻倍增加。另外我將這對最優組合放在實際預測中查看效果,效果也是不甚理想啊:
圖5 GridSearch尋到的最優C&gamma組合預測的效果
效果不理想的原因與我設置的C值與gamma值不全肯定有很大的關系,所以再利用我們這種方法就不怎么可行了,於是我們繼續更改:
第一種笨方法:
還是利用GridSearch,只不過在C、gamma賦值處將固定值替換為初值加步長的形式,用到了range()函數。
第二種方法是利用我前文所講到的離子群優化算法尋優:
離子群優化算法(Particle Swarm Optimization Algorithm, PSO)是一種基於群體協作的隨機搜索算法,其原理是源於對於鳥類捕食行為的研究,通過群體中的個體之間的協作核信息共享來尋找最優解,PSO算法的優勢在於簡單的算法實現和簡潔的參數設置,目前較廣泛的應用於函數優化、圖像處理等方面,但是其缺點也是不可忽視的:提前收斂、容易陷入局部最優、維數災難等缺點。
PSO算法的基本流程為:
Step1:初始化一群粒子(假設群體規模為已知),初始化隨機位置及速度;
Step2:評價每個粒子的適應度(計算其與最優位置的距離);
Step3:對每個微粒,將其適應值與其經過的最好位置pbest作比較,如果較好的話,則更新較好位置為最好位置;
Step4:加入權重因子,繼續迭代更改粒子的速度及位置;
Step5:重復step2和step3直至達到結束條件(全局最優/迭代次數上限),否則繼續執行2,3。
(未完待續,詳情見下篇)