Support Vector Machine实现(二)

soft margin的优化目标:    

                                          

最终转为:

                                                       

该等式优化问题使用SMO算法(sequential minimal optimization)进行训练

面向对象风格代码如下:

import numpy as np


class SupportVectorMachine:
    def __init__(self, maxIter=1e3, C=1, tol=1e-2, kernel=('lin', 0)):
        '''

        Parameters
        ----------
        maxIter:最大迭代次数默认1000次
        C:比例常数C
        tol:间隔容差
        kernel:核函数类型,默认为线性核
        '''
        self.X = None
        self.labels = None
        self.maxIter = maxIter
        self.C = C
        self.tol = tol
        self.kernel = kernel
        self.KernerMatrix = None   #样本数据输入核函数得到的Gram矩阵
        self.w = None   #线性分隔超平面的系数
        self.b = 0      #线性分隔超平面的截距
        self.alphas = None   #拉格朗日系数
        self.errCache = None

    def __initParams(self, X, labels):
        self.X = np.mat(X);
        self.labels = np.mat(labels.reshape((-1, 1)))
        m, n = X.shape
        self.alphas = np.mat(np.zeros((m, 1)))
        self.errCache = np.mat(np.zeros((m, 2)))
        self.w = np.zeros((n, 1))
        self.KernelMatrix = np.mat(np.zeros((m, m)))  # 计算Gram矩阵
        for i in range(m):
            self.KernelMatrix[:, i] = self.calKernelMatrix(X[i, :])

    def calKernelMatrix(self, sample):
        m, n = self.X.shape
        kernel = np.mat(np.zeros((m, 1)))
        if self.kernel[0] == 'lin':
            kernel = self.X * sample.T
        elif self.kernel[0] == 'rbf':
            for j in range(m):
                delta = self.X[j, :] - sample
                kernel[j] = delta * delta.T
            kernel = np.exp(kernel / (-2 * self.kernel[1] ** 2))
        else:
            raise NameError('not exists kernel function')
        return kernel

    def train(self, X, y):
        self.__initParams(X, y)
        self.__SequentialMinimalOptimization()

    def __calcEk(self, k):
        Fx_k = np.float(np.multiply(self.alphas, self.labels).T * self.KernelMatrix[:, k]) + self.b
        E_k = Fx_k - float(self.labels[k])
        return E_k

    def __updateE_k(self, k):
        E_k = self.__calcEk(k)
        self.errCache[k] = [1, E_k]

    @staticmethod
    def randSelectE_J(i, m):
        j = i
        while j == i:
            j = int(np.random.uniform(0, m))
        return j

    def __selectE_J(self, i, E_i):
        m = self.X.shape[0]
        maxK = -1
        maxDeltaE = 0
        E_j = 0
        self.errCache[i] = [1, E_i]
        validErrCacheList = np.nonzero(self.errCache[:, 0].A)[0]
        if len(validErrCacheList) > 1:
            for k in validErrCacheList:
                if k == i:
                    continue
                E_k = self.__calcEk(k)
                deltaE = abs(E_i - E_k)
                if deltaE > maxDeltaE:
                    maxK = k
                    maxDeltaE = deltaE
                    E_j = E_k
            return maxK, E_j
        else:
            j = self.randSelectE_J(i, m)
            E_j = self.__calcEk(j)
        return j, E_j

    @staticmethod
    def clipAlpha(aj, L, H):
        if aj > H:
            aj = H
        if aj < L:
            aj = L
        return aj

    def __calW(self):
        m, n = self.X.shape
        for i in range(m):
            self.w += np.multiply(self.alphas[i] * self.labels[i], self.X[i, :].T)

    def __innerL(self, i):
        E_i = self.__calcEk(i)
        if (self.labels[i] * E_i < -self.tol and self.alphas[i] < self.C) or (
                self.labels[i] * E_i > self.tol and self.alphas[i] > 0):
            j, E_j = self.__selectE_J(i, E_i)
            alpha_i_old = self.alphas[i].copy()
            alpha_j_old = self.alphas[j].copy()
            if self.labels[i] != self.labels[j]:
                L = max(0, self.alphas[j] - self.alphas[i])
                H = min(self.C, self.C + self.alphas[j] - self.alphas[i])
            else:
                L = max(0, self.alphas[j] + self.alphas[i] - self.C)
                H = min(self.C, self.alphas[j] + self.alphas[i])
            if L == H:
                print("L==H")
                return 0
            eta = self.KernelMatrix[i, i] + self.KernelMatrix[j, j] - 2 * self.KernelMatrix[i, j]
            if eta <= 0:
                print("eta<=0")
                return 0
            self.alphas[j] += self.labels[j] * (E_i - E_j) / eta
            self.alphas[j] = self.clipAlpha(self.alphas[j], L, H)
            self.__updateE_k(j)
            bi = self.b - E_i - self.labels[i] * (self.alphas[i] - alpha_i_old) * self.KernelMatrix[i, i] - \
                 self.labels[j] * (self.alphas[j] - alpha_j_old) * self.KernelMatrix[i, j]
            bj = self.b - E_j - self.labels[i] * (self.alphas[i] - alpha_j_old) * self.KernelMatrix[i, j] - \
                 self.labels[j] * (self.alphas[j] - alpha_j_old) * self.KernelMatrix[j, j]
            if 0 < self.alphas[i] < self.C:
                self.b = bi
            elif 0 < self.alphas[j] < self.C:
                self.b = bj
            else:
                self.b = (bi + bj) / 2
            return 1
        else:
            return 0

    def __SequentialMinimalOptimization(self):
        num = 0
        entireSet = True
        alphaPairsChanged = 0
        m = self.X.shape[0]
        while num < self.maxIter and (entireSet or alphaPairsChanged > 0):
            alphaPairsChanged = 0
            if entireSet:
                for i in range(m):
                    alphaPairsChanged += self.__innerL(i)
                    print("fullSet,iter: %d i:%d,pairs changed %d" % (num, i, alphaPairsChanged))
            else:
                nonBounds = np.nonzero((self.alphas.A > 0) * (self.alphas.A < self.C))[0]
                for i in nonBounds:
                    print("non-bound,iter:%d i:%d,pairs changed %d" % (num, i, alphaPairsChanged))
            num += 1
            if entireSet:
                entireSet = False
            elif alphaPairsChanged == 0:
                entireSet = True
            print("iteration number:%d" % num)
            print("entireSet:%s" % entireSet)
            print("alphaChanged:%d" % alphaPairsChanged)
        self.__calW()

    def getSupportVectors(self):
        supportVectors=self.X[self.alphas>0]
        return supportVectors

    def predict(self,X):
        y_pred=np.dot(X,self.w)
        y_pred[y_pred>0]=1
        y_pred[y_pred<=0]=-1
        return y_pred

测试代码如下:

from sklearn import datasets
from sklearn.model_selection import train_test_split

digits = datasets.load_digits()
X=digits.data
y=digits.target
X0=X[y==0]
X1=X[y==1]
y0=y[y==0]
y1=y[y==1]
X=np.concatenate((X0,X1))
y=np.concatenate((y0,y1))
X_train,X_test,y_train,y_test=train_test_split(X,y)


svm=SupportVectorMachine(maxIter=1e4)
svm.train(X,y)
y_pred=svm.predict(X)
print((y_pred==y).astype(np.int).mean())

参考文献:《机器学习实战》部分代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值