目录11

文章统计学习方法第 2 章

感知机

统计学习方法 · 第 2 / 3 章

感知机

输入空间: X⊆Rn\mathcal{X}\subseteq\mathbb{R}^n , x=(x(1),x(2),⋯ ,x(n))x=(x^{(1)},x^{(2)},\cdots,x^{(n)})

输出空间: Y={+1,−1}\mathcal{Y}=\{+1,-1\}, y∈Yy\in\mathcal{Y}

感知机:

f(x)=sign⁡(w⋅x+b)f(x)=\operatorname{sign}(w\cdot x+b)

其中,w=(w(1),w(2),⋯ ,w(n))T∈Rnw=(w^{(1)},w^{(2)},\cdots,w^{(n)})^{\mathrm{T}}\in\mathbb{R}^n 称为权值(Weight),b∈Rb\in\mathbb{R} 称为偏置(Bias)

假设空间: F={f∣f(x)=w⋅x+b}\mathcal{F}=\{f\mid f(x)=w\cdot x+b\}


感知机有如下几何解释:线性方使用方程

w⋅x+b=0w\cdot x+b=0

对应于特征空间 Rn\mathbb{R}^n 中的一个超平面 SS。其中,ww 是超平面的法向量,bb 是超平面的截距。

image.png

线性可分

给定数据集

T={(x1,y1),(x2,y2),⋯ ,(xN,yN)}T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\}

若存在某个超平面 S:w⋅x+b=0S:w\cdot x+b=0, 能够将数据集的正负实例点完全正确地划分到超平面两侧,即

{yi=+1,则 w⋅xi+b>0,yi=−1,则 w⋅xi+b<0,\begin{cases}
y_i=+1, & \text{则 } w\cdot x_i+b>0,\\
y_i=-1, & \text{则 } w\cdot x_i+b<0,
\end{cases}

那么,称 TT 为线性可分数据集;否则,称 TT 为线性不可分数据集。

线性可分的充要条件

设集合 S⊂RnS \subset \mathbf{R}^n 是由 Rn\mathbf{R}^n 中的 kk 个点所组成的集合,即 S={x1,x2,⋯ ,xk}S = \{x_1,x_2,\cdots,x_k\}。定义 SS 的凸壳 conv(S)\text{conv}(S) 为

conv(S)={x=∑i=1kλixi ∣ ∑i=1kλi=1, λi≥0, i=1,2,⋯ ,k}\text{conv}(S) = \left\{ x = \sum_{i=1}^{k}\lambda_i x_i \,\bigg|\, \sum_{i=1}^{k}\lambda_i = 1,\ \lambda_i \ge 0,\ i=1,2,\cdots,k \right\}

凸壳是凸集

设正实例点集为 PP,负实例点集为 NN, 则样本集线性可分的充分必要条件是正实例点集所构成的凸壳与负实例点集所构成的凸壳互不相交。即:

样本集线性可分  ⟺  conv⁡(P)∩conv⁡(N)=∅\text{样本集线性可分}
\iff
\operatorname{conv}(P)\cap\operatorname{conv}(N)=\varnothing

必要性证明
样本集线性可分  ⟹  conv⁡(P)∩conv⁡(N)=∅.\text{样本集线性可分}
\implies
\operatorname{conv}(P)\cap\operatorname{conv}(N)=\varnothing.

∀u∈conv⁡(P)\forall u\in \operatorname{conv}(P), 带入超平面判别函数:

w⊤u+b=w⊤(∑i=1mλixi)+b=∑i=1mλiw⊤xi+b=∑i=1mλi(w⊤xi+b).\begin{aligned}
w^\top u+b
&=w^\top\left(\sum_{i=1}^{m}\lambda_i x_i\right)+b\\
&=\sum_{i=1}^{m}\lambda_i w^\top x_i+b\\
&=\sum_{i=1}^{m}\lambda_i(w^\top x_i+b).
\end{aligned}

由于每个正实例点都满足 w⊤xi+b>0,w^\top x_i+b>0, 并且 λi≥0\lambda_i\geq0、∑iλi=1\sum_i\lambda_i=1,因此

w⊤u+b>0w^\top u+b>0

这说明正实例点的整个凸壳都位于超平面的正侧, 同理可证明负实例点凸壳位于负侧

充分性证明
conv⁡(P)∩conv⁡(N)=∅  ⟹  样本集线性可分.\operatorname{conv}(P)\cap\operatorname{conv}(N)=\varnothing
\implies
\text{样本集线性可分}.

正、负实例点集都是非空有限点集,所以它们的凸壳都是非空紧凸集。又因为两个凸壳互不相交,所以它们之间存在一对距离最短的点。记为

u∗∈conv⁡(P),v∗∈conv⁡(N).u^*\in\operatorname{conv}(P),
\qquad
v^*\in\operatorname{conv}(N).

令 d=v∗−u∗d=v^*-u^*, 由于两个凸壳互不相交,故 u∗≠v∗,d≠0.u^*\neq v^*, d\neq0.

由于凸壳一定是凸集,∀u∈conv⁡(P)\forall u\in\operatorname{conv}(P) ,有:

(1−t)u∗+tu∈conv⁡(P),0≤t≤1(1-t)u^*+tu\in\operatorname{conv}(P),
\qquad 0\leq t\leq1

根据最短距离的性质,函数

∥v∗−[u∗+t(u−u∗)]∥2\left\|v^*-\big[u^*+t(u-u^*)\big]\right\|^2

在 t=0t=0 处取得区间 [0,1][0,1] 上的最小值,因此其右导数非负

−2(v∗−u∗)⊤(u−u∗)≥0.i.e.d⊤(u−u∗)≤0-2(v^*-u^*)^\top(u-u^*)\geq0.\quad i.e.\quad d^\top(u-u^*)\leq0

即 d⊤u≤d⊤u∗d^\top u\leq d^\top u^*, 同理,∀v∈conv⁡(N)\forall v\in\operatorname{conv}(N),可得 d⊤v≥d⊤v∗d^\top v\geq d^\top v^*, 因此

d⊤u≤d⊤u∗<d⊤v∗≤d⊤v.d^\top u\leq d^\top u^*
<
d^\top v^*
\leq d^\top v.

严格不等式来自

d⊤v∗−d⊤u∗=d⊤(v∗−u∗)=d⊤d=∥d∥2>0.\begin{aligned}
d^\top v^*-d^\top u^*
&=d^\top(v^*-u^*)\\
&=d^\top d\\
&=\|d\|^2\\
&>0.
\end{aligned}

定义

w=d,b=−d⊤u∗+d⊤v∗2.w=d,\qquad b=-\frac{d^\top u^*+d^\top v^*}{2}.

于是超平面为

d⊤x−d⊤u∗+d⊤v∗2=0.\boxed{ d^\top x-\frac{d^\top u^*+d^\top v^*}{2}=0 }.

令

c=d⊤u∗+d⊤v∗2.c=\frac{d^\top u^*+d^\top v^*}{2}.

因为

d⊤u∗<c<d⊤v∗,d^\top u^*<c<d^\top v^*,

所以对于任意正实例点 x∈Px\in P,由于 x∈conv⁡(P)x\in\operatorname{conv}(P),有

d⊤x≤d⊤u∗<c,d^\top x\leq d^\top u^*<c,

即

d⊤x−c<0.d^\top x-c<0.

对于任意负实例点 z∈Nz\in N,由于 z∈conv⁡(N)z\in\operatorname{conv}(N),有

d⊤z≥d⊤v∗>c,d^\top z\geq d^\top v^*>c,

即

d⊤z−c>0.d^\top z-c>0.

因此,该超平面将正、负实例点严格分开。 如果希望正实例点位于正侧,只需将 w,bw,b 同时取反即可,不影响超平面的几何位置。 所以样本集线性可分,充分性得证。


用Python验证线性可分

Python63 行

import numpy as np
from scipy.optimize import linprog


def check_linear_separable(X, y):
    """
    根据正负样本凸壳是否相交,判断数据集是否线性可分。

    参数
    ----
    X : shape (n_samples, n_features)
        样本特征矩阵
    y : shape (n_samples,)
        标签,必须为 -1 或 +1

    返回
    ----
    bool
        True 表示线性可分,False 表示线性不可分
    """
    X = np.asarray(X, dtype=float)
    y = np.asarray(y)

    P = X[y == 1]    # 正类样本
    N = X[y == -1]   # 负类样本

    if len(P) == 0 or len(N) == 0:
        raise ValueError("数据集必须同时包含正类和负类样本")

    # 设 z = [lambda, mu]
    m, n = len(P), len(N)
    d = X.shape[1]

    # P.T @ lambda - N.T @ mu = 0
    A_eq = np.block([
        [P.T, -N.T],
        [np.ones((1, m)), np.zeros((1, n))],
        [np.zeros((1, m)), np.ones((1, n))]
    ])

    b_eq = np.r_[np.zeros(d), 1.0, 1.0]

    # 目标函数为 0,只需判断是否存在可行解
    result = linprog(
        c=np.zeros(m + n),
        A_eq=A_eq,
        b_eq=b_eq,
        bounds=(0, None),
        method="highs"
    )

    if result.status == 0:
        # 存在两个凸壳的公共点
        return False
    elif result.status == 2:
        # 约束不可行,凸壳不相交
        return True
    else:
        # 求解器未能明确判断,不能擅自归类
        raise RuntimeError(
            f"无法确定线性可分性:{result.message}"
        )

损失函数

对于任意 x0∈Rnx_0\in\mathbb{R}^n,其到超平面 SS 的距离为

∣w⋅x0+b∣∥w∥\frac{\left|w\cdot x_0+b\right|}{\|w\|}
  • 若 x0x_0 是正确分类点,则

    ∣w⋅x0+b∣∥w∥={w⋅x0+b∥w∥,y0=+1,−w⋅x0+b∥w∥,y0=−1.\frac{\left|w\cdot x_0+b\right|}{\|w\|}
    =
    \begin{cases}
    \dfrac{w\cdot x_0+b}{\|w\|}, & y_0=+1,\\
    -\dfrac{w\cdot x_0+b}{\|w\|}, & y_0=-1.
    \end{cases}
  • 若 x0x_0 是错误分类点,则

    ∣w⋅x0+b∣∥w∥={−w⋅x0+b∥w∥,y0=+1,w⋅x0+b∥w∥,y0=−1.=−y0(w⋅x0+b)∥w∥\frac{\left|w\cdot x_0+b\right|}{\|w\|}=
    \begin {cases}
    -\dfrac {w\cdot x_0+b} {\|w\|}, & y_0=+1,\\
    \dfrac {w\cdot x_0+b} {\|w\|}, & y_0=-1.
    \end {cases}
    =
    -\frac{y_0(w\cdot x_0+b)}{\|w\|}

误分类点 xix_i 到 SS 的距离:

−1∥w∥yi(w⋅xi+b)-\frac{1}{\|w\|}y_i(w\cdot x_i+b)

所有误分类点到 SS 的距离:

−1∥w∥∑xi∈Myi(w⋅xi+b)-\frac{1}{\|w\|}\sum_{x_i\in M}y_i(w\cdot x_i+b)

其中,MM 代表所有误分类点的集合。

损失函数:

L(w,b)=−∑xi∈Myi(w⋅xi+b)L(w,b)=-\sum_{x_i\in M}y_i(w\cdot x_i+b)

模型参数估计:

arg⁡min⁡w,bL(w,b)\arg\min_{w,b} L(w,b)

梯度:

∇wL(w,b)=−∑xi∈Myixi,∇bL(w,b)=−∑xi∈Myi\nabla_w L(w,b)=-\sum_{x_i\in M}y_i x_i,
\qquad
\nabla_b L(w,b)=-\sum_{x_i\in M}y_i

参数更新:

  • 批量梯度下降法(Batch Gradient Descent):每次迭代时使用所有误分类点来进行参数更新。
w←w+η∑xi∈Myixi,b←b+η∑xi∈Myiw\leftarrow w+\eta\sum_{x_i\in M}y_i x_i,
    \qquad
    b\leftarrow b+\eta\sum_{x_i\in M}y_i

其中,0<η≤1 0< \eta \leq 1 代表步长。

  • 随机梯度下降法(Stochastic Gradient Descent):每次随机选取一个误分类点。
w←w+ηyixi,b←b+ηyiw\leftarrow w+\eta y_i x_i,
    \qquad
    b\leftarrow b+\eta y_i

学习算法的原始形式

输入: 训练数据集 T={(x1,y1),(x2,y2),⋯ ,(xN,yN)}T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\},其中 xi∈X=Rnx_i\in\mathcal{X}=\mathbb{R}^n,yi∈Y={−1,+1}y_i\in\mathcal{Y}=\{-1,+1\},i=1,2,⋯ ,Ni=1,2,\cdots,N;学习率 η\eta(0<η≤10<\eta\leq 1)。

输出: w,bw,b;感知机模型 f(x)=sign⁡(w⋅x+b)f(x)=\operatorname{sign}(w\cdot x+b)。

  1. 选取初值 w0,b0w_0,b_0;

  2. 在训练集中选取数据 (xi,yi)(x_i,y_i);

  3. 如果 yi(w⋅xi+b)≤0y_i(w\cdot x_i+b)\leq 0,则

    w←w+ηyixib←b+ηyiw\leftarrow w+\eta y_i x_i\\
    b\leftarrow b+\eta y_i
  4. 转至步骤(2),直至训练集中没有误分类点。

原始算法的Python实现

Python49 行
import numpy as np

class Perceptron:
    def __init__(self, eta=0.1, max_iter=10000):
        self.eta = eta
        self.max_iter = max_iter
        self.w = None
        self.b = None
        self.n_iter_ = 0
        self.converged_ = False

    def fit(self, X, y):
        X = np.asarray(X, dtype=float)
        y = np.asarray(y)

        n_samples, n_features = X.shape

        # 1. 初始化参数
        self.w = np.zeros(n_features)
        self.b = 0.0

        # 2. 反复遍历训练集
        for epoch in range(self.max_iter):
            errors = 0

            for i in range(n_samples):
                x_i, y_i = X[i], y[i]

                # 3. 判断是否误分类
                if y_i * (self.w @ x_i + self.b) <= 0:
                    self.w += self.eta * y_i * x_i
                    self.b += self.eta * y_i
                    errors += 1

            self.n_iter_ = epoch + 1

            # 4. 没有误分类点,训练结束
            if errors == 0:
                self.converged_ = True
                break

        return self

    def predict_one(self, x):
        return 1 if self.w @ x + self.b >= 0 else -1

    def predict(self, X):
        X = np.asarray(X, dtype=float)
        return np.where(X @ self.w + self.b >= 0, 1, -1)

实例

数据点为

Python2 行
X = np.array([[3,3], [4,3], [1,1]])
y = np.array([1, 1, -1])

image.png

构建最优化问题:

min⁡w,bL(w,b)=−∑xi∈Myi(w⋅xi+b)\min_{w,b} L(w,b) = -\sum_{x_i\in M} y_i(w \cdot x_i + b)

按照原始算法求解 w,bw,b。η=1\eta = 1。

(1)取初值 w0=0, b0=0w_0 = 0,\ b_0 = 0

(2)对 x1=(3,3)Tx_1 = (3,3)^\mathrm{T},y1(w0⋅x1+b0)=0y_1(w_0 \cdot x_1 + b_0) = 0,未能被正确分类,更新 w, bw,\ b

w1=w0+y1x1=(3,3)T, b1=b0+y1=1w_1 = w_0 + y_1 x_1 = (3,3)^\mathrm{T},\ b_1 = b_0 + y_1 = 1

得到线性模型

w1⋅x+b1=3x(1)+3x(2)+1w_1 \cdot x + b_1 = 3x^{(1)} + 3x^{(2)} + 1

(3)对 x1,x2x_1,x_2,显然,yi(w1⋅xi+b1)>0y_i(w_1 \cdot x_i + b_1) > 0,被正确分类,不修改 w, bw,\ b; 对 x3=(1,1)Tx_3 = (1,1)^\mathrm{T},y3(w1⋅x3+b1)<0y_3(w_1 \cdot x_3 + b_1) < 0,被误分类,更新 w, bw,\ b。

w2=w1+y3x3=(2,2)T, b2=b1+y3=0w_2 = w_1 + y_3 x_3 = (2,2)^\mathrm{T},\ b_2 = b_1 + y_3 = 0

得到线性模型

w2⋅x+b2=2x(1)+2x(2)w_2 \cdot x + b_2 = 2x^{(1)} + 2x^{(2)}

如此继续下去,直到

w7=(1,1)T, b7=−3w_7 = (1,1)^\mathrm{T},\ b_7 = -3
w7⋅x+b7=x(1)+x(2)−3w_7 \cdot x + b_7 = x^{(1)} + x^{(2)} - 3

对所有数据点 yi(w7⋅xi+b7)>0y_i(w_7 \cdot x_i + b_7) > 0,没有误分类点,损失函数达到极小。

分离超平面为:x(1)+x(2)−3=0x^{(1)} + x^{(2)} - 3 = 0

感知机模型为:f(x)=sign⁡(x(1)+x(2)−3)f(x) = \operatorname{sign}(x^{(1)} + x^{(2)} - 3)

迭代次数 误分类点 ww bb w⋅x+bw \cdot x + b
0 00 0 0 0 0
1 x1x_1 (3,3)T(3,3)^\mathrm{T} 1 1 3x(1)+3x(2)+1 3x^{(1)} + 3x^{(2)} + 1
2 x3x_3 (2,2)T(2,2)^\mathrm{T} 0 0 2x(1)+2x(2) 2x^{(1)} + 2x^{(2)}
3 x3x_3 (1,1)T(1,1)^\mathrm{T} −1-1 x(1)+x(2)−1x^{(1)} + x^{(2)} - 1
4 x3x_3 (0,0)T(0,0)^\mathrm{T} −2-2 −2-2
5 x1x_1 (3,3)T(3,3)^\mathrm{T} −1-1 3x(1)+3x(2)−1 3x^{(1)} + 3x^{(2)} - 1
6 x3x_3 (2,2)T(2,2)^\mathrm{T} −2-2 2x(1)+2x(2)−2 2x^{(1)} + 2x^{(2)} - 2
7 x3x_3 (1,1)T(1,1)^\mathrm{T} −3-3 x(1)+x(2)−3x^{(1)} + x^{(2)} - 3
8 无 (1,1)T(1,1)^\mathrm{T} −3-3 x(1)+x(2)−3x^{(1)} + x^{(2)} - 3

image.png

绘图代码
Python123 行

import numpy as np
import matplotlib.pyplot as plt
import math

# ==================== 1. 输入数据 ====================
X = np.array([[3, 3], [4, 3], [1, 1]])
y = np.array([1, 1, -1])

eta = 1                 # 学习率
max_updates = 100        # 最大更新次数

# LaTeX 公式设置(需要本机安装 LaTeX)
plt.rcParams['text.usetex'] = True
plt.rcParams['text.latex.preamble'] = r'\usepackage{amsmath}'

# ==================== 2. 感知机迭代 ====================
w = np.zeros(X.shape[1], dtype=float)
b = 0.0

# 保存初始状态
history = [(w.copy(), b, None)]

for _ in range(max_updates):
    updated = False

    # 按输入顺序寻找第一个误分类点
    for i in range(len(X)):
        if y[i] * (np.dot(w, X[i]) + b) <= 0:
            w += eta * y[i] * X[i]
            b += eta * y[i]

            # 保存更新后的参数和本次误分类点
            history.append((w.copy(), b, i))
            updated = True
            break

    # 一轮扫描没有误分类点,算法收敛
    if not updated:
        break
else:
    print(f"达到最大更新次数 {max_updates},算法可能尚未收敛。")

# ==================== 3. 绘制迭代过程 ====================
n = len(history)
cols = min(3, n)
rows = math.ceil(n / cols)

fig, axes = plt.subplots(
    rows, cols,
    figsize=(5 * cols, 4 * rows),
    squeeze=False
)

# 统一坐标范围,方便比较, 实际使用可能需要微调
xmin, ymin = X.min(axis=0) - 5
xmax, ymax = X.max(axis=0) + 5
x1 = np.linspace(xmin, xmax, 300)

for step, (w, b, error_idx) in enumerate(history):
    ax = axes.flat[step]

    # 绘制两类样本
    ax.scatter(
        X[y == 1, 0], X[y == 1, 1],
        marker='o', facecolors='none',
        edgecolors='black', s=60, label=r'$+1$'
    )
    ax.scatter(
        X[y == -1, 0], X[y == -1, 1],
        marker='x', color='black',
        s=60, label=r'$-1$'
    )

    # 高亮本次更新所使用的误分类点
    if error_idx is not None:
        ax.scatter(
            X[error_idx, 0], X[error_idx, 1],
            s=180, facecolors='none',
            edgecolors='red', linewidths=1.8,
            label='Misclassified'
        )

    # 绘制 w·x + b = 0
    if np.allclose(w, 0):
        message = 'All points on boundary' if np.isclose(b, 0) \
                  else 'No decision boundary'
        ax.text(
            0.5, 0.5, message,
            transform=ax.transAxes,
            ha='center', va='center'
        )
    elif not np.isclose(w[1], 0):
        x2 = -(w[0] * x1 + b) / w[1]
        ax.plot(x1, x2, color='tab:red', lw=1.8)
    else:
        ax.axvline(-b / w[0], color='tab:red', lw=1.8)

    ax.set_title(
        rf'Iteration {step}: '
        rf'$w=({w[0]:g},{w[1]:g})^T,\ b={b:g}$'
    )
    ax.set_xlim(xmin, xmax)
    ax.set_ylim(ymin, ymax)
    ax.set_xlabel(r'$x^{(1)}$')
    ax.set_ylabel(r'$x^{(2)}$')
    ax.tick_params(direction='in')
    ax.grid(alpha=0.2)

    # 只在第一张图中显示图例
    if step == 0:
        ax.legend(frameon=False, fontsize=9)

# 隐藏多余子图
for ax in axes.flat[n:]:
    ax.set_visible(False)

fig.suptitle('Perceptron Learning Process', fontsize=15)
plt.tight_layout()
plt.show()

print(f"总更新次数:{len(history) - 1}")
print(f"最终 w = {w}, b = {b}")

算法的收敛性

记 w^=(wT,b)T\hat{w}=(w^T,b)^T,x^=(xT,1)T\hat{x}=(x^T,1)^T,则分离超平面可以写为

w^⋅x^=0\hat{w}\cdot\hat{x}=0

若训练集

T={(x1,y1),(x2,y2),⋯ ,(xN,yN)}T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\}

线性可分,其中,xi∈X⊆Rnx_i\in\mathcal{X}\subseteq\mathbb{R}^n,yi∈Y={+1,−1}y_i\in\mathcal{Y}=\{+1,-1\},则

  1. 存在满足条件 ∥w^opt∥=1\|\hat{w}_{opt}\|=1 的超平面 w^opt⋅x^=0\hat{w}_{opt}\cdot\hat{x}=0 可将 TT 完全正确分开;且 ∃γ>0\exists\gamma>0,对所有 i=1,2,⋯ ,Ni=1,2,\cdots,N,
yi(w^opt⋅x^i)≥γy_i(\hat{w}_{opt}\cdot\hat{x}_i)\geq\gamma
  1. 令 R=max⁡1≤i≤N∥x^i∥R=\max_{1\leq i\leq N}\|\hat{x}_i\|,则感知机算法在 TT 上的误分类次数 kk 满足不等式
k≤(Rγ)2k\leq\left(\frac{R}{\gamma}\right)^2

证明

1.由于 TT 是线性可分的,由定义,存在超平面可将训练数据集完全正确分开,取此超平面为

w^opt⋅x^=wopt⋅x+bopt=0,∥w^opt∥=1\hat{w}_{opt}\cdot\hat{x}=w_{opt}\cdot x+b_{opt}=0 ,\quad\|\hat{w}_{opt}\|=1

由于对有限的 i=1,2,⋯ ,Ni=1,2,\cdots,N,均有

yi(w^opt⋅x^i)=yi(wopt⋅xi+bopt)>0y_i(\hat{w}_{opt}\cdot\hat{x}_i)
=y_i(w_{opt}\cdot x_i+b_{opt})>0

记 γ=min⁡i{yi(wopt⋅xi+bopt)}\gamma=\min_i\left\{y_i(w_{opt}\cdot x_i+b_{opt})\right\} , 则有

yi(w^opt⋅x^i)=yi(wopt⋅xi+bopt)≥γy_i(\hat{w}_{opt}\cdot\hat{x}_i)
=y_i(w_{opt}\cdot x_i+b_{opt})\geq\gamma

2.假设 w^0=0\hat w_0 = 0,若实例被误分类,则更新权重。令 w^k−1\hat w_{k-1} 为第 kk 个误分类实例之前的扩充权重向量,即

‘w^k−1=(wk−1T, bk−1)‘`\hat w_{k-1} = (w_{k-1}^T,\ b_{k-1})`

若

yi(w^k−1⋅x^i)=yi(wk−1⋅xi+bk−1)≤0y_i(\hat w_{k-1} \cdot \hat x_i) = y_i(w_{k-1} \cdot x_i + b_{k-1}) \le 0

则,(xi,yi)(x_i,y_i) 为第 kk 个误分类实例,更新参数,

{wk←wk−1+ηyixibk←bk−1+ηyii.e.w^k=w^k−1+ηyix^i\begin{cases}
w_k \leftarrow w_{k-1} + \eta y_i x_i \\
b_k \leftarrow b_{k-1} + \eta y_i
\end{cases}
\quad i.e.\quad\hat w_k = \hat w_{k-1} + \eta y_i \hat x_i

下面推导两个不等式

{w^k⋅w^opt≥kηγ∥w^k∥2≤kη2R2\begin{cases}
\hat w_k \cdot \hat w_{opt} \ge k\eta\gamma \\
\|\hat w_k\|^2 \le k\eta^2 R^2
\end{cases}

w^k⋅w^opt≥kηγ\hat w_k \cdot \hat w_{opt} \ge k\eta\gamma:

w^k⋅w^opt=(w^k−1+ηyix^i)⋅w^opt=w^k−1⋅w^opt+ηyiw^opt⋅x^i≥w^k−1⋅w^opt+ηγ≥w^k−2⋅w^opt+2ηγ⋮≥w^1⋅w^opt+(k−1)ηγ≥w^0⋅w^opt+kηγ=kηγ\begin{aligned}
\hat w_k \cdot \hat w_{opt} &= (\hat w_{k-1} + \eta y_i \hat x_i) \cdot \hat w_{opt} \\
&= \hat w_{k-1} \cdot \hat w_{opt} + \eta y_i \hat w_{opt} \cdot \hat x_i \\
&\ge \hat w_{k-1} \cdot \hat w_{opt} + \eta\gamma \\
&\ge \hat w_{k-2} \cdot \hat w_{opt} + 2\eta\gamma \\
&\quad \vdots \\
&\ge \hat w_1 \cdot \hat w_{opt} + (k-1)\eta\gamma \\
&\ge \hat w_0 \cdot \hat w_{opt} + k\eta\gamma \\
&= k\eta\gamma
\end{aligned}

∥w^k∥2≤kη2R2\|\hat w_k\|^2 \le k\eta^2 R^2:

∥w^k∥2=∥w^k−1+ηyix^i∥2=∥w^k−1∥2+2ηyiw^k−1⋅x^i+η2∥x^i∥2≤∥w^k−1∥2+η2∥x^i∥2≤∥w^k−1∥2+η2R2≤∥w^k−2∥2+2η2R2⋮≤∥w^1∥2+(k−1)η2R2≤∥w^0∥2+kη2R2=kη2R2\begin{aligned}
\|\hat w_k\|^2 &= \|\hat w_{k-1} + \eta y_i \hat x_i\|^2 \\
&= \|\hat w_{k-1}\|^2 + 2\eta y_i \hat w_{k-1} \cdot \hat x_i + \eta^2 \|\hat x_i\|^2 \\
&\le \|\hat w_{k-1}\|^2 + \eta^2 \|\hat x_i\|^2 \\
&\le \|\hat w_{k-1}\|^2 + \eta^2 R^2 \\
&\le \|\hat w_{k-2}\|^2 + 2\eta^2 R^2 \\
&\quad \vdots \\
&\le \|\hat w_1\|^2 + (k-1)\eta^2 R^2 \\
&\le \|\hat w_0\|^2 + k\eta^2 R^2 \\
&= k\eta^2 R^2
\end{aligned}

结合所得两个不等式,

{w^k⋅w^opt≥kηγ∥w^k∥2≤kη2R2  ⟹  kηγ≤w^k⋅w^opt≤∥w^k∥∥w^opt∥≤kηR\begin{cases}
\hat w_k \cdot \hat w_{opt} \ge k\eta\gamma \\
\|\hat w_k\|^2 \le k\eta^2 R^2
\end{cases}
\implies k\eta\gamma \le \hat w_k \cdot \hat w_{opt} \le \|\hat w_k\| \|\hat w_{opt}\| \le \sqrt{k}\eta R

得到:

kηγ≤kηR  ⟹  k2γ2≤kR2k\eta\gamma \le \sqrt{k}\eta R \implies k^2\gamma^2 \le kR^2

于是,有

k≤(Rγ)2k \le \left(\frac{R}{\gamma}\right)^2
  • 收敛性:对于线性可分的 TT,经过有限次搜索,可得将 TT 完全正确分开的分离超平面。
  • 依赖性:不同的初值选择,或者迭代过程中不同的误分类点选择顺序,可能会得到不同的分离超平面。
  • 对于线性不可分的 TT,算法不收敛,迭代结果会发生震荡。
  • 为得到唯一分离超平面,需增加约束条件。

对偶形式

在原始形式中,若(xi,yi)(x_i,y_i)为误分类点,可如下更新参数:

w←w+ηyixi;b←b+ηyiw \leftarrow w + \eta y_i x_i; \qquad b \leftarrow b + \eta y_i

假设初始值 w0=0, b0=0w_0 = \boldsymbol{0},\ b_0 = 0,对误分类点 (xi,yi)(x_i,y_i) 通过上述公式更新参数,修改 nin_i 次之后,w,bw,b 的增量分别为 αiyixi\alpha_i y_i x_i 和 αiyi\alpha_i y_i,其中 αi=niη\alpha_i = n_i \eta。

最后学习到的参数为,

w=∑i=1Nαiyixi;b=∑i=1Nαiyiw = \sum_{i=1}^{N} \alpha_i y_i x_i; \qquad b = \sum_{i=1}^{N} \alpha_i y_i

算法 (感知机学习算法的对偶形式)

输入:线性可分的数据集 T={(x1,y1),(x2,y2),⋯ ,(xN,yN)}T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\},其中 xi∈Rn, yi∈{−1,+1}, i=1,2,⋯ ,Nx_i\in\mathbf{R}^n,\ y_i\in\{-1,+1\},\ i=1,2,\cdots,N;学习率 η (0<η≤1)\eta\ (0<\eta\le1);

输出:α,b\alpha,b;感知机模型 f(x)=sign(∑j=1Nαjyjxj⋅x+b)f(x) = \text{sign}\left(\sum_{j=1}^{N}\alpha_j y_j x_j \cdot x + b\right),其中 α=(α1,α2,⋯ ,αN)T\alpha = (\alpha_1,\alpha_2,\cdots,\alpha_N)^\text{T}。

(1) α←0, b←0\alpha \leftarrow 0,\ b \leftarrow 0;

(2) 在训练集中选取数据 (xi,yi)(x_i,y_i);

(3) 如果 yi(∑j=1Nαjyjxj⋅xi+b)≤0y_i\left(\sum_{j=1}^{N}\alpha_j y_j x_j \cdot x_i + b\right) \le 0,

αi←αi+ηb←b+ηyi\begin{aligned}
\alpha_i &\leftarrow \alpha_i + \eta \\
b &\leftarrow b + \eta y_i
\end{aligned}

(4) 转至 (2) 直到没有误分类数据。


(3) 若 yi(∑j=1Nαjyjxj⋅xi+b)≤0y_i\left(\sum_{j=1}^{N}\alpha_j y_j x_j \cdot x_i + b\right) \le 0,

αi←αi+η;b←b+ηyi\alpha_i \leftarrow \alpha_i + \eta; \qquad b \leftarrow b + \eta y_i

迭代条件:

yi(∑j=1Nαjyjxj⋅xi+b)=yi[(α1y1x1+α2y2x2+⋯+αNyNxN)⋅xi+b]=yi(α1y1x1⋅xi+α2y2x2⋅xi+⋯+αNyNxN⋅xi+b)≤0\begin{aligned}
y_i\left(\sum_{j=1}^{N}\alpha_j y_j x_j \cdot x_i + b\right)
&= y_i\left[(\alpha_1 y_1 x_1 + \alpha_2 y_2 x_2 + \cdots + \alpha_N y_N x_N)\cdot x_i + b\right] \\
&= y_i\left(\alpha_1 y_1 x_1\cdot x_i + \alpha_2 y_2 x_2\cdot x_i + \cdots + \alpha_N y_N x_N\cdot x_i + b\right) \\
&\le 0
\end{aligned}

Gram 矩阵:

G=[xi⋅xj]N×N=[x1⋅x1x1⋅x2⋯x1⋅xNx2⋅x1x2⋅x2⋯x2⋅xN⋮⋮⋮xN⋅x1xN⋅x2⋯xN⋅xN]G = [x_i \cdot x_j]_{N\times N} =
\begin{bmatrix}
x_1\cdot x_1 & x_1\cdot x_2 & \cdots & x_1\cdot x_N \\
x_2\cdot x_1 & x_2\cdot x_2 & \cdots & x_2\cdot x_N \\
\vdots & \vdots & & \vdots \\
x_N\cdot x_1 & x_N\cdot x_2 & \cdots & x_N\cdot x_N
\end{bmatrix}

对偶形式的Python实现

Python63 行

import numpy as np


class Perceptron:
    def __init__(self, eta=0.1, max_iter=10000):
        self.eta = eta
        self.max_iter = max_iter
        self.alpha = None
        self.b = 0.0
        self.X_train = None
        self.y_train = None
        self.converged_ = False
        self.n_iter_ = 0

    def fit(self, X, y):
        X = np.asarray(X, dtype=float)
        y = np.asarray(y, dtype=int)

        n_samples = len(X)
        self.X_train = X
        self.y_train = y

        # (1) 初始化 alpha 和 b
        self.alpha = np.zeros(n_samples)
        self.b = 0.0
        self.converged_ = False

        # 预先计算训练样本之间的内积
        gram = X @ X.T

        # (2) 反复选取训练样本
        for epoch in range(self.max_iter):
            errors = 0

            for i in range(n_samples):
                # sum(alpha_j * y_j * x_j · x_i) + b
                score = np.sum(
                    self.alpha * self.y_train * gram[:, i]
                ) + self.b

                # (3) 如果误分类,更新 alpha_i 和 b
                if y[i] * score <= 0:
                    self.alpha[i] += self.eta
                    self.b += self.eta * y[i]
                    errors += 1

            self.n_iter_ = epoch + 1

            # (4) 没有误分类点,训练结束
            if errors == 0:
                self.converged_ = True
                break

        return self

    def decision_function(self, X):
        X = np.asarray(X, dtype=float)
        gram = X @ self.X_train.T
        return gram @ (self.alpha * self.y_train) + self.b

    def predict(self, X):
        return np.where(self.decision_function(X) >= 0, 1, -1)