当前位置: 首页 > news >正文

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

一、 硬间隔-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.shapeY=Y.where(Y!="高推荐",other=1) # 高推荐设置为1
Y=Y.where(Y!="低推荐",other=-1) # 低推荐设置为-1class SMO():def __init__(self,X,Y):self.X = Xself.Y = Yself.m = X.shape[1]self.n = Y.shape[0]self.lamb = np.zeros(self.n)self.b = 0self.W0 = np.zeros(self.m)self.times = 10000self.Finish = Falseself.break_kkt = {}self.break_kkt_list = []self.E = Nonedef count_break_KKT(self):del self.break_kktself.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 = Falsewhile 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.Ye1 = self.E[index1]self.E1E2_all = e1-self.Eself.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 -= 1for 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 = Truebreaktemp = sum(x1*x1+x2*x2-2*x1*x2)A = -lamb1_old*y1-lamb2_old*y2if temp==0:# print("当前的index1和index2的X值一致,可以直接将index2的lamb2_new等同于lamb1_new")# time.sleep(3)continuelamb2_new = lamb2_old + e1_e2*y2/tempif lamb2_new<0:continueelif y1*y2 == 1:if lamb2_new > -A*y1:continueelif y1*y2 == -1:if lamb2_new < A*y1:continuelamb1_new = -lamb2_new*y1*y2-A*y1time.sleep(3)self.W0 = self.W0+(lamb1_new-lamb1_old)*y1*x1+(lamb2_new-lamb2_old)*y2*x2self.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_newself.lamb[index2]=lamb2_newprint(f"新的lamb:{self.lamb},\nW0为{self.W0},b为{self.b}")select_lamb1 = Truebreakelse:print(f"可选的E2中:{self.E1E2_sort},没有满足条件的lamb2")if select_lamb1:select_lamb1 = Falseself.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 = Trueprint("已全部服从KKT条件")sum_lamb_y = sum(self.lamb*self.Y)print(f"汇总后的值是否为零:{sum_lamb_y}")print(self.lamb)breaklambs = 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)/2self.b = b_newprint("++++++++++++++++++++++++++++++++++++")breakprint(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.bself.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.shapeY=Y.where(Y!="高推荐",other=1) # 高推荐设置为1
Y=Y.where(Y!="低推荐",other=-1) # 低推荐设置为-1class SMO():def __init__(self,X,Y):self.X = Xself.Y = Yself.C = 1self.m = X.shape[1]self.n = Y.shape[0]self.lamb = np.zeros(self.n)self.β = self.C-self.lambself.b = 0self.W0 = np.zeros(self.m)self.times = 5self.Finish = Falseself.break_kkt = {}self.break_kkt_list = []self.E = Nonedef count_break_KKT(self):del self.break_kktself.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 = Falsewhile 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.Ye1 = self.E[index1]self.E1E2_all = e1-self.Eself.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 -= 1for 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 = Truebreaktemp = sum(x1*x1+x2*x2-2*x1*x2)A = -lamb1_old*y1-lamb2_old*y2if temp==0:continuelamb2_new = lamb2_old + e1_e2*y2/tempif y1*y2 == 1:L = max(0,-self.C-A*y1,)H = min(self.C,-A*y1)if lamb2_new<L:lamb2_new = Lelif lamb2_new>H:lamb2_new = Helif lamb2_new<H and lamb2_new>L:passelse:print("怎么H还比L小呢")self.Finish = Truebreakelif y1*y2 == -1:L = max(0,A*y1)H = min(self.C,self.C+A*y1)if lamb2_new<L:lamb2_new = Lelif lamb2_new>H:lamb2_new = Helif lamb2_new<H and lamb2_new>L:passelse:print("怎么H还比L小呢")self.Finish = Truebreaklamb1_new = -lamb2_new*y1*y2-A*y1time.sleep(3)self.W0 = self.W0+(lamb1_new-lamb1_old)*y1*x1+(lamb2_new-lamb2_old)*y2*x2self.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_newself.lamb[index2]=lamb2_newself.β[index1]=self.C-lamb1_newself.β[index2]=self.C-lamb2_newprint(f"新的lamb:{self.lamb},\nW0为{self.W0},b为{self.b}")select_lamb1 = Truebreakelse:print(f"可选的E2中:{self.E1E2_sort},没有满足条件的lamb2")if select_lamb1:select_lamb1 = Falseself.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 = Trueprint("已全部服从KKT条件")sum_lamb_y = sum(self.lamb*self.Y)print(f"汇总后的值是否为零:{sum_lamb_y}")print(self.lamb)breaklambs = 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)/2self.b = b_newprint("++++++++++++++++++++++++++++++++++++")breakprint(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.bself.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_newmap_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()

相关文章:

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

一、 硬间隔-SMO算法推导 明天再说&#xff0c;啊。。。。感觉天空明朗了很多&#xff0c;即使现在已经很晚了 还是要打开柯南&#xff0c;看看电视&#xff0c;等待天气预报所说的台风天吧&#xff01; 一时之间&#xff0c;忽然失去了用markdown语法写下推导过程的勇气。。。…...

mac的终端通过code .指令快速启动vscode

通过在vscode中安装"code"命令工具 打开vsocode&#xff0c;使用快捷键⇧⌘P&#xff0c;然后输入shell&#xff0c;会弹出来“Shell命令&#xff1a;在PATH中安装‘code’命令”浮窗&#xff0c;选择安装就可以了&#xff0c;然后就可以在终端通过code .来快速启动…...

前端系统使用iframe下载文件

需求描述 前端调用后端的接口&#xff0c;获取到文件的路径&#xff0c;并下载。 碰到的问题 页面组件存在与云端的组件库&#xff0c;使用window.open()无法满足需求&#xff08;在当前页面下载&#xff09;&#xff0c;因为路径是跨域的&#xff0c;所以决定使用iframe的方…...

RabbitMQ - 简单案例

目录 0.引用 1.Hello world 2.轮训分发消息 2.1 抽取工具类 2.2 启动两个工作线程接受消息 2.4 结果展示 3.消息应答 3.1 自动应答 3.2 手动消息应答的方法 3.3 消息自动重新入队 3.4 消息手动应答代码 4.RabbitMQ 持久化 4.1 队列如何实现持久化 4.2 消息实现持久化 5.不…...

《吐血整理》高级系列教程-吃透Fiddler抓包教程(30)-Fiddler如何抓Android7.0以上的Https包-番外篇

1.简介 通过宏哥前边几篇文章的讲解和介绍想必大家都知道android7.0以上&#xff0c;有android的机制不在信任用户证书&#xff0c;导致https协议无法抓包。除非把证书装在系统信任的证书里&#xff0c;此时手机需要root权限。但是大家都知道root手机是非常繁琐的且不安全&…...

服务器被攻击了怎么办?

服务器被攻击是无法避免的&#xff0c;但是我们能通过做好防护措施&#xff0c;提高服务器的安全性&#xff0c;降低被攻击的几率。那么当服务器已经被 攻击了&#xff0c;怎样才能降低损失呢&#xff1f;该怎样补救&#xff1f; 断开网络 全部的攻击都来自于网络&#xff0c;因…...

P1156 垃圾陷阱(背包变形)

垃圾陷阱 题目描述 卡门――农夫约翰极其珍视的一条 Holsteins 奶牛――已经落了到 “垃圾井” 中。“垃圾井” 是农夫们扔垃圾的地方&#xff0c;它的深度为 D D D&#xff08; 2 ≤ D ≤ 100 2 \le D \le 100 2≤D≤100&#xff09;英尺。 卡门想把垃圾堆起来&#xff0c…...

[Docker实现测试部署CI/CD----构建成功后钉钉告警(7)]

目录 15、钉钉告警创建项目群&#xff0c;然后添加机器人添加机器人Jenkins 系统配置项目配置修改Jenkinsfile文件&#xff0c;添加钉钉提示信息测试 不修改Jenkinsfile文件&#xff0c;添加钉钉提示信息测试 15、钉钉告警 创建项目群&#xff0c;然后添加机器人 首先需要在钉…...

UE5 半透明覆层材质

文章目录 前言介绍示例1示例2示例3 前言 本文采用虚幻5.2.1版本演示&#xff0c;介绍半透明覆层材质&#xff08;覆层材质&#xff09;。 介绍 半透明覆层材质是 UE5.1 版本 更新的功能&#xff0c;使用半透明覆层材质&#xff0c;可以轻松的给物体表面附着一层材质。 在UE5…...

在Raspberry Pi 4上安装Ubuntu 20.04 + ROS noetic(不带显示器)

在Raspberry Pi 4上安装Ubuntu 20.04 ROS noetic&#xff08;不带显示器&#xff09; 1. 所需设备 所需设备&#xff1a; 树莓派 4 B 型 wifi microSD 卡&#xff1a;最小 32GB MicroSD 转 SD 适配器 &#xff08;可选&#xff09;显示器&#xff0c;鼠标等 2. 树莓派…...

CommStudio for .NET Crack

CommStudio for .NET Crack CommStudio for.NET使您的应用程序可以轻松地使用串行端口和调制解调器进行通信。CommStudio for.NET是一套全面的组件和可视化调试工具&#xff0c;可将远程系统和设备与visual Studio 2005和visual Studio 2008集成。开发与遗留系统和外部设备集成…...

蓝桥杯上岸考点清单 (冲刺版)!!!

大家好 我是寸铁&#x1f4aa; 真题千千万万遍&#xff0c;蓝桥省一自然现&#xff01; ✌️ 日更3000里&#xff0c;蓝桥眷顾你 &#x1f31f; 暴力出奇迹&#xff0c;打表过样例 &#x1f44a; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &#…...

AI一键生成短视频

AI一键生成推文短视频 阅读时长&#xff1a;10分钟 本文内容&#xff1a; 结合开源AI&#xff0c;一键生成短视频发布到常见的某音&#xff0c;某手平台&#xff0c;狠狠赚一笔 前置知识&#xff1a; 1.基本的 python 编程知识 2.chatGPT 使用过 3.stable diffution 使用过 成果…...

基于MATLAB长时间序列遥感数据分析(以MODIS数据处理为例)

MATLAB MATLAB是美国MathWorks公司出品的商业数学软件&#xff0c;用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理、量化金融与风险管理、机器人&#xff0c;控制系统等领域。 [1] MATLAB是matrix&laboratory两个词的组合&#xff0c;意为矩阵工厂&a…...

postgresql之内存池-AllocsetContext

一、简介 postgresql大部分的内存分配管理都是通过MemoryContext进行操作的&#xff0c; 多个相关的MemoryContext构成了一个树型结构&#xff0c; 多个树构成了一个森林。 实现了三种MemoryContext: SlabContextGenerationContextAllocSetContext 使用全局变量CurrentMemo…...

QT 使用单例模式

目录 1. 单例模式介绍 2.单例模式实现 1. 单例模式介绍 有些时候我们在做 qt 项目的时候,要用到很多类. 例如我们用到的类有 A,B,C,D. 其中,A 是 B,C,D 中都需要用到的类,A 类非常的抢手. 但是,A 类非常的占内存,定义一个 A 对象需要 500M 内存,假如在 B,C,D 中都定义一个 A 类…...

接口测试——postman接口测试(三)

目录 1. postman介绍与安装 2. postman发送get请求 3. postman发送post请求 1. postman介绍与安装 安装网址&#xff1a;Postman安装教程&#xff1a;留言找我要即可 2. postman发送get请求 import pymysql from flask import Flask,request# 这里是mysql的基本连接信息 c…...

react中hooks的理解与使用

一、作用 我们知道react组件有两种写法一种是类组件&#xff0c;另一种是函数组件。而函数组件是无状态组件&#xff0c;如果我们要想改变组件中的状态就无法实现了。为此&#xff0c;在react16.8版本后官方推出hooks&#xff0c;用于函数组件更改状态。 二、常用API 1、use…...

STM32的电动自行车信息采集上报系统(学习)

摘要 针对电动自行车实时监管不便的问题&#xff0c;设计了一种基于STM32的电动自行车信息采集系统&#xff0c;通过获取电池、位置和行驶状态信息并上报到服务器中&#xff0c;实现实时监管。 通过多路串口请求电池、行驶状态和位置信息&#xff0c;以并发方式进行数据接收、…...

蓝桥杯上岸每日N题 第七期(小猫爬山)!!!

蓝桥杯上岸每日N题 第七期(小猫爬山)&#xff01;&#xff01;&#xff01; 同步收录 &#x1f447; 蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(第四期DFS) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &a…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...