1.拉格朗日乘子法
1. 拉格朗日函数
凸优化:凸优化问题是指优化问题的目标函数是凸函数,而约束集合是凸集。在这样的问题中,局部最优解同时也是全局最优解
对于优化问题
xmin f(x)s.t.gi(x)≤0,i=1,2,...,mhj(x)=0,j=1,2,...n
拉格朗日函数定义为
L(x,λ,μ)=f(x)+i=1∑mλigi(x)+j=1∑nμjhj(x)
2. minmax 不等式
ymax xmin f(x,y)≤xmin ymax f(x,y)
实际上对于任意 x0,y0 ,下列式子恒成立
xmin f(x,y0)≤f(x0,y0)≤ ymax f(x0,y)
去掉中间部分再分别取最大值和最小值可以证明上述不等式
3. 拉格朗日函数的原问题和对偶问题
1.弱对偶
对于L(x,λ,μ)=f(x)+i=1∑mλigi(x)+j=1∑nμjhj(x)
λ≥0,μmax L(x,λ,μ)={ f(x), +∞,满足约束时不满足约束时
如果x满足约束,那么L(x,λ,μ)右面两组约束都为0。 对于μigi(x) 满足约束的时候显然为0
对于λihi(x) 由于hi(x)≤0 所以取最大值的状态只能为0
如果不满足约束 λihi(x)>0 ,适当选取μi的时候 μig(x)>0 取最大都会趋向于正无穷
满足约束时,两边对x取最小值之后则有
xmin λ≥0,μmax L(x,λ,μ)=xmin f(x)
成立,都称为原始问题, 原始问题与对偶问题的关系如下
xmin λ≥0,μmax L(x,λ,μ)≥λ≥0,μmax xminL(x,λ,μ)
2.强对偶
当然如果这俩能取等是最好的,可以取等号的时候是强对偶,这样可以解决很多问题
Slater 条件给出了强对偶的充分条件
∀fi(x) 都为凸函数
∃ x∈relint D 满足 fi(x)<0
KKT 条件给出了强对偶的必要条件
gi(x)≤0
hi(x)=0
∇xL(x,λ,μ)=0
λi≥0
λigi(x)=0
2. 超平面的表示与计算分隔平面
平面可用 wTx+b=0 表述
距离 d=∥w∥wTx+b , 对于分类边界也可转化为 ∥w∥dwTx+b=1
实际上可以得到新的w,b 满足 wTx+b=1
分类时{ wTx+b≥1, wTx+b≤−1,yi=1yi=−1
两平面之间的距离
d=∥w∥2
为了容错性,需要找到最大间距
w,bmax∥w∥2s.t.yi(wTx+b)≥1
等价于
w,bmin21∥w∥2s.t.yi(wTx+b)≥1
构造拉格朗日函数
L(w,b,α)=21∥w∥2=+i=1∑mαi(1−yi(wTx+b))
3. SMO (Squential Minimal Optimization)
1. 对偶问题
对L(w,b,α) 求w,b,αi偏导
∂w∂L=w−i=1∑mαiyixi=0
∂b∂L=i=1∑mαiyi=0
考虑到原问题
w,bmin21∥w∥2s.t.yi(wTx+b)≥1
得到到对偶问题
αmax i=1∑mαi −21i=1∑mj=1∑mαiαjyiyjxiTxj s.ti=1∑mαiyi=0,α≥0,i=1,2,..,m
由偏导求得的结果得
f(x)=wTx+b=i=1∑mαiyixiTx+b
由KKT 条件得
⎩⎨⎧ αi≥0 yif(xi)−1≥0 αi(yif(xi)−1)=0
实际上 如果 αi=0 ,该样本不会有任何影响
如果 αi>0 , 由上述第三个条件得到 yif(xi)=1
2. 算法流程
(实际上数学功底不好,写错也是常见的)
SMO 算法的思路是固定αi之外的所有参数,求αi上的极值
选取一对需要更新的变量αi,αj
固定αi,αj之外的所有参数, 求解αmax i=1∑mαi−21i=1∑mj=1∑mαiαjyiyjxiTxj
s.ti=1∑mαiyi=0, α≥0,i=1,2,..,m
条件下更新的 αi,αj
满足约束αiyi+αjyj=c
不失一般性,我们选择 α1,α2 作为变量
L(α1,α2)=α1+α2+i=3∑mαi
−21[α1y1α1y1x1Tx1+2α1y1α2y2x1Tx2+2j=3∑mα1y1αjyj
+α2y2α2y2x2Tx2+2j=3∑mα2y2αjyjx2Txj+i=3∑mj=3∑mαiαjyiyjxiTxj]
满足约束α1y1+α2y2=c
令
Kij=xiTxj
α1=y1(c−α2y2) , (∣yi∣=1)
带入得
L(α2)=y1(c−α2y2)+α2
−21[(c−α2y2)2K11+2(c−α2y2)α2y2K12+α22K22
+2i=3∑m(c−α2y2)αiyiK1i+2i=3∑mα2y2αiyiK2i]
求偏导得
∂α2∂L=−y1y2+1
−21[2(α2y2−c)y2K11+2cy2K12−4α2K12+2α2K22
−2i=1∑my2αiyiK1i+2i=3∑my2αiyiK2i]
∂α2∂L=0(实际上也就是L(α2) 的值不变)
α2(K11+K22−2K12)
=1−y1y2+cy2K11−cy2K12+i=3∑my2αiyiK1i−i=3∑my2αiyiK2i
=y2(y2−y1+cK11−cK12+i=3∑mαiyiK1i−i=1∑3αiyiK2i)
又由于f(x1)=wTx1+b=i=1∑mαiyixiTx1+b=α1y1x1Tx1+α2y2x2Tx1+i=3∑mαiyixiTx1+b
f(x1)=α1y1K11+α2y2K12+i=1∑mαiyiK1i+b
得到
i=3∑mαiyiK1i=f(x1)−α1y1K11−α2y2K12−b
同理可推理出
i=3∑mαiyiK2i=f(x2)−α1y1K12−α2y2K22−b
带入 α2(K11+K22−2K12)
=y2(y2−y1+cK11−cK12+i=3∑mαiyiK1i−i=3∑mαiyiK2i)
得到
α2(K11+K22−2K12)
=y2(f(x1)−y1−((f(x2)−y2)+α2y2(K11+K22−2K12))
令
ξ=K11+K22−2K12E1=f1(x)−y1E2=f2(x)−y2
α2ξ=E1−E2+α2y2ξ
α2new=α2y2+ξE1−E2
α1new=α1old+y1y2(α2old−α2new)
得到这些公式之后,还要考虑 αi 的取值范围满足0≤αi≤C(C 为正则约束)
满足方形约束
当y1=y2时 ,令α1−α2=k
max(0,−k)≤α2≤min(C,C−k)
当y1=y2 的时候,令α1+α2=k
max(0,k−C)≤α2≤min(C,k)
对于b 的更新
当0<α1new<c 满足时y1(wTx1+b)=1
得到wTx1+b=y1
b1new=y1−α1newy1K11−α2newy2K12−i=3∑mαiyiK1i
其中
y1−i=3∑mαiyiK1i=−E1+α1oldy1K11+α2oldy2K12+bold
则
b1new=−E1+bold+y1K11(α1old−α1new)+y2K12(α2old−α2new)
b2new=−E2+bold+y1K12(α1old−α1new)+y1K22(α2old−α2new)
bnew=2b1new+b2new
如何选择需要的α1,α2 呢
第一个选择违背kkt条件程度最大的,第二个选择∣E1−E2∣ 最大的
3.算法实现
import numpy as np
class SVM:
def __init__(self, kernel='linear', c=1.0, max_iter=1e9, eps=1e-9):
self.kernel = str(kernel)
self.max_iter = int(max_iter)
self.eps = float(eps)
self.C = float(c)
self.B = 0.0
def __init_args(self, features, labels):
self.X = features
self.Y = labels
self.m, self.n = self.X.shape
self.Alpha = np.zeros(self.m)
def f(self, i):
c = self.B
for j in range(self.m):
s = sum(self.X[j] * self.X[i])
c += self.Alpha[j] * self.Y[j] * s
return c
def k(self, i, j):
return sum(self.X[i] * self.X[j])
def e(self, i):
return self.f(i) - self.Y[i]
def kkt(self, i):
st1 = self.Alpha[i] >= 0
st2 = self.Y[i] * self.f(i) >= 1
st3 = self.Alpha[i]*(self.Y[i] * self.f(i) - 1) == 0
st = st1 and st2 and st3
return st
def select_ij(self):
working_set = [i for i in range(self.m) if not self.kkt(i)]
e_cache = [self.e(i) for i in working_set]
e_full = [self.e(i) for i in range(self.m)]
if len(working_set) == 0:
return -1, -1
max_pair_product = -np.inf
i1 = working_set[e_cache.index(max(e_cache))]
i2 = -1
for j in working_set:
if j == i1:
continue
pair_product = abs(e_full[i1] - e_full[j])
if pair_product > max_pair_product:
max_pair_product = pair_product
i2 = j
if max_pair_product <= self.eps:
return -1, -1
return i1, i2
@staticmethod
def limits(alpha, low, high):
if alpha < low:
return low
elif alpha > high:
return high
else:
return alpha
def fit(self, features, labels):
self.__init_args(features, labels)
for _ in range(self.max_iter):
i, j = self.select_ij()
if i == -1 and j == -1:
break
alpha_i_old = self.Alpha[i]
alpha_j_old = self.Alpha[j]
tau_ij = self.k(i, i) + self.k(j, j) - 2 * self.k(i, j)
alpha_j_new = alpha_j_old * self.Y[j] + (self.e(i) - self.e(j)) / tau_ij
if self.Y[i] != self.Y[j]:
low = max(0.0, (alpha_j_old - alpha_i_old).item())
high = min(self.C, self.C + (alpha_j_old - alpha_i_old).item())
else:
low = max(0.0, (alpha_i_old + alpha_j_old - self.C).item())
high = min(self.C, (alpha_j_old + alpha_i_old).item())
self.Alpha[j] = self.limits(alpha_j_new, low, high)
self.Alpha[i] = alpha_i_old + self.Y[i] * self.Y[j] * (alpha_j_old - self.Alpha[j])
bi = - self.e(i) + self.B + self.Y[i] * self.k(i, i) * (alpha_i_old - self.Alpha[i]) \
+ self.Y[j] * self.k(i, j) * (alpha_j_old - self.Alpha[j])
bj = - self.e(j) + self.B + self.Y[i] * self.k(i, j) * (alpha_i_old - self.Alpha[i]) \
+ self.Y[j] * self.k(j, j) * (alpha_j_old - self.Alpha[j])
if 0 < self.Alpha[i] < self.C:
self.B = bi
elif 0 < self.Alpha[j] < self.C:
self.B = bi
else:
self.B = (bi + bj) / 2
def get_coefficient(self):
weights = np.zeros(self.n, dtype=float)
for i in range(self.m):
weights += self.Y[i] * self.Alpha[i] * self.X[i]
bias = self.B
return weights, bias
x = np.array([[1, 0, 1], [1, 1, 0], [0, 1, 1],
[3, 0, 0], [0, 3, 0], [0, 0, 3]])
y = np.array([-1, -1, -1, 1, 1, 1])
svm = SVM(kernel='linear', c=1000, max_iter=1e4)
svm.fit(x, y)
w, b = svm.get_coefficient()
print(w)
print(b)
z = sum((w * x).T) + b
print(z)
抽空找个数据集跑跑