支持向量機SVM源碼(SMO算法)的python實現

本次是學習了李航博士《統計學習分析》后實現了算法,算法實現了線性支持向量機和非線性支持向量機,采用SMO算法求解。算法中實現了兩種核函數:高斯核函數和多項式核函數。下面代碼中采用的數據集為鳶尾花數據集中的兩個類別的數據,若要嘗試非線性數據集可以從筆者github:https://github.com/Tomator01/-Machine-Learning

獲取,但是readfile函數需要修改一下。

smo算法是分解算法,算法步驟:

筆者程序還存在的問題:1、KKT條件還是不太清楚。2、當使用核函數時還存在部分問題,輸出的拉格朗日乘子看著不對勁,等過段時間再來看看吧~~哭~~也希望各位大佬讀者能幫忙指出錯誤,謝謝。

程序中的注釋已經寫的非常清楚了,包括公式的引用,這里不再贅述算法步驟。

# -*- coding:utf-8 -*-# SVM支持向量機:實現了線性支持向量機和非線性支持向量機(多項式核函數、高斯核函數),非線性還有問題有待解決。#author:Tomator# 測試數據集為鳶尾花數據集中提取的兩種類型數據import numpy as npimport matplotlib.pyplot as pltimport randomdef readfile(filename):    """    讀取數據集    W:特征向量數組    label:標簽(類別)列表    :param filename:    :return:特征向量數組和標簽集合列表    """    save_path="D:\\python3_anaconda3\\學習\機器學習\\機器學習數據集\\"    with open(save_path+filename,"r") as f:        length=len(f.readlines())        print(filename,"length: %d"%length)        W = np.zeros((length,4))        label=[]        i=0        f.seek(0,0)        for line in f.readlines():            linestr=line.strip()            linestrlist=line.split(",")            # print(linestrlist)            # 鳶尾屬植物數據集的特征共有四個            number_data=[float(j) for j in linestrlist[0:-1]]            W[i,:]=np.array(number_data)            label.append(linestrlist[-1].strip("\n"))            i+=1    return W,labeldef createDataset(filename):    """    創建待分類數據集    """    data_vector,label_str=readfile(filename)    # print(data_vector,"\n",label)    # 將原始數據集中非字符串標簽改為用數字代表,用于后續畫圖    data_label=np.zeros(len(label_str))    for i in range(len(label_str)):        if label_str[i]=="Iris-setosa":            data_label[i]=-1        elif label_str[i]=="Iris-versicolor":            data_label[i] = 1    return  data_vector,data_label# # 將原始數據集劃分為訓練集和測試集,splitRatio為劃分比例。# def splitDataset(dataset, splitRatio):#     trainSize = int(len(dataset) * splitRatio)#     trainSet = []#     copy = list(dataset)#     while len(trainSet) < trainSize:#         index = random.randrange(len(copy))#         # 原始數據集剔除訓練集之后剩下的就是測試集#         trainSet.append(copy.pop(index))#     return [trainSet, copy]class SVM(object):    """    kernel="linear" or "gaussian" or "poly",分別代表線性分類器、高斯核函數、多項式核函數;    kernel_para:表示核函數的參數,高斯核函數為高斯核參數,多項式核函數為p;    epsilon:誤差精度;    maxepoch:最大迭代次數;    C:懲罰因子    train_vector:訓練數據集的特征向量    train_label:訓練數據集的分類標簽    train_nums:訓練數據集的樣本數    train_err:每個樣本的預測誤差    alpha:拉格朗日乘子    """    def __init__(self,kernel="linear",kernel_para=0.0,epsilon = 1e-6,maxepoch=2000,C=1.0,):        self.kernel=kernel        self.kernel_para=kernel_para        self.epsilon=epsilon        self.maxepoch=maxepoch        self.train_vector=None        self.train_label=None        self.train_nums = None        self.train_err=None        self.alpha=None        self.C=C    # 初始化參數    def init_parameters(self,train_vector,train_label):        self.train_vector=train_vector        self.train_label=train_label        self.train_nums = len(train_label)        # 預測誤差初始化為-yi        self.train_err= -self.train_label        self.alpha=np.zeros(self.train_nums)        self.b=0    # 選擇第二個變量,《統計學習方法》P129    def select_second_alpha(self,ind1):        E1=self.train_err[ind1]        max_diff=0        ind2=None        train_exit_err = np.nonzero(self.train_err)[0]        if len(train_exit_err)>1:            for i in train_exit_err:                # 與indx不相等                if i == ind1:                    continue                diff=abs(self.train_err[i]-E1)                # print("diff",diff)                if diff>max_diff:                    max_diff=diff                    ind2=i        return ind2    # 計算核內積    def cal_k(self,x,y):        # 線性,沒有核函數        if self.kernel == "linear":            return np.dot(x,y)        # 高斯核函數        elif self.kernel == "gaussian":            dot_ = np.dot(x, y)            result=np.sum(np.exp(-np.square(x-y)/(2*(self.kernel_para**2))))            return result        # 多項式核函數        elif self.kernel == "poly":            dot_=np.dot(x,y)            return np.sum((dot_+1)**self.kernel_para)        else:            # 核函數名稱不正確            exit("the kernel show be "linear、gaussian、poly"")    #更新參數,參考《統計學習方法》P125-P130.Platt序列最小最優化算法    def update(self,ind1,ind2):        # 挑選出的兩個樣本的alpha、對應的預測值及誤差和閾值b        old_alpha1=self.alpha[ind1]        old_alpha2=self.alpha[ind2]        y1=self.train_label[ind1]        y2=self.train_label[ind2]        # print(ind1,ind2,y1,y2)        # 公式7.104        if  y1 == y2:            L=max(0.0,old_alpha2 + old_alpha1 - self.C)            H=min(self.C,old_alpha2 + old_alpha1)        else:            L = max(0.0, old_alpha2 - old_alpha1)            H = min(self.C, self.C+old_alpha2 - old_alpha1)        if L == H:            return 0        E1=self.train_err[ind1]        E2=self.train_err[ind2]        K11 = self.cal_k(self.train_vector[ind1],self.train_vector[ind1])        K12 = self.cal_k(self.train_vector[ind1],self.train_vector[ind2])        K22 = self.cal_k(self.train_vector[ind2], self.train_vector[ind2])        # print("k11",K11,"k22",K22)        # 公式7.107        eta=K11 + K22 - 2 * K12        if eta <=0 :            return 0        # 公式7.106        new_unc_alpha=old_alpha2+y2*(E1-E2)/eta        # 公式7.108        if new_unc_alpha > H:            new_alpha2=H        elif new_unc_alpha < L:            new_alpha2=L        else:            new_alpha2=new_unc_alpha        #     公式7.109        new_alpha1=old_alpha1+y1*y2*(old_alpha2-new_alpha2)        # 更新拉格朗日參數        self.alpha[ind1]=new_alpha1        self.alpha[ind2]=new_alpha2        # 公式7.115        new_b1=-E1-y1*K11*(new_alpha1 - old_alpha1)-y2*K12*(new_alpha2 - old_alpha2)+self.b        # 公式7.116        new_b2=-E2 - y1 * K12 * (new_alpha1 - old_alpha1) - y2 * K22 * (new_alpha2 - old_alpha2) + self.b        # P130文字部分        if 0 < new_alpha1 < self.C:            self.b = new_b1        elif 0 < new_alpha2 < self.C:            self.b = new_b2        else:            self.b = (new_b1 + new_b2) / 2        #     更新預測誤差        self.train_err[ind1] = np.sum(self.train_label * self.alpha * self.cal_k(self.train_vector,self.train_vector[ind1])) + self.b - self.train_label[ind1]        self.train_err[ind2] = np.sum(self.train_label * self.alpha * self.cal_k(self.train_vector,self.train_vector[ind2])) + self.b - self.train_label[ind2]        return 1    # 訓練模型    def train(self,train_vector,train_label):        # 初始化參數        self.init_parameters(train_vector,train_label)        epochs=0        # 迭代次數小于最大迭代次數        while epochs<self.maxepoch:            for i in range(self.train_nums):                # 挑選第一個變量,P129   SMO算法                if not self.satify_kkt(self.train_label[i],self.train_err[i],self.alpha[i]):                    ind1=i                    # 挑選第二個變量                    ind2=self.select_second_alpha(ind1)                    # 更新參數                    self.update(ind1,ind2)                    print(ind1,ind2,self.alpha[ind1],self.alpha[ind2])            epochs+=1        # print(epochs)        # 返回拉格朗日乘子        return self.alpha    # 判斷是否滿足KKT條件    def satify_kkt(self,y,err,alpha):        # 在精度范圍內判斷是否滿足KTT條件        r = y * err        # r<0,則yg<1, alpha=C則符合;r>0,則yg>1, alpha=0則符合        if (r < -self.epsilon and alpha < self.C) or (r > self.epsilon and alpha > 0):            return False        return True    # 利用訓練好的模型進行預測測試集    # test_vector為單個待測數據的特征向量    def predict(self,test_vector):        # P131公式        g=np.sum(self.alpha*self.train_label*self.cal_k(self.train_vector,test_vector))        # sign        if (g + self.b)>=0:            return 1        else:            return -1# 主函數def main():    # 訓練數據集    filename="iris_all_2class.data"    data_vector, label_num=createDataset(filename)    # print(data_vector,label_num)    s=SVM(kernel="linear",epsilon = 0.001,maxepoch=100,C=0.6)    # s = SVM(kernel="gaussian",kernel_para=1.3, epsilon=0.001, maxepoch=500, C=0.6)    alpha=s.train(data_vector,label_num)    print(alpha)    # 測試數據集    test_filename = "testiris.data"    test_vector, test_label = createDataset(test_filename)    score=0    # 計算預測精度    for x,y in zip(test_vector,test_label):        if s.predict(x) == y:            score+=1    print(score/len(test_label))if __name__ == "__main__":    main()

免責聲明:本文僅代表文章作者的個人觀點,與本站無關。其原創性、真實性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容文字的真實性、完整性和原創性本站不作任何保證或承諾,請讀者僅作參考,并自行核實相關內容。

http://image99.pinlue.com/thumb/img_jpg/TqvABsT7z7bfiagTaOJmpiap1tHuN1tEs1xaKr6BwoyjmDO9X3WgzIibDibJaVMhwnvKU1ChvRbqjdhScv0P2ushcA/0.jpeg
我要收藏
贊一個
踩一下
分享到
?
分享
評論
首頁
四川金7乐奖金设置