本代碼用的是徑向基核函數(shù),徑向基函數(shù)是一個(gè)采用向量作為自變量的函數(shù),能夠基于向量距離運(yùn)算輸出一個(gè)標(biāo)量。
所謂徑向基函數(shù) (Radial Basis Function 簡(jiǎn)稱(chēng) RBF), 就是某種沿徑向?qū)ΨQ(chēng)的標(biāo)量函數(shù)。 通常定義為空間中任一點(diǎn)x到某一中心xc之間歐氏距離的單調(diào)函數(shù) , 可記作 k(||x?xc||), 其作用往往是局部的 , 即當(dāng)x遠(yuǎn)離xc時(shí)函數(shù)取值很小。最常用的徑向基函數(shù)是高斯核函數(shù) ,形式為k(||x?xc||)=exp(?||X?XC||22σ2) 其中xc為核函數(shù)中心,σ為函數(shù)的寬度參數(shù) , 控制了函數(shù)的徑向作用范圍。——百度
其中σ是用戶定義的用于確定到達(dá)率,或者說(shuō)函數(shù)值跌落到0的速度參數(shù)。
"""
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()
需要注意的就是核函數(shù)的應(yīng)用,和普通的Platt SMO有幾處不同,還有就是kernelTrans函數(shù)是如何利用核函數(shù)進(jìn)行分類(lèi)的,我手寫(xiě)了下代碼過(guò)程:
和代碼對(duì)應(yīng)起來(lái)就是高斯核函數(shù)的應(yīng)用過(guò)程。
運(yùn)行結(jié)果:
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參數(shù)以觀察測(cè)試錯(cuò)誤率,訓(xùn)練錯(cuò)誤率,支持向量個(gè)數(shù)隨k1的變化情況。
σ太小的話,支持向量變多;σ太大,其支持向量的數(shù)目少了很多,并且測(cè)試錯(cuò)誤率也在下降。所以σ值的設(shè)置在某處存在著最優(yōu)值,如果降低σ,那么訓(xùn)練錯(cuò)誤率就會(huì)降低,但是測(cè)試錯(cuò)誤率卻在上升。
支持向量機(jī)的數(shù)目存在一個(gè)最優(yōu)值。SVM的優(yōu)點(diǎn)在于它對(duì)數(shù)據(jù)的高效分類(lèi)。如果支持向量太少,就會(huì)得到一個(gè)很差的決策邊界;如果支持向量太多,也就相當(dāng)于每次都會(huì)利用整個(gè)數(shù)據(jù)集進(jìn)行分類(lèi),這種分類(lèi)方法稱(chēng)為k近鄰。
手寫(xiě)識(shí)別問(wèn)題回顧
基于SVM的數(shù)字識(shí)別
收集數(shù)據(jù):提供的文本文件準(zhǔn)備數(shù)據(jù):基于二值圖像構(gòu)造向量分析數(shù)據(jù):對(duì)圖像向量進(jìn)行目測(cè)訓(xùn)練算法:采用兩種不同的核函數(shù),并對(duì)徑向基核函數(shù)采用不同的設(shè)置來(lái)運(yùn)行SMO算法。測(cè)試算法:編寫(xiě)一個(gè)函數(shù)來(lái)測(cè)試不同的核函數(shù)并計(jì)算錯(cuò)誤率使用算法:一個(gè)圖像識(shí)別的完整應(yīng)用還需要一些圖像處理的知識(shí),這里不再介紹。
"""
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))
函數(shù)loadImages()是已經(jīng)被重構(gòu)為自身的一個(gè)函數(shù),參考:KNN識(shí)別手寫(xiě)體數(shù)字
其中有一個(gè)很大的區(qū)別是。在前面的KNN識(shí)別手寫(xiě)體中直接應(yīng)用類(lèi)別標(biāo)簽,既數(shù)字向量對(duì)應(yīng)相應(yīng)的數(shù)字大小,而同支持向量機(jī)一起使用時(shí),類(lèi)別標(biāo)簽為-1或者+1,因此,一旦碰到數(shù)字9,則輸出類(lèi)別標(biāo)簽-1,否則輸出+1。因?yàn)檫@里只做二分類(lèi),因此除了1和9之外的數(shù)字都被去掉了。
運(yùn)行結(jié)果:
.
.
.
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
嘗試不同的σ值,并嘗試線性核函數(shù),得到:
偷了個(gè)懶,這里是機(jī)器學(xué)習(xí)實(shí)戰(zhàn)中的結(jié)果。分析:
在σ取10左右時(shí)可以得到最小的測(cè)試錯(cuò)誤率
- 數(shù)據(jù)的不同,特征數(shù)的不同,得到的最佳σ<script type="math/tex" id="MathJax-Element-816">σ</script>值就會(huì)不同
- 最小的訓(xùn)練錯(cuò)誤率并不對(duì)應(yīng)于最小的支持向量數(shù)目
- 線性核函數(shù)的效果并不是特別的糟糕。
總結(jié)
以上是生活随笔為你收集整理的支持向量机—核函数源码分析(2)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。