一、什么是感知机?

假设有这样一个场景:小明想购买一辆汽车,该车定价为 10 万(100k)元。小明计划以 2 万(20k)元 的现金支付,剩余的 8 万(80k)元 通过贷款来支付。为了评估小明的贷款申请是否能够获批,汽车销售公司会结合历史客户贷款审批数据以及小明的实际情况进行综合分析。

通常,汽车销售公司认为以下关键变量会显著影响审批结果。为遵循金融行业中的常用习惯,本文中所有货币单位均统一使用 千元

  • 年收入(单位:千元)
  • 现有负债(单位:千元)
  • 申请金额(单位:千元)

为此,公司收集了 10 组历史审批数据:

年收入 现有负债 申请金额 审批结果
60 20 100 -1
80 50 120 -1
100 30 80 +1
120 70 150 -1
150 40 100 +1
180 90 200 -1
200 60 150 +1
90 30 70 +1
130 50 90 +1
70 40 110 -1

其中,审批结果 +1 表示贷款获批,-1 表示贷款被拒绝;小明的贷款申请数据为:年收入 100 千元,现有负债 50 千元,申请金额 80 千元。

感知机正是处理这类二分类问题的线性分类模型。当学习数据(即历史数据)满足线性可分条件时,它能够构造出一条超平面,将数据恰当地分隔为两类。利用该超平面,对待分类数据进行预测,可以实现对小明贷款申请结果的快速评估。

因此,感知机的核心问题在于如何构造合适的超平面,并基于该超平面更新模型参数。

二、感知机核心原理

在上一节中,我们直观地介绍了感知机的应用场景。接下来,我们将对感知机进行精确定义,并详细阐述其核心原理与学习策略。

2.1 模型定义

目标:寻找一个线性决策边界(超平面) 模型定义为
$$ f(x) = \text{sign}(w \cdot x + b) $$

其中,

  • $w \in \mathbb{R}^n$ 为法向量,决定超平面的方向;
  • $b \in \mathbb{R}$ 为偏置项,决定超平面在空间中的位置偏移。

几何意义

  • 在二维空间中,决策边界为直线:$w_1x_1 + w_2x_2 + b = 0$
  • 在三维或更高维空间中,该边界为平面或超平面。

示例: 针对第一节的贷款审批问题,假设参数取值为 $w = [0.5, -0.3, -0.2]$ 和 $b = -10$,则决策函数可写为
$$ 0.5 \times (\text{年收入}) -0.3 \times (\text{负债}) -0.2 \times (\text{申请金额}) -10 = 0. $$ 当计算结果 $\geq 0$ 时,贷款申请被视为通过;反之则拒绝。

2.2 学习策略

在了解了感知机的工作原理后,我们进一步思考:如何利用已有数据来优化模型参数 $w$ 和 $b$,使得分类器达到最佳性能? 为了最小化误分类样本到超平面的距离总和,对于单个样本 $(x_i,y_i)$ 与超平面的几何距离为
$$ \frac{|w \cdot x_i + b|}{\|w\|}. $$ 当样本满足 $y_i(w \cdot x_i + b) < 0$ 时,说明其被误分类,此时定义损失函数为
$$ L(w,b) = -\sum_{x_i \in M} y_i (w \cdot x_i + b), $$ 其中 $M$ 表示误分类样本集合。
这样的设计特点有:

  • 仅对误分类样本进行惩罚;
  • 误分类样本距超平面越远,其损失越大。

由此,我们将感知机学习目标转化为最小化损失函数 $ L(w,b)$ 。

三、感知机学习算法

对于感知机学习,也就是计算参数值的解,我们一般基于随机梯度下降(SGD)得到。这里我们先给出算法。

3.1 参数更新规则

对损失函数求偏导得到梯度方向: $$ \frac{\partial L}{\partial w} = -\sum_{x_i \in M} y_i x_i, \quad \frac{\partial L}{\partial b} = -\sum_{x_i \in M} y_i. $$ 基于梯度更新思想,我们采用下面的更新规则(每次选择一个误分类样本进行更新): $$ w \leftarrow w + \eta\, y_i\, x_i, \\ b \leftarrow b + \eta\, y_i, $$ 其中 $\eta$ 为学习率。

以小明的贷款数据为例:

  • 假设当前误分类样本为 $x_i=(100,50,80)$ 且标签为 $y_i=1$,学习率 $\eta = 0.001$;
  • 更新时,权重向量增加 $0.001 \times 1 \times [100,50,80]$,偏置增加 $0.001 \times 1$;
  • 更新后的决策平面向该样本方向移动,从而提高后续正确分类的可能性。

3.2 算法收敛性保证

根据 Novikoff 定理,只要数据线性可分,感知机算法必定收敛。设存在分离超平面
$$ w_{opt} \cdot x + b_{opt} = 0, $$ 且所有样本满足
$$ y_i(w_{opt} \cdot x_i + b_{opt}) \geq \gamma\quad(\gamma > 0), $$ 则误分类次数 $k$ 的上界为
$$ k \le \left(\frac{R}{\gamma}\right)^2, \quad \text{其中 } R = \max \|x_i\|. $$

3.3 Python 实现

import numpy as np

class Perceptron:
    def __init__(self, learning_rate=0.01, max_iterations=1000):
        self.lr = learning_rate # 学习率
        self.max_iterations = max_iterations       # 最大迭代次数
        self.w = None # 权重向量
        self.b = 0.0 # 偏置项

    def fit(self, X, y):
        n_samples, n_features = X.shape
        self.w = np.zeros(n_features)  # 初始化权重

        # 随机梯度下降过程
        for _ in range(self.max_iterations):
            misclassified = 0
            for idx, x_i in enumerate(X):
                linear = np.dot(x_i, self.w) + self.b
                y_pred = 1 if linear >= 0 else -1  # 符号函数

                if y_pred != y[idx]:
                    self.w += self.lr * y[idx] * x_i
                    self.b += self.lr * y[idx]
                    misclassified += 1
            if misclassified == 0:  # 满足提前终止条件
                break

    def predict(self, X):
        linear = np.dot(X, self.w) + self.b
        return np.sign(linear)

# 贷款审批数据集(单位均为千元)
X = np.array([
    [60, 20, 100],
    [80, 50, 120],
    [100, 30, 80],
    [120, 70, 150],
    [150, 40, 100],
    [180, 90, 200],
    [200, 60, 150],
    [90, 30, 70],
    [130, 50, 90],
    [70, 40, 110]
])
y = np.array([-1, -1, 1, -1, 1, -1, 1, 1, 1, -1])

# 训练模型并预测小明的贷款审批结果
model = Perceptron(learning_rate=0.001, max_iterations=100)
model.fit(X, y)

xiaoming = np.array([[100, 50, 80]])
print("预测结果:", model.predict(xiaoming))  # 输出为:1(表示贷款获批)

四、对偶形式实现

感知机还有另一种形式的实现,即对偶形式。

想象在每次发现误分类的时,系统都会调整参数。经过多次修正,最终决策规则实际上是所有历史误判案例的加权组合。

即: $$ w = \sum_{i=1}^N \alpha_i y_i x_i \\ b = \sum_{i=1}^N \alpha_i y_i $$ $\alpha_i $为样本i被误分次数

将其带入原始形式: $$ f(x) = \text{sign}(w \cdot x + b) $$ 可以得到表达式: $$ f(x) = \text{sign}\left( \sum_{i=1}^N \alpha_i y_i (x_i \cdot x) + \sum_{i=1}^N \alpha_i y_i \right) $$

预计算Gram矩阵 $G_{ij}=x_i \cdot x_j$,使得每次迭代只需查表计算,避免重复特征内积运算,可以加速计算。

代码实现

import numpy as np

class DualPerceptron:
    def __init__(self, learning_rate=0.001, max_iterations=1000):
        self.learning_rate = learning_rate # 学习率
        self.max_iterations = max_iterations # 最大迭代次数
        self.alpha = None # 样本系数向量
        self.b = 0 # 偏置项
        self.gram = None # Gram 矩阵
        self.X = None # 存储训练数据
        self.y = None # 存储训练标签

    def fit(self, X, y):
        n_samples = X.shape[0]
        self.alpha = np.zeros(n_samples)
        self.X = X.copy() # 保存训练数据
        self.y = y.copy() # 保存训练标签

        self.gram = np.dot(X, X.T)

        for _ in range(self.max_iterations):
            error_count = 0
            for j in range(n_samples):
                # 计算预测值
                pred = np.sum(self.alpha * y * self.gram[j]) + self.b
                if y[j] * pred <= 0:  # 误分类条件
                    self.alpha[j] += self.learning_rate
                    self.b += self.learning_rate * y[j]
                    error_count += 1
            if error_count == 0:
                break

    def predict(self, X_test):
        w = np.sum(self.alpha[:, None] * self.y[:, None] * self.X, axis=0)
        scores = np.dot(X_test, w) + self.b
        return np.sign(scores)

五、感知机的局限性

感知机是线性模型,不能表示复杂的函数,比如异或(XOR),考虑到如下数据分布:

输入1 输入2 输出
0 0 -1
0 1 +1
1 0 +1
1 1 -1

在平面直角坐标系中,无法找到一条直线将两类点分开,此时感知机就无法工作了。