一步步教你輕松學支持向量機SVM算法之案例篇2
(白寧超 2018年10月22日10:09:07)
摘要:支持向量機即SVM(Support Vector Machine) ,是一種監督學習算法,屬於分類的范疇。首先,支持向量機不是一種機器,而是一種機器學習算法。在數據挖掘的應用中,與無監督學習的聚類相對應和區別。廣泛應用於機器學習,計算機視覺和數據挖掘當中。(本文原創,轉載必須注明出處.)
目錄
1 機器學習:一步步教你輕松學KNN模型算法
2 機器學習:一步步教你輕松學決策樹算法
3 機器學習:一步步教你輕松學朴素貝葉斯模型算法理論篇1
4 機器學習:一步步教你輕松學朴素貝葉斯模型實現篇2
5 機器學習:一步步教你輕松學朴素貝葉斯模型算法Sklearn深度篇3
6 機器學習:一步步教你輕松學邏輯回歸模型算法
7 機器學習:一步步教你輕松學K-means聚類算法
8 機器學習:一步步教你輕松學關聯規則Apriori算法
9 機器學習: 一步步教你輕松學支持向量機SVM算法之理論篇1
10 機器學習: 一步步教你輕松學支持向量機SVM算法之案例篇2
11 機器學習: 一步步教你輕松學主成分分析PCA降維算法
12 機器學習: 一步步教你輕松學支持向量機SVM降維算法
更多文章請點擊這里>>
上節回顧,還記得如何基於svm進行分類?
尋找最大間隔
怎么尋找最大間隔
點到超平面的距離
- 分隔超平面
函數間距
: \(y(x)=w^Tx+b\) - 分類的結果: \(f(x)=sign(w^Tx+b)\) (sign表示>0為1,<0為-1,=0為0)
- 點到超平面的
幾何間距
: \(d(x)=(w^Tx+b)/||w||\) (||w||表示w矩陣的二范數=> \(\sqrt{w^T*w}\), 點到超平面的距離也是類似的)
拉格朗日乘子法
- 類別標簽用-1、1,是為了后期方便 \(label*(w^Tx+b)\) 的標識和距離計算;如果 \(label*(w^Tx+b)>0\) 表示預測正確,否則預測錯誤。
- 現在目標很明確,就是要找到
w
和b
,因此我們必須要找到最小間隔的數據點,也就是前面所說的支持向量
。- 讓最小的距離取最大.(最小的距離:就是最小間隔的數據點;最大:就是最大間距,為了找出最優超平面--最終就是支持向量)
- 目標函數:\(arg: max_{w, b} \left( min[label*(w^Tx+b)]*\frac{1}{||w||} \right) \)
- 如果 \(label*(w^Tx+b)>0\) 表示預測正確,也稱
函數間隔
,\(||w||\) 可以理解為歸一化,也稱幾何間隔
。 - 令 \(label*(w^Tx+b)>=1\), 因為0~1之間,得到的點是存在誤判的可能性,所以要保障 \(min[label*(w^Tx+b)]=1\),才能更好降低噪音數據影響。
- 所以本質上是求 \(arg: max_{關於w, b} \frac{1}{||w||} \);也就說,我們約束(前提)條件是: \(label*(w^Tx+b)=1\)
- 如果 \(label*(w^Tx+b)>0\) 表示預測正確,也稱
- 新的目標函數求解: \(arg: max_{關於w, b} \frac{1}{||w||} \)
- => 就是求: \(arg: min_{關於w, b} ||w|| \) (求矩陣會比較麻煩,如果x只是 \(\frac{1}{2}*x^2\) 的偏導數,那么。。同樣是求最小值)
- => 就是求: \(arg: min_{關於w, b} (\frac{1}{2}*||w||^2)\) (二次函數求導,求極值,平方也方便計算)
- 本質上就是求線性不等式的二次優化問題(求分隔超平面,等價於求解相應的凸二次規划問題)
- 通過拉格朗日乘子法,求二次優化問題
- 假設需要求極值的目標函數 (objective function) 為 f(x,y),限制條件為 φ(x,y)=M # M=1
- 設g(x,y)=M-φ(x,y) # 臨時φ(x,y)表示下文中 \(label*(w^Tx+b)\)
- 定義一個新函數: F(x,y,λ)=f(x,y)+λg(x,y)
- a為λ(a>=0),代表要引入的拉格朗日乘子(Lagrange multiplier)
- 那么: \(L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha_i * [1 - label * (w^Tx+b)]\)
- 因為:\(label*(w^Tx+b)>=1, \alpha>=0\) , 所以 \(\alpha*[1-label*(w^Tx+b)]<=0\) , \(\sum_{i=1}^{n} \alpha_i * [1-label*(w^Tx+b)]<=0\)
- 當 \(label*(w^Tx+b)>1\) 則 \(\alpha=0\) ,表示該點為非支持向量
- 相當於求解: \(max_{關於\alpha} L(w,b,\alpha) = \frac{1}{2} *||w||^2\)
- 如果求: \( min_{關於w, b}\frac{1}{2} *||w||^2 \) , 也就是要求: \(min_{關於w, b} \left( max_{關於\alpha} L(w,b,\alpha)\right)\)
- 現在轉化到對偶問題的求解
- \(min_{關於w, b} \left(max_{關於\alpha} L(w,b,\alpha) \right) \) >= \(max_{關於\alpha} \left(min_{關於w, b}\ L(w,b,\alpha) \right) \)
- 現在分2步
- 先求: \(min_{關於w, b} L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^n \alpha_i * [1 - label * (w^Tx+b)]\)
- 就是求
L(w,b,a)
關於[w, b]的偏導數, 得到w和b的值
,並化簡為:L和a的方程
。 - 參考: 如果公式推導還是不懂,也可以參考《統計學習方法》李航-P103<學習的對偶算法>
- 終於得到課本上的公式: \(max_{\alpha} [ \sum_{i=1}^m \alpha_i - \frac{1}{2}\sum^m ·\alpha_i·\alpha_j·\langle x^{(i)}, x^{(j)} \rangle ]\)
- 約束條件: \(a>=0\) 並且 \(\sum_{i=1}^{m} a_i·label_i=0\)
拉格朗日乘子法理解
要求\( f(x,y)\),在\( g(x,y)=c\)時的最大值時,我們可以引入新變量拉格朗日乘數\(\lambda\) ,這時我們只需要下列拉格朗日函數的極值,為了幫助大家更好的理解,請參照下圖(綠線標出的是約束g(x,y) = c的點的軌跡。藍線是f的等高線。箭頭表示斜率,和等高線的法線平行。):
$$ {\mathcal {L}}(x,y,\lambda )=f(x,y)+\lambda \cdot {\Big (}g(x,y)-c{\Big )} $$
拉格朗日乘子證明過程
設函數\( f(x,y)\)在 \( A\)點處有極值\( \kappa\),且在\( A\)點的鄰域內連續。則在\( A\)點處有\( f\left(x,y\right)=\kappa \)
另有一常值函數:\( g\left(x,y\right)=c\)
二函數在 \(A\)點處的全微分為
$$
\mathrm {d} f={\frac {\partial {f}}{\partial {x}}}\mathrm {d} x+{\frac {\partial {f}}{\partial {y}}}\mathrm {d} y=0
$$
$$
\mathrm {d} g={\frac {\partial {g}}{\partial {x}}}\mathrm {d} x+{\frac {\partial {g}}{\partial {y}}}\mathrm {d} y=0
$$
由於\( \mathrm {d} x\)和\(\mathrm{d}y\)是任取的無窮小量,故該線性方程組的系數成比例,有
$$
{\dfrac {\dfrac {\partial {f}}{\partial {x}}}{\dfrac {\partial {g}}{\partial {x}}}}={\dfrac {\dfrac {\partial {f}}{\partial {y}}}{\dfrac {\partial {g}}{\partial {y}}}}=-\lambda
$$
即
$$
{\frac {\partial {f}}{\partial {y}}}+\lambda \cdot {\frac {\partial {g}}{\partial {y}}}=0
{\frac {\partial {f}}{\partial {y}}}+\lambda \cdot {\frac {\partial {g}}{\partial {y}}}=0
$$
將上二式分別乘以\( \mathrm{d}x \)和\(\mathrm{d}y\),再相加並積分,得到一新函數
$$ {\mathcal {L}}(x,y,\lambda )=f(x,y)+\lambda \cdot g(x,y)$$
那么,求原函數極值的問題就轉化為求該函數極值的問題。類似地,這種求極值的方法也可以推廣到多維函數 \(f\left(x_1,\ldots ,x_n\right)\)。
拉格朗日乘子簡單例子
求此方程的最小值:\( f(x, y) = x^2 y\),同時未知數滿足\(x^2 + y^2 = 1\)
因為只有一個未知數的限制條件,我們只需要用一個乘數\(\lambda \).
$$ g (x, y) = x^2 +y^2 -1 $$
$$\Phi (x, y, \lambda) = f(x,y) + \lambda g(x, y) = x^2 y + \lambda (x^2 + y^2 - 1)$$
將所有 \(\Phi\) 方程的偏微分設為零,得到一個方程組,最小值是以下方程組的解中的一個:
$$2 x y + 2 \lambda x = 0$$
$$ x^2 + 2 \lambda y = 0$$
$$ x^2 + y^2 -1 = 0$$
松弛變量(slack variable)
我們知道幾乎所有的數據都不那么干凈, 通過引入松弛變量來 允許數據點可以處於分隔面錯誤的一側
。
約束條件: \(C>=a>=0\) 並且 \(\sum_{i=1}^{m} a_i·label_i=0\)
總的來說:
- 常量C是
懲罰因子
, 表示離群點的權重(用於控制“最大化間隔”和“保證大部分點的函數間隔小於1.0” )- \(label*(w^Tx+b) > 1\) and alpha = 0 (在邊界外,就是非支持向量)
- \(label*(w^Tx+b) = 1\) and 0< alpha < C (在分割超平面上,就支持向量)
- \(label*(w^Tx+b) < 1\) and alpha = C (在分割超平面內,是誤差點 -> C表示它該受到的懲罰因子程度)
- C值越大,表示離群點影響越大,就越容易過度擬合;反之有可能欠擬合。
- 我們看到,目標函數控制了離群點的數目和程度,使大部分樣本點仍然遵守限制條件。
- 例如:正類有10000個樣本,而負類只給了100個(C越大表示100個負樣本的影響越大,就會出現過度擬合,所以C決定了負樣本對模型擬合程度的影響!,C就是一個非常關鍵的優化點!)
- 這一結論十分直接,SVM中的主要工作就是要求解 alpha.
SMO 高效優化算法
SVM有很多種實現,最流行的一種實現是: 序列最小優化(Sequential Minimal Optimization, SMO)算法
。下面還會介紹一種稱為 核函數(kernel)
的方式將SVM擴展到更多數據集上。 注意:SVM幾何含義比較直觀,但其算法實現較復雜,牽扯大量數學公式的推導。
序列最小優化(Sequential Minimal Optimization, SMO)
- John Platt於1996年創建
- SMO用途:用於訓練 SVM
- SMO目標:求出一系列 alpha 和 b,一旦求出 alpha,就很容易計算出權重向量 w 並得到分隔超平面。
- SMO思想:是將大優化問題分解為多個小優化問題來求解的。
- SMO原理:每次循環選擇兩個 alpha 進行優化處理,一旦找出一對合適的 alpha,那么就增大一個同時減少一個。
- 這里指的合適必須要符合一定的條件
- 這兩個 alpha 必須要在間隔邊界之外
- 這兩個 alpha 還沒有進行過區間化處理或者不在邊界上。
- 之所以要同時改變2個 alpha;原因是我們有一個約束條件: \(\sum_{i=1}^{m} a_i·label_i=0\);如果只是修改一個 alpha,很可能導致約束條件失效。
- 這里指的合適必須要符合一定的條件
SMO 偽代碼大致如下:
創建一個 alpha 向量並將其初始化為0向量
當迭代次數小於最大迭代次數時(外循環)
對數據集中的每個數據向量(內循環):
如果該數據向量可以被優化
隨機選擇另外一個數據向量
同時優化這兩個向量
如果兩個向量都不能被優化,退出內循環
如果所有向量都沒被優化,增加迭代數目,繼續下一次循環
SVM 開發流程
收集數據:可以使用任意方法。
准備數據:需要數值型數據。
分析數據:有助於可視化分隔超平面。
訓練算法:SVM的大部分時間都源自訓練,該過程主要實現兩個參數的調優。
測試算法:十分簡單的計算過程就可以實現。
使用算法:幾乎所有分類問題都可以使用SVM,值得一提的是,SVM本身是一個二類分類器,對多類問題應用SVM需要對代碼做一些修改。
SVM 算法特點
優點:泛化(由具體的、個別的擴大為一般的,就是說:模型訓練完后的新樣本)錯誤率低,計算開銷不大,結果易理解。
缺點:對參數調節和核函數的選擇敏感,原始分類器不加修改僅適合於處理二分類問題。
使用數據類型:數值型和標稱型數據。
SVM無核函數分類
這里是對小規模數據點進行分類
收集數據
文本文件格式:
3.542485 1.977398 -1
3.018896 2.556416 -1
7.551510 -1.580030 1
2.114999 -0.004466 -1
8.127113 1.274372 1
准備數據
'''對文件進行逐行解析,從而得到第行的類標簽和整個數據矩陣'''
def loadDataSet(fileName):
dataMat = [];labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr = line.strip().split('\t')
dataMat.append([float(lineArr[0]), float(lineArr[1])])
labelMat.append(float(lineArr[2]))
return dataMat, labelMat
訓練算法
簡化版SMO(序列最小優化)算法
'''
簡化版SMO(序列最小優化)算法
輸入參數:
C 松弛變量(常量值),允許有些數據點可以處於分隔面的錯誤一側。控制最大化間隔和
保證大部分的函數間隔小於1.0這兩個目標權重。通過調節該參數達到不同的結果。
toler 容錯率(在某個體系中能減小一些因素或選擇對某個系統產生不穩定的概率。)
maxIter 退出前最大的循環次數
返回參數:
b 模型的常量值
alphas 拉格朗日乘子
'''
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
dataMatrix = mat(dataMatIn)
labelMat = mat(classLabels).transpose()
m, n = shape(dataMatrix)
# 初始化 b和alphas(alpha有點類似權重值。)
b = 0;alphas = mat(zeros((m, 1)))
# 沒有任何alpha改變的情況下遍歷數據的次數
iter = 0
while (iter < maxIter):
# w = calcWs(alphas, dataMatIn, classLabels)
# print("w:", w)
# 記錄alpha是否已經進行優化,每次循環時設為0,然后再對整個集合順序遍歷
alphaPairsChanged = 0
for i in range(m):
# 我們預測的類別 y = w^Tx[i]+b; 其中因為 w = Σ(1~n) a[n]*lable[n]*x[n]
fXi = float(multiply(alphas, labelMat).T*(dataMatrix*dataMatrix[i, :].T)) + b
# 預測結果與真實結果比對,計算誤差Ei
Ei = fXi - float(labelMat[i])
# 約束條件 (KKT條件是解決最優化問題的時用到的一種方法。我們這里提到的最優化問題通常是指對於給定的某一函數,求其在指定作用域上的全局最小值)
# 0<=alphas[i]<=C,但由於0和C是邊界值,我們無法進行優化,因為需要增加一個alphas和降低一個alphas。
# 表示發生錯誤的概率:labelMat[i]*Ei 如果超出了 toler, 才需要優化。至於正負號,我們考慮絕對值就對了。
'''
# 檢驗訓練樣本(xi, yi)是否滿足KKT條件
yi*f(i) >= 1 and alpha = 0 (outside the boundary)
yi*f(i) == 1 and 0<alpha< C (on the boundary)
yi*f(i) <= 1 and alpha = C (between the boundary)
'''
if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
# 如果滿足優化的條件,我們就隨機選取非i的一個點,進行優化比較
j = selectJrand(i, m)
# 預測j的結果
fXj = float(multiply(alphas, labelMat).T*(dataMatrix*dataMatrix[j, :].T)) + b
Ej = fXj - float(labelMat[j])
alphaIold = alphas[i].copy()
alphaJold = alphas[j].copy()
# L和H用於將alphas[j]調整到0-C之間。如果L==H,就不做任何改變,直接執行continue語句
# labelMat[i] != labelMat[j] 表示異側,就相減,否則是同側,就相加。
if (labelMat[i] != labelMat[j]):
L = max(0, alphas[j] - alphas[i])
H = min(C, C + alphas[j] - alphas[i])
else:
L = max(0, alphas[j] + alphas[i] - C)
H = min(C, alphas[j] + alphas[i])
# 如果相同,就沒發優化了
if L == H:
# print("L==H")
continue
# eta是alphas[j]的最優修改量,如果eta==0,需要退出for循環的當前迭代過程
# 參考《統計學習方法》李航-P125~P128<序列最小最優化算法>
eta = 2.0 * dataMatrix[i, :]*dataMatrix[j, :].T - dataMatrix[i, :]*dataMatrix[i, :].T - dataMatrix[j, :]*dataMatrix[j, :].T
if eta >= 0:
# print("eta>=0")
continue
# 計算出一個新的alphas[j]值
alphas[j] -= labelMat[j]*(Ei - Ej)/eta
# 並使用輔助函數,以及L和H對其進行調整
alphas[j] = clipAlpha(alphas[j], H, L)
# 檢查alpha[j]是否只是輕微的改變,如果是的話,就退出for循環。
if (abs(alphas[j] - alphaJold) < 0.00001):
# print("j not moving enough")
continue
# 然后alphas[i]和alphas[j]同樣進行改變,雖然改變的大小一樣,但是改變的方向正好相反
alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])
# 在對alpha[i], alpha[j] 進行優化之后,給這兩個alpha值設置一個常數b。
# w= Σ[1~n] ai*yi*xi => b = yj- Σ[1~n] ai*yi(xi*xj)
# 所以: b1 - b = (y1-y) - Σ[1~n] yi*(a1-a)*(xi*x1)
# 為什么減2遍? 因為是 減去Σ[1~n],正好2個變量i和j,所以減2遍
b1 = b - Ei- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i, :]*dataMatrix[i, :].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[i, :]*dataMatrix[j, :].T
b2 = b - Ej- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i, :]*dataMatrix[j, :].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[j, :]*dataMatrix[j, :].T
if (0 < alphas[i]) and (C > alphas[i]):
b = b1
elif (0 < alphas[j]) and (C > alphas[j]):
b = b2
else:
b = (b1 + b2)/2.0
alphaPairsChanged += 1
# print("iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
# 在for循環外,檢查alpha值是否做了更新,如果在更新則將iter設為0后繼續運行程序
# 知道更新完畢后,iter次循環無變化,才推出循環。
if (alphaPairsChanged == 0):
iter += 1
else:
iter = 0
# print("iteration number: %d" % iter)
return b, alphas
SVM簡化版,應用簡化版SMO算法處理小規模數據集;SVM完整版,使用完整 Platt SMO算法加速優化,優化點:選擇alpha的方式不同。具體代碼實現下面提供完整代碼下載
核函數(kernel) 使用
對於線性可分的情況,效果明顯,對於非線性的情況也一樣,此時需要用到一種叫核函數(kernel)
的工具將數據轉化為分類器易於理解的形式。利用核函數將數據映射到高維空間,使用核函數可以將數據從某個特征空間到另一個特征空間的映射。(通常情況下:這種映射會將低維特征空間映射到高維空間。) 如果覺得特征空間很難理解,可以把核函數想象成一個包裝器(wrapper)或者是接口(interface),它能將數據從某個很難處理的形式轉換成為另一個較容易處理的形式。經過空間轉換后:低維需要解決的非線性問題,就變成了高維需要解決的線性問題。SVM 優化特別好的地方,在於所有的運算都可以寫成內積(inner product: 是指2個向量相乘,得到單個標量 或者 數值);內積替換成核函數的方式被稱為核技巧(kernel trick)
或者核"變電"(kernel substation)
. 核函數並不僅僅應用於支持向量機,很多其他的機器學習算法也都用到核函數。最流行的核函數:徑向基函數(radial basis function).
手寫數字識別的優化(有核函數)
你的老板要求:你寫的那個手寫識別程序非常好,但是它占用內存太大。顧客無法通過無線的方式下載我們的應用。所以:我們可以考慮使用支持向量機,保留支持向量就行(knn需要保留所有的向量),就可以獲得非常好的效果。
收集數據:提供的文本文件
00000000000000001111000000000000
00000000000000011111111000000000
00000000000000011111111100000000
00000000000000011111111110000000
00000000000000011111111110000000
00000000000000111111111100000000
00000000000000111111111100000000
00000000000001111111111100000000
00000000000000111111111100000000
00000000000000111111111100000000
00000000000000111111111000000000
00000000000001111111111000000000
00000000000011111111111000000000
00000000000111111111110000000000
00000000001111111111111000000000
00000001111111111111111000000000
00000011111111111111110000000000
00000111111111111111110000000000
00000111111111111111110000000000
00000001111111111111110000000000
00000001111111011111110000000000
00000000111100011111110000000000
00000000000000011111110000000000
00000000000000011111100000000000
00000000000000111111110000000000
00000000000000011111110000000000
00000000000000011111110000000000
00000000000000011111111000000000
00000000000000011111111000000000
00000000000000011111111000000000
00000000000000000111111110000000
00000000000000000111111100000000
准備數據:基於二值圖像構造向量
將 32*32的文本轉化為 1*1024的矩陣
def img2vector(filename):
returnVect = zeros((1, 1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0, 32 * i + j] = int(lineStr[j])
return returnVect
def loadImages(dirName):
from os import listdir
hwLabels = []
print(dirName)
trainingFileList = listdir(dirName) # load the training set
m = len(trainingFileList)
trainingMat = zeros((m, 1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0] # take off .txt
classNumStr = int(fileStr.split('_')[0])
if classNumStr == 9:
hwLabels.append(-1)
else:
hwLabels.append(1)
trainingMat[i, :] = img2vector('%s/%s' % (dirName, fileNameStr))
return trainingMat, hwLabels
分析數據:對圖像向量進行目測
訓練算法
采用兩種不同的核函數,並對徑向基核函數采用不同的設置來運行SMO算法.
'''核轉換函數
X dataMatIn數據集
A dataMatIn數據集的第i行的數據
kTup 核函數的信息
'''
def kernelTrans(X, A, kTup):
m, n = shape(X)
K = mat(zeros((m, 1)))
if kTup[0] == 'lin':
# linear kernel: m*n * n*1 = m*1
K = X * A.T
elif kTup[0] == 'rbf':
for j in range(m):
deltaRow = X[j, :] - A
K[j] = deltaRow * deltaRow.T
# 徑向基函數的高斯版本
K = exp(K / (-1 * kTup[1] ** 2)) # divide in NumPy is element-wise not matrix like Matlab
else:
raise NameError('Houston We Have a Problem -- That Kernel is not recognized')
return K
"""
完整SMO算法外循環,與smoSimple有些類似,但這里的循環退出條件更多一些
Args:
dataMatIn 數據集
classLabels 類別標簽
C 松弛變量(常量值),允許有些數據點可以處於分隔面的錯誤一側。
控制最大化間隔和保證大部分的函數間隔小於1.0這兩個目標的權重。
可以通過調節該參數達到不同的結果。
toler 容錯率
maxIter 退出前最大的循環次數
kTup 包含核函數信息的元組
Returns:
b 模型的常量值
alphas 拉格朗日乘子
"""
def smoP(dataMatIn, classLabels, C, toler, maxIter, kTup=('lin', 0)):
# 創建一個 optStruct 對象
oS = optStruct(mat(dataMatIn), mat(classLabels).transpose(), C, toler, kTup)
iter = 0
entireSet = True
alphaPairsChanged = 0
# 循環遍歷:循環maxIter次 並且 (alphaPairsChanged存在可以改變 or 所有行遍歷一遍)
while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
alphaPairsChanged = 0
# 當entireSet=true or 非邊界alpha對沒有了;就開始尋找 alpha對,然后決定是否要進行else。
if entireSet:
# 在數據集上遍歷所有可能的alpha
for i in range(oS.m):
# 是否存在alpha對,存在就+1
alphaPairsChanged += innerL(i, oS)
# print("fullSet, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
iter += 1
# 對已存在 alpha對,選出非邊界的alpha值,進行優化。
else:
# 遍歷所有的非邊界alpha值,也就是不在邊界0或C上的值。
nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
for i in nonBoundIs:
alphaPairsChanged += innerL(i, oS)
# print("non-bound, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
iter += 1
# 如果找到alpha對,就優化非邊界alpha值,否則,就重新進行尋找,如果尋找一遍 遍歷所有的行還是沒找到,就退出循環。
if entireSet:
entireSet = False # toggle entire set loop
elif alphaPairsChanged == 0:
entireSet = True
print("iteration number: %d" % iter)
return oS.b, oS.alphas
測試算法:便攜一個函數來測試不同的和函數並計算錯誤率
def testDigits(kTup=('rbf', 10)):
# 1. 導入訓練數據
dataArr, labelArr = loadImages('input/6.SVM/trainingDigits')
b, alphas = smoP(dataArr, labelArr, 200, 0.0001, 10000, kTup)
datMat = mat(dataArr)
labelMat = mat(labelArr).transpose()
svInd = nonzero(alphas.A > 0)[0]
sVs = datMat[svInd]
labelSV = labelMat[svInd]
# print("there are %d Support Vectors" % shape(sVs)[0])
m, n = shape(datMat)
errorCount = 0
for i in range(m):
kernelEval = kernelTrans(sVs, datMat[i, :], kTup)
# 1*m * m*1 = 1*1 單個預測結果
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]): errorCount += 1
print("the training error rate is: %f" % (float(errorCount) / m))
# 2. 導入測試數據
dataArr, labelArr = loadImages('input/6.SVM/testDigits')
errorCount = 0
datMat = mat(dataArr)
labelMat = mat(labelArr).transpose()
m, n = shape(datMat)
for i in range(m):
kernelEval = kernelTrans(sVs, datMat[i, :], kTup)
# 1*m * m*1 = 1*1 單個預測結果
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]): errorCount += 1
print("the test error rate is: %f" % (float(errorCount) / m))
參考文獻
- scikit中文社區:http://sklearn.apachecn.org/cn/0.19.0/
- ApacheCN
- GitHub:https://github.com/BaiNingchao/MachineLearning-1
- 圖書:《機器學習實戰》
- 圖書:《自然語言處理理論與實戰》
完整代碼下載
作者聲明
本文版權歸作者所有,旨在技術交流使用。未經作者同意禁止轉載,轉載后需在文章頁面明顯位置給出原文連接,否則相關責任自行承擔。