感知机的概念、计算过程与实例(附代码演示)

458 阅读4分钟

感知机的定义与概念

  • 感知机是一种二分类的线性分类模型,属于人工神经网络的基础单元。由Frank Rosenblatt于1957年提出,用于解决线性可分问题。

  • 感知机的模型结构

    • 输出:特征向量x=(x1,x2,,xn)x=(x_1,x_2,···,x_n)

    • 权重:w=(w1,w2,wn)w=(w_1,w_2,···,w_n)

    • 偏置:标量b

    • 激活函数:

      f(z)={1if z0,0if z<0.f(z) = \begin{cases} 1 & \text{if } z \geq 0, \\ 0 & \text{if } z < 0. \end{cases}
    • 输出:y=f(wx+b)y = f(w·x+b)

感知机的计算过程

  • 给定输入x,权重w,和偏置b,感知机输出:
    y=σ(w,x+b)σ(x)={1if x>01otherwisey = \sigma \left( \langle w, x \rangle + b \right) \quad \sigma(x) = \begin{cases} 1 & \text{if } x > 0 \\ -1 & \text{otherwise} \end{cases}

其中的<w,x>的含义是向量w与向量x做内积。

image.png

  • 例如有特征向量(x1, x2),则计算过程可以表示为:

image.png 这样子就可以在线性方程中将数据划分为正、负两类。

感知机的损失函数

  • 感知机的训练需要优化参数w和b,所以我们需要定义一个损失函数,用损失函数的结果优化模型参数。

  • 最简单的方式就是定义误分类样本与直线的距离。

    loss=1wwx0+bloss = \frac{1}{||w||} |w·x_0+b|
    w=w12+w22++wn2||w||=\sqrt{w_1^2+w_2^2+···+w_n^2}
  • 对应感知机来说也可以用以下公式

loss=1w(y0wx0+b)loss = \frac{1}{||w||}(-y_0·|w·x_0+b|)

其中的x0x_0为输入,y0y_0为样本预测的标签。

  • 又由于感知机关心的是怎么将两类样本正确的分类开,对正确分类的样本点到决策函数的距离大小并不关心,因此我们可以省略其中1w\frac{1}{|w|}这样子我们就得到了最后的损失函数。
loss(w,b)=xiyi(wxi+b)loss(w, b) = - \sum_{x_i}y_i(w·x_i+b)

感知机的训练实例

  • 感知机的训练过程如下:

    初始化权重 w = 0 偏置 b = 0 重复epoch迭代次数 计算线性输出[<w,xix_i>+b] 若线性输出≥0则预测值yiy_i=1否则预测值yiy_i=0 更新w = w + lr(学习率) · (yy-yiy_i) · xix_i 更新b = b + lr(学习率) · (yy-yiy_i)

  • 以下是一个感知机训练的例子

class Perceptron:
    def __init__(self, learning_rate=0.01, epochs=100):
        self.lr = learning_rate    # 学习率
        self.epochs = epochs       # 迭代次数
        self.weights = None        # 权重
        self.bias = 0             # 偏置

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

        for _ in range(self.epochs):
            for idx, x_i in enumerate(X):
                linear_output = np.dot(x_i, self.weights) + self.bias
                y_pred = 1 if linear_output >= 0 else 0  # 阶跃函数
                update = self.lr * (y[idx] - y_pred)     # 权重更新量
                self.weights += update * x_i  # 更新权重
                self.bias += update  # 更新偏置量

    def predict(self, X):
        linear_output = np.dot(X, self.weights) + self.bias
        return np.where(linear_output >= 0, 1, 0)  # 返回预测标签(0或1)
  • 接下来我们通过生成随机样本来验证该感知机的可行性

    生成数据的代码如下:

    # 生成两类线性可分数据
    np.random.seed(42)
    X_class1 = np.random.randn(50, 2) + np.array([2, 2])  # 类别1(标签1)
    X_class0 = np.random.randn(50, 2) + np.array([-2, -2]) # 类别0(标签0)
    X = np.vstack((X_class1, X_class0))  # 垂直堆叠,增加行数
    y = np.array([1] * 50 + [0] * 50)  # 标签
    
  • 并绘制出原本数据分布

    # 绘制原始数据
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap='bwr', edgecolors='k')
    plt.title("Original Data")
    plt.show()
    
  • 接下来我们用该数据对感知机进行训练,并输出权重与偏置

    # 初始化并训练感知机
    perceptron = Perceptron(learning_rate=0.1, epochs=50)
    perceptron.fit(X, y)
    
    # 打印学到的权重和偏置
    print(f"权重: {perceptron.weights}, 偏置: {perceptron.bias}")
    
  • 最后我们绘制出分类之后的数据分布

    # 绘制决策边界
    def plot_decision_boundary(X, y, model):
        # 确定绘制范围
        x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        # 生成网格点
        xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01),
                             np.arange(y_min, y_max, 0.01))
        # 预测整个网络的类别
        Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
        # 绘制边界与样本点
        plt.contourf(xx, yy, Z, alpha=0.3, cmap='bwr')
        plt.scatter(X[:, 0], X[:, 1], c=y, cmap='bwr', edgecolors='k')
        plt.title("Perceptron Decision Boundary")
        plt.xlabel("Feature 1")
        plt.ylabel("Feature 2")
        plt.show()
    
    plot_decision_boundary(X, y, perceptron)
    
  • 最后结果如图所示

image.png

image.png 完整代码如下:

import numpy as np
import matplotlib.pyplot as plt

class Perceptron:
    def __init__(self, learning_rate=0.01, epochs=100):
        self.lr = learning_rate    # 学习率
        self.epochs = epochs       # 迭代次数
        self.weights = None        # 权重
        self.bias = 0             # 偏置

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

        for _ in range(self.epochs):
            for idx, x_i in enumerate(X):
                linear_output = np.dot(x_i, self.weights) + self.bias
                y_pred = 1 if linear_output >= 0 else 0  # 阶跃函数
                update = self.lr * (y[idx] - y_pred)     # 权重更新量
                self.weights += update * x_i  # 更新权重
                self.bias += update  # 更新偏置量

    def predict(self, X):
        linear_output = np.dot(X, self.weights) + self.bias
        return np.where(linear_output >= 0, 1, 0)  # 返回预测标签(0或1)

# 生成两类线性可分数据
np.random.seed(42)
X_class1 = np.random.randn(50, 2) + np.array([2, 2])  # 类别1(标签1)
X_class0 = np.random.randn(50, 2) + np.array([-2, -2]) # 类别0(标签0)
X = np.vstack((X_class1, X_class0))  # 垂直堆叠,增加行数
y = np.array([1] * 50 + [0] * 50)  # 标签

# 绘制原始数据
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='bwr', edgecolors='k')
plt.title("Original Data")
plt.show()

# 初始化并训练感知机
perceptron = Perceptron(learning_rate=0.1, epochs=50)
perceptron.fit(X, y)

# 打印学到的权重和偏置
print(f"权重: {perceptron.weights}, 偏置: {perceptron.bias}")

# 绘制决策边界
def plot_decision_boundary(X, y, model):
    # 确定绘制范围
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    # 生成网格点
    xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01),
                         np.arange(y_min, y_max, 0.01))
    # 预测整个网络的类别
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
    # 绘制边界与样本点
    plt.contourf(xx, yy, Z, alpha=0.3, cmap='bwr')
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap='bwr', edgecolors='k')
    plt.title("Perceptron Decision Boundary")
    plt.xlabel("Feature 1")
    plt.ylabel("Feature 2")
    plt.show()

plot_decision_boundary(X, y, perceptron)

感知机总结以及其所存在的问题

  • 感知机作为最早存在的二分类模型之一,它的求解算法十分简单。但是它也存在一个比较严重的问题,感知机只能产生线性切割面,无法处理XOR问题。

  • XOR问题即当数据分布无法使用一条线性函数将其区分的话,那么感知机将无法进行求解。

image.png

如图所示,该图中的数据分布无法通过一条线性可分的函数进行求解分类,所以感知机对这类问题将无法进行求解。为了解决该问题,我们便引入了多层感知机来求解该问题。