感知机模型对数据的要求:训练数据集中需存在某个超平面能够将数据集的正实例点和负实例点完全正确地划分到超平面的两侧,即训练数据集是线性可分的。因为只有当训练数据集是线性可分时,感知机学习算法才是收敛的;如果训练数据集线性不可分,则感知机学习算法不收敛,迭代结果会发生震荡。当训练数据集线性不可分时,可以使用线性支持向量机。

感知机模型的学习过程:依据训练数据集求得感知机模型,即求得模型参数 w w w b b b

感知机模型的预测过程:通过学习得到的感知机模型,计算新的输入实例所对应的输出类别。

感知机模型的类别划分

  • 用于解决二类分类问题的监督学习模型
  • 非概率模型:模型取函数形式(而非概率模型的条件概率分布形式)
  • 线性模型:模型函数为线性函数
  • 参数化模型:模型参数的维度固定
  • 判别模型:直接学习决策函数 f ( x ) f(x) f(x)

感知机模型的主要优点:算法简单,易于实现。

感知机模型的主要缺点:要求训练数据集线性可分。


2.2 感知机学习策略

常见范数的定义和性质

范数 ,对应闵可夫斯基距离 (Minkowski distance) 。假设 n 维向量 x = ( x 1 , x 2 , ⋯   , x n ) T x = (x_1,x_2,\cdots,x_n)^T x=(x1,x2,,xn)T,其 Lp 范数记作 ∣ ∣ x ∣ ∣ p ||x||_p xp,定义为 ∣ ∣ x ∣ ∣ p = ( ∣ x 1 ∣ p + ∣ x 2 ∣ p + ⋯ + ∣ x n ∣ p ) 1 p ||x||_p = (|x_1|^p+|x_2|^p+\cdots+|x_n|^p)^{\frac{1}{p}} xp=(x1p+x2p++xnp)p1 。范数具有如下定义:

  • 正定性: ∣ ∣ x ∣ ∣ ≥ 0 ||x|| \ge 0 x0,且有 ∣ ∣ x ∣ ∣ = 0 ⇔ x = 0 ||x||=0 \Leftrightarrow x=0 x=0x=0
  • 正齐次性: ∣ ∣ c x ∣ ∣ = ∣ c ∣   ∣ ∣ x ∣ ∣ ||cx|| = |c| \ ||x|| cx=c x
  • 次可加性(三角不等式): ∣ ∣ x + y ∣ ∣ ≤ ∣ ∣ x ∣ ∣ + ∣ ∣ y ∣ ∣ ||x+y|| \le ||x|| + ||y|| x+yx+y

L0 范数

假设 n 维向量 x = ( x 1 , x 2 , ⋯   , x n ) T x = (x_1,x_2,\cdots,x_n)^T x=(x1,x2,,xn)T,其 L0 范数记作 ∣ ∣ x ∣ ∣ 0 ||x||_0 x0,定义为向量中非 0 元素的个数。

L1 范数

假设 n 维向量 x = ( x 1 , x 2 , ⋯   , x n ) T x = (x_1,x_2,\cdots,x_n)^T x=(x1,x2,,xn)T,其 L1 范数记作 ∣ ∣ x ∣ ∣ 1 ||x||_1 x1,定义为 ∣ ∣ x ∣ ∣ 1 = ∣ x 1 ∣ + ∣ x 2 ∣ + ⋯ + ∣ x n ∣ ||x||_1 = |x_1|+|x_2|+\cdots+|x_n| x1=x1+x2++xn 。向量的 L1 范数即为向量中各个元素绝对值之和,对应曼哈顿距离 (Manhattan distance)。

L2 范数

假设 n 维向量 x = ( x 1 , x 2 , ⋯   , x n ) T x = (x_1,x_2,\cdots,x_n)^T x=(x1,x2,,xn)T,其 L2 范数记作 ∣ ∣ x ∣ ∣ 2 ||x||_2 x2,定义为 ∣ ∣ x ∣ ∣ 2 = ( ∣ x 1 ∣ 2 + ∣ x 2 ∣ 2 + ⋯ + ∣ x n ∣ 2 ) 1 2 ||x||_2 = (|x_1|^2+|x_2|^2+\cdots+|x_n|^2)^{\frac{1}{2}} x2=(x12+x22++xn2)21 。向量的 L2 范数即为向量中各个元素平方和的平方根,对应欧式距离 (Manhattan distance)。

无穷范数

假设 n 维向量 x = ( x 1 , x 2 , ⋯   , x n ) T x = (x_1,x_2,\cdots,x_n)^T x=(x1,x2,,xn)T,其无穷范数记作 ∣ ∣ x ∣ ∣ ∞ ||x||_\infty x,定义为 ∣ ∣ x ∣ ∣ ∞ = m a x ( ∣ x 1 ∣ , ∣ x 2 ∣ , ⋯   , ∣ x n ∣ ) ||x||_\infty = max(|x_1|,|x_2|,\cdots,|x_n|) x=max(x1,x2,,xn) 。向量的无穷范数即为向量中各个元素绝对值的最大值,对应切比雪夫距离 (Chebyshev distance)。

点到超平面距离公式的推导过程

已知 S S S 为 n 维欧式空间中的 n-1 维超平面 w ⋅ x + b = 0 w·x + b =0 wx+b=0,其中 w w w x x x 均为 n 维向量;另有 n 维空间中的点 x 0 = ( x 0 ( 1 ) , x 0 ( 2 ) , ⋯   , x 0 ( n ) ) x_0 = (x_0^{(1)},x_0^{(2)},\cdots,x_0^{(n)}) x0=(x0(1),x0(2),,x0(n)) 。求证:点 P P P 到超平面 S S S 的距离 d = 1 ∣ ∣ w ∣ ∣ 2 ∣ w ⋅ x 0 + b ∣ d = \frac{1}{||w||_2} |w·x_0+b| d=w21wx0+b ,其中 ∣ ∣ w ∣ ∣ 2 ||w||_2 w2 w w w 的 2-范数。

证明如下:

由超平面 S S S 的定义式可知 w w w 为超平面 S S S 的法向量, b b b 为超平面 S S S 的截距。

设点 x 0 x_0 x0 在超平面 S S S 上的投影为 x 1 = ( x 1 ( 1 ) , x 1 ( 2 ) , ⋯   , x 1 ( n ) ) x_1 = (x_1^{(1)},x_1^{(2)},\cdots,x_1^{(n)}) x1=(x1(1),x1(2),,x1(n)) ,则有

w ⋅ x 1 + b = 0 (1) w · x_1 + b = 0 \tag{1} wx1+b=0(1)

P P P 到超平面 S S S 的距离 d d d 即为向量 x 0 x 1 ⃗ \vec{x_0 x_1} x0x1 的长度。

因为 x 0 x 1 ⃗ \vec{x_0 x_1} x0x1 与超平面 S S S 的法向量 w w w 平行,所以 x 0 x 1 ⃗ \vec{x_0 x_1} x0x1 与法向量夹角的余弦值 c o s θ = 0 cos \theta = 0 cosθ=0 ,故有

w ⋅ x 0 x 1 ⃗ = ∣ w ∣   ∣ x 0 x 1 ⃗ ∣   c o s θ = ∣ w ∣   ∣ x 0 x 1 ⃗ ∣ = [ ( w ( 1 ) ) 2 + ( w ( 2 ) ) 2 + ⋯ + ( w ( n ) ) 2 ] 1 2   d = ∣ ∣ w ∣ ∣ 2 d (2) \begin{aligned} w · \vec{x_0 x_1} & = |w| \ |\vec{x_0 x_1}| \ cos \theta \\ & = |w| \ |\vec{x_0 x_1}| \\ & = [(w^{(1)})^2 + (w^{(2)})^2 + \cdots + (w^{(n)})^2]^\frac{1}{2} \ d \\ & = ||w||_2 d \end{aligned} \tag{2} wx0x1 =w x0x1  cosθ=w x0x1 =[(w(1))2+(w(2))2++(w(n))2]21 d=w2d(2)

又有(应用向量点积的分配律)

w ⋅ x 0 x 1 ⃗ = w ( 1 ) ( x 1 ( 1 ) − x 0 ( 1 ) ) + w ( 2 ) ( x 1 ( 2 ) − x 0 ( 2 ) ) + ⋯ + w ( n ) ( x 1 ( n ) − x 0 ( n ) ) = ( w ( 1 ) x 1 ( 1 ) + w ( 2 ) x 1 ( 2 ) + ⋯ + w ( n ) x 1 ( n ) ) − ( w ( 1 ) x 0 ( 1 ) + w ( 2 ) x 0 ( 2 ) + ⋯ + w ( n ) x 0 ( n ) ) = w ⋅ x 1 − w ⋅ x 0 (3) \begin{aligned} w · \vec{x_0 x_1} & = w^{(1)} (x_1^{(1)} - x_0^{(1)}) + w^{(2)} (x_1^{(2)} - x_0^{(2)}) + \cdots + w^{(n)} (x_1^{(n)} - x_0^{(n)}) \\ & = (w^{(1)} x_1^{(1)} + w^{(2)} x_1^{(2)} + \cdots + w^{(n)} x_1^{(n)}) - (w^{(1)} x_0^{(1)} + w^{(2)} x_0^{(2)} + \cdots + w^{(n)} x_0^{(n)}) \\ & = w·x_1 - w·x_0 \end{aligned} \tag{3} wx0x1 =w(1)(x1(1)x0(1))+w(2)(x1(2)x0(2))++w(n)(x1(n)x0(n))=(w(1)x1(1)+w(2)x1(2)++w(n)x1(n))(w(1)x0(1)+w(2)x0(2)++w(n)x0(n))=wx1wx0(3)

由式(1),有 w ⋅ x 1 = − b w·x_1 = -b wx1=b,故式(3)可以写成

w ⋅ x 0 x 1 ⃗ = w ⋅ x 1 − w ⋅ x 0 = − b − w ⋅ x 0 (4) \begin{aligned} w · \vec{x_0 x_1} & = w·x_1 - w·x_0 \\ & = -b - w·x_0 \end{aligned} \tag{4} wx0x1 =wx1wx0=bwx0(4)

由式(2)和式(4),得

∣ ∣ w ∣ ∣ 2 d = ∣ − b − w ⋅ x 0 ∣ d = 1 ∣ ∣ w ∣ ∣ 2 ∣ w ⋅ x 0 + b ∣ \begin{aligned} ||w||_2 d = |-b - w·x_0| \\ d = \frac{1}{||w||_2} |w·x_0+b| \end{aligned} w2d=bwx0d=w21wx0+b

得证。

2.3.1:感知机学习算法的原始形式

【补充说明】在例 2.1 的迭代过程中,选择误分类点的规则是选择索引最小的误分类点。

从梯度到更新方法的说明

对于某个参数组合 ( w 0 , b 0 ) (w_0,b_0) (w0,b0),因为误分类点集合 M M M 是固定的,所以梯度 ∇ L ( w 0 , b 0 ) \nabla L(w_0,b_0) L(w0,b0) 也是固定的,梯度在 w w w 上的分量 ∇ w L ( w 0 , b 0 ) \nabla w L(w_0,b_0) wL(w0,b0) 就是损失函数对 w w w 的偏导数,梯度在 b b b 上的分量 ∇ b L ( w 0 , b 0 ) \nabla_b L(w_0,b_0) bL(w0,b0) 就是损失函数对 b b b 的偏导数,于是有梯度

∇ w L ( w 0 , b 0 ) = L w ′ ( w 0 , b 0 ) = − ∑ x i ∈ M y i x i \nabla_w L(w_0,b_0) = L'_w(w_0,b_0) = - \sum_{x_i \in M} y_i x_i wL(w0,b0)=Lw(w0,b0)=xiMyixi

∇ b L ( w 0 , b 0 ) = L b ′ ( w 0 , b 0 ) = − ∑ x i ∈ M y i \nabla_b L(w_0,b_0) = L'_b(w_0,b_0) = - \sum_{x_i \in M} y_i bL(w0,b0)=Lb(w0,b0)=xiMyi

因为完整地计算出梯度需要用到所有的样本点,时间成本较高,所以这里我们使用时间速度快的随机梯度下降法。在每次迭代过程中,不是一次使 M M M 中所有误分类点的梯度下降,而是一次随机选取一个误分类点,使其梯度下降。对于单个误分类点 ( x i , y i ) (x_i,y_i) (xi,yi),有梯度

∇ w L ( w 0 , b 0 ) = L w ′ ( w 0 , b 0 ) = − y i x i \nabla_w L(w_0,b_0) = L'_w(w_0,b_0) = - y_i x_i wL(w0,b0)=Lw(w0,b0)=yixi

∇ b L ( w 0 , b 0 ) = L b ′ ( w 0 , b 0 ) = − y i \nabla_b L(w_0,b_0) = L'_b(w_0,b_0) = - y_i bL(w0,b0)=Lb(w0,b0)=yi

据此更新 w w w b b b

w ← w + η ( − ∇ w L ( w 0 , b 0 ) ) = w + η y i x i w \leftarrow w + \eta(-\nabla_w L(w_0,b_0)) = w + \eta y_i x_i ww+η(wL(w0,b0))=w+ηyixi

b ← b + η ( − ∇ b L ( w 0 , b 0 ) ) = b + η y i b \leftarrow b + \eta(-\nabla_b L(w_0,b_0)) = b + \eta y_i bb+η(bL(w0,b0))=b+ηyi

其中, η \eta η 为步长,通常取值范围为 ( 0 , 1 ] (0,1] (0,1],又称为学习率。

感知机学习算法的原始形式(Python 实现)

源码地址】code.perceptron.original_form_of_perceptron

# https://github.com/ChangxingJiang/Data-Mining-HandBook/blob/master/code/perceptron/_original_form.py

def original_form_of_perceptron(x, y, eta):
    """感知机学习算法的原始形式

    :param x: 输入变量
    :param y: 输出变量
    :param eta: 学习率
    :return: 感知机模型的w和b
    """
    n_samples = len(x)  # 样本点数量
    n_features = len(x[0])  # 特征向量维度数
    w0, b0 = [0] * n_features, 0  # 选取初值w0,b0

    while True:  # 不断迭代直至没有误分类点
        for i in range(n_samples):
            xi, yi = x[i], y[i]
            if yi * (sum(w0[j] * xi[j] for j in range(n_features)) + b0) <= 0:
                w1 = [w0[j] + eta * yi * xi[j] for j in range(n_features)]
                b1 = b0 + eta * yi
                w0, b0 = w1, b1
                break
        else:
            return w0, b0

源码地址】测试

>>> from code.perceptron import original_form_of_perceptron
>>> dataset = [[(3, 3), (4, 3), (1, 1)], [1, 1, -1]]
>>> original_form_of_perceptron(dataset[0], dataset[1], eta=1)
([1, 1], -3)

2.3.2:感知机学习算法的收敛性证明

【补充说明】扩充权重向量:扩充权重向量即将偏置并入权重向量的向量 w ^ = ( w T , b ) T \hat{w} = (w^T,b)^T w^=(wT,b)T。(下文直接使用了“扩充权重向量”的名称)

【补充说明】误分类次数:即存在误分类实例的迭代次数。

【问题】 为什么一定存在 ∣ ∣ w ^ o p t ∣ ∣ = 1 ||\hat{w}_{opt}||=1 w^opt=1

不妨设超平面的扩充权重向量为

w ^ o p t ′ = ( w ′ o p t T , b ′ ) T = ( w o p t ′ ( 1 ) , w o p t ′ ( 2 ) , ⋯   , w o p t ′ ( n ) , b ′ ) T \hat{w}'_{opt}=({w'}_{opt}^T,b')^T = (w'^{(1)}_{opt},w'^{(2)}_{opt},\cdots,w'^{(n)}_{opt},b')^T w^opt=(woptT,b)T=(wopt(1),wopt(2),,wopt(n),b)T

∣ ∣ w ^ o p t ′ ∣ ∣ ! = 1 ||\hat{w}'_{opt}||!=1 w^opt!=1,于是存在

w ^ o p t = ( w o p t ′ ( 1 ) ∣ ∣ w ^ o p t ′ ∣ ∣ , w o p t ′ ( 2 ) ∣ ∣ w ^ o p t ′ ∣ ∣ , ⋯   , w o p t ′ ( n ) ∣ ∣ w ^ o p t ′ ∣ ∣ , b ′ ∣ ∣ w ^ o p t ′ ∣ ∣ ) T \hat{w}_{opt} = (\frac{w'^{(1)}_{opt}}{||\hat{w}'_{opt}||},\frac{w'^{(2)}_{opt}}{||\hat{w}'_{opt}||},\cdots,\frac{w'^{(n)}_{opt}}{||\hat{w}'_{opt}||},\frac{b'}{||\hat{w}'_{opt}||})^T w^opt=(w^optwopt(1),w^optwopt(2),,w^optwopt(n),w^optb)T

此时 ∣ ∣ w ^ o p t ∣ ∣ = 1 ||\hat{w}_{opt}||=1 w^opt=1。得证。

例如: x ( 1 ) + x ( 2 ) − 3 x^{(1)}+x^{(2)}-3 x(1)+x(2)3 的扩充权重向量为 w ′ ^ = ( 1 , 1 , − 3 ) T \hat{w'} = (1,1,-3)^T w^=(1,1,3)T ∣ ∣ w ′ ^ ∣ ∣ = 11 ||\hat{w'}||=\sqrt{11} w^=11 ,于是有 w ^ = ( 1 11 , 1 11 , − 3 11 ) T \hat{w}=(\frac{1}{\sqrt{11}},\frac{1}{\sqrt{11}},\frac{-3}{\sqrt{11}})^T w^=(11 1,11 1,11 3)T,使 ∣ ∣ w ^ ∣ ∣ = 1 ||\hat{w}||=1 w^=1

2.3.3:感知机学习算法的对偶形式

【问题】为什么要有感知机学习算法的对偶形式?

结论是感知机学习算法的对偶形式在一定条件下的运算效率更高。下面我们展开讨论。

首先考虑感知机学习算法的原始形式和对偶形式的时间复杂度。原始形式每次迭代的时间复杂度为 O ( S × N ) O(S×N) O(S×N),总时间复杂度为 O ( S × N × K ) O(S×N×K) O(S×N×K);对偶形式每次迭代的时间复杂度为 O ( S 2 ) O(S^2) O(S2),另外还需要计算 Gram 矩阵的时间复杂度 O ( S 2 × N ) O(S^2×N) O(S2×N),总时间复杂度为 O ( S 2 × N + S 2 × K ) O(S^2×N+S^2×K) O(S2×N+S2×K);其中 S S S 为样本点数量, N N N 为样本特征向量的维度数, K K K 为迭代次数。

因为原始形式和对偶形式的迭代步骤是相互对应的,所以一般来说,原始形式更适合维度少、数量高的训练数据,对偶形式更适合维度高、数量少的训练数据。

计算 Gram 矩阵(Python 实现)

源码地址】code.perceptron.count_gram

# https://github.com/ChangxingJiang/Data-Mining-HandBook/blob/master/code/perceptron/_gram.py

def count_gram(x):
    """计算Gram矩阵

    :param x: 输入变量
    :return: 输入变量的Gram矩阵
    """
    n_samples = len(x)  # 样本点数量
    n_features = len(x[0])  # 特征向量维度数
    gram = [[0] * n_samples for _ in range(n_samples)]  # 初始化Gram矩阵

    # 计算Gram矩阵
    for i in range(n_samples):
        for j in range(i, n_samples):
            gram[i][j] = gram[j][i] = sum(x[i][k] * x[j][k] for k in range(n_features))

    return gram

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

源码地址】code.perceptron.dual_form_perceptron

# https://github.com/ChangxingJiang/Data-Mining-HandBook/blob/master/code/perceptron/_dual_form.py

from . import count_gram  # code.perceptron.count_gram

def dual_form_perceptron(x, y, eta):
    """感知机学习算法的对偶形式
    :param x: 输入变量
    :param y: 输出变量
    :param eta: 学习率
    :return: 感知机模型的a(alpha)和b
    """
    n_samples = len(x)  # 样本点数量
    a0, b0 = [0] * n_samples, 0  # 选取初值a0(alpha),b0
    gram = count_gram(x)  # 计算Gram矩阵

    while True:  # 不断迭代直至没有误分类点
        for i in range(n_samples):
            yi = y[i]

            val = 0
            for j in range(n_samples):
                xj, yj = x[j], y[j]
                val += a0[j] * yj * gram[i][j]

            if (yi * (val + b0)) <= 0:
                a0[i] += eta
                b0 += eta * yi
                break
        else:
            return a0, b0

源码地址】测试

>>> from code.perceptron import dual_form_perceptron
>>> dataset = [[(3, 3), (4, 3), (1, 1)], [1, 1, -1]]  # 训练数据集
>>> dual_form_perceptron(dataset[0], dataset[1], eta=1)
([2, 0, 5], -3)