统计学习方法02:感知机

最近学习了《统计学习方法》第2章:感知机,些许心得与数学推导,浅浅记录一下。


感知机是二分类的线性分类模型,属于判别模型。简单点说,感知机模型是定义在特征空间中的线性分类模型。在本部分的开头,直接给出感知机模型的数学定义。

假设输入空间 X ⊆ R p − 1 \mathcal{X} \subseteq \mathbb{R}^{p-1} XRp1,输出空间 Y = { + 1 , − 1 } \mathcal{Y}=\{ +1,-1 \} Y={+1,1};而把输入空间中的具体点(特征向量)记为 x \mathbf{x} x,输出的类别(标签)记作 y y y。感知机模型描述为

f ( x ) = s i g n ( w T x + b ) \begin{align} f(\mathbf{x})=sign(\mathbf{w}^T\mathbf{x}+b) \end{align} f(x)=sign(wTx+b)

当然,我们可以给 x \mathbf{x} x增添一个偏置项维度 x 0 = 1 x_0=1 x0=1,而对应的 w \mathbf{w} w增添维度 w 0 = b w_0=b w0=b,上述表达式变为

f ( x ) = s i g n ( w T x ) \begin{align} f(\mathbf{x}) &= sign(\mathbf{w}^T\mathbf{x}) \end{align} f(x)=sign(wTx)

其中 s i g n ( x ) sign(x) sign(x)函数实现分类功能, x ∈ X ⊆ R p , y ∈ Y \mathbf{x}\in \mathcal{X}\subseteq \mathbb{R}^p,y\in \mathcal{Y} xXRp,yY,而 w ∈ R p \mathbf{w}\in \mathbb{R}^p wRp即为待优化参数。

为了方便表示,我们把训练数据集表示为

T = { ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) , ⋯   , ( x ( n ) , y ( n ) ) } \begin{align} T=\{ (\mathbf{x}^{(1)},y^{(1)}), (\mathbf{x}^{(2)},y^{(2)}), \cdots, (\mathbf{x}^{(n)},y^{(n)}) \} \end{align} T={(x(1),y(1)),(x(2),y(2)),,(x(n),y(n))}

而感知机模型的学习策略,就是通过这些训练数据集来学习得到最优的参数 w \mathbf{w} w
这里强调一下(和教材中的习惯有些许差异),使用带圆括号的上标表示单个样本,而下标表示某一维度,比如 x 1 ( 1 ) x_1^{(1)} x1(1)表示第1个样本中特征向量的第1维度分量。

感知机学习策略

首先提出一个概念:线性可分性。对于给出的数据集T,我们把这些数据点在一个p维空间中标出,如果存在一个超平面S可以把这些数据点一分为二(完全正确地将两个类别的数据点划分到超平面的两侧),那么数据集T线性可分。

接下来探究一下感知机的学习策略。

实际上,感知机的学习策略可以理解为错误驱动下的参数更新策略,即建立损失函数、梯度下降、更新参数的迭代过程。那么,既然上文已经提出了感知机模型的基本形式,接下来我们就设计一个损失函数,希望它能够满足我们通过其梯度下降不断降低损失的需求。

教材中提到了一个很自然的想法:使用误判的数据点个数作为损失函数,即

L ( X ) = ∑ i = 1 n [ y ( i ) w T x ( i ) < 0 ] \begin{align} \mathcal{L}(\mathbf{X}) = \sum_{i=1}^{n}\left[y^{(i)} \mathbf{w}^T \mathbf{x}^{(i)} < 0\right] \end{align} L(X)=i=1n[y(i)wTx(i)<0]

其中[]表示艾弗森括号,当其中的表达式成立时结果为1,否则为0。

但是很遗憾,这个损失函数虽然直观,但是并不可导。我们希望设计更加合适的损失函数,转用其中的因子 l o s s i = y ( i ) w T x ( i ) loss_i=y^{(i)}\mathbf{w}^T\mathbf{x}^{(i)} lossi=y(i)wTx(i),因为误分类时这个因子 l o s s i < 0 loss_i<0 lossi<0一定成立。更方便地,如果将所有模型误分类的样本组成集合M,则使用如下的损失函数

L ( X ) = ∑ x ( i ) ∈ M ( − y ( i ) w T x ( i ) ) \begin{align} \mathcal{L}(\mathbf{X}) = \sum_{\mathbf{x}^{(i)}\in M}\left(-y^{(i)} \mathbf{w}^T \mathbf{x}^{(i)} \right) \end{align} L(X)=x(i)M(y(i)wTx(i))

感知机学习算法

上文已经提过,感知机学习算法由错误驱动,且已经得到损失函数,接下来的最小化损失函数求解最优参数,就是感知机学习算法的原始形式:

min ⁡ w , b L ( w , b ) = min ⁡ w , b ∑ x ( i ) ∈ M ( − y ( i ) w T x ( i ) ) \begin{align} \min_{\mathbf{w},b}\mathcal{L}(\mathbf{w},b)=\min_{\mathbf{w},b}\sum_{\mathbf{x}^{(i)}\in M}\left(-y^{(i)} \mathbf{w}^T \mathbf{x}^{(i)} \right) \end{align} w,bminL(w,b)=w,bminx(i)M(y(i)wTx(i))

参数更新使用梯度下降法。我们求出损失函数关于参数的偏导数

∇ w L = ∑ x ( i ) ∈ M ( − y ( i ) x ( i ) ) \begin{align} \nabla_{\mathbf{w}}\mathcal{L} &= \sum_{\mathbf{x}^{(i)}\in M}\left(-y^{(i)} \mathbf{x}^{(i)} \right) \end{align} wL=x(i)M(y(i)x(i))

而更新过程是每次选取一个误分类点,进行梯度下降,即

w ← w + η y ( i ) x ( i ) \begin{align} \mathbf{w} &\gets \mathbf{w} + \eta y^{(i)}\mathbf{x}^{(i)} \\ \end{align} ww+ηy(i)x(i)
最后,算法过程就是常规的梯度下降迭代过程,不再赘述。

感知机算法的收敛性

对于线性可分的数据集感知机学习算法原始形式收敛,即经过有限次迭代可以得到一个将训练数据集完全正确划分的分离超平面。

我们把符合条件的参数记作 w ∗ \mathbf{w}_* w,而 w ∗ T x = 0 \mathbf{w}_*^T\mathbf{x}=0 wTx=0即为将数据集完全正确划分的超平面,故

∀ i ∈ { 1 , 2 , … , n } , y ( i ) ( w ∗ T x ( i ) ) > 0 \begin{align} \forall i \in \{1,2,\dots,n\},y^{(i)}(\mathbf{w}_*^T\mathbf{x}^{(i)})>0 \end{align} i{1,2,,n},y(i)(wTx(i))>0

取出

γ = min ⁡ i y ( i ) ( w ∗ T x ( i ) ) > 0 \begin{align} \gamma = \min_i y^{(i)}(\mathbf{w}_*^T\mathbf{x}^{(i)})>0 \end{align} γ=iminy(i)(wTx(i))>0

因此,下式必定成立

∃ γ > 0 , s . t . y ( i ) ( w ∗ T x ( i ) ) ⩾ γ \begin{align} \exist \gamma > 0, s.t.y^{(i)}(\mathbf{w}_*^T\mathbf{x}^{(i)})\geqslant \gamma \end{align} γ>0,s.t.y(i)(wTx(i))γ

另外,记 R = max ⁡ 1 ⩽ i ⩽ n ∣ ∣ x ( i ) ∣ ∣ R=\max\limits_{1\leqslant i \leqslant n}||\mathbf{x}^{(i)}|| R=1inmax∣∣x(i)∣∣,下面证明:

数据集上的误分类次数k满足不等式

k ⩽ ( R γ ) 2 \begin{align} k \leqslant \left( \frac{R}{\gamma} \right)^2 \end{align} k(γR)2

从而原始的感知机学习算法可以收敛。

在参数更新过程中,我们记出现第k个误判点之前的参数为 w ( k − 1 ) \mathbf{w}^{(k-1)} w(k1),下式一定成立

y ( i ) w ( k − 1 ) T x ( i ) ⩽ 0 \begin{align} y^{(i)}\mathbf{w}^{(k-1)T}\mathbf{x}^{(i)} \leqslant 0 \end{align} y(i)w(k1)Tx(i)0

更新当前的参数,得到新的参数,即

w ( k ) = w ( k − 1 ) + η y ( i ) x ( i ) \begin{align} \mathbf{w}^{(k)} = \mathbf{w}^{(k-1)}+\eta y^{(i)}\mathbf{x}^{(i)} \end{align} w(k)=w(k1)+ηy(i)x(i)

因此,由(11)(14)可以得到

w ∗ T w ( k ) = w ∗ T w ( k − 1 ) + η y ( i ) w ∗ T x ( i ) ⩾ w ∗ T w ( k − 1 ) + η γ ⩾ w ∗ T w ( k − 2 ) + 2 η γ ⩾ … ⩾ k η γ \begin{align} \mathbf{w}_*^T\mathbf{w}^{(k)}&=\mathbf{w}_*^T\mathbf{w}^{(k-1)}+\eta y^{(i)}\mathbf{w}_*^T\mathbf{x}^{(i)} \\ &\geqslant \mathbf{w}_*^T\mathbf{w}^{(k-1)}+\eta \gamma \\ &\geqslant \mathbf{w}_*^T\mathbf{w}^{(k-2)}+2\eta\gamma \\ &\geqslant \dots \\ &\geqslant k\eta\gamma \end{align} wTw(k)=wTw(k1)+ηy(i)wTx(i)wTw(k1)+ηγwTw(k2)+2ηγkηγ

∣ ∣ w ( k ) ∣ ∣ 2 2 = ∣ ∣ w ( k − 1 ) + η y ( i ) x ( i ) ∣ ∣ 2 2 = ∣ ∣ w ( k − 1 ) ∣ ∣ 2 2 + 2 η y ( i ) w ( k − 1 ) T x ( i ) + η 2 ∣ ∣ x ( i ) ∣ ∣ 2 2 ⩽ ∣ ∣ w ( k − 1 ) ∣ ∣ 2 2 + η 2 ∣ ∣ x ( i ) ∣ ∣ 2 2 ⩽ ∣ ∣ w ( k − 1 ) ∣ ∣ 2 2 + η 2 R 2 ⩽ ∣ ∣ w ( k − 2 ) ∣ ∣ 2 2 + 2 η 2 R 2 ⩽ … ⩽ k η 2 R 2 \begin{align} ||\mathbf{w}^{(k)}||^2_2 &= ||\mathbf{w}^{(k-1)}+\eta y^{(i)}\mathbf{x}^{(i)}||^2_2 \\ &=||\mathbf{w}^{(k-1)}||^2_2+2\eta y^{(i)}\mathbf{w}^{(k-1)T}\mathbf{x}^{(i)}+\eta^2||\mathbf{x}^{(i)}||^2_2 \\ &\leqslant ||\mathbf{w}^{(k-1)}||^2_2 +\eta^2||\mathbf{x}^{(i)}||^2_2 \\ &\leqslant ||\mathbf{w}^{(k-1)}||^2_2 +\eta^2R^2 \\ &\leqslant ||\mathbf{w}^{(k-2)}||^2_2 + 2\eta^2R^2 \\ &\leqslant \dots \\ &\leqslant k\eta^2R^2 \end{align} ∣∣w(k)22=∣∣w(k1)+ηy(i)x(i)22=∣∣w(k1)22+2ηy(i)w(k1)Tx(i)+η2∣∣x(i)22∣∣w(k1)22+η2∣∣x(i)22∣∣w(k1)22+η2R2∣∣w(k2)22+2η2R2kη2R2

结合上述(19)(26)两式,得

k η γ ⩽ w ∗ T w ( k ) ⩽ ∣ ∣ w ∗ ∣ ∣ 2   ∣ ∣ w ( k ) ∣ ∣ 2 ⩽ k η R \begin{align} k\eta\gamma \leqslant \mathbf{w}_*^T\mathbf{w}^{(k)} \leqslant ||\mathbf{w}_*||_2 \ ||\mathbf{w}^{(k)}||_2 \leqslant \sqrt{k}\eta R \end{align} kηγwTw(k)∣∣w2 ∣∣w(k)2k ηR

因此推导出

k ⩽ ( R γ ) 2 \begin{align} k \leqslant \left( \frac{R}{\gamma} \right)^2 \end{align} k(γR)2

课后习题

习题2.1

  Minsky 与 Papert 指出:感知机因为是线性模型,所以不能表示复杂的函数,如异或 (XOR)。验证感知机为什么不能表示异或。

事实上,感知机是一种线性分类模型,数据集线性可分是其适用前提。罗列出异或的四种输入输出情况:

x 1 x_1 x1 x 2 x_2 x2 x 1 ⊕ x 2 x_1\oplus x_2 x1x2 l a b e l label label
000-1
0111
1011
110-1

将表中四种情况在二维平面图中标出,可以发现无论如何,也找不到一条可以将四个点完全一分为二的直线,也就是说,感知机模型无法找到这样的一条直线,是不适用的。
异或线性不可分

习题2.2

  模仿例题 2.1,构建从训练数据求解感知机模型的例子。例题中,训练数据集包括正实例点和负实例点,其中正实例点包括 { ( 3 , 3 ) T , ( 4 , 3 ) T } \{(3,3)^T,(4,3)^T\} {(3,3)T,(4,3)T},负实例点包括 { ( 1 , 1 ) T } \{(1,1)^T\} {(1,1)T},试求解感知机模型。

数学原理层面的知识上文已经提过了,这里着重从代码层面手撸一遍,加深印象。

import numpy as np
from matplotlib import pyplot as plt

基本流程和传统训练模型过程类似,准备数据集、构建模型、开启训练、计算损失函数值、梯度下降更新参数,最后模型收敛评估一下。
首先准备数据集,这里很简单,就是例子给出来的三个数据点,使用两个ndarray类型数组存储。另外,自己写了一个数据迭代器,每次产出一个样本 x , y \mathbf{x},y x,y,作为后续模型的输入。当然,直接写一个普通的for循环一个一个读入也可以。

# 先准备好数据集,虽然它很少
features = np.array([[3, 3], [4, 3], [1, 1]])
labels = np.array([1, 1, -1])

# 准备一个数据读取器
class DataLoader():
    def __init__(self, features: np.ndarray, labels: np.ndarray) -> None:
        self.x = np.append(features, np.ones(features.shape[0]).reshape(-1, 1), axis=1)
        self.y = labels

    def load_data(self):
        for i in range(self.x.shape[0]):
            yield self.x[i], self.y[i]

构建一个很基本的感知机模型,注意全部省去了参数 b b b,相应地, w 、 x \mathbf{w}、\mathbf{x} wx中的添加一个维度。

# 准备我们自己的模型,感知机模型
class Perceptron():
    ''' 
    感知机模型:y=w'@x,省略b,添加偏置项 
    dim: 输入特征向量的原始维度
    '''
    def __init__(self, dim) -> None:
        self.initParams(dim+1)

    
    def initParams(self, dim):
        ''' 初始化权重参数w '''
        self.weight = np.zeros(dim)


    def forward(self, x: np.ndarray):
        ''' 
        x: ndarray,输入模型的特征向量
        模型内容,很简单的一个矩阵乘法
        '''
        return self.weight@x

接下来定义好损失函数,可以准备训练了。

# 准备训练,定义一个训练类Train,需要:读取数据、模型输出、损失函数、梯度下降
def calculate_loss(y, out):
    ''' 
    计算损失函数值
    y: 标签
    out: 模型输出值
    '''
    return y*out

def train(model: Perceptron, dataloader: DataLoader, lr=1):
	'''
	训练;
	model: 模型,这里是感知机模型
	dataloader: 数据迭代器,即上文实现的DataLoader实例
	lr: 学习率,默认为1
	'''
    train_count = 0 # 记录训练次数
    is_mistake_exist = True # 指示当前是否有误判
    while is_mistake_exist:
        is_mistake_exist = False # 初始化,没有误判
        for x, y in dataloader.load_data():
            # 计算损失函数
            out = model.forward(x)
            loss = calculate_loss(y, out)
            if loss <= 0:
                # loss小于0,表示当前样本点误判,根据当前误判样本,更新模型参数
                model.weight += lr * x * y
                train_count += 1
                is_mistake_exist = True
                print(f'------ epoch{train_count}, loss:{loss} , weight:{model.weight}-----')
                # 每次拿一个误判点更新参数,因此直接break开启新的一轮迭代
                break
    print(f'over...it takes {train_count} epochs totally')

执行下面的代码,开启训练。

dataloader = DataLoader(features, labels)
model = Perceptron(2)
print('初始化时的模型参数:', model.weight)
# 开启训练
train(model, dataloader)

训练过程中控制台输出结果如图。
训练日志

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

midLakePavilion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值