用Python和sklearn从零实现感知器算法:一个鸢尾花分类的保姆级教程

机器学习的世界里,感知器算法就像是一把打开分类问题大门的钥匙。它简单、直观,却蕴含着神经网络最基础的思想。今天,我们就用Python和sklearn,从零开始实现这个经典算法,并用鸢尾花数据集来验证它的威力。

1. 环境准备与数据探索

在开始之前,确保你的Python环境已经安装了以下库:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split

鸢尾花数据集是机器学习中最经典的数据集之一,它包含了三种鸢尾花的四个特征:花萼长度、花萼宽度、花瓣长度和花瓣宽度。为了简化问题,我们先专注于两个类别和两个特征:

# 加载数据
iris = datasets.load_iris()
X = iris.data[:, :2]  # 只取前两个特征
y = iris.target

# 只保留两个类别
X = X[y < 2]
y = y[y < 2]

# 可视化数据
plt.scatter(X[y==0, 0], X[y==0, 1], color='red', label='类别0')
plt.scatter(X[y==1, 0], X[y==1, 1], color='blue', label='类别1')
plt.xlabel('花萼长度')
plt.ylabel('花萼宽度')
plt.legend()
plt.show()

提示:在实际项目中,数据可视化是理解数据分布的关键步骤,不要跳过这个环节。

2. 感知器算法原理与实现

感知器算法的核心思想很简单:找到一个超平面,能够将不同类别的数据点分开。具体来说:

  1. 初始化权重向量w(通常随机初始化)
  2. 对于每个训练样本:
    • 计算预测值:sign(w·x)
    • 如果预测错误,调整权重:w = w + η(y - ŷ)x
  3. 重复上述步骤,直到所有样本都被正确分类或达到最大迭代次数

让我们用Python实现这个算法:

class Perceptron:
    def __init__(self, learning_rate=0.01, max_iter=1000):
        self.lr = learning_rate  # 学习率
        self.max_iter = max_iter  # 最大迭代次数
        
    def fit(self, X, y):
        # 添加偏置项
        X = np.insert(X, 0, 1, axis=1)
        # 初始化权重
        self.w = np.random.rand(X.shape[1])
        
        for _ in range(self.max_iter):
            error_count = 0
            for xi, target in zip(X, y):
                # 计算预测值
                predict = np.sign(np.dot(self.w, xi))
                # 感知器输出是±1,所以需要转换标签
                if target == 0:
                    target = -1
                # 更新权重
                if predict != target:
                    self.w += self.lr * (target - predict) * xi
                    error_count += 1
            # 如果没有错误,提前终止
            if error_count == 0:
                break
                
    def predict(self, X):
        X = np.insert(X, 0, 1, axis=1)
        return np.where(np.dot(X, self.w) > 0, 1, 0)

3. 训练模型与评估

现在我们可以使用自己实现的感知器来训练模型:

# 数据预处理:标准化
X_normalized = (X - X.mean(axis=0)) / X.std(axis=0)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_normalized, y, test_size=0.2, random_state=42)

# 训练模型
perceptron = Perceptron(learning_rate=0.1, max_iter=1000)
perceptron.fit(X_train, y_train)

# 评估模型
train_acc = np.mean(perceptron.predict(X_train) == y_train)
test_acc = np.mean(perceptron.predict(X_test) == y_test)
print(f"训练集准确率: {train_acc:.2f}")
print(f"测试集准确率: {test_acc:.2f}")

4. 可视化决策边界

理解模型如何做出决策非常重要,我们可以绘制决策边界:

def plot_decision_boundary(model, X, y):
    # 设置坐标轴范围
    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()])
    Z = Z.reshape(xx.shape)
    
    # 绘制等高线图
    plt.contourf(xx, yy, Z, alpha=0.4)
    plt.scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k')
    plt.xlabel('标准化花萼长度')
    plt.ylabel('标准化花萼宽度')
    plt.title('感知器决策边界')
    plt.show()

plot_decision_boundary(perceptron, X_normalized, y)

5. 使用sklearn的Perceptron

虽然我们自己实现了感知器,但了解如何使用sklearn内置的实现也很重要:

from sklearn.linear_model import Perceptron
from sklearn.preprocessing import StandardScaler

# 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 划分训练测试集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# 创建并训练模型
sk_perceptron = Perceptron(max_iter=1000, eta0=0.1, random_state=42)
sk_perceptron.fit(X_train, y_train)

# 评估
train_acc = sk_perceptron.score(X_train, y_train)
test_acc = sk_perceptron.score(X_test, y_test)
print(f"sklearn感知器训练集准确率: {train_acc:.2f}")
print(f"sklearn感知器测试集准确率: {test_acc:.2f}")

# 可视化决策边界
plot_decision_boundary(sk_perceptron, X_scaled, y)

6. 常见问题与调优技巧

在实际应用中,你可能会遇到以下问题:

  1. 模型不收敛

    • 检查数据是否线性可分
    • 尝试调整学习率(通常0.1-0.01之间)
    • 增加最大迭代次数
  2. 过拟合

    • 使用正则化(sklearn的Perceptron支持L2正则化)
    • 增加训练数据量
    • 减少特征数量
  3. 特征重要性

    • 感知器的权重可以反映特征重要性
    print("特征重要性:", sk_perceptron.coef_)
    

注意:感知器对特征缩放敏感,务必进行标准化或归一化处理。

7. 扩展到多类别分类

虽然感知器本质上是二分类器,但我们可以通过以下策略实现多分类:

  1. 一对多(One-vs-Rest) :为每个类别训练一个分类器
  2. 一对一(One-vs-One) :为每对类别训练一个分类器

sklearn的Perceptron默认支持多分类:

# 使用所有类别
X = iris.data[:, :2]
y = iris.target

# 标准化
X_scaled = StandardScaler().fit_transform(X)

# 训练模型
multi_perceptron = Perceptron(max_iter=1000, random_state=42)
multi_perceptron.fit(X_scaled, y)

# 可视化决策边界
plot_decision_boundary(multi_perceptron, X_scaled, y)

在实际项目中,我发现数据预处理步骤往往比模型选择更重要。花时间理解数据分布、处理异常值、选择合适的特征,通常能带来比调参更大的性能提升。感知器虽然简单,但在线性可分问题上表现优异,而且训练速度快,是很好的基准模型。

更多推荐