支持向量機通俗導論——學習(一)

SVM三層理解鏈接1
原文鏈接2
代碼鏈接3
一週時間的SVM簡單版本的代碼讓人讀的心力交瘁,讓我這個編程小白太難受!!!
流程圖
1、MySVM

# -*- coding:utf-8 -*-
import random
import numpy as np
import matplotlib.pyplot as plt

#輔助函數一: 創建一個樣本點返回來的隨機整數(0,m),即選擇alpha(i,j)
def seclectJrand(i,m):
    j = i
    while (j == i):
        j  = int(random.uniform(0,m)) #返回一個浮點數 N,取值範圍爲0,m
    return j

# 輔助函數二:將aj規劃到[0,C]範圍
def ClipAlpha(aj,H,L):
    if aj > H:
        aj = H
    if aj < L:
        aj = L
    return aj

# 最簡版本SMO算法
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
    dataMatrix = np.mat(dataMatIn)
    labelMat = np.mat(classLabels).transpose()
    b = 0
    m,n = np.shape(dataMatrix)
    alphas = np.mat(np.zeros((m,1)))
    iter_num = 0

    while(iter_num < maxIter):
        alphaPairsChanged = 0
        for i in range(m):
            #注意一
            fXi = float(np.multiply(alphas,labelMat).T * (dataMatrix * dataMatrix[i,:].T)) + b # 前面得到alpha*yi*xi的總和,再乘以x
            Ei = fXi - float(labelMat[i])
            # 啓發式選擇,即尋找那些誤差過大(正間隔和否間隔)且在(0,C)範圍內的alpha進行優化
            if ((labelMat[i]*Ei < -toler) and (alphas[i] < C )or\
               ((labelMat[i])*Ei > toler) and (alphas[i] > 0)):#不滿足KKT
                j = seclectJrand(i,m)
                fXj = float(np.multiply(alphas,labelMat).T * (dataMatrix * dataMatrix[j,:].T)) + b
                Ej = fXj - float(labelMat[j])
                alphaIold = alphas[i].copy()
                alphaJold = alphas[j].copy()
                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 = dataMatrix[i,:] * dataMatrix[i,:].T + dataMatrix[j,:] * dataMatrix[j,:].T\
                      - 2.0 * dataMatrix[i,:]*dataMatrix[j,:].T
                if eta <= 0:
                    print("eta<=0")
                    continue
                alphas[j] += labelMat[j] * (Ei -Ej)/eta
                alphas[j] = ClipAlpha(alphas[j],H,L)

                if (abs(alphas[j] -alphaJold) < 0.0001):
                    print("alpha_j變化小,不需要更新")
                    continue

                #注意三
                alphas[i] += labelMat[j] * labelMat[i] * (alphaJold - alphas[j])

                #注意四
                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("第%d次迭代 樣本: %d, alpha優化次數: %d"%(iter_num, i, alphaPairsChanged))

        if(alphaPairsChanged == 0):
            iter_num += 1  #沒必要優化
        else:
            iter_num = 0   #繼續優化
        #注意五:返回的b是實數,alphas是一個[m,1]矩陣
        print("迭代次數: %d" % iter_num)
    return b, alphas

# 計算w
def calcWs(dataMat, labelMat, alphas):
    alphas, dataMat, labelMat = np.array(alphas), np.array(dataMat), np.array(labelMat)
    # np.tile:np.tile(a,(1,2))第一個參數爲Y軸擴大倍數,第二個爲X軸擴大倍數,即原矩陣列數*2
    w = np.dot((np.tile(labelMat.reshape(1, -1).T, (1, 2)) * dataMat).T, alphas)  #得到2*1
    return w.tolist()  # 數組——>列表

def showClassifer(dataMat, labelMat, alphas,w ,b):
    data_plus = []
    data_minus = []
    #注意六 把原始點畫出來,橙色的點對應的標籤是-1,藍色的點對應的標籤是1
    for i in range(len(dataMat)):
        if labelMat[i] > 0:
            data_plus.append(dataMat[i])
        else:
            data_minus.append(dataMat[i])

    data_plus_np = np.array(data_plus)
    data_minus_np = np.array(data_minus)
    plt.scatter(np.transpose(data_plus_np)[0],np.transpose(data_plus_np)[1],s = 30, alpha=0.7)
    plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1], s=30, alpha=0.7)
    #注意七:畫出訓練出來的超平面
    x1 = max(dataMat)[0]
    x2 = min(dataMat)[0]
    a1, a2 = w
    b = float(b)
    a1 = float(a1[0])
    a2 = float(a2[0])
    y1, y2 = (-b - a1 * x1) / a2,(-b - a1 * x2) / a2 # 縱座標max  min
    plt.plot([x1,x2],[y1,y2])
    # 注意八 畫出向量機點,和那些“忽略”的點
    # 帶有紅色圓圈的是支持向量機點即間隔線上的點,帶有黑色的點是間隔線內的點
    for i, alpha in enumerate(alphas): #將一個可遍歷的數據對象組合爲一個索引序列,同時列出數據和數據下標
        if 0.6 > abs(alpha) > 0:
            x, y = dataMat[i]
            plt.scatter([x],[y],s=150,c='none',alpha=0.7,linewidths=1.5,edgecolors='red')
        if 0.6 == abs(alpha):
            x, y = dataMat[i]
            plt.scatter([x],[y],s=150,c='none',alpha=0.7,linewidths=1.5,edgecolors='black')
    plt.show()

2、MyTest

# -*- coding: utf-8 -*-#
import MySVM as svm
x=[[1,8],[3,20],[1,15],[3,35],[5,35],[4,40],[7,80],[6,49],[1.5,25],[3.5,45],[4.5,50],[6.5,15],[5.5,20],[5.8,74],[2.5,5]]
y=[1,1,-1,-1,1,-1,-1,1,-1,-1,-1,1,1,-1,1]
b,alphas = svm.smoSimple(x,y,0.6,0.001,40)
w = svm.calcWs(x,y,alphas)
print(b,'\n',alphas,'\t',w)
svm.showClassifer(x,y,alphas, w, b)

3、結果
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章