天天看點

吳恩達機器學習課後習題(支援向量機)

一、支援向量機

支援向量機(SVM)為一種機器學習方法,在邏輯回歸的基礎上修改代價公式h(x)為coost(X*theta.T),可用于畫決策曲線。

二、實作SVM

1)線性SVM

導入資料包。

import numpy as np
import pandas as pd
from scipy.io import loadmat
import matplotlib.pyplot as plt
           

先實驗資料集ex6data1.mat,此資料集X每個訓練樣本有兩個特征X1,X2,y為1/0。

raw_data = loadmat("E:\\Pycharm\\workspace\\ex_Andrew\\ex6_Andrew\\ex6data1.mat")
data = pd.DataFrame(raw_data.get('X'),columns=['X1','X2']) #data為資料集中的X,兩列設為X1,X2
data['y'] = raw_data.get('y') #data設為50行三列的資料集
           

設計畫圖函數,畫出ex6data1的圖像,此圖像可以線性分割,但有一個特殊的資料點。

def plot_init_data(data,fig,ax): #繪制散點圖,有一個特殊樣本資料
    positive = data[data['y'].isin([1])] #資料集中y等于1的行
    negative = data[data['y'].isin([0])] #資料集中y等于0的行
    ax.scatter(positive['X1'],positive['X2'],s=50,marker='x',label='positive')
    ax.scatter(negative['X1'],negative['X2'],s=50,marker='o',label='negative')
    ax.legend()

fig,ax = plt.subplots(figsize=(8,6))
plot_init_data(data,fig,ax) #畫出初始資料散點圖
           

使用支援向量機,直接調用python包中svm.LinearSVC函數,C的初始值設為1時能最大程度上拟合,C設為100時,會受到特殊點影響。

from sklearn import svm
svc = svm.LinearSVC(C=1, loss='hinge', max_iter=10000) #max_iter應設的更高些,不會出bug
#設定C等于100,會得到不太拟合的線,向特殊點出傾斜
svc.fit(data[['X1','X2']],data['y']) #fit函數需要設定在score之前,計算訓練集X的均值,方差,最大值,最小值,這些訓練集X固有的屬性
svc.score(data[['X1','X2']],data['y']) #資料集的得分為0.9803921568627451
           

設計計算決策邊界函數,傳回X1,X2兩點用于繪制散點圖,決策函數邊界根據資料集中兩個特征的邊界決定。

svc.decision_function函數用于計算樣本點到超平面的距離。

在之前的圖上畫出線性邊界,實作支援向量機進行線性分割。

def find_decision_boundary(svc,x1min,x1max,x2min,x2max,diff):
    x1 = np.linspace(x1min,x1max,1000)
    x2 = np.linspace(x2min,x2max,1000)
    cordinates = [(x,y) for x in x1 for y in x2] #cordinates為(x,y)的元組,變為1000*1000個元組,而非對應1000個元組
    x_cord,y_cord = zip(*cordinates) #加*,解壓為二維元組,x_cord,y_cord分别有1000*1000個資料
    c_val = pd.DataFrame({'x1':x_cord,'x2':y_cord}) #c_val中的x1,x2資料量翻倍
    c_val['cval'] = svc.decision_function(c_val[['x1', 'x2']]) #計算樣本點到分割超平面的函數距離,指派給cval列
    decision = c_val[np.abs(c_val['cval']) < diff] #篩選資料大小合适地資料
    return decision.x1,decision.x2

x1, x2 = find_decision_boundary(svc, 0, 4, 1.5, 5, 2 * 10**-3)
ax.scatter(x1,x2,c='r',label='Boundary',s=10) #增加散點類型決策邊界
           

2)高斯核心SVM

設計高斯核心函數,參數為x1,x2(資料集和樣本點)、sigma。可以直接使用x1,x2的數組調用高斯函數,得到高斯函數值fi。

def gaussain_kernel(x1,x2,sigma): #高斯核心函數
    return np.exp(-np.sum((x1-x2)**2)/(2*sigma**2))
           

處理資料集ex6data2.mat,此資料集有兩個特折X1,X2,輸出為y,畫出其圖像,發現不适合使用線性拟合,使用高斯核心對其進行分類。

raw_data = loadmat("E:\\Pycharm\\workspace\\ex_Andrew\\ex6_Andrew\\ex6data2.mat")
data = pd.DataFrame(raw_data['X'],columns=['X1','X2'])
data['y'] = raw_data['y']

fig,ax = plt.subplots(figsize=(8,6))
plot_init_data(data,fig,ax)
           

使用python工具包中的SVC函數直接得到svc,C設為100,不需要自己定義高斯核心,畫出圖像後得到非線性的高斯核心的決策邊界,可以很好拟合資料。

svc = svm.SVC(C=100,gamma=10,probability=True)
svc.fit(data[['X1','X2']],data['y'])
svc.score(data[['X1','X2']],data['y']) #高斯核心的得分為0.9698725376593279

x1,x2 = find_decision_boundary(svc,0,1,0.4,1,0.01) #svc使用的是高斯核心
ax.scatter(x1,x2,s=10)
           

3)選擇高斯核心的參數問題

如何選擇C,sigma(gamma)的值?(此處sigma與gamma代表的含義一樣)

處理資料ex6data3.mat,其中分為訓練集與測試集,有X1,X2兩個特征和輸出值y,并畫出圖像。

raw_data = loadmat("E:\\Pycharm\\workspace\\ex_Andrew\\ex6_Andrew\\ex6data3.mat")
X = raw_data['X']
Xval = raw_data['Xval']
y = raw_data['y'].ravel()
yval = raw_data['yval'].ravel()

fig,ax = plt.subplots(figsize=(8,6))
data = pd.DataFrame(raw_data['X'],columns=['X1','X2'])
data['y'] = raw_data['y']
plot_init_data(data,fig,ax)
           

初始化C與gamma參數數值,測試選擇最合适的參數值。

C_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]
gamma_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]
best_score = 0
best_params = {'C':None,'gamma':None}
           

計算C與gamma分别為數組中值時的svc得分。

print("計算篩選高斯核心合适的C和gamma:")
for C in C_values:
    for gamma in gamma_values:
        svc = svm.SVC(C=C,gamma=gamma)
        svc.fit(X,y)
        score = svc.score(Xval,yval)
        if (score>best_score):
            best_score = score
            best_params['C'] = C
            best_params['gamma'] = gamma

print("best_score,best_params:",best_score,best_params)
           

使用得到的最合适的C與gamma,對測試集調用支援向量機,畫出決策曲線。

svc = svm.SVC(C=best_params['C'],gamma=best_params['gamma'])
svc.fit(X,y)
x1,x2 = find_decision_boundary(svc,-0.6, 0.3, -0.7, 0.6, 0.005)
ax.scatter(x1,x2,s=10)
plt.show()
           

4)使用支援向量機分類垃圾郵件問題

處理兩個資料集問價spam,分為訓練集與測試集,用于訓練分辨垃圾郵件的SVM。

調用SVC函數,并計算訓練集和測試集分别的計算精确率。

spam_train = loadmat("E:\\Pycharm\\workspace\\ex_Andrew\\ex6_Andrew\\spamTrain.mat")
spam_test = loadmat("E:\\Pycharm\\workspace\\ex_Andrew\\ex6_Andrew\\spamTest.mat")
X = spam_train['X'] #(4000,1899)
Xtest = spam_test['Xtest'] #(1000,1899)
y = spam_train['y'].ravel() #(4000,)
ytest = spam_test['ytest'].ravel() #(1000,)
svc = svm.SVC()
svc.fit(X,y)
print('Training accuracy = {0}%'.format(np.round(svc.score(X, y) * 100, 2)))
print('Test accuracy = {0}%'.format(np.round(svc.score(Xtest, ytest) * 100, 2)))
           

計算出樣本點到超平面的距離,并提取出合适距離的資料項(篩選距離可自己定,此處為0)。

使用voc資料樣本(1899行)提取出分辨垃圾郵件的關鍵字。

kw = np.eye(1899) #1899階機關矩陣
spam_val = pd.DataFrame({'idx':range(1899)}) #自定義(1899,1)的等差數列,0-1898
spam_val['isspam'] = svc.decision_function(kw) #計算樣本點到分割超平面的函數距離
decision = spam_val[spam_val['isspam']>0] #提取出距離大于0的資料

voc = pd.read_csv("E:\\Pycharm\\workspace\\ex_Andrew\\ex6_Andrew\\vocab.txt", header=None, names=['idx', 'voc'], sep = '\t')
spamvoc = voc.loc[list(decision['idx'])]
print(spamvoc)
           

三、運作結果

1)線性SVM

線性支援向量機,得到線性分割曲線,若改變C的值(1->100),直線向特殊點方向傾斜。

吳恩達機器學習課後習題(支援向量機)

2)高斯核心SVM

使用高斯核心解決非線性問題。

吳恩達機器學習課後習題(支援向量機)

對資料集3首先進行篩選參數C與sigma後畫出曲線。

吳恩達機器學習課後習題(支援向量機)

3)訓練SVM實作垃圾郵件分類

使用資料集訓練出垃圾郵件可能的關鍵字,計算出與超平面的距離後選擇垃圾郵件中關鍵字頻率,并列印出垃圾郵件常用的關鍵字。

吳恩達機器學習課後習題(支援向量機)
吳恩達機器學習課後習題(支援向量機)