機械学習 - SMO アルゴリズムの導出と実践

1. ハードマージン-SMOアルゴリズムの導出

明日話しましょう、ああ。空がだいぶ晴れてきたような気がします。もう遅いですが
、まだコナンをつけてテレビを見て、天気予報が言っていた台風の日を待たなければなりません!

しばらくの間、導出過程をマークダウン構文で記述する勇気が急に失われてしまった。上記は線形分離性の場合に私が導出した smo アルゴリズムですが、実際の本で与えられている smo アルゴリズムはソフト区間を追加した後の smo アルゴリズムです。私が手動でプッシュした乱雑な SMO を読んだ後、他の人の導出を見てみましょう
以上只是理解版本的推导,但实际要用在程序上理解,绝不能这么不严谨
是的,我现在终于理解为什么为什么为什么课本要列出那么多奇奇怪怪的符号
那都是非常简便的表达方式!
! SMO乳母レベルの指導ようやく自分の導出が抜け穴だらけだったことが
分かり、泣いて泣いた、またスクラッチペーパーで導出を続けよう〜

最後のラグランジュ双対問題は、
MAX λ L ( λ ) = ∑ λ i − ∑ i = 1 n ∑ j = 1 n λ i λ jxixjyiyj 2 MAX_λL(λ)=∑λ_i-\frac{∑_ {i= 1}^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j}{2}マックス_ _L ( λ )=私は2i = 1j = 1私はjバツ私はバツjy私はyj
つまり、λ の値を調整することで、L 関数の最大値が解決されます。

最大: L 最大: Lマックス_ _:L は、Min : − L Min:- L を求めるように変換できます。_:L,即解M in λ L ( λ ) = ∑ i = 1 n ∑ j = 1 n λ i λ jxixjyiyj 2 − ∑ λ i Min_λL(λ)=\frac{∑_{i=1}^n∑ _{j=1}^nλ_iλ_jx_ix_jy_iy_j}{2}-∑λ_i_ _L ( λ )=2i = 1j = 1私はjバツ私はバツjy私はyj私は

次に、SMO アルゴリズムを使用してM in : L Min: Lを解きます。_:L
① 最終データを KKT 条件に適合させる:

  • λ g ( w , b ) ≤ 0 であり、 λ ≥ 0 であることを保証する必要があるため、KKT 条件の判定は具体的には次の 2 種類に分けられます。保証されているため、KKT条件の判定は具体的に以下の2つに分かれますλg ( w , _b )0 そして、λ を確実にするために、0なので、KK T条件の判定は以下の 2 種類に分けられます。
    • λ i > 0 のとき、 g ( w , b ) = 0 、すなわち 1 − yi ∗ ( W xi + b ) = 0 λ_i>0 のとき、g(w,b)=0、すなわち 1-y_i*(Wx_i+ b)=0私は0 , g ( w ,b )=0 、つまり1y私は( W x私は+b )=0
    • λ i = 0 のとき、 g ( w , b ) ≤ 0 、つまり 1 − yi ∗ ( W xi + b ) ≤ 0 λ_i = 0 のとき、g(w,b) ≤0、つまり 1-y_i*(Wx_i+ b)≤0私は=0 , g ( w ,b )0 、つまり1y私は( W x私は+b )0

②λは常にΣ λ iyi = 0 Σλ_iyi=0を満たす必要があります。S.L _私ははい、はい=0

SMO アルゴリズムのアイデア: 反復 + 貪欲

各データは λ 値、つまりxi 、 yi 、 λ i x_i,y_i,λ_iに対応するため、バツ私はy私は私は, したがって、n 個のデータがあると仮定すると、n 個の λ が存在し、直接導出するのは非常に困難です。

SMO アルゴリズムは次のことを提案します。各反復 2 λ、これらの 2 λ が能力の範囲内であることを確認し、L(λ) 関数の値を可能な限り最適化するように努め、常に Σ λ iyi = 0 Σλ_iyi=0 を維持しますS.L _私ははい、はい=0

これは、貪欲なアルゴリズムのようなもので、局所最適、そして最終的に全体最適になります。
簡単に言うと、社会を良くするために全員が最善を尽くし、そうすれば社会は最高になります。すべての質問で最高のスコアを取得し
、そして、あなたはローリングキングまたはローリングです クイーンSMOのアイデア
、そして最初に金持ちになり、後で金持ちになるという高度な概念でさえ、λを同時に状態に到達させることはできませんが、一度に2つを作ることはできますΣ λ iyi = 0 Σλ_iyi=0を保証できる時間S.L _私ははい、はい=0、L を連続的に最適値に到達させることも可能

1. アルゴリズムのステップの概要

SMO の反復 + 貪欲なアイデアを明確にした後、次の手順に簡略化できます。

①未知のパラメータに初期値を代入します。
②2 つの反復 λ を選択します (選択した 2 つの λ をそれぞれ λ_1 と λ_2 で表します)。
③反復後に λ を解き、 λ≥0 の条件が満たされるかどうかを判断します。
④現在のW new 、 bnew を計算します。 W_{新しい}、b_{新しい}W新しい_ _b新しい_ _
⑤ KKT 条件がすべて満たされるか、反復回数に達した場合、SMO アルゴリズムを停止します。

簡単そうに見える手順が実は難しい

①未知のパラメータに初期値を代入する

  • λ: λ_i には値 0 が完全に割り当てられるため、Σ λ iyi = 0 Σλ_iyi=0S.L _私ははい、はい=0
  • W: W = ∑ λ i x i ∗ y i ,因此 W 也全为 0 W=∑λ_ix_i*y_i,因此W也全为0 W=私はバツ私はy私はなので、Wもすべて0
  • b: b には条件要件がなく、値 0 を直接割り当てることができます。

[なお、λの数はn個であり、n個のデータにはn個のλが存在することを意味し、Wの数は影響因子の数(つまり、xはm種類あり、性別、年齢などの m W)]
収入の 3 つの影響要因 x が幸福度 y を決定すると、W は 3 つあります

② 2 つの反復 λ を選択します (選択した 2 つの λ を表すために λ_1、λ_2 を使用します)

  • λ1の選択方法:KKT条件から最も外れるλを選択する

KKT条件からの逸脱の度合いはどのように判断すればよいのでしょうか?

  • λ g ( w , b ) ≤ 0 であり、 λ ≥ 0 であることを保証する必要があるため、KKT 条件の判定は具体的には次の 2 種類に分けられます。保証されているため、KKT条件の判定は具体的に以下の2つに分かれますλg ( w , _b )0 そして、λ を確実にするために、0なので、KK T条件の判定は以下の 2 種類に分けられます。
    • λ i > 0 のとき、 g ( w , b ) = 0 、すなわち 1 − yi ∗ ( W xi + b ) = 0 λ_i>0 のとき、g(w,b)=0、すなわち 1-y_i*(Wx_i+ b)=0私は0 , g ( w ,b )=0 、つまり1y私は( W x私は+b )=0

    • λ i = 0 のとき、 g ( w , b ) ≤ 0 、つまり 1 − yi ∗ ( W xi + b ) ≤ 0 λ_i = 0 のとき、g(w,b) ≤0、つまり 1-y_i*(Wx_i+ b)≤0私は=0 , g ( w ,b )0 、つまり1y私は( W x私は+b )0

    • 1 つ目は KKT 条件に違反すること、2 つ目は違反の程度を g(w,b) の値によって測定すること、つまりmax:g(w,b) の下でλ i λ_iを選択することです。私は
      ただし、KKT 条件に違反する g(w,b) の降順リストを取得するには、KKT 条件の違反の度合いを降順に並べることをお勧めします。

    • このようにして、g(w,b) の最大値に対応する λ1 が反復条件を満たさない場合、2 番目に大きい g(w,b) に対応する次善の λ1 を選択できます。

    • 【判断1】 λ1 が選択できない場合は、すべての λ が KKT 条件を満たしていることを意味し、SMO アルゴリズムを停止してもよい

  • λ2の選択方法:λ2を最も変化させることができるλを選択する
    • λ 2 new = λ 2 old + yi ∗ ( E 1 − E 2 ) K 11 + K 22 − 2 K 12の性質λ2_{new}=λ2_{old}+\frac{y_i*(E1-E2) . } {K11+K22-2K12}l 2新しい_ _=l 2古い_ _+K11 + K22−2K12 _ _ _ _ _ _y私は( E 1 E 2 )∣ E 1 − E 2 ∣ 大|E1-E2|大のときE 1大きなE 2∣ は、 2 つの対応するデータ間のギャップが比較的大きいことを示します (式は後で推定されます)。
    • [判断 1] |E1-E2| の現在の最大の λ2 が条件を満たさない場合は、|E1-E2| の対応する 2 番目に大きい λ を再選択します。
    • 【判断2】 λ2が選択できない場合は、λ1を選択し直してから、λ2を選択する
      • λ1 の再選択とは、KKT 条件から最も逸脱した λ ではなく、次に逸脱する λ を選択することを指します [λ1 を降順に選択]

λ 2 新しい λ2_{新しい}l 2新しい_ _式の導出:

求解M in λ L ( λ ) = ∑ i = 1 n ∑ j = 1 n λ i λ jxixjyiyj 2 − ∑ λ i Min_λL(λ)=\frac{∑_{i=1}^n∑_{j= 1}^nλ_iλ_jx_ix_jy_iy_j}{2}-∑λ_i_ _L ( λ )=2i = 1j = 1私はjバツ私はバツjy私はyj私は、すべての λ が初期値を持つことが知られています。

次に、未知の量として λ1 と λ2 を選び出し、L の極値を見つけます。これを繰り返すと、残りの λ3...λn を定数として扱う必要があります。

  • 未知数: λ1、λ2
  • 定数: λ3…λn
  • 実際には、λ1 と λ2 自体は古い値を持っていますが、解決したいのはそれらの新しい値であるため、これらは未知の量として扱われ、導出されます。

step1: したがって、まず L(λ) の λ1 と λ2 を含む項目を分離し、次の導出を実行します。

分母: 1 2 ∑ i = 1 n ∑ j = 1 n λ i λ jxixjyiyj \frac{1}{2}∑_{i=1}^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j21i = 1j = 1私はjバツ私はバツjy私はyj
= 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 λ iyixi + ∑ i = 3 n ∑ j = 3 n λ i λ jxixjyiyj ] =\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[ l12y12バツ1バツ1+22y22バツ2バツ2+211y1y2バツ1バツ2+2 ( l1y1バツ1+2y2バツ2)i = 3私はy私はバツ私は+i = 3j = 3私はjバツ私はバツjy私はyj]

注:× 1. × 1 × 1. × 1x 1。x 1の中央の点は内積を表します。なぜなら、 xi x_iバツ私はそれはベクトルそのものであり、ベクトルの乗算は内積演算です。

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

step2: 後続の導出定数が不要な場合は削除します。定数の最初と 2 番目の部分を削除した後、反復変数のみを含む 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 λ iyixi ] + λ 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+λ_2Q=21[ l12y12バツ1バツ1+22y22バツ2バツ2+211y1y2バツ1バツ2+2 ( l1y1バツ1+2y2バツ2)i = 3私はy私はバツ私は]+1+2

step3: 未知の λ を 1 つだけ含むように Q 関数を変更し、次を導出します。

によるΣ λ iyi = 0 Σλ_iyi=0S.L _私ははい、はい=0得られる値:λ 1 y 1 + λ 2 y 2 = − ∑ i = 3 n λ i = − λ 1 古い 1 − λ 2 古い 2 λ_1y_1+λ_2y_2=-∑^n_{i=3}λ_i=-λ_ {1old}y_1-λ_{2old}y_21y1+2y2=i = 3私は=l1_ _y12_ _y2

ここで、λ1 と λ2 は未知の量、λ 1 old 、λ 2 old λ_{1old}、λ_{2old}を表します。1_ _2_ _その後、古い価値観を表現します

∑ i = 3 n λ i = λ 1 oldy 1 + λ 2 oldy 2 、定数 A とみなすことができ、計算を簡素化するのに便利です ∑^n_{i=3}λ_i=λ_{1old}y_1+ λ_{2old}y_2、これを定数 A として使用すると、計算を簡素化するのに便利です。i = 3私は=1_ _y1+2_ _y2、定数Aとみなすことができ、計算を簡素化するのに便利です。

λ 1 y 1 + λ 2 y 2 = − A λ_1y_1+λ_2y_2=-A1y1+2y2=A の両辺に y1 を乗算し、転置します: λ1 = -λ2y1y2-Ay1

λ 1 = − λ 2 y 1 y 2 − A y 1 を Q 関数に代入しますλ1 = -λ2y1y2-Ay1 を Q 関数に代入しますl1 _=λ 2 y 1 y 2A y 1をQ関数代入すると、未知量 λ2 のみを含む Q 関数が得られ、λ2 を導出した後、次のようになります。

Q λ 2 ' = ははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははは -Ql 2=ははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははは_ _ _ _ _ _ _

然后再将 A = ∑ i = 3 n λ i A=∑^n_{i=3}λ_i =i = 3私は代入 Q λ 2 ′ Q'_{λ2} Ql 2W xi + b − yi = E i 、予測値と実際の値の間のギャップを表します Wx_i+b-y_i = E_i 、予測値と実際の値の間のギャップを表します×私は+by私は=E私は、予測値と実際の値の差を表します

次に、Q λ 2 ' = 0 Q'_{λ2} =0 とします。Ql 2=0 を計算し、最後に λ 2 = λ 2 old + 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}l 2=2_ _+× 1. × 1 × 2. × 2−2 × 1. × 2 _ _y 2 ( E 1 E 2 )

③ 反復後の λ を解き、反復条件が満たされるかどうかを判定する

λ の条件は次のとおりです: λ≥0、つまり、取得した λ1 と λ2 は ≥0 の条件を満たさなければなりません。

前のステップにより、 λ 2 = λ 2 old + 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}l 2=2_ _+× 1. × 1 × 2. × 2−2 × 1. × 2 _ _y 2 ( E 1 E 2 )

したがって、 λ2≥0 と判断できます。

  • λ2≧0の場合は下に進みます
  • λ2 が条件を満たさない場合は、戻って λ2 を再選択します

次に、 λ 1 = − λ 2 y 1 y 2 − A y 1 ≥ 0 λ_1 = -λ_2y_1y_2-Ay_1≥0から λ1 を判断します。1=l2y1y2ああ_10 件あります

  • y1y2 = 1の場合:λ 2 ≦ − A y 1 λ2 ≦ -Ay_1l 2-はい_1
  • y1y2 = -1の場合λ 2 ≧ A y 1 λ2 ≧ Ay_1l 2ああ_1
  • いずれかの条件が満たされていれば続行します
  • 上記 2 つの条件が満たされない場合は、戻って λ2 を再選択します

④ 現在のW new 、 bnew W_{new}、b_{new}を計算します。W新しい_ _b新しい_ _

λ≧0を満たすλ1とλ2を計算した後、それを最新のW new W_{new}を解くために代入できます。W新しい_ _

Wold = λ 1 oldy 1 x 1 + λ 2 oldy 2 x 2 + ∑ i = 3 n λ iyixi W_{old} = λ_{1old}y_1x_1+λ_{2old}y_2x_2+∑^n_{i=3}λ_iy_ix_iW古い_ _=1_ _y1バツ1+2_ _y2バツ2+i = 3私はy私はバツ私は

W new = λ 1 y 1 x 1 + λ 2 y 2 x 2 + ∑ i = 3 n λ iyixi W_{new} = λ_{1}y_1x_1+λ_{2}y_2x_2+∑^n_{i=3}λ_iy_ix_iW新しい_ _=1y1バツ1+2y2バツ2+i = 3私はy私はバツ私は

W new = W old + ( λ 1 − λ 1 old ) y 1 x 1 + ( λ 2 − λ 2 old ) y 2 x 2 W_{new} = W_{old}+(λ_{1}-λ_{ 1old})y_1x_1+(λ_{2}-λ_{2old})y_2x_2W新しい_ _=W古い_ _+( l11_ _) y1バツ1+( l22_ _) y2バツ2

W の各更新は、選択された 2 つの λ にのみ関連していることがわかります。

bnew b_{新しい}b新しい_ _、2 つの境界上の b1 と b2 の中央値を表します。

境界かどうかはどうやって判断するのでしょうか?

実際、λ>0 の場合、つまり λ>0 となり、g(w,b)=0 になります。これは、前のラグランジュ乗数法におけるサポート ベクトル、つまり境界であることが証明されています。

したがって、λ1 と λ2 が両方とも 0 より大きい場合、g ( w , b ) = 1 − yi ( W newxi + b ) = 0 に従って、 g(w,b) = 1-y_i(W_{new}x_i+b ) = 0g ( w ,b )=1y私は( W新しい_ _バツ私は+b )=0

求出
b 1 new = y 1 − W newx 1 b_{1new}= y_1-W_{new}x_1b1新しい_ _=y1W新しい_ _バツ1
b 2 new = y 2 − W newx 2 b_{2new}= y_2-W_{new}x_2b2新しい_ _=y2W新しい_ _バツ2

bnew = b 1 new + b 2 new 2 b_{new} = \frac{b_{1new}+b_{2new}}{2}b新しい_ _=2b1新しい_ _+ b2新しい_ _

⑤ KKT 条件がすべて満たされるか、反復回数に達した場合、SMO アルゴリズムを停止します。

ハードインターバルの場合の導出は比較的単純ですが、実際には難しく、理解できないものもあります
l1 _新しい_ _l 2新しい_ _λ≧0の条件を満たさないので、λを選び直す必要があるのですが、どうやって選び直すのでしょうか?

  • 最初に λ2 を再選択します: すべての λ の |E1-E2| を降順に並べ、その後、対応する λ2 をλ 1 new 、λ 2 new λ1_{new}、λ2_{new}になるまで順番に選択します。l1 _新しい_ _l 2新しい_ _すべてが λ≥0 を満たす
  • 1 パスですべての |E1-E2|>0 λ が選択されても、まだ λ≥0 を満たすことができない場合、現在の λ1 では一時的に適切な λ2 を選択できないことを意味します。
    • 次に、λ1 を再選択することができ、再選択されたものが 2 番目に違反している KKT 条件 λ になります。
  • 適切な λ2 が選択された場合、λ1 と λ2 の反復が完了した後、戻って λ1 と λ2 を再選択し、新しいラウンドの反復を開始します。

しかし実際には、データが線形分離可能であっても、データ量が大きくなると収束しにくく、複数のデータ間を行ったり来たりすることが多く、KKT条件を完全に満たすことができず、その後の反復も満たすことができません。 λ≧0

したがって、この問題を解決するために、反復回数を制限する方法が採用されます。

そしてテストするときは、私自身が線形関係を設計しました。つまり、
影響因子 x が 1 つだけで、それが線形関係にある場合、データ量が少ない場合 (10 項目のテスト)、完全かつ正確に分類できます。

しかし、トレーニングのプロセス。本当に遅い、遅い、遅い。新しい線形インパクトファクターを追加する限り、データはまだ 10 個あります。それは非常に遅く、すべての λ が KKT 条件を満たすまで収束するのは困難です。トレーニングは 10,000 回の反復後にのみ終了し、自動的に停止します。幸いなことに、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()

2. ソフト区間双対問題と smo アルゴリズムの導出

新しいビューの対偶の問題:MAX λ L ( λ ) = ∑ λ i − ∑ i = 1 n ∑ j = 1 n λ i λ jxixjyiyj 2 MAX_λL(λ)=∑λ_i-\frac{∑_{i =1} ^n∑_{j=1}^nλ_iλ_jx_ix_jy_iy_j}{2}マックス_ _L ( λ )=私は2i = 1j = 1私はjバツ私はバツjy私はyj


これは、線形分離可能性 (つまり、ハード区間) のケースに基づいています。データが厳密に線形分離可能でなくなると、SVM は失敗します。SMOアルゴリズムを適用して正しい λ を計算できない可能性が非常に高いと思います。

したがって、各データにはスラック変数ξ i ξ_iが導入されます。バツ私は

すると、各データの制約条件はyi ( W xi + b ) ≥ 1 − ξ i y_i(Wx_i+b) ≥ 1-ξ_i となります。y私は( W x私は+b )1バツ私は、ξ≧0

ただし、スラック変数を導入するには、目的関数にペナルティ項を追加する必要があります。

元の目的関数min : f = w 2 2 min:f=\frac{w^2}{2}:f=2w2

ペナルティ項目を追加すると、 min : f = w 2 2 + C ∑ ξ i min:f=\frac{w^2}{2}+C∑ξ_i:f=2w2+Cバツ私は

ここで C は独自に設定したペナルティ項目の重み定数です

実際、私も非常に混乱しています。このペナルティ項目は目的関数の実現にどのような影響を与えるのでしょうか?

私の最初のアイデアによれば、元の関数w 2 2 + C ∑ ξ i \frac{w^2}{2}+C∑ξ_iにペナルティ項を入れます。2w2+Cバツ私はつまり、w 2 2 および C ∑ ξ i \frac{w^2}{2} および C∑ξ_i とします。2w2Cバツ私は相互影響や相互制約がある場合は、f(x)を可能な限り最小値にする

那么min : f = w 2 2 + C ∑ ξ i min:f=\frac{w^2}{2}+C∑ξ_i:f=2w2+Cバツ私はこのうち、C が比較的大きい場合、スラック変数は目的関数 f に大きな影響を与えるため、学習プロセスでは ξ を最小化する傾向があり、w はそれほど重要ではありません。

逆に、C が比較的小さい場合、スラック変数 ξ は目的関数 f にほとんど影響を与えないため、トレーニング プロセス中に w が最小化される傾向があり、スラック変数の重要性は比較的低くなります。

通常、sklearn ではこの C のデフォルト値は 1 ですが、学習結果が満足できない場合もあるので、状況に応じて C を設定します (この部分は後で説明します)。

現在のソリューションの目標は次のとおりです。

  • 目的関数はmin です: f = w 2 2 + C ∑ ξ i min:f=\frac{w^2}{2}+C∑ξ_i:f=2w2+Cバツ私は
  • 制約は次のとおりです。
    • g ( w , b , ξ ) = 1 − ξ i − yi ( W xi + b ) ≤ 0 g(w,b,ξ) = 1-ξ_i-y_i(Wx_i+b)≤0g ( w ,b × =1バツ私はy私は( W x私は+b )0、これは制約 1
    • ξ≧0 ξ≧0バツ0、これは制約 2 であり、- ξ ≤ 0 -ξ≤0×0

ラグランジュ乗数法に従って、関数 L が確立されます。
最初の制約条件、対応するラグランジュ乗数を表すために λ を使用します。
2 番目の制約条件、対応するラグランジュ乗数を表すために β を使用します。

min L ( w , b , ξ , λ , β ) = w 2 2 + C Σ ξ i + Σ λ i [ 1 − ξ i − yi ( W xi + b ) ] − Σ β i ξ i min L(w ,b,ξ,λ,β)=\frac{w^2}{2}+CΣξ_i+Σλ_i[1-ξ_i-y_i(Wx_i+b)]-Σβ_iξ_i最小L ( w ,b × l b )=2w2+CS × _私は+S.L _私は[ 1バツ私はy私は( W x私は+b )]Sb _私はバツ私は

KKTの条件は次のとおりです。

  • λ i [ 1 − ξ i − yi ( W xi + b ) ] = 0 λ_i[1-ξ_i-y_i(Wx_i+b)]=0私は[ 1バツ私はy私は( W x私は+b )]=0
    • λ i > 0 の場合、1 − ξ i − yi ( W xi + b ) = 0 λ_i>0 の場合、1-ξ_i-y_i(Wx_i+b)=0私は>0、1 _ _バツ私はy私は( W x私は+b )=0
    • λ i = 0 の場合、1 − ξ i − yi ( W xi + b ) ≤ 0 λ_i=0 の場合、1-ξ_i-y_i(Wx_i+b) ≤0私は=0、1 _ _バツ私はy私は( W x私は+b )0
  • β i ξ i = 0 β_iξ_i=0b私はバツ私は=0
    • β i > 0 の場合、ξ i = 0 β_i>0 の場合、ξ_i=0b私は>0 ξ私は=0
    • β i = 0 の場合、ξ i ≥ 0 β_i=0 の場合、ξ_i ≥ 0b私は=0 ξ私は0

将拉格朗日関数,改对カップリング:
min L ( w , b , ξ , λ , β ) = w 2 2 + C Σ ξ i + Σ λ i [ 1 − ξ i − yi ( W xi + b ) ] − Σ β i ξ i min L(w,b,ξ,λ,β)=\frac{w^2}{2}+CΣξ_i+Σλ_i[1-ξ_i-y_i(Wx_i+b)]- Σβ_iξ_i最小L ( w ,b × l b )=2w2+CS × _私は+S.L _私は[ 1バツ私はy私は( W x私は+b )]Sb _私はバツ私は

極値を見つけるプロセスでは、最初に w、b、ξ の最小値を見つける必要があるため、双対関数は前のハード区間として次のように推定できます。

Max λ , β M inw , b , ξ L ( w , b , ξ , λ , β ) = w 2 2 + C Σ ξ i + Σ λ i [ 1 − ξ i − yi ( W xi + 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マックス_ _l b_ _w b ξL ( w ,b × l b )=2w2+CS × _私は+S.L _私は[ 1バツ私はy私は( W x私は+b )]Sb _私はバツ私は

まず、w、b、各 ξ_i の偏導関数をそれぞれ計算し、次の 3 つの条件を取得します。

  • w = Λ λ ixiyiw = Λ_ix_iy_iw=S.L _私はバツ私はy私は———①
  • Σ λ 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 が完全に割り当てられるため、Σ λ iyi = 0 Σλ_iyi=0S.L _私ははい、はい=0
  • ξ: ξ i 由 C − λ i 得到 ξ_i由C-λ_i得到 バツ私はby C私は得る
  • W: W = ∑ λ i x i ∗ y i ,因此 W 也全为 0 W=∑λ_ix_i*y_i,因此W也全为0 W=私はバツ私はy私はなので、Wもすべて0
  • b: b には条件要件がなく、値 0 を直接割り当てることができます。

② 2 つの反復 λ を選択します (選択した 2 つの λ を表すために λ_1、λ_2 を使用します)

  • λ1の選択方法:KKT条件から最も外れるλを選択する

    • 1 つ目は KKT 条件の違反です。

      • λ i [ 1 − ξ i − yi ( W xi + b ) ] = 0 λ_i[1-ξ_i-y_i(Wx_i+b)]=0私は[ 1バツ私はy私は( W x私は+b )]=0
      • λ i > 0 の場合、1 − ξ i − yi ( W xi + b ) = 0 λ_i>0 の場合、1-ξ_i-y_i(Wx_i+b)=0私は>0、1 _ _バツ私はy私は( W x私は+b )=0
      • λ i = 0 の場合、1 − ξ i − yi ( W xi + b ) ≤ 0 λ_i=0 の場合、1-ξ_i-y_i(Wx_i+b) ≤0私は=0、1 _ _バツ私はy私は( W x私は+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 new = λ 2 old + yi ∗ ( E 1 − E 2 ) K 11 + K 22 − 2 K 12の性質λ2_{new}=λ2_{old}+\frac{y_i*(E1-E2) . } {K11+K22-2K12}l 2新しい_ _=l 2古い_ _+K11 + K22−2K12 _ _ _ _ _ _y私は( E 1 E 2 )∣ E 1 − E 2 ∣ 大|E1-E2|大のときE 1大きなE 2∣ は、 2 つの対応するデータ間のギャップが比較的大きいことを示します (式は後で推定されます)。
    • [判断 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_1y 1 y 2のとき=1時間:C+ああ_1l 2ああ_1——————②
    • 上記の式①を組み合わせて、最終的な λ2 の値の範囲を取得します。
    • max ( 0 , A y 1 ) ≤ λ 2 ≤ min ( C , C + A y 1 ) max(0,Ay_1)≤λ_2≤min(C,C+Ay_1)最大( 0 , _ああ_1)2( C ,C+ああ_1)
    • L ≤ λ 2 ≤ HL ≤ λ_2 ≤ Hに簡略化L2H
  • L ≤ λ 2 ≤ HL ≤ λ_2 ≤ Hの場合L2H、下に進みます
  • 条件が満たされない場合は、直接剪定してから下に進みます。
    • λ2>Hの場合、λ2=Hとする
    • λ2<Lの場合は、λ2=Lとする

④ 現在のW new 、 bnew W_{new}、b_{new}を計算します。W新しい_ _b新しい_ _

计算出满足λ≥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 new = y 1 − W newx 1 b_{1new}= y_1-W_{new}x_1b1新しい_ _=y1W新しい_ _バツ1
b 2 new = y 2 − W newx 2 b_{2new}= y_2-W_{new}x_2b2新しい_ _=y2W新しい_ _バツ2

bnew = b 1 new + b 2 new 2 b_{new} = \frac{b_{1new}+b_{2new}}{2}b新しい_ _=2b1新しい_ _+ b2新しい_ _

⑤ 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()

おすすめ

転載: blog.csdn.net/weixin_50348308/article/details/132130513