天天看点

吴恩达机器学习课后习题(支持向量机)

一、支持向量机

支持向量机(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实现垃圾邮件分类

使用数据集训练出垃圾邮件可能的关键字,计算出与超平面的距离后选择垃圾邮件中关键字频率,并打印出垃圾邮件常用的关键字。

吴恩达机器学习课后习题(支持向量机)
吴恩达机器学习课后习题(支持向量机)