机器学习——SMO算法推导与实践

news2024/12/30 2:05:02

一、 硬间隔-SMO算法推导

明天再说,啊。。。。感觉天空明朗了很多,即使现在已经很晚了
还是要打开柯南,看看电视,等待天气预报所说的台风天吧!

一时之间,忽然失去了用markdown语法写下推导过程的勇气。。。以上只是自己在线性可分的情况下,推导的smo算法但实际书本上给出的smo算法,是增加了软间隔后的smo算法
以上只是理解版本的推导,但实际要用在程序上理解,绝不能这么不严谨
是的,我现在终于理解为什么为什么为什么课本要列出那么多奇奇怪怪的符号
那都是非常简便的表达方式!
看了自己手动推的乱七八糟SMO,再看看人家推导的!SMO保姆级教学
我终于发现自己推导的漏洞百出
哭了,哭着重新在草稿纸继续推导吧~

最后拉格朗日对偶问题,是要求解
M A X λ L ( λ ) = ∑ λ i − ∑ i = 1 n ∑ j = 1 n λ i λ j x i x j y i y j 2 MAX_λL(λ)=∑λ_i-\frac{∑_{i=1}^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j}{2} MAXλL(λ)=λi2i=1nj=1nλiλjxixjyiyj
即,通过调整λ值,求解L函数极大值

M a x : L Max: L Max:L可以转化为求 M i n : − L Min:- L Min:L,即求解 M i n λ L ( λ ) = ∑ i = 1 n ∑ j = 1 n λ i λ j x i x j y i y j 2 − ∑ λ i Min_λL(λ)=\frac{∑_{i=1}^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j}{2}-∑λ_i MinλL(λ)=2i=1nj=1nλiλjxixjyiyjλi

接下来,采用SMO算法,求解 M i n : L Min: L Min:L
①使最终数据符合KKT条件:

  • λ g ( w , b ) ≤ 0 , 且要保证 λ ≥ 0 ,因此 K K T 条件的判断具体分为以下两种 λg(w,b)≤0,且要保证λ≥0,因此KKT条件的判断具体分为以下两种 λg(w,b)0,且要保证λ0,因此KKT条件的判断具体分为以下两种
    • λ i > 0 时, g ( w , b ) = 0 ,即 1 − y i ∗ ( W x i + b ) = 0 λ_i>0时,g(w,b)=0,即1-y_i*(Wx_i+b)=0 λi0时,g(w,b)=0,即1yi(Wxi+b)=0
    • λ i = 0 时, g ( w , b ) ≤ 0 ,即 1 − y i ∗ ( W x i + b ) ≤ 0 λ_i = 0时,g(w,b)≤0,即1-y_i*(Wx_i+b)≤0 λi=0时,g(w,b)0,即1yi(Wxi+b)0

②λ要始终满足 Σ λ i y i = 0 Σλ_iyi=0 Σλiyi=0

SMO算法思想:迭代+贪心

由于每条数据都对应一个λ值,即 x i , y i , λ i x_i,y_i,λ_i xi,yi,λi,因此假设有n条数据,就有n个λ,直接求导非常困难

SMO算法提出:每次迭代2个λ,确保这2个λ都在自己能力范围内,都尽可能使L(λ)函数值达到最优,且始终保持 Σ λ i y i = 0 Σλ_iyi=0 Σλiyi=0

这就像是贪心算法,局部最优,最终全局最优
简单点:每个人都拼尽全力让社会更美好,那么社会才会达到最美好
每道题都拿最高分,你就是rolling king or rolling queen
SMO思想,甚至还有先富带后富的先进理念,我无法同时让λ达到状态,但我可以每次2个,既可以保证 Σ λ i y i = 0 Σλ_iyi=0 Σλiyi=0,还可以逐次使L达到最优

1. 算法步骤总览

明确SMO的迭代+贪心的思想后,就可以简化为以下几个步骤:

①未知参数赋初值:
②选择2个迭代λ(用λ_1,λ_2分别代表选中的2个λ)
③求解迭代后的λ,并判断是否满足λ≥0条件
④计算当前的 W n e w , b n e w W_{new},b_{new} Wnew,bnew
⑤若全部满足KKT条件,或达到迭代次数,则停止SMO算法

看似简单的步骤,实则难于上青天

①未知参数赋初值

  • λ:λ_i全赋值为0,这样即可保证 Σ λ i y i = 0 Σλ_iyi=0 Σλiyi=0
  • W: W = ∑ λ i x i ∗ y i ,因此 W 也全为 0 W=∑λ_ix_i*y_i,因此W也全为0 W=λixiyi,因此W也全为0
  • b:b没有条件限制要求,可以直接赋值为0

【需要注意:λ的个数为n,表示n条数据就有n个λ,W的个数为影响因素的个数(即有m种x,就有m个W)】
例如性别、年龄、收入这三个影响因素x,决定幸福感y,那么W就有3个

②选择2个迭代λ(用λ_1,λ_2分别代表选中的2个λ)

  • λ1的选择方式:选择最偏离KKT条件的λ

如何判断偏离KKT条件的程度?

  • λ g ( w , b ) ≤ 0 , 且要保证 λ ≥ 0 ,因此 K K T 条件的判断具体分为以下两种 λg(w,b)≤0,且要保证λ≥0,因此KKT条件的判断具体分为以下两种 λg(w,b)0,且要保证λ0,因此KKT条件的判断具体分为以下两种
    • λ i > 0 时, g ( w , b ) = 0 ,即 1 − y i ∗ ( W x i + b ) = 0 λ_i>0时,g(w,b)=0,即1-y_i*(Wx_i+b)=0 λi0时,g(w,b)=0,即1yi(Wxi+b)=0

    • λ i = 0 时, g ( w , b ) ≤ 0 ,即 1 − y i ∗ ( W x i + b ) ≤ 0 λ_i = 0时,g(w,b)≤0,即1-y_i*(Wx_i+b)≤0 λi=0时,g(w,b)0,即1yi(Wxi+b)0

    • 首先是违反KKT条件,其次违反程度通过g(w,b)值来衡量,即选择max:g(w,b)下的 λ i λ_i λi
      但建议还是将违反KKT条件的程度,降序排列得到一个违反KKT条件的g(w,b)降序列表

    • 这样,如果g(w,b)的最大值对应的λ1不满足迭代条件时,还可以退而求其次,选g(w,b)第二大对应的λ1

    • 【判断1】若选不到λ1,说明所有λ都满足KKT条件,可停止SMO算法

  • λ2的选择方式:选择能使λ2改变最大的λ
    • 公式推导出的 λ 2 n e w = λ 2 o l d + y i ∗ ( E 1 − E 2 ) K 11 + K 22 − 2 K 12 λ2_{new}=λ2_{old}+\frac{y_i*(E1-E2)}{K11+K22-2K12} λ2new=λ2old+K11+K222K12yi(E1E2) ,当 ∣ E 1 − E 2 ∣ 大 |E1-E2|大 E1E2∣说明这两条对应的数据差距比较大【后续再公式推导】
    • 【判断1】若当前|E1-E2|最大的λ2不满足条件要求,则重选λ,即退而求其次选|E1-E2|第二大对应的λ2
    • 【判断2】若选不到λ2,则重新选λ1,再选λ2
      • 重选λ1,是指选下一个偏离KKT条件的λ,而不是最偏离了【降序选择λ1】

λ 2 n e w λ2_{new} λ2new的公式推导:

求解 M i n λ L ( λ ) = ∑ i = 1 n ∑ j = 1 n λ i λ j x i x j y i y j 2 − ∑ λ i Min_λL(λ)=\frac{∑_{i=1}^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j}{2}-∑λ_i MinλL(λ)=2i=1nj=1nλiλjxixjyiyjλi,已知所有λ均有初值

那么单独挑出λ1、λ2作为未知量来求L极值,并迭代,则需将其余的λ3…λn都当作常数

  • 未知量:λ1、λ2
  • 常数:λ3…λn
  • 实际λ1、λ2本身是有旧值的,但我们要求解的是它们的新值,因此当作未知量来求导

step1: 因此先分离出L(λ)里含λ1和λ2的项,再进行求导:

第一部分: 1 2 ∑ i = 1 n ∑ j = 1 n λ i λ j x i x j y i y j \frac{1}{2}∑_{i=1}^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j 21i=1nj=1nλiλjxixjyiyj
= 1 2 [ λ 1 2 y 1 2 x 1 . x 1 + λ 2 2 y 2 2 x 2 . x 2 + 2 λ 1 λ 1 y 1 y 2 x 1 . x 2 + 2 ( λ 1 y 1 x 1 + λ 2 y 2 x 2 ) ∑ i = 3 n λ i y i x i + ∑ i = 3 n ∑ j = 3 n λ i λ j x i x j y i y j ] =\frac{1}{2}[ λ_1^2y_1^2x_1.x_1+λ_2^2y_2^2x_2.x_2+2λ_1λ_1y_1y_2x_1.x_2+2(λ_1y_1x_1+λ_2y_2x_2)∑^n_{i=3}λ_iy_ix_i+∑_{i=3}^n∑_{j=3}^nλ_iλ_jx_ix_jy_iy_j] =21[λ12y12x1.x1+λ22y22x2.x2+2λ1λ1y1y2x1.x2+2(λ1y1x1+λ2y2x2)i=3nλiyixi+i=3nj=3nλiλjxixjyiyj]

注: x 1. x 1 x1.x1 x1.x1中间的点,表示点积,因为 x i x_i xi本身是向量,向量相乘即为点积运算

第二部分: ∑ λ i = λ 1 + λ 2 + ∑ i = 3 n λ i ∑λ_i=λ_1+λ_2+∑^n_{i=3}λ_i λi=λ1+λ2+i=3nλi

step2: 后续求导常数无用则删: 将第一、二部分常数删去后,再拼接成只含迭代变量的Q(λ1,λ2)函数

Q = 1 2 [ λ 1 2 y 1 2 x 1 . x 1 + λ 2 2 y 2 2 x 2 . x 2 + 2 λ 1 λ 1 y 1 y 2 x 1 . x 2 + 2 ( λ 1 y 1 x 1 + λ 2 y 2 x 2 ) ∑ i = 3 n λ i y i x i ] + λ 1 + λ 2 Q = \frac{1}{2}[ λ_1^2y_1^2x_1.x_1+λ_2^2y_2^2x_2.x_2+2λ_1λ_1y_1y_2x_1.x_2+2(λ_1y_1x_1+λ_2y_2x_2)∑^n_{i=3}λ_iy_ix_i]+λ_1+λ_2 Q=21[λ12y12x1.x1+λ22y22x2.x2+2λ1λ1y1y2x1.x2+2(λ1y1x1+λ2y2x2)i=3nλiyixi]+λ1+λ2

step3: 将Q函数变为只含一个未知λ,再求导

Σ λ i y i = 0 Σλ_iyi=0 Σλiyi=0可得: λ 1 y 1 + λ 2 y 2 = − ∑ i = 3 n λ i = − λ 1 o l d y 1 − λ 2 o l d y 2 λ_1y_1+λ_2y_2=-∑^n_{i=3}λ_i=-λ_{1old}y_1-λ_{2old}y_2 λ1y1+λ2y2=i=3nλi=λ1oldy1λ2oldy2

这里的λ1、λ2表示未知量, λ 1 o l d , λ 2 o l d λ_{1old},λ_{2old} λ1old,λ2old则表示它们的旧值

∑ i = 3 n λ i = λ 1 o l d y 1 + λ 2 o l d y 2 ,可视为常数 A ,方便简化计算 ∑^n_{i=3}λ_i=λ_{1old}y_1+λ_{2old}y_2,可视为常数 A,方便简化计算 i=3nλi=λ1oldy1+λ2oldy2,可视为常数A,方便简化计算

λ 1 y 1 + λ 2 y 2 = − A λ_1y_1+λ_2y_2=-A λ1y1+λ2y2=A两边乘以y1,并移项可得:λ1 = -λ2y1y2-Ay1

λ 1 = − λ 2 y 1 y 2 − A y 1 代入 Q 函数 λ1 = -λ2y1y2-Ay1代入Q函数 λ1=λ2y1y2Ay1代入Q函数,即可得到只含未知量λ2的Q函数,并对λ2求导后,整理可得

Q λ 2 ′ = h a h a h a h h a h a h a h h a h a h 放弃 m a r k d o w n 语法写推导,太长辣救命 Q'_{λ2} = hahahahhahahahhahah放弃markdown语法写推导,太长辣救命 Qλ2=hahahahhahahahhahah放弃markdown语法写推导,太长辣救命

然后再将 A = ∑ i = 3 n λ i A=∑^n_{i=3}λ_i A=i=3nλi代入 Q λ 2 ′ Q'_{λ2} Qλ2,并令 W x i + b − y i = E i ,表示预测值与真实值的差距 Wx_i+b-y_i = E_i,表示预测值与真实值的差距 Wxi+byi=Ei,表示预测值与真实值的差距

则令 Q λ 2 ′ = 0 Q'_{λ2} =0 Qλ2=0,最终整理出 λ 2 = λ 2 o l d + y 2 ∗ ( E 1 − E 2 ) x 1. x 1 + x 2. x 2 − 2 x 1. x 2 λ2 = λ_{2old}+\frac{y2*(E1-E2)}{x1.x1+x2.x2-2x1.x2} λ2=λ2old+x1.x1+x2.x22x1.x2y2(E1E2)

③求解迭代后的λ,并判断是否满足迭代条件

λ的条件要满足:λ≥0,即求解出的λ1、λ2都要满足≥0的条件

通过上一步可得到 λ 2 = λ 2 o l d + y 2 ∗ ( E 1 − E 2 ) x 1. x 1 + x 2. x 2 − 2 x 1. x 2 λ2 = λ_{2old}+\frac{y2*(E1-E2)}{x1.x1+x2.x2-2x1.x2} λ2=λ2old+x1.x1+x2.x22x1.x2y2(E1E2)

因此可先判断λ2≥0:

  • 如果λ2≥0,即继续往下
  • 如果λ2不满足条件,则返回重选λ2

接着判断λ1,由 λ 1 = − λ 2 y 1 y 2 − A y 1 ≥ 0 λ_1 = -λ_2y_1y_2-Ay_1≥0 λ1=λ2y1y2Ay10可得

  • 当y1y2 = 1时: λ 2 ≤ − A y 1 λ2≤-Ay_1 λ2Ay1
  • 当y1y2 = -1时: λ 2 ≥ A y 1 λ2≥Ay_1 λ2Ay1
  • 如果满足任意一个条件,则继续往下
  • 如果以上两个条件都不满足,则返回重选λ2

④计算当前的 W n e w , b n e w W_{new},b_{new} Wnew,bnew

计算出满足λ≥0的λ1和λ2后,可代入求解最新的 W n e w W_{new} Wnew

W o l d = λ 1 o l d y 1 x 1 + λ 2 o l d y 2 x 2 + ∑ i = 3 n λ i y i x i W_{old} = λ_{1old}y_1x_1+λ_{2old}y_2x_2+∑^n_{i=3}λ_iy_ix_i Wold=λ1oldy1x1+λ2oldy2x2+i=3nλiyixi

W n e w = λ 1 y 1 x 1 + λ 2 y 2 x 2 + ∑ i = 3 n λ i y i x i W_{new} = λ_{1}y_1x_1+λ_{2}y_2x_2+∑^n_{i=3}λ_iy_ix_i Wnew=λ1y1x1+λ2y2x2+i=3nλiyixi

W n e w = W o l d + ( λ 1 − λ 1 o l d ) y 1 x 1 + ( λ 2 − λ 2 o l d ) y 2 x 2 W_{new} = W_{old}+(λ_{1}-λ_{1old})y_1x_1+(λ_{2}-λ_{2old})y_2x_2 Wnew=Wold+(λ1λ1old)y1x1+(λ2λ2old)y2x2

可见,每次W的更新,都只与选择的两个λ有关

b n e w b_{new} bnew,表示两条边界上的b1和b2的中间值

怎么判断是不是边界呢?

其实也就是当λ>0的时候,因此λ>0,则g(w,b)=0,这在之前的拉格朗日乘数法中已证明为支持向量,即边界

因此,则当λ1、λ2均大于0时,根据 g ( w , b ) = 1 − y i ( W n e w x i + b ) = 0 g(w,b) = 1-y_i(W_{new}x_i+b) = 0 g(w,b)=1yi(Wnewxi+b)=0

求出
b 1 n e w = y 1 − W n e w x 1 b_{1new}= y_1-W_{new}x_1 b1new=y1Wnewx1
b 2 n e w = y 2 − W n e w x 2 b_{2new}= y_2-W_{new}x_2 b2new=y2Wnewx2

b n e w = b 1 n e w + b 2 n e w 2 b_{new} = \frac{b_{1new}+b_{2new}}{2} bnew=2b1new+b2new

⑤若全部满足KKT条件,或达到迭代次数,则停止SMO算法

硬间隔情况下的推导比较简单,但实践起来困难重重,并且有些是想不通的
1、 选出违反KKT条件最严重的λ1,再选出|E1-E2|值最大的λ2,但计算出的 λ 1 n e w 、 λ 2 n e w λ1_{new}、λ2_{new} λ1newλ2new并不满足λ≥0的条件,因此需要重新选λ,但要怎么重新选呢?

  • 先重新选λ2:将所有λ的|E1-E2|进行降序排列,再依次选择对应的λ2,直到 λ 1 n e w 、 λ 2 n e w λ1_{new}、λ2_{new} λ1newλ2new都满足λ≥0
  • 如果所有的|E1-E2|>0的λ都选择了个遍,却依然无法满足λ≥0,则说明当前的λ1暂时选不出合适的λ2
    • 则可以重新选择λ1,重新选择的是违反KKT条件程度第二的λ
  • 如果选到了合适的λ2,则在完成λ1、λ2的迭代后,返回重新选λ1、λ2,开始新一轮的迭代。

但在实践过程中,即使数据是线性可分的,可一旦数据量很多的时候,就很难收敛了,并且经常在几个数据里来回震荡,无法完全满足KKT条件的同时还能满足迭代后的λ≥0

于是,为了解决这个问题,采取了限定迭代次数的方法

并且测试时,自己设计了线性关系,哎。。。总之就是
如果只有一个影响因素x,并且成线性关系时,数据量少的情况下(测试10条),是可以完全准确分类的。。。。

但是训练的过程。。。真的好慢好慢好慢啊。。。。只要我多加一个新的线性影响因素,还是10条数据。。。。。就会慢的要死,并且还很难收敛到所有λ都满足KKT条件,只有到10000次迭代次数自动停止后,才结束训练,不过好在10条数据内,2个影响因素,都还是100%的分类准确率
我甚至不敢测试多几条数据。。。。但还是英勇的选择了100条数据进行训练…
果然还是无法完全收敛满足KKT条件,超过迭代次数后停下来的
最终准确率还是降低了…差不多就得了…实际它都没迭代完。。。但数据量实在太大了
在这里插入图片描述

import numpy as np
import pandas as pd
import random
import matplotlib.pyplot as plt
import time
# 获取所需数据:
datas = pd.read_excel('./datas6.xlsx')
important_features = ['推荐分值','专业度','推荐类型']
# datas = pd.read_excel('./datas5.xlsx')
# important_features = ['推荐分值','推荐类型']

datas_1 = datas[important_features].head(100)
Y = datas_1['推荐类型']
X = datas_1.drop('推荐类型',axis=1)
X_features = X.columns
Y_features = '推荐类型'
rows,columns = datas_1.shape

Y=Y.where(Y!="高推荐",other=1) # 高推荐设置为1
Y=Y.where(Y!="低推荐",other=-1) # 低推荐设置为-1

class SMO():
    def __init__(self,X,Y):
        self.X = X
        self.Y = Y
        self.m = X.shape[1]
        self.n = Y.shape[0]
        self.lamb = np.zeros(self.n)
        self.b = 0
        self.W0 = np.zeros(self.m)
        self.times = 10000
        self.Finish = False
        self.break_kkt = {}
        self.break_kkt_list = []
        self.E = None


    def count_break_KKT(self):
        del self.break_kkt
        self.break_kkt = {}
        self.g = 1-self.Y*((self.W0*self.X).sum(axis=1)+self.b)
        # time.sleep(3)
        for index,g_value in self.g.items():
            a = self.lamb[index]
            if a < 0:
                raise Exception(f"lamb1_new为{lamb1_new},还是小于0")
            elif a == 0 and g_value>0:
                self.break_kkt[index] = abs(g_value)
            elif a > 0 and g_value!=0:
                self.break_kkt[index] = abs(g_value)


    def run(self):
        select_lamb1 = False
        while not self.Finish and self.times>0:
            if len(self.break_kkt_list) == 0:
                self.count_break_KKT()
                self.break_kkt_list = sorted(self.break_kkt.items(), key=lambda d: d[1], reverse=True)
                if len(self.break_kkt_list) == 0:
                    print("已全部满足KKT")
                    break
            # print(f"当前违反KKT条件的:{self.break_kkt_list}")
            # time.sleep(3)
            index1 = self.break_kkt_list.pop(0)[0]
            x1 = self.X.iloc[index1]
            y1 = self.Y[index1]
            lamb1_old = self.lamb[index1]
            self.E = (self.W0 * self.X).sum(axis=1) + self.b - self.Y
            e1 = self.E[index1]
            self.E1E2_all = e1-self.E
            self.E1E2_abs = (e1-self.E).abs()
            self.E1E2_sort = sorted(self.E1E2_abs.items(), key=lambda d: d[1], reverse=True)
            # print(f"当前的E1E2排序:{self.E1E2_sort}")
            # time.sleep(3)
            self.times -= 1
            for j in self.E1E2_sort:
                index2 = j[0]

                x2 = self.X.iloc[index2]
                y2 = self.Y[index2]
                lamb2_old = self.lamb[index2]
                e1_e2 = self.E1E2_all[index2]
                # print(f"选中的第{index1}和第{index2}的E差值为{e1_e2},参数:{self.lamb[index1], self.lamb[index2]}")
                # time.sleep(3)
                if e1_e2 == 0:
                    # print(f"由于两者的E差值为0,因此不进行迭代")
                    select_lamb1 = True
                    break
                temp = sum(x1*x1+x2*x2-2*x1*x2)
                A = -lamb1_old*y1-lamb2_old*y2
                if temp==0:
                    # print("当前的index1和index2的X值一致,可以直接将index2的lamb2_new等同于lamb1_new")
                    # time.sleep(3)
                    continue
                lamb2_new = lamb2_old + e1_e2*y2/temp
                if lamb2_new<0:

                    continue
                elif y1*y2 == 1:
                    if lamb2_new > -A*y1:

                        continue
                elif y1*y2 == -1:
                    if lamb2_new < A*y1:

                        continue
                lamb1_new = -lamb2_new*y1*y2-A*y1

                time.sleep(3)
                self.W0 = self.W0+(lamb1_new-lamb1_old)*y1*x1+(lamb2_new-lamb2_old)*y2*x2
                self.W0 = np.array(self.W0)
                b1_new = y1-sum(self.W0*x1)
                b2_new = y2-sum(self.W0*x2)
                self.b = np.array((b1_new+b2_new)/2)
                self.lamb[index1]=lamb1_new
                self.lamb[index2]=lamb2_new
                print(f"新的lamb:{self.lamb},\nW0为{self.W0},b为{self.b}")

                select_lamb1 = True
                break
            else:
                print(f"可选的E2中:{self.E1E2_sort},没有满足条件的lamb2")

            if select_lamb1:
                select_lamb1 = False
                self.count_break_KKT()
                self.break_kkt_list = sorted(self.break_kkt.items(), key=lambda d: d[1], reverse=True)
                if len(self.break_kkt_list)==0:
                    self.Finish = True
                    print("已全部服从KKT条件")
                    sum_lamb_y = sum(self.lamb*self.Y)
                    print(f"汇总后的值是否为零:{sum_lamb_y}")
                    print(self.lamb)
                    break

        lambs = sorted([(index1,value1) for index1,value1 in enumerate(self.lamb)],key=lambda x:x[1],reverse=True)
        index1 = lambs.pop(0)[0]
        for index2,value2 in enumerate(lambs):
            if self.Y[index1]!=self.Y[index2] and value2!=0:
                print("++++++++++++++++++++++++++++++++++++")
                x1 = self.X.iloc[index1]
                x2 = self.X.iloc[index2]
                print(self.W0*x1)
                print((self.W0*x1).sum(axis=0))
                b1_new = np.array(self.Y[index1]-(self.W0*x1).sum(axis=0))
                b2_new = np.array(self.Y[index2]-(self.W0*x2).sum(axis=0))
                b_new = (b1_new+b2_new)/2
                self.b = b_new
                print("++++++++++++++++++++++++++++++++++++")
                break

        print(b1_new,b2_new)

        self.W0 = np.array([self.W0])
        print("_________________________")
        print(self.W0)
        print(self.X)
        print(self.b)

        print("_________________________")
        z = (self.W0*self.X).sum(axis=1)+self.b
        self.Y_pre = []
        """原本计划是要大于支持向量边界时,也就是大于1,才能分类为1,小于-1才能分类为-1,但实际还是有些点位于<-1,1>之间,导致无法完全实现点全在两侧边界分类的效果,因此直接用中间的分类函数进行分"""
        for i in z:
            if i>=0:
                self.Y_pre.append(1)
            elif i<0:
                self.Y_pre.append(-1)
        print(f"分类准确率为:{round(sum(self.Y_pre==self.Y)/self.Y.shape[0]*100,2)}%")



test = SMO(X,Y)
test.run()

二、 软间隔-对偶问题及smo算法推导

之前要求的对偶问题: M A X λ L ( λ ) = ∑ λ i − ∑ i = 1 n ∑ j = 1 n λ i λ j x i x j y i y j 2 MAX_λL(λ)=∑λ_i-\frac{∑_{i=1}^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j}{2} MAXλL(λ)=λi2i=1nj=1nλiλjxixjyiyj

是基于线性可分的情况下【即硬间隔】,一旦数据并不是严格线性可分的情况下,SVM就会失效
我猜测,极有可能无法应用SMO算法,计算出正确的λ

因此,每条数据都引入一个松弛变量的 ξ i ξ_i ξi

然后每条数据的约束条件就变为 y i ( W x i + b ) ≥ 1 − ξ i y_i(Wx_i+b)≥1-ξ_i yi(Wxi+b)1ξi,且ξ≥0

但引入松弛变量,就要在目标函数上,增加惩罚项

原目标函数 m i n : f = w 2 2 min:f=\frac{w^2}{2} min:f=2w2

增加惩罚项后为 m i n : f = w 2 2 + C ∑ ξ i min:f=\frac{w^2}{2}+C∑ξ_i min:f=2w2+Cξi

这里的C是我们自己设置的惩罚项权重常数

其实我也很困惑:这个惩罚项到底对于目标函数的实现,有什么作用呢?

按我的初步想法是,将惩罚项放入原函数中, w 2 2 + C ∑ ξ i \frac{w^2}{2}+C∑ξ_i 2w2+Cξi 表示,要让 w 2 2 和 C ∑ ξ i \frac{w^2}{2}和C∑ξ_i 2w2Cξi在相互影响相互制约的情况下,尽可能使f(x)达到极小值

那么 m i n : f = w 2 2 + C ∑ ξ i min:f=\frac{w^2}{2}+C∑ξ_i min:f=2w2+Cξi中,如果C比较大,那么松弛变量对目标函数f的影响就会比较大,因此会在训练过程中,偏向于让ξ达到最小,而w就没那么重要了

反之,如果C比较小,则松弛变量ξ对目标函数f的影响就比较小,因此训练过程中会倾向于让w达到最小,松弛变量反而就相对没那么重要。

通常在sklearn中,这个C默认取值为1,但训练出来的结果未必令人满意的,因此要看情况设置C【这部分回头再说】

现在的求解目标为:

  • 目标函数为 m i n : f = w 2 2 + C ∑ ξ i min:f=\frac{w^2}{2}+C∑ξ_i min:f=2w2+Cξi
  • 约束条件为:
    • g ( w , b , ξ ) = 1 − ξ i − y i ( W x i + b ) ≤ 0 g(w,b,ξ) = 1-ξ_i-y_i(Wx_i+b)≤0 g(w,b,ξ)=1ξiyi(Wxi+b)0 ,这是约束条件1
    • ξ ≥ 0 ξ≥0 ξ0,这是约束条件2,转为 − ξ ≤ 0 -ξ≤0 ξ0

根据拉格朗日乘数法,建立函数L:
第一个约束条件,用λ表示对应的拉格朗日乘子
第二个约束条件,用β表示对应的拉格朗日乘子

m i n L ( w , b , ξ , λ , β ) = w 2 2 + C Σ ξ i + Σ λ i [ 1 − ξ i − y i ( W x i + b ) ] − Σ β i ξ i min L(w,b,ξ,λ,β)=\frac{w^2}{2}+CΣξ_i+Σλ_i[1-ξ_i-y_i(Wx_i+b)]-Σβ_iξ_i minL(w,b,ξ,λ,β)=2w2+CΣξi+Σλi[1ξiyi(Wxi+b)]Σβiξi

KKT条件是:

  • λ i [ 1 − ξ i − y i ( W x i + b ) ] = 0 λ_i[1-ξ_i-y_i(Wx_i+b)]=0 λi[1ξiyi(Wxi+b)]=0
    • λ i > 0 时, 1 − ξ i − y i ( W x i + b ) = 0 λ_i>0时, 1-ξ_i-y_i(Wx_i+b)=0 λi>0时,1ξiyi(Wxi+b)=0
    • λ i = 0 时, 1 − ξ i − y i ( W x i + b ) ≤ 0 λ_i=0时, 1-ξ_i-y_i(Wx_i+b)≤0 λi=0时,1ξiyi(Wxi+b)0
  • β i ξ i = 0 β_iξ_i=0 βiξi=0
    • β i > 0 时, ξ i = 0 β_i>0时,ξ_i=0 βi>0时,ξi=0
    • β i = 0 时, ξ i ≥ 0 β_i=0时,ξ_i≥0 βi=0时,ξi0

将拉格朗日函数,变为对偶问题:
m i n L ( w , b , ξ , λ , β ) = w 2 2 + C Σ ξ i + Σ λ i [ 1 − ξ i − y i ( W x i + b ) ] − Σ β i ξ i min L(w,b,ξ,λ,β)=\frac{w^2}{2}+CΣξ_i+Σλ_i[1-ξ_i-y_i(Wx_i+b)]-Σβ_iξ_i minL(w,b,ξ,λ,β)=2w2+CΣξi+Σλi[1ξiyi(Wxi+b)]Σβiξi

由于求极值过程中,是要先w,b,ξ求其极小值,因此可以同之前硬间隔那样推导出对偶函数为

M a x λ , β M i n w , b , ξ L ( w , b , ξ , λ , β ) = w 2 2 + C Σ ξ i + Σ λ i [ 1 − ξ i − y i ( W x i + b ) ] − Σ β i ξ i Max_{λ,β}Min_{w,b,ξ} L(w,b,ξ,λ,β)=\frac{w^2}{2}+CΣξ_i+Σλ_i[1-ξ_i-y_i(Wx_i+b)]-Σβ_iξ_i Maxλ,βMinw,b,ξL(w,b,ξ,λ,β)=2w2+CΣξi+Σλi[1ξiyi(Wxi+b)]Σβiξi

先对w,b,以及每一个ξ_i分别求偏导,得到以下三个条件

  • w = Σ λ i x i y i w = Σλ_ix_iy_i w=Σλixiyi ———①
  • Σ λ i y i = 0 Σλ_iy_i=0 Σλiyi=0 ———②
  • C − λ i − β i = 0 C-λ_i-β_i = 0 Cλiβi=0 ———③

C − Σ λ i − Σ β i = 0 C-Σλ_i-Σβ_i = 0 CΣλiΣβi=0转为 Σ β i = C − Σ λ i Σβ_i =C-Σλ_i Σβi=CΣλi,并结合①,代入对偶函数,整理得到

M a x ( λ ) = Σ λ i − 1 2 Σ Σ λ i λ j y i y j x i x j Max(λ) =Σλ_i-\frac{1}{2}ΣΣλ_iλ_jy_iy_jx_ix_j Max(λ)=Σλi21ΣΣλiλjyiyjxixj,这会发现,与之前硬间隔的对偶函数是一样的

松弛变量ξ及对应的拉格朗日乘子β,对目标求解并无直接影响。

但KKT条件是对SMO迭代时,有条件上的额外限制

KKT条件是:

  • λ i [ 1 − ξ i − y i ( W x i + b ) ] = 0 λ_i[1-ξ_i-y_i(Wx_i+b)]=0 λi[1ξiyi(Wxi+b)]=0
    • λ i > 0 时, 1 − ξ i − y i ( W x i + b ) = 0 λ_i>0时, 1-ξ_i-y_i(Wx_i+b)=0 λi>0时,1ξiyi(Wxi+b)=0
    • λ i = 0 时, 1 − ξ i − y i ( W x i + b ) ≤ 0 λ_i=0时, 1-ξ_i-y_i(Wx_i+b)≤0 λi=0时,1ξiyi(Wxi+b)0
    • 总之: λ i ≥ 0 λ_i≥0 λi0————①
  • β i ξ i = 0 β_iξ_i=0 βiξi=0
    • β i = C − λ i > 0 时, ξ i = 0 β_i=C-λ_i>0时,ξ_i=0 βi=Cλi>0时,ξi=0
    • β i = C − λ i = 0 时, ξ i ≥ 0 β_i=C-λ_i=0时,ξ_i≥0 βi=Cλi=0时,ξi0
    • 总之: β i = C − λ i ≥ 0 β_i=C-λ_i≥0 βi=Cλi0——————②
  • 由①②综合得: C ≥ λ i ≥ 0 C≥λ_i≥0 Cλi0

对偶补充条件是:

  • w = Σ λ i x i y i w = Σλ_ix_iy_i w=Σλixiyi
  • Σ λ i y i = 0 Σλ_iy_i=0 Σλiyi=0
  • C − λ i − β i = 0 C-λ_i-β_i = 0 Cλiβi=0

在SMO迭代时的5个步骤里,在第①③④⑤上都有各自对应的调整

①未知参数赋初值:C、λ、w、b

  • C: 可先设置为1
  • λ:λ_i全赋值为0,这样即可保证 Σ λ i y i = 0 Σλ_iyi=0 Σλiyi=0
  • ξ: ξ i 由 C − λ i 得到 ξ_i由C-λ_i得到 ξiCλi得到
  • W: W = ∑ λ i x i ∗ y i ,因此 W 也全为 0 W=∑λ_ix_i*y_i,因此W也全为0 W=λixiyi,因此W也全为0
  • b:b没有条件限制要求,可以直接赋值为0

②选择2个迭代λ(用λ_1,λ_2分别代表选中的2个λ)

  • λ1的选择方式:选择最偏离KKT条件的λ

    • 首先是违反KKT条件,

      • λ i [ 1 − ξ i − y i ( W x i + b ) ] = 0 λ_i[1-ξ_i-y_i(Wx_i+b)]=0 λi[1ξiyi(Wxi+b)]=0
      • λ i > 0 时, 1 − ξ i − y i ( W x i + b ) = 0 λ_i>0时, 1-ξ_i-y_i(Wx_i+b)=0 λi>0时,1ξiyi(Wxi+b)=0
      • λ i = 0 时, 1 − ξ i − y i ( W x i + b ) ≤ 0 λ_i=0时, 1-ξ_i-y_i(Wx_i+b)≤0 λi=0时,1ξiyi(Wxi+b)0
    • 其次违反程度通过 m a x : 1 − ξ − y ( w x + b ) max:1-ξ-y(wx+b) max:1ξy(wx+b)值来衡量,即选择 m a x : 1 − ξ − y ( w x + b ) max:1-ξ-y(wx+b) max:1ξy(wx+b)下的 λ i λ_i λi
      但建议还是将违反KKT条件的程度,降序排列得到一个违反KKT条件的 1 − ξ − y ( w x + b ) 1-ξ-y(wx+b) 1ξy(wx+b)降序列表

    • 这样,如果 1 − ξ − y ( w x + b ) 1-ξ-y(wx+b) 1ξy(wx+b)的最大值对应的λ1不满足迭代条件时,还可以退而求其次,选 1 − ξ − y ( w x + b ) 1-ξ-y(wx+b) 1ξy(wx+b)第二大对应的λ1

    • 【判断1】若选不到λ1,说明所有λ都满足KKT条件,可停止SMO算法

  • λ2的选择方式:选择能使λ2改变最大的λ

    • 公式推导出的 λ 2 n e w = λ 2 o l d + y i ∗ ( E 1 − E 2 ) K 11 + K 22 − 2 K 12 λ2_{new}=λ2_{old}+\frac{y_i*(E1-E2)}{K11+K22-2K12} λ2new=λ2old+K11+K222K12yi(E1E2) ,当 ∣ E 1 − E 2 ∣ 大 |E1-E2|大 E1E2∣说明这两条对应的数据差距比较大【后续再公式推导】
    • 【判断1】若当前|E1-E2|最大的λ2不满足条件要求,则重选λ,即退而求其次选|E1-E2|第二大对应的λ2
    • 【判断2】若选不到λ2,则重新选λ1,再选λ2
      • 重选λ1,是指选下一个偏离KKT条件的λ,而不是最偏离了【降序选择λ1】

最终整理出 λ 2 = λ 2 o l d + y 2 ∗ ( E 1 − E 2 ) x 1. x 1 + x 2. x 2 − 2 x 1. x 2 λ2 = λ_{2old}+\frac{y2*(E1-E2)}{x1.x1+x2.x2-2x1.x2} λ2=λ2old+x1.x1+x2.x22x1.x2y2(E1E2)

③求解迭代后的λ,并判断是否满足迭代条件

条件1:λ≥0,即求解出的λ1、λ2都要满足 C ≥ λ ≥ 0 C≥λ≥0 Cλ0的条件

通过上一步可得到 λ 2 = λ 2 o l d + y 2 ∗ ( E 1 − E 2 ) x 1. x 1 + x 2. x 2 − 2 x 1. x 2 λ2 = λ_{2old}+\frac{y2*(E1-E2)}{x1.x1+x2.x2-2x1.x2} λ2=λ2old+x1.x1+x2.x22x1.x2y2(E1E2)

因此可先判断 0 ≤ λ 2 ≤ C 0≤λ2≤C 0λ2C ————①

接着判断λ1,由 C ≥ λ 1 = − λ 2 y 1 y 2 − A y 1 ≥ 0 C≥λ_1 = -λ_2y_1y_2-Ay_1≥0 Cλ1=λ2y1y2Ay10可得

  • 当 y 1 y 2 = 1 时: C ≥ − λ 2 − A y 1 ≥ 0 当y1y2 = 1时:C≥ -λ_2-Ay_1≥0 y1y2=1时:Cλ2Ay10
    • C + A y 1 ≤ λ 2 ≤ − A y 1 C+Ay_1≤λ_2≤-Ay_1 C+Ay1λ2Ay1 结合上式①得λ2最终的取值范围:
    • m a x ( 0 , C + A y 1 ) ≤ λ 2 ≤ m i n ( C , − A y 1 ) max(0,C+Ay_1)≤λ_2≤min(C,-Ay_1) max(0,C+Ay1)λ2min(C,Ay1)
    • 简化为 L ≤ λ 2 ≤ H L≤λ_2≤H Lλ2H
  • 当 y 1 y 2 = − 1 时: C + A y 1 ≥ λ 2 ≥ A y 1 当y1y2 = -1时:C+Ay_1≥λ2≥Ay_1 y1y2=1时:C+Ay1λ2Ay1 ——————②
    • 结合上式①得λ2最终的取值范围:
    • m a x ( 0 , A y 1 ) ≤ λ 2 ≤ m i n ( C , C + A y 1 ) max(0,Ay_1)≤λ_2≤min(C,C+Ay_1) max(0,Ay1)λ2min(C,C+Ay1)
    • 简化为 L ≤ λ 2 ≤ H L≤λ_2≤H Lλ2H
  • 如果满足 L ≤ λ 2 ≤ H L≤λ_2≤H Lλ2H,则继续往下
  • 如果不满足条件,则直接剪枝后,再继续往下
    • 当λ2>H时,使λ2=H
    • 当λ2<L时,使λ2=L

④计算当前的 W n e w , b n e w W_{new},b_{new} Wnew,bnew

计算出满足λ≥0的λ1和λ2后,可代入求解最新的 W n e w W_{new} Wnew

W o l d = λ 1 o l d y 1 x 1 + λ 2 o l d y 2 x 2 + ∑ i = 3 n λ i y i x i W_{old} = λ_{1old}y_1x_1+λ_{2old}y_2x_2+∑^n_{i=3}λ_iy_ix_i Wold=λ1oldy1x1+λ2oldy2x2+i=3nλiyixi

W n e w = λ 1 y 1 x 1 + λ 2 y 2 x 2 + ∑ i = 3 n λ i y i x i W_{new} = λ_{1}y_1x_1+λ_{2}y_2x_2+∑^n_{i=3}λ_iy_ix_i Wnew=λ1y1x1+λ2y2x2+i=3nλiyixi

W n e w = W o l d + ( λ 1 − λ 1 o l d ) y 1 x 1 + ( λ 2 − λ 2 o l d ) y 2 x 2 W_{new} = W_{old}+(λ_{1}-λ_{1old})y_1x_1+(λ_{2}-λ_{2old})y_2x_2 Wnew=Wold+(λ1λ1old)y1x1+(λ2λ2old)y2x2

可见,每次W的更新,都只与选择的两个λ有关

b n e w b_{new} bnew,表示两条边界上的b1和b2的中间值

怎么判断是不是边界呢?

其实也就是当λ>0的时候,因此λ>0,则g(w,b)=0,这在之前的拉格朗日乘数法中已证明为支持向量,即边界

因此,则当λ1、λ2均大于0且小于C时,根据 g ( w , b ) = 1 − ξ i − y i ( W n e w x i + b ) = 0 g(w,b) = 1-ξ_i-y_i(W_{new}x_i+b) = 0 g(w,b)=1ξiyi(Wnewxi+b)=0

要求松弛变量 ξ i ξ_i ξi同时为0,则 β i > 0 β_i>0 βi>0,即 C − λ i > 0 C-λ_i>0 Cλi>0

综合得 当 0<λ<C时,该数据为支持向量上的边界点,则可以求出b

求出
b 1 n e w = y 1 − W n e w x 1 b_{1new}= y_1-W_{new}x_1 b1new=y1Wnewx1
b 2 n e w = y 2 − W n e w x 2 b_{2new}= y_2-W_{new}x_2 b2new=y2Wnewx2

b n e w = b 1 n e w + b 2 n e w 2 b_{new} = \frac{b_{1new}+b_{2new}}{2} bnew=2b1new+b2new

⑤若全部满足KKT条件,或达到迭代次数,则停止SMO算法

增加软间隔后的分类速度其实很快,而且准确率相对高一点点儿

代码改动量不大,主要只需要改动的是计算出新的λ时,要进行判断和剪枝
在这里插入图片描述

import numpy as np
import pandas as pd
import random
import matplotlib.pyplot as plt
import time
# 获取所需数据:
datas = pd.read_excel('./datas6.xlsx')
important_features = ['推荐分值','专业度','推荐类型']
# datas = pd.read_excel('./datas5.xlsx')
# important_features = ['推荐分值','推荐类型']

datas_1 = datas[important_features].head(100)
Y = datas_1['推荐类型']
X = datas_1.drop('推荐类型',axis=1)
X_features = X.columns
Y_features = '推荐类型'
rows,columns = datas_1.shape

Y=Y.where(Y!="高推荐",other=1) # 高推荐设置为1
Y=Y.where(Y!="低推荐",other=-1) # 低推荐设置为-1

class SMO():
    def __init__(self,X,Y):
        self.X = X
        self.Y = Y
        self.C = 1
        self.m = X.shape[1]
        self.n = Y.shape[0]
        self.lamb = np.zeros(self.n)
        self.β = self.C-self.lamb
        self.b = 0
        self.W0 = np.zeros(self.m)
        self.times = 5
        self.Finish = False
        self.break_kkt = {}
        self.break_kkt_list = []
        self.E = None


    def count_break_KKT(self):
        del self.break_kkt
        self.break_kkt = {}
        self.g = 1-self.Y*((self.W0*self.X).sum(axis=1)+self.b)
        # time.sleep(3)
        for index,g_value in self.g.items():
            a = self.lamb[index]
            if a < 0:
                raise Exception(f"lamb1_new为{self.lamb1_new},还是小于0")
            elif a == 0 and g_value>0:
                self.break_kkt[index] = abs(g_value)
            elif a > 0 and g_value!=0:
                self.break_kkt[index] = abs(g_value)


    def run(self):
        select_lamb1 = False
        while not self.Finish and self.times>0:
            if len(self.break_kkt_list) == 0:
                self.count_break_KKT()
                self.break_kkt_list = sorted(self.break_kkt.items(), key=lambda d: d[1], reverse=True)
                if len(self.break_kkt_list) == 0:
                    print("已全部满足KKT")
                    break
            # print(f"当前违反KKT条件的:{self.break_kkt_list}")
            # time.sleep(3)
            index1 = self.break_kkt_list.pop(0)[0]
            x1 = self.X.iloc[index1]
            y1 = self.Y[index1]
            lamb1_old = self.lamb[index1]
            self.E = (self.W0 * self.X).sum(axis=1) + self.b - self.Y
            e1 = self.E[index1]
            self.E1E2_all = e1-self.E
            self.E1E2_abs = (e1-self.E).abs()
            self.E1E2_sort = sorted(self.E1E2_abs.items(), key=lambda d: d[1], reverse=True)
            # print(f"当前的E1E2排序:{self.E1E2_sort}")
            # time.sleep(3)
            self.times -= 1
            for j in self.E1E2_sort:
                index2 = j[0]

                x2 = self.X.iloc[index2]
                y2 = self.Y[index2]
                lamb2_old = self.lamb[index2]
                e1_e2 = self.E1E2_all[index2]
                # print(f"选中的第{index1}和第{index2}的E差值为{e1_e2},参数:{self.lamb[index1], self.lamb[index2]}")
                # time.sleep(3)
                if e1_e2 == 0:
                    # print(f"由于两者的E差值为0,因此不进行迭代")
                    select_lamb1 = True
                    break
                temp = sum(x1*x1+x2*x2-2*x1*x2)
                A = -lamb1_old*y1-lamb2_old*y2
                if temp==0:
                    continue
                lamb2_new = lamb2_old + e1_e2*y2/temp

                if y1*y2 == 1:
                    L = max(0,-self.C-A*y1,)

                    H = min(self.C,-A*y1)
                    if lamb2_new<L:
                        lamb2_new = L
                    elif lamb2_new>H:
                        lamb2_new = H
                    elif lamb2_new<H and lamb2_new>L:
                        pass
                    else:
                        print("怎么H还比L小呢")
                        self.Finish = True
                        break
                elif y1*y2 == -1:
                    L = max(0,A*y1)
                    H = min(self.C,self.C+A*y1)
                    if lamb2_new<L:
                        lamb2_new = L
                    elif lamb2_new>H:
                        lamb2_new = H
                    elif lamb2_new<H and lamb2_new>L:
                        pass
                    else:
                        print("怎么H还比L小呢")
                        self.Finish = True
                        break

                lamb1_new = -lamb2_new*y1*y2-A*y1

                time.sleep(3)
                self.W0 = self.W0+(lamb1_new-lamb1_old)*y1*x1+(lamb2_new-lamb2_old)*y2*x2
                self.W0 = np.array(self.W0)
                if 0<lamb1_new and lamb1_new<self.C:
                    b1_new = y1-sum(self.W0*x1)
                if 0 < lamb2_new and lamb2_new < self.C:
                    b2_new = y2-sum(self.W0*x2)
                self.b = np.array((b1_new+b2_new)/2)
                self.lamb[index1]=lamb1_new
                self.lamb[index2]=lamb2_new
                self.β[index1]=self.C-lamb1_new
                self.β[index2]=self.C-lamb2_new
                print(f"新的lamb:{self.lamb},\nW0为{self.W0},b为{self.b}")
                select_lamb1 = True
                break
            else:
                print(f"可选的E2中:{self.E1E2_sort},没有满足条件的lamb2")

            if select_lamb1:
                select_lamb1 = False
                self.count_break_KKT()
                self.break_kkt_list = sorted(self.break_kkt.items(), key=lambda d: d[1], reverse=True)
                if len(self.break_kkt_list)==0:
                    self.Finish = True
                    print("已全部服从KKT条件")
                    sum_lamb_y = sum(self.lamb*self.Y)
                    print(f"汇总后的值是否为零:{sum_lamb_y}")
                    print(self.lamb)
                    break

        lambs = sorted([(index1,value1) for index1,value1 in enumerate(self.lamb)],key=lambda x:x[1],reverse=True)
        index1 = lambs.pop(0)[0]
        for index2,value2 in enumerate(lambs):
            if self.Y[index1]!=self.Y[index2] and value2!=0:
                print("++++++++++++++++++++++++++++++++++++")
                x1 = self.X.iloc[index1]
                x2 = self.X.iloc[index2]
                print(self.W0*x1)
                print((self.W0*x1).sum(axis=0))
                b1_new = np.array(self.Y[index1]-(self.W0*x1).sum(axis=0))
                b2_new = np.array(self.Y[index2]-(self.W0*x2).sum(axis=0))
                b_new = (b1_new+b2_new)/2
                self.b = b_new
                print("++++++++++++++++++++++++++++++++++++")
                break

        print(b1_new,b2_new)

        self.W0 = np.array([self.W0])
        print("_________________________")
        print(self.W0)
        print(self.X)
        print(self.b)

        print("_________________________")
        z = (self.W0*self.X).sum(axis=1)+self.b
        self.Y_pre = []
        for i in z:
            if i>=0:
                self.Y_pre.append(1)
            elif i<0:
                self.Y_pre.append(-1)
            else:
                print("居然还有点位于支持向量中间的点,big problem")
        print(f"分类准确率为:{round(sum(self.Y_pre==self.Y)/self.Y.shape[0]*100,2)}%")
        print("_________________________")
        z = (self.W0*self.X).sum(axis=1)+self.b
        # z1 = (self.W0 * self.X).sum(axis=1) + b1_new
        # z2 = (self.W0 * self.X).sum(axis=1) + b2_new
        map_color = {-1: 'r', 1: 'g'}
        color = list(map(lambda x: map_color[x], self.Y))
        plt.scatter(np.array(z), np.array(self.Y),c=color)

        plt.show()


test = SMO(X,Y)
test.run()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/842520.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

干货:电视盒子什么牌子好?数码博主盘点电视盒子排名

电视盒子是我们观影不可缺少的装备之一&#xff0c;但面对众多的电视盒子产品&#xff0c;大家在选购时会疑惑电视盒子什么牌子好&#xff0c;我作为数码测评博主&#xff0c;本期要分享的主题是电视盒子&#xff0c;通过对比今年测评过的26款热门电视盒子&#xff0c;我整理了…

Tkinter的超强主题扩展-ttkbootstrap

1、前言 在上一篇文章 将Python脚本集成到GUI工具包-Tkinter 里&#xff0c;作者介绍了 Tkinter&#xff0c;并开发了测试参数组合生成器小工具&#xff0c;虽然功能上已经满足了所要的需求&#xff0c;但是页面展示方面很不美观&#xff0c;需要改进一下。 本篇将介绍一款 T…

使用罗技鼠标后 弹出当前页面的脚本发生错误AppData/Local/Temp/LogiUI/Pak/js/jquery-1.3.2.min.js解决

使用的台式机&#xff0c;没有蓝牙驱动&#xff0c;在用logi无线鼠标时&#xff0c;把鼠标连接插入台式机后弹出的如上图所示这个提示&#xff0c;无论是点是/否&#xff0c;还是X掉上图提示&#xff0c;电脑右下角的图依然存在。不习惯这丫的存在。 我重启还是有&#xff0c;然…

1. Git基础知识

文章目录 Git基础知识一、集中式与分布式二、中心服务器三、工作流四、分支实现五、冲突六、Fast forward七、储藏&#xff08;Stashing&#xff09;八、SSH 传输设置九、.gitignore 文件十、Git 命令一览十一、和远端仓库交互 Git基础知识 一、集中式与分布式 Git 属于分布式…

服务蓝图:提升和改善服务系统的工具

服务蓝图&#xff1a;提升和改善服务系统的工具 Service Blueprint 翻译成服务提供计划比较恰当 趣讲大白话&#xff1a;精细耕耘&#xff0c;才有好体验 【趣讲信息科技249期】 **************************** 西方擅长的是工具和方法 把一件事情透过工具和方法做到人人能懂 日…

Day 74:通用BP神经网络 (1. 集中管理激活函数)

代码&#xff1a; package dl;/*** Activator.*/public class Activator {/*** Arc tan.*/public final char ARC_TAN a;/*** Elu.*/public final char ELU e;/*** Gelu.*/public final char GELU g;/*** Hard logistic.*/public final char HARD_LOGISTIC h;/*** Identit…

web集群学习:源码安装nginx配置启动服务脚本、IP、端口、域名的虚拟主机

目录 1、源码安装nginx&#xff0c;并提供服务脚本。 2、配置基于ip地址的虚拟主机 3、配置基于端口的虚拟主机 4、配置基于域名的虚拟主机 1、源码安装nginx&#xff0c;并提供服务脚本。 1、源码安装会有一些软件依赖 &#xff08;1&#xff09;检查并安装 Nginx 基础依赖…

探索远程访问内网群晖NAS 6.X(使用独立域名)【内网穿透】

使用自己的域名远程访问内网群晖NAS 6.X【内网穿透】 文章目录 使用自己的域名远程访问内网群晖NAS 6.X【内网穿透】 在之前的文章中&#xff0c;我们向大家演示了如何使用cpolar&#xff0c;创建一条固定的、能够在公共互联网登录内网群晖NAS的数据隧道。这条隧道已经能够应对…

新能源汽车交流充电桩控制主板的功能维度

新能源汽车交流充电桩控制主板的功能维度 交流充电桩主板是电动汽车充电站的关键组件&#xff0c;它负责控制充电过程&#xff0c;保护设备和电网免受电动汽车充电的冲击。它具有控制、保护、检测、报警和记录等功能&#xff0c;可以有效地控制充电过程&#xff0c;保证交流充电…

防火墙第五次作业

1. 什么是恶意软件&#xff1f; 恶意软件官方的一个定义&#xff1a;恶意软件 (Malware) 从“恶意”(malicious) 和“软件”(software) 这两个词合并而来&#xff0c;是一个通用术语&#xff0c;可以指代病毒、蠕虫、特洛伊木马、勒索软件、间谍软件、广告软件和其他类型的有害…

java 版本企业招标投标管理系统源码+多个行业+tbms+及时准确+全程电子化tbms

​ 功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查…

图像 检测 - DETR: End-to-End Object Detection with Transformers (arXiv 2020)

图像 检测 - DETR: End-to-End Object Detection with Transformers - 端到端目标检测的Transformers&#xff08;arXiv 2020&#xff09; 摘要1. 引言2. 相关工作2.1 集预测2.2 Transformers和并行解码2.3 目标检测 3. DETR模型References 声明&#xff1a;此翻译仅为个人学习…

【Grafana】中文界面配置 v10.0.3

比如通过 docker run -d -p 3000:3000 -v /e/code/monitor/grafana/grafana.ini.txt:/etc/grafana/grafana.ini grafana/grafana运行一个容器&#xff08;最新是v10.0.3&#xff09;。 在 /admin/settings 可以看到 users 部分有一个 default_language 配置。 所以在挂载到 …

EC200 CAT1 拨号PPP

**硬件支持型号 点击 查看 硬件支持 详情** DTU701 产品详情 DTU702 产品详情 DTU801 产品详情 DTU802 产品详情 DTU902 产品详情 G5501 产品详情 目前 DTU系列 产品&#xff0c;WIFI4G拨号 &#xff0c;默认开机自启动拨号。 WIFI 只需要 根据现场 修改SSID热点和密码…

智慧消防 | 气体灭火系统压力在线监测正当其时

气体灭火设备在消防安全系统中扮演着重要的角色。根据最新版的《气瓶安全技术规程》TSG23-2021规定&#xff0c;IG541气体钢瓶需要每3年进行一次检测。未按时进行检测可能导致压力掉压、瓶体外部锈蚀、钢瓶位置钢瓶内部腐蚀等风险&#xff0c;这些问题都可能对消防安全和效能产…

MySQL面试1

Mysql的面试突击1 Mysql的体系结构是什么样子的&#xff08;查询语句怎么进行执行的&#xff09; mysql的架构&#xff1a;单进程多线程的架构模式 CLient -----> Server架构 Mysql的链接方式有没有性能优化的点 2个点 查询缓存(Query Cache) MySQL 内部自带了一个缓存模…

【Nginx】静态资源部署、反向代理、负载均衡

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ nginx静态资源部署、反向代理、负载均衡 &…

laravel项目运行问题记录

一.首页404未找到 检查项目配置是否配置好 解决地址:phpstudy网站或站点创建成功,打开无响应_php打开提示站点创建成功_荒-漠的博客-CSDN博客 二.vendor目录不存在 composer未安装 解决地址:laravel安装composer依赖_荒-漠的博客-CSDN博客 三.首页可以展示 里面路径404 未配…

c++游戏制作指南(三):c++剧情类文字游戏的制作

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f35f;欢迎来到静渊隐者的csdn博文&#xff0c;本文是c游戏制作指南的一部&#x1f35f; &#x1f355;更多文章请点击下方链接&#x1f355; &#x1f368; c游戏制作指南&#x1f3…

SQL ASNI where from group order 顺序 where和having,SQL底层执行原理

SQL语句执行顺序&#xff1a; from–>where–>group by -->having — >select --> order 第一步&#xff1a;from语句&#xff0c;选择要操作的表。 第二步&#xff1a;where语句&#xff0c;在from后的表中设置筛选条件&#xff0c;筛选出符合条件的记录。 …