感知机算法(Perceptron)

在 20 世纪 50 年代,弗兰克·罗森布莱特(Frank Rosenblatt)提出了感知机算法,其最初的目的是教会计算机识别图像。感知机的基本思路是简单模型神经元细胞的的运行原理。

1. 感知机原理

  1. f(x) > 0 时,输出 1 类别
  2. f(x) < 0 时,输出 -1 类别

感知机的损失函数为:

  1. 如果样本的真实类别是 1,当该样本分类错误时,则 wTx + b 小于 0
  2. 如果样本的真实类别是 -1,当该样本分类错误时,则 wTx + b 大于 0
  3. M 表示分类错误的样本
  4. 由于样本分类错误时,模型的输出和真实标签符号相反,故而添加符号转正

对 w、b 求导,使用梯度下降算法来优化模型参数。

w、b 的更新公式如下:

当碰到分类错误样本时,就对模型的参数进行更新,如何判断样本分类错误,可以使用下面的公式来判断:

2. 感知机示例

下面代码的基本思路:

  1. 遍历样本
  2. 如果发现样本分类错误,则使用该样本的梯度更新模型参数
  3. 直到模型能够把所有的样本都能正确分类
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt
import numpy as np
import random
import math
from sklearn.linear_model import Perceptron

# 初始化模型参数
np.random.seed(5)
w = np.random.randn(2, 1)
b = np.zeros(1)


# 假设函数
def percetron(x):
    return x @ w + b


def plot_boundary(model, x, y):

    # 绘制分类边界
    x1, x2 = np.meshgrid(
        np.linspace(x[:, 0].min() - 1, x[:, 0].max() + 1, 1000),
        np.linspace(x[:, 1].min() - 1, x[:, 1].max() + 1, 1000))

    data = np.c_[x1.ravel(), x2.ravel()]
    y_pred = np.where(percetron(data) >= 0, 1, -1).reshape(1, -1)
    
    plt.contourf(x1, x2, y_pred.reshape(1000, 1000), cmap=plt.cm.Blues)
    # 绘制原始数据散点图
    plt.scatter(x[:, 0], x[:, 1], c=y)
    
    plt.show()


# 构建分类数据集
def create_dataset(sample_number=100, random_state=None):

    x, y = make_classification(n_samples=sample_number,
                               n_features=2,
                               n_redundant=0,
                               n_clusters_per_class=1, random_state=random_state)
    # 将0类别使用-1表示
    y = np.where(y == 0, -1, y)
    return x, y


# 数据加载器
def data_loader(x, y):

    # 生成样本索引
    sample_index = list(range(len(y)))
    # 打乱数据索引
    random.shuffle(sample_index)
    # 每次返回一个样本
    for idx in sample_index:
        yield x[idx], y[idx]

# 优化方法
def optimizer(x, y, lr=0.1):

    global w, b

    # 计算样本梯度
    w_g = (-y * x).reshape(2, 1)
    b_g = -y

    # 更新模型参数
    w -= lr * w_g
    b -= lr * b_g


def perceptron_loss(y_pred, y_true):
    return -y_pred * y_true


# 1. 手动实现简单的感知机训练过程
def test01():

    # 1. 构建分类样本
    data_x, data_y = create_dataset(sample_number=20, random_state=1)

    # 2. 感知机训练
    tol = 1e-3
    for idx in range(1000):

        total_loss = 0.0
        error_number = 0
        for x, y in data_loader(data_x, data_y):

            # 训练样本送入模型
            output = percetron(x)
            # 如果样本预测错误, 则使用该样本更新参数
            loss = perceptron_loss(output.squeeze(), y)
            if loss > 0:
                # 更新模型参数
                optimizer(x, y, lr=0.0001)
                # 统计损失信息
                total_loss += loss
                error_number += 1

        print('epoch %d loss: %.2f' % (idx + 1, total_loss))

    # 4. 绘制分类边界
    plot_boundary(percetron, data_x, data_y)


# 2. 使用 sklearn 的 Perceptron API
def test02():

    # 1. 构建分类样本
    data_x, data_y = create_dataset(sample_number=20, random_state=1)

    # 2. 模型训练
    estimator = Perceptron(random_state=0)
    estimator.fit(data_x, data_y)

    # 3. 绘制分类边界
    x1, x2 = np.meshgrid(
        np.linspace(data_x[:, 0].min() - 1, data_x[:, 0].max() + 1, 1000),
        np.linspace(data_x[:, 1].min() - 1, data_x[:, 1].max() + 1, 1000))

    data = np.c_[x1.ravel(), x2.ravel()]
    y_pred = estimator.predict(data)
    plt.contourf(x1, x2, y_pred.reshape(1000, 1000), cmap=plt.cm.Blues)
    plt.scatter(data_x[:, 0], data_x[:, 1], c=data_y)
    plt.show()


if __name__ == '__main__':
    test01()
    test02()

程序多次运行之后,我们发现感知机模型并不是唯一的。

未经允许不得转载:一亩三分地 » 感知机算法(Perceptron)
评论 (0)

6 + 5 =