本代碼用的是徑向基核函數,徑向基函數是一個采用向量作為自變量的函數,能夠基于向量距離運算輸出一個標量。
所謂徑向基函數 (Radial Basis Function 簡稱 RBF), 就是某種沿徑向對稱的標量函數。 通常定義為空間中任一點x到某一中心xc之間歐氏距離的單調函數 , 可記作 k(||x?xc||), 其作用往往是局部的 , 即當x遠離xc時函數取值很小。最常用的徑向基函數是高斯核函數 ,形式為k(||x?xc||)=exp(?||X?XC||22σ2) 其中xc為核函數中心,σ為函數的寬度參數 , 控制了函數的徑向作用范圍。——百度
其中σ是用戶定義的用于確定到達率,或者說函數值跌落到0的速度參數。
"""
Created on Sun Oct 22 09:44:54 2017@author: LiLong
"""
from numpy
import *
from time
import sleep
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
def selectJrand(i,m):j=i
while (j==i):j = int(random.uniform(
0,m))
return j
def clipAlpha(aj,H,L):if aj > H: aj = H
if L > aj:aj = L
return aj
def kernelTrans(X, A, kTup): m,n = shape(X)K = mat(zeros((m,
1)))
if kTup[
0]==
'lin': K = X * A.T
elif kTup[
0]==
'rbf':
for j
in range(m):deltaRow = X[j,:] - AK[j] = deltaRow*deltaRow.TK = exp(K/(-
1*kTup[
1]**
2))
else:
raise NameError(
'Houston We Have a Problem -- \That Kernel is not recognized')
return K
class optStruct:def __init__(self,dataMatIn, classLabels, C, toler, kTup): self.X = dataMatInself.labelMat = classLabelsself.C = Cself.tol = tolerself.m = shape(dataMatIn)[
0]self.alphas = mat(zeros((self.m,
1)))self.b =
0self.eCache = mat(zeros((self.m,
2))) self.K = mat(zeros((self.m,self.m)))
for i
in range(self.m):self.K[:,i] = kernelTrans(self.X, self.X[i,:], kTup)
def calcEk(oS, k):fXk = float(multiply(oS.alphas,oS.labelMat).T*oS.K[:,k] + oS.b)Ek = fXk - float(oS.labelMat[k])
return Ek
def selectJ(i, oS, Ei): maxK = -
1; maxDeltaE =
0; Ej =
0oS.eCache[i] = [
1,Ei] validEcacheList = nonzero(oS.eCache[:,
0].A)[
0]
if (len(validEcacheList)) >
1:
for k
in validEcacheList:
if k == i:
continue Ek = calcEk(oS, k)deltaE = abs(Ei - Ek)
if (deltaE > maxDeltaE):maxK = k; maxDeltaE = deltaE; Ej = Ek
return maxK, Ej
else: j = selectJrand(i, oS.m)Ej = calcEk(oS, j)
return j, Ej
def updateEk(oS, k):Ek = calcEk(oS, k)oS.eCache[k] = [
1,Ek]
def innerL(i, oS):Ei = calcEk(oS, i)
if ((oS.labelMat[i]*Ei < -oS.tol)
and (oS.alphas[i] < oS.C))
or ((oS.labelMat[i]*Ei > oS.tol)
and (oS.alphas[i] >
0)):j,Ej = selectJ(i, oS, Ei) alphaIold = oS.alphas[i].copy(); alphaJold = oS.alphas[j].copy();
if (oS.labelMat[i] != oS.labelMat[j]):L = max(
0, oS.alphas[j] - oS.alphas[i])H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
else:L = max(
0, oS.alphas[j] + oS.alphas[i] - oS.C)H = min(oS.C, oS.alphas[j] + oS.alphas[i])
if L==H:
print "L==H";
return 0eta =
2.0 * oS.K[i,j] - oS.K[i,i] - oS.K[j,j]
if eta >=
0:
print "eta>=0";
return 0oS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/etaoS.alphas[j] = clipAlpha(oS.alphas[j],H,L)updateEk(oS, j)
if (abs(oS.alphas[j] - alphaJold) <
0.00001):
print "j not moving enough";
return 0oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])updateEk(oS, i) b1 = oS.b - Ei- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.K[i,i] - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.K[i,j]b2 = oS.b - Ej- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.K[i,j]- oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.K[j,j]
if (
0 < oS.alphas[i])
and (oS.C > oS.alphas[i]): oS.b = b1
elif (
0 < oS.alphas[j])
and (oS.C > oS.alphas[j]): oS.b = b2
else: oS.b = (b1 + b2)/
2.0return 1else:
return 0def smoP(dataMatIn, classLabels, C, toler, maxIter,kTup=('lin', 0)): oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler, kTup)iter =
0entireSet =
True; alphaPairsChanged =
0while (iter < maxIter)
and ((alphaPairsChanged >
0)
or (entireSet)):alphaPairsChanged =
0if entireSet:
for i
in range(oS.m): alphaPairsChanged += innerL(i,oS)
print "fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)iter +=
1else: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 +=
1if entireSet: entireSet =
False elif (alphaPairsChanged ==
0): entireSet =
True print "iteration number: %d" % iter
return oS.b,oS.alphas
def calcWs(alphas,dataArr,classLabels):X = mat(dataArr); labelMat = mat(classLabels).transpose()m,n = shape(X)w = zeros((n,
1))
for i
in range(m):w += multiply(alphas[i]*labelMat[i],X[i,:].T)
return w
def testRbf(k1=1.3):dataArr,labelArr = loadDataSet(
'testSetRBF.txt')b,alphas = smoP(dataArr, labelArr,
200,
0.0001,
10000, (
'rbf', k1)) 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 =
0for i
in range(m): kernelEval = kernelTrans(sVs,datMat[i,:],(
'rbf', k1)) predict=kernelEval.T * multiply(labelSV,alphas[svInd]) + b
if sign(predict)!=sign(labelArr[i]): errorCount +=
1print "the training error rate is: %f" % (float(errorCount)/m)dataArr,labelArr = loadDataSet(
'testSetRBF2.txt')errorCount =
0datMat=mat(dataArr); labelMat = mat(labelArr).transpose()m,n = shape(datMat)
for i
in range(m):kernelEval = kernelTrans(sVs,datMat[i,:],(
'rbf', k1))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) testRbf()
需要注意的就是核函數的應用,和普通的Platt SMO有幾處不同,還有就是kernelTrans函數是如何利用核函數進行分類的,我手寫了下代碼過程:
和代碼對應起來就是高斯核函數的應用過程。
運行結果:
L==H
fullSet, iter:
0 i:
0,
pairs changed
0
fullSet, iter:
0 i:
1,
pairs changed
1
fullSet, iter:
0 i:
2,
pairs changed
2
fullSet, iter:
0 i:
3,
pairs changed
3
fullSet, iter:
0 i:
4,
pairs changed
3
...,
fullSet, iter:
5 i:
92,
pairs changed
0
fullSet, iter:
5 i:
93,
pairs changed
0
j
not moving enough
fullSet, iter:
5 i:
94,
pairs changed
0
fullSet, iter:
5 i:
95,
pairs changed
0
fullSet, iter:
5 i:
96,
pairs changed
0
fullSet, iter:
5 i:
97,
pairs changed
0
fullSet, iter:
5 i:
98,
pairs changed
0
fullSet, iter:
5 i:
99,
pairs changed
0
iteration number:
6
there are
26 Support Vectors
the training
error rate is:
0.090000
the test
error rate is:
0.180000
可以更換不同的k1參數以觀察測試錯誤率,訓練錯誤率,支持向量個數隨k1的變化情況。
σ太小的話,支持向量變多;σ太大,其支持向量的數目少了很多,并且測試錯誤率也在下降。所以σ值的設置在某處存在著最優值,如果降低σ,那么訓練錯誤率就會降低,但是測試錯誤率卻在上升。
支持向量機的數目存在一個最優值。SVM的優點在于它對數據的高效分類。如果支持向量太少,就會得到一個很差的決策邊界;如果支持向量太多,也就相當于每次都會利用整個數據集進行分類,這種分類方法稱為k近鄰。
手寫識別問題回顧
基于SVM的數字識別
收集數據:提供的文本文件準備數據:基于二值圖像構造向量分析數據:對圖像向量進行目測訓練算法:采用兩種不同的核函數,并對徑向基核函數采用不同的設置來運行SMO算法。測試算法:編寫一個函數來測試不同的核函數并計算錯誤率使用算法:一個圖像識別的完整應用還需要一些圖像處理的知識,這里不再介紹。
"""
Created on Sun Oct 22 09:44:54 2017"""
"""
Created on Sun Oct 22 09:44:54 2017@author: LiLong
"""
from numpy
import *
from time
import sleep
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
def selectJrand(i,m):j=i
while (j==i):j = int(random.uniform(
0,m))
return j
def clipAlpha(aj,H,L):if aj > H: aj = H
if L > aj:aj = L
return aj
def kernelTrans(X, A, kTup): m,n = shape(X)K = mat(zeros((m,
1)))
if kTup[
0]==
'lin': K = X * A.T
elif kTup[
0]==
'rbf':
for j
in range(m):deltaRow = X[j,:] - AK[j] = deltaRow*deltaRow.TK = exp(K/(-
1*kTup[
1]**
2))
else:
raise NameError(
'Houston We Have a Problem -- \That Kernel is not recognized')
return K
class optStruct:def __init__(self,dataMatIn, classLabels, C, toler, kTup): self.X = dataMatInself.labelMat = classLabelsself.C = Cself.tol = tolerself.m = shape(dataMatIn)[
0]self.alphas = mat(zeros((self.m,
1)))self.b =
0self.eCache = mat(zeros((self.m,
2))) self.K = mat(zeros((self.m,self.m)))
for i
in range(self.m):self.K[:,i] = kernelTrans(self.X, self.X[i,:], kTup)
def calcEk(oS, k):fXk = float(multiply(oS.alphas,oS.labelMat).T*oS.K[:,k] + oS.b)Ek = fXk - float(oS.labelMat[k])
return Ek
def selectJ(i, oS, Ei): maxK = -
1; maxDeltaE =
0; Ej =
0oS.eCache[i] = [
1,Ei] validEcacheList = nonzero(oS.eCache[:,
0].A)[
0]
if (len(validEcacheList)) >
1:
for k
in validEcacheList:
if k == i:
continue Ek = calcEk(oS, k)deltaE = abs(Ei - Ek)
if (deltaE > maxDeltaE):maxK = k; maxDeltaE = deltaE; Ej = Ek
return maxK, Ej
else: j = selectJrand(i, oS.m)Ej = calcEk(oS, j)
return j, Ej
def updateEk(oS, k):Ek = calcEk(oS, k)oS.eCache[k] = [
1,Ek]
def innerL(i, oS):Ei = calcEk(oS, i)
if ((oS.labelMat[i]*Ei < -oS.tol)
and (oS.alphas[i] < oS.C))
or ((oS.labelMat[i]*Ei > oS.tol)
and (oS.alphas[i] >
0)):j,Ej = selectJ(i, oS, Ei) alphaIold = oS.alphas[i].copy(); alphaJold = oS.alphas[j].copy();
if (oS.labelMat[i] != oS.labelMat[j]):L = max(
0, oS.alphas[j] - oS.alphas[i])H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
else:L = max(
0, oS.alphas[j] + oS.alphas[i] - oS.C)H = min(oS.C, oS.alphas[j] + oS.alphas[i])
if L==H:
print "L==H";
return 0eta =
2.0 * oS.K[i,j] - oS.K[i,i] - oS.K[j,j]
if eta >=
0:
print "eta>=0";
return 0oS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/etaoS.alphas[j] = clipAlpha(oS.alphas[j],H,L)updateEk(oS, j)
if (abs(oS.alphas[j] - alphaJold) <
0.00001):
print "j not moving enough";
return 0oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])updateEk(oS, i) b1 = oS.b - Ei- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.K[i,i] - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.K[i,j]b2 = oS.b - Ej- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.K[i,j]- oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.K[j,j]
if (
0 < oS.alphas[i])
and (oS.C > oS.alphas[i]): oS.b = b1
elif (
0 < oS.alphas[j])
and (oS.C > oS.alphas[j]): oS.b = b2
else: oS.b = (b1 + b2)/
2.0return 1else:
return 0def smoP(dataMatIn, classLabels, C, toler, maxIter,kTup=('lin', 0)): oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler, kTup)iter =
0entireSet =
True; alphaPairsChanged =
0while (iter < maxIter)
and ((alphaPairsChanged >
0)
or (entireSet)):alphaPairsChanged =
0if entireSet:
for i
in range(oS.m): alphaPairsChanged += innerL(i,oS)
print "fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)iter +=
1else: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 +=
1if entireSet: entireSet =
False elif (alphaPairsChanged ==
0): entireSet =
True print "iteration number: %d" % iter
return oS.b,oS.alphas
def calcWs(alphas,dataArr,classLabels):X = mat(dataArr); labelMat = mat(classLabels).transpose()m,n = shape(X)w = zeros((n,
1))
for i
in range(m):w += multiply(alphas[i]*labelMat[i],X[i,:].T)
return w
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 listdirhwLabels = []trainingFileList = listdir(dirName) m = len(trainingFileList)trainingMat = zeros((m,
1024))
for i
in range(m):fileNameStr = trainingFileList[i]fileStr = fileNameStr.split(
'.')[
0] 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
def testDigits(kTup=('rbf', 10)):dataArr,labelArr = loadImages(
'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 =
0for i
in range(m):kernelEval = kernelTrans(sVs,datMat[i,:],kTup)predict=kernelEval.T * multiply(labelSV,alphas[svInd]) + b
if sign(predict)!=sign(labelArr[i]): errorCount +=
1print "the training error rate is: %f" % (float(errorCount)/m)dataArr,labelArr = loadImages(
'testDigits')errorCount =
0datMat=mat(dataArr); labelMat = mat(labelArr).transpose()m,n = shape(datMat)
for i
in range(m):kernelEval = kernelTrans(sVs,datMat[i,:],kTup)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) testDigits((
'rbf',
20))
函數loadImages()是已經被重構為自身的一個函數,參考:KNN識別手寫體數字
其中有一個很大的區別是。在前面的KNN識別手寫體中直接應用類別標簽,既數字向量對應相應的數字大小,而同支持向量機一起使用時,類別標簽為-1或者+1,因此,一旦碰到數字9,則輸出類別標簽-1,否則輸出+1。因為這里只做二分類,因此除了1和9之外的數字都被去掉了。
運行結果:
.
.
.
fullSet, iter:
3 i:
398, pairs changed
0
L==H
fullSet, iter:
3 i:
399, pairs changed
0
fullSet, iter:
3 i:
400, pairs changed
0
L==H
fullSet, iter:
3 i:
401, pairs changed
0
iteration
number:
4
there are
47 Support Vectors
the training
error rate
is:
0.004975
the test
error rate
is:
0.016129
嘗試不同的σ值,并嘗試線性核函數,得到:
偷了個懶,這里是機器學習實戰中的結果。分析:
在σ取10左右時可以得到最小的測試錯誤率
- 數據的不同,特征數的不同,得到的最佳σ<script type="math/tex" id="MathJax-Element-816">σ</script>值就會不同
- 最小的訓練錯誤率并不對應于最小的支持向量數目
- 線性核函數的效果并不是特別的糟糕。
總結
以上是生活随笔為你收集整理的支持向量机—核函数源码分析(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。