ML算法:SVM
SVM
实验目标
学习支持向量机的原理,用两种不同的算法寻找支持向量机的解,并完成比较。
实验原理
对于给定样本集,SVM的任务就是基于样本集在样本空间中确定一个划分超平面,将不同类别的样本分开,即找到w,b使得$y(w^T+b)\ge1$成立,并实现最大化间隔,即最大化$||w||^{-1}$。为求解w的最优解,,由于实验中的数据并不完全是线性可分的,因此采用硬间隔的方法求解存在困难,本次实验分别采用梯度下降法和SMO(序列最小优化算法)来求解。
SVM的任务:
- 梯度下降法
SVM中的梯度下降方法也类似于逻辑回归中的梯度下降原理,根据迭代的值计算损失和梯度更新参数,每次迭代过程中,计算当前的$y*(w^T+b)$的值,将值小于一的样本进行累加计算梯度,根据梯度和学习率更新w和b。
- SMO
SMO算法是John Platt在《Sequential Minimal Optimization: A Fast Algorithm for Training Support Vector Machines》一文中提出的算法,对于解决稀疏数据集上的问题有着很高的效率。已知SVM的对偶问题为:
其中,我们要对$(\alpha_1,\alpha_2,…,\alpha_N)$进行优化,但数据集较大时,直接对N个参数进行优化的效率将很低,因此采用SMO算法求解,SMO的原理是将N个参数的优化问题转化为多个子问题,其中每个子问题只对两个参数进行优化,每次计算并选取一对最优的$(\alpha_i,\alpha_j)$更新对应的$\alpha$值。
实验步骤
梯度下降法
- 损失函数$loss=1/2*w^tw+\sum_{i=1}^mmax{0,1-y_i(wx_i-b)}$
mar=y*(np.dot(X,self.w)+self.b)
lo=0.5*np.sum(np.square(self.w))+np.maximum(0, 1 - mar).sum()
if lo<tol :
break
- 损失函数的梯度$\Delta=\lambda w-\sum{i=1}^mI|wx_i+b<1|*x_iy_i$,根据梯度更新w和b
for i in range(len(y)):
if y[i]*(X[i].dot(self.w)+self.b) <= 1:
self.w -= lr * (-y[i]*X[i].reshape((len(X[i]),1)))
self.b -= lr * -y[i]
SMO
- 预先存储部分数据,提高运算效率
self.m=X.shape[0]
self.alpha=np.zeros((self.m,1))#每次迭代更新
self.err=-self.y.flatten()#每次迭代更新
#以下是为了方便计算loss的值,不需要计算loss值时无需计算,更节省时间和内存
self.kappa_x=self.kappa(self.X,self.X)
self.mulyx=np.outer(self.y,self.y)*self.kappa_x
self.outer_a=np.zeros((self.m,self.m))#每次迭代更新
- 损失函数$\sum _\alpha ^N \alpha _i-1/2*\sum_{i=1}^N\sum_{j=1}^Ny_iy_j\alpha_i\alpha_jx_i^Tx_j$
0.5*(np.sum(self.mulyx*self.outer_a)-np.sum(self.alpha))
- 选取合适的$(\alpha _i,\alpha_j)$
- 统计误差不为0的索引值,有不为零的误差时统计误差
- 没有为零误差时,随机选取$\alpha$
if arrlen> 1:
t=np.argmax(np.abs(self.err-ei))
if t!=i and self.step(t,i)==1:
return 1
arr=np.where((self.alpha!=0 )& (self.alpha!=self.c))[0]
ra=np.roll(arr,np.random.choice(np.arange(self.m)))
for j in ra:
if j!=i and self.step(j,i)==1:
return 1
ra=np.roll(np.arange(self.m), np.random.choice(np.arange(self.m)))
for j in ra:
if j!=i and self.step(j,i)==1:
return 1
- 每次迭代更新数据
if y1 !=y2:#取L,H
t1=max(0,a2-a1)
t2=min(0,a2-a1)+self.c
else:
t1=max(self.c,a1+a2)-self.c
t2=min(self.c,a1+a2)
if t1==t2:
return 0
t3=k11+k22-2*k12
if t3>0:#求解\alpha新值
al2 = a2+y2*(e1-e2)/t3
if al2<t1:
al2=t1
elif al2>t2:
al2=t2
else:
#意外情况,很少出现
self.alpha[i2]=t1
loss1=self.calloss()
self.alpha[i2]=t2
loss2=self.calloss()
self.alpha[i2]=a2
if loss1-loss2>self.eps:
al2=t2
elif loss2-loss1>self.eps:
al2=t1
else:
al2=a2
if al2<=0:
al2=0.0
#截断处理
al2=max(al2,0.0)
al2=min(al2,self.c)
if np.abs(al2-a2)<self.eps*(al2 + a2 + self.eps):
return 0
al1=a1+(a2-al2)*y1*y2
b1 = e1 + y1*(al1 - a1)*k11 + y2*(al2 - a2)*k12 + self.b
b2 = e2 + y1*(al1 - a1)*k12 + y2*(al2 - a2)*k22 + self.b
#根据以上的al1,al2,b1,b2更新参数的值:b,alpha,err
- 迭代
- change:上一轮迭代中发生改变的数目
- entire:是否对全部的参数进行检查
- ep:记录迭代次数,迭代次数到最大时退出循环
while change>0 or entire:
change=0
if entire :
for i in range(self.m):
if self.examine(i):
change+=1
else:
ra=np.where((self.alpha!=0) & (self.alpha!=self.c))[0]
for i in ra:
if self.examine(i):
change+=1
if(ep>self.max_iters):
break
if entire==1:
entire=0
elif change==0:
entire=1
数据处理,预测和分析
本次实验对训练集和测试集按照8:2进行划分:
mu = np.mean(X_norm,0)
sigma = np.std(X_norm,0)
for i in range(X.shape[1]):
X_norm[:,i]=(X_norm[:,i]-mu[i])/sigma[i]
y=np.array(y)
num=np.random.permutation(len(y))
X_norm=X_norm[num]
y=y[num]
a=int(0.2*len(y))
X_test=X_norm[0:a-1,:]
X_train=X_norm[a:,:]
y_test=y[0:a-1]
y_train=y[a:]
预测函数:
m,n=X.shape
##SMO: p=np.dot(self.kappa(X, self.X),self.alpha*self.y) - self.b
##GD: p=np.dot(X,self.w)+self.b
for i in range(m):
ans=np.sign(p[i])
res.append(ans)
损失曲线:
loss=model2.fit(X_train,y_train)
x = np.arange(1,len(loss)+1)
plt.plot(x,loss)
plt.xlabel(u"times")
plt.ylabel(u"loss-value")
plt.show()
数据分析
pred2 = model.predict(X_test)
m = len(y_test)
for i in range(m):
if(y_test[i]>0 and pred2[i]>0):
sum+=1
if(y_test[i]<0 and pred2[i]<0):
sum+=1
print(sum/m) #SVM预测的准确率
for i in range(m):
if pred1[i]==pred2[i]:
i+=1
print(i/m) #SVM1,SVM2的预测相似率
实验结论和分析
梯度下降法
参数解释:
X,训练集X
y,训练集
lr,学习率,默认为0.001
max_iters,最大迭代次数,默认为1000
tol,最小误差, 0.0001
训练一次的损失曲线如下所示(数据规模为10000,维度为20,训练时间为41.2s),初始的loss值较大是训练时预设了w的值为随机生成np.random.randn(col,1)
.
准确率:96.1%
可以看出梯度下降的收敛效果较好,在训练次数较低时损失函数显著降低,得到的结果准确率也较高。
而当学习率变大(改为lr=0.1)时,损失函数会发生震荡,导致预测的准确率降低
SMO
参数解释:
- dim:数据的维度
- c:松弛变量,默认为1.0
- eps:容错率,默认为0.01
- max_iters:最大迭代次数,默认为1000
- kappa函数:选取的核函数类型,默认为linear
训练一次的损失曲线(数据规模为2000,20维,训练次数1000,训练时间10s)
梯度下降法和SMO的对比
总体来说,梯度下降法和SMO的准确率均在90%以上,收敛的速度都很快,对结果预测的相似度为97%,两种模型的训练效果均较好,梯度下降的准确率相对更高,训练过程相对更直观;SMO的优势在于每次只对最小的问题进行优化,问题规模小,占用的内存少,对硬件的要求低,对于不同的参数,或者较大或较小的数据集都有较好的结果。