1 SVM最大间距回顾:
-
设SVM找到的决策边界函数为:f(x)=wTx+b(其中w就相当于Logistic回归当中的θ1、θ2…,b就是θ0)。当f(x)=0时就代表样本点在决策边界线上。如下图所示当f(x)<-1时就是负样本,f(x)>1时就是正样本,因此可见f(x)描述了一个样本点到决策边界线上的距离。
-
为什么SVM被称为最大间距分类器呢?
-
首先要搞清楚样本点到决策边界线f(x)=0的距离如何表示,上面我们已经谈到了,f(x)可以用来表示样本点到决策边界线的距离。那么要求间距也就时求min r (r=f(x)),也就是离决策边界线最近的点到决策边界线的距离。但是这样时不合理的,因为若等比的放大w和b,那么f(x)也就会被等比的放大。
-
假设有一个样本点x,x0为x做垂直于决策边界线的直线的交点。r为x到决策边界线的距离,w为垂直于决策边界的向量。
- 又因为我们的目标是寻找最大间距,所以问题就转化为了求max r’。有因为有约束条件y(wTx+b)>1(即f(x)的绝对值大于1),所以问题就转化为了寻找最小的w。
- 下面就可以看到支持向量机为什么又叫最大间隔分类器了。
- 假设我们的决策边界线为下图下左半部分的绿色线,此时我们的p(间距)是非常小的,那么在约束条件下,我们要使得我们的θ(即w)足够的大。这就和我们的目标相冲突了。
- 如果我们是右边的那条边界,此时p(间距)就大多了,因此我们的θ就可以变小了,从而也就达到了我们本来的目标。这就是为什么要找最大间距。
2 寻找最优参数:
- 首先读取数据,绘制图像
import numpy as np
import scipy.io as sio
import scipy.optimize as opt
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
def loadData():
d = sio.loadmat('E:/Data/Ng/Coursera-ML-AndrewNg-Notes-master/code/ex5-bias vs variance/ex5data1.mat')
return map(np.ravel,[d['X'],d['y'],d['Xval'],d['yval'],d['Xtest'],d['ytest']])
if __name__=='__main__':
X, y, Xval, yval, Xtest, ytest = loadData()
X,Xval,Xtest = [np.insert(x.reshape(x.shape[0],1),0,values = np.ones(x.shape[0]),axis=1) for x in (X,Xval,Xtest)] #批量加人为全为1的列
plt.figure(figsize = (10,10),dpi=70)
plt.scatter(X,y,s=20,color = 'red')
plt.show()
- 计算出最优θ
def cost(theta,X,y):
m = X.shape[0]
inner = X @theta - y #(12,1)
square_sum = inner.T @inner
cost = square_sum/(2*m)
return cost
def regularize_cost(theta,X,y,l):
m=X.shape[0]
reg_term = np.power(theta[1:],2).sum()*(l/(2*m))
return cost(theta,X,y)+reg_term
def gradient(theta,X,y):
m=X.shape[0]
inner = X.T @ (X @ theta - y)
return inner/m
def regularize_gradient(theta,X,y,l=1):
m=X.shape[0]
theta_ = theta.copy()
theta_[0]=0
reg_term = theta_*(l/m)
return reg_term + gradient(theta,X,y)
def linear_regression_np(X,y,l):
m = X.shape[1]
theta = np.ones(m)
res = opt.minimize(fun=regularize_cost
,x0=theta,args=(X,y,l)
,method='TNC'
,jac=regularize_gradient
,options={'disp': True})
return res
if __name__ == '__main__':
theta = np.ones(X.shape[0])
final_theta = linear_regression_np(X, y, l=0).get('x')
final_theta
Y=final_theta[0]+final_theta[1]*X[:,1]
#plt.figure(figsize=(10,10),dpi=70)
plt.scatter(X[:,1],y,s=30,color='red')
plt.plot(X[:,1],Y)
plt.show()
结果如下:
可见拟合的不是很好
- 画出训练集和交叉验证集的代价函数曲线
training_cost = [] #初始化一个列表
cv_cost=[]
for i in range(1,X.shape[0]+1): #样本量逐个增加
res = linear_regression_np(X[:i,:], y[:i], l=0) #获取最优θ
train_cost = regularize_cost(res.x,X[:i,:],y[:i],l=0) #计算训练集代价函数
cvcost = regularize_cost(res.x,Xval,yval,l=0) #计算交叉验证集训练函数
training_cost.append(train_cost)
cv_cost.append(cvcost)
plt.plot(np.arange(11+1),training_cost,color='blue',label='training cost')
plt.plot(np.arange(11+1),cv_cost,color='red',label='cv cost')
plt.legend(loc=1)
plt.show()
结果如下:
从下图可以看见,随着样本量的增加,交叉验证集上的代价值不断的下降,训练集的代价函数不断的上升。但是没有相交,存在着一个空隙,即欠拟合。
- 应对欠拟合,我们可以利用增加多项式;增加特征数;降低lambda值的方式来应对。
这里我们使用的是增加高次项。
def poly_features(x,power,as_ndarray=False):
df = {'f{}'.format(i): np.power(x,i)
for i range(1,power+1)}
df = pd.DataFrame(df)
if as_ndarray:
return np.array(df)
else:
return df
def normalize(data):
return data.apply(lambda columns: (columns - columns.mean())/columns.std())
def prepare_poly_features(*args,power):
"""用来咱批量增加高次项
Params:
args:训练集/交叉验证集/测试集
power:最高次方
return:
处理完毕的数据"""
def prepare(x,power):
df = poly_features(x,power=power)
df = normalize(df).values
return np.insert(df,0,np.ones(df.shape[0]),axis=1)
return [prepare(x) for x in args]
if __name__ == '__main__':
X_,X_test,X_val = prepare_poly_data(X,Xtest,Xval,power=8)
X_[:3,:]
结果为:
- 接下来重新绘制
def plot_learning_curve(X, y, Xval, yval, l=0):
training_cost, cv_cost = [], []
m = X.shape[0]
for i in range(1, m + 1):
# regularization applies here for fitting parameters
res = linear_regression_np(X[:i, :], y[:i], l=l)
# remember, when you compute the cost here, you are computing
# non-regularized cost. Regularization is used to fit parameters only
tc = cost(res.x, X[:i, :], y[:i])
cv = cost(res.x, Xval, yval)
training_cost.append(tc)
cv_cost.append(cv)
plt.plot(np.arange(1, m + 1), training_cost, label='training cost')
plt.plot(np.arange(1, m + 1), cv_cost, label='cv cost')
plt.legend(loc=1)
if __name___ == '__main__':
plot_learning_curve(X_poly, y, Xval_poly, yval, l=0)
plt.show()
可见训练集的代价函数总为1,不够真实,那么可能是过拟合了。
- 尝试λ=1
plot_learning_curve(X_poly, y, Xval_poly, yval, l=1)
plt.show()
训练集的代价函数提高了一点,但是还是太低
- 再试试λ=100
plot_learning_curve(X_poly, y, Xval_poly, yval, l=100)
plt.show()
这下欠拟合了
- 找到最佳的λ:
l_candidate = [0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10] #逐个尝试
training_cost, cv_cost = [], []
for l in l_candidate:
res = linear_regression_np(X_poly, y, l)
tc = cost(res.x, X_poly, y)
cv = cost(res.x, Xval_poly, yval)
training_cost.append(tc)
cv_cost.append(cv)
plt.plot(l_candidate, training_cost, label='training')
plt.plot(l_candidate, cv_cost, label='cross validation')
plt.legend(loc=2)
plt.xlabel('lambda')
plt.ylabel('cost')
plt.show()
l_candidate[np.argmin(cv_cost)] #找到交叉验证集当中代价函数最小的那个λ的索引
- 看看那个比较好
for l in l_candidate:
theta = linear_regression_np(X_poly, y, l).x
print('test cost(l={}) = {}'.format(l, cost(theta, Xtest_poly, ytest)))
可见λ=0.3时最小
3 总结:
寻找合适λ可以先试着画几个图,然后再确定值来依此遍历,然后找出使得代价函数最小的那个λ,然后再看测试集,找到测试集当中最小的λ。