【统计学习方法】从零实现感知机:在鸢尾花数据集上的二分类实战与可视化分析

张开发
2026/4/17 19:34:17 15 分钟阅读

分享文章

【统计学习方法】从零实现感知机:在鸢尾花数据集上的二分类实战与可视化分析
1. 感知机与鸢尾花数据集入门指南第一次接触机器学习的朋友们你们好今天我要带大家用最基础的工具从零开始实现一个感知机模型并在经典的鸢尾花数据集上实战演练。不用担心数学基础我会用最生活化的方式解释每个概念。感知机是机器学习中最简单的分类算法之一就像小朋友学认水果给你一个苹果和一个橙子你记住苹果是圆的、橙子也是圆的但苹果更红橙子更橙——这就是感知机在做的事情。而鸢尾花数据集就是我们的水果篮子里面有三种不同的鸢尾花每种花都有四个特征花萼长宽、花瓣长宽我们要做的就是教会计算机区分它们。为什么要从零实现而不是直接调用现成的库呢这就像学做菜直接点外卖当然方便但要真正掌握火候和调味还得从切菜开始。用NumPy手写感知机能让你真正理解算法背后的数学原理这对后续学习更复杂的模型大有裨益。2. 环境准备与数据初探2.1 搭建基础开发环境工欲善其事必先利其器。我们需要准备以下工具Python 3.6建议使用Anaconda发行版NumPy数值计算核心库pandas数据处理利器matplotlib可视化神器安装命令非常简单pip install numpy pandas matplotlib2.2 理解鸢尾花数据集鸢尾花数据集包含150个样本每个样本有4个特征和1个标签特征花萼长度、花萼宽度、花瓣长度、花瓣宽度单位厘米标签山鸢尾(Iris-setosa)、变色鸢尾(Iris-versicolor)、维吉尼亚鸢尾(Iris-virginica)我们先加载数据并做个快速浏览import pandas as pd # 从网络直接加载数据集 url https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data df pd.read_csv(url, headerNone, names[sepal_len, sepal_wid, petal_len, petal_wid, class]) # 查看前5条数据 print(df.head())你会看到类似这样的输出sepal_len sepal_wid petal_len petal_wid class 0 5.1 3.5 1.4 0.2 Iris-setosa 1 4.9 3.0 1.4 0.2 Iris-setosa 2 4.7 3.2 1.3 0.2 Iris-setosa 3 4.6 3.1 1.5 0.2 Iris-setosa 4 5.0 3.6 1.4 0.2 Iris-setosa2.3 数据可视化分析让我们用matplotlib绘制前两个特征的散点图import matplotlib.pyplot as plt # 提取三类花的特征 setosa df[df[class]Iris-setosa] versicolor df[df[class]Iris-versicolor] virginica df[df[class]Iris-virginica] fig, ax plt.subplots(figsize(10,6)) ax.scatter(setosa[sepal_len], setosa[sepal_wid], labelSetosa, markero) ax.scatter(versicolor[sepal_len], versicolor[sepal_wid], labelVersicolor, markerx) ax.scatter(virginica[sepal_len], virginica[sepal_wid], labelVirginica, marker) ax.set_xlabel(Sepal Length (cm)) ax.set_ylabel(Sepal Width (cm)) ax.legend() plt.title(Iris Dataset Scatter Plot) plt.show()从图中可以明显看出Setosa与其他两类花能够被一条直线分开而Versicolor和Virginica则有部分重叠。这就是为什么我们选择Setosa和Versicolor进行二分类任务——它们线性可分最适合感知机这样的线性分类器。3. 感知机算法原理剖析3.1 感知机的数学模型感知机的基本思想很简单找到一个超平面在二维情况下就是一条直线将不同类别的数据分开。这个超平面可以用一个线性方程表示f(x) w·x b其中w是权重向量决定超平面的方向b是偏置项决定超平面的位置x是输入特征向量对于二分类问题我们定义当f(x) ≥ 0时预测为正类1当f(x) 0时预测为负类-13.2 感知机的学习过程感知机的训练过程就像蒙着眼睛找门随机初始化w和b相当于站在房间某个随机位置对每个训练样本计算当前预测值f(x)如果预测错误调整w和b向正确的方向迈一小步重复直到所有样本都被正确分类调整权重的规则随机梯度下降 w w η·y·x b b η·y其中η是学习率步长大小y是真实标签1或-1。3.3 为什么能保证收敛一个有趣的数学性质如果数据是线性可分的感知机保证在有限步内收敛。这就像在一个没有障碍物的房间里只要你一直朝着门的方向走最终一定能出去。不过要注意学习率η不能太大否则会跨过最优解也不能太小否则收敛太慢通常设置在0.1到0.01之间4. 从零实现感知机模型4.1 模型类框架搭建让我们用Python实现感知机import numpy as np class Perceptron: def __init__(self, learning_rate0.01, epochs100): self.lr learning_rate # 学习率 self.epochs epochs # 训练轮数 self.weights None # 权重 self.bias None # 偏置 def activation(self, x): 激活函数简单阶跃函数 return 1 if x 0 else -1 def predict(self, x): 预测函数 linear_output np.dot(x, self.weights) self.bias return self.activation(linear_output)4.2 训练过程实现关键的fit方法实现def fit(self, X, y): # 初始化参数 n_samples, n_features X.shape self.weights np.zeros(n_features) self.bias 0 # 转换为numpy数组提高效率 y np.array([1 if i 0 else -1 for i in y]) for _ in range(self.epochs): for idx, x_i in enumerate(X): prediction self.predict(x_i) update self.lr * (y[idx] - prediction) self.weights update * x_i self.bias update4.3 应用到鸢尾花数据准备二分类数据# 提取Setosa和Versicolor两类花使用前两个特征 X df.iloc[0:100, [0, 1]].values y df.iloc[0:100, 4].values y np.where(y Iris-setosa, 1, -1) # Setosa为1其他为-1 # 训练模型 perceptron Perceptron(learning_rate0.1, epochs100) perceptron.fit(X, y) print(f训练后的权重{perceptron.weights}) print(f训练后的偏置{perceptron.bias})5. 结果可视化与分析5.1 绘制决策边界决策边界是模型学习到的分界线我们可以用以下代码可视化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.02), np.arange(y_min, y_max, 0.02)) # 预测网格点类别 Z np.array([model.predict(np.array([xi, yi])) for xi, yi in zip(xx.ravel(), yy.ravel())]) Z Z.reshape(xx.shape) # 绘制 plt.contourf(xx, yy, Z, alpha0.4) plt.scatter(X[:, 0], X[:, 1], cy, s20, edgecolork) plt.xlabel(Sepal Length) plt.ylabel(Sepal Width) plt.title(Perceptron Decision Boundary) plt.show() plot_decision_boundary(X, y, perceptron)5.2 与sklearn实现对比为了验证我们的实现是否正确可以与scikit-learn官方实现对比from sklearn.linear_model import Perceptron sk_perceptron Perceptron(max_iter100, eta00.1, random_state1) sk_perceptron.fit(X, y) # 比较权重 print(我们的实现权重, perceptron.weights) print(sklearn权重, sk_perceptron.coef_[0]) # 比较偏置 print(我们的实现偏置, perceptron.bias) print(sklearn偏置, sk_perceptron.intercept_[0])你会发现两者结果非常接近说明我们的实现是正确的5.3 模型评估指标虽然感知机在这个简单数据集上表现很好但我们还是应该量化评估def accuracy(y_true, y_pred): return np.mean(y_true y_pred) # 训练集准确率 predictions np.array([perceptron.predict(x) for x in X]) print(f训练准确率{accuracy(y, predictions):.2%})在这个线性可分的数据集上你应该能看到100%的准确率。如果尝试用Versicolor和Virginica两类花它们不是线性可分的准确率就会下降这时就需要更复杂的模型了。6. 进阶讨论与常见问题6.1 学习率的影响学习率η是感知机最重要的超参数之一。让我们做个实验learning_rates [0.0001, 0.001, 0.01, 0.1, 1.0] accuracies [] for lr in learning_rates: model Perceptron(learning_ratelr, epochs100) model.fit(X, y) preds np.array([model.predict(x) for x in X]) acc accuracy(y, preds) accuracies.append(acc) print(f学习率 {lr:.4f} 的准确率{acc:.2%}) plt.plot(learning_rates, accuracies, o-) plt.xscale(log) plt.xlabel(Learning Rate (log scale)) plt.ylabel(Accuracy) plt.title(Learning Rate vs Accuracy) plt.show()你会发现学习率太小如0.0001收敛太慢可能无法在指定epoch内达到最优学习率太大如1.0可能震荡无法收敛0.01-0.1通常是不错的选择6.2 特征标准化的重要性虽然在这个简单例子中我们跳过了这一步但在实际应用中特征标准化往往能显著提高模型性能from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X) # 用标准化后的数据训练 perceptron_scaled Perceptron(learning_rate0.1, epochs100) perceptron_scaled.fit(X_scaled, y) # 比较训练速度 print(原始数据训练准确率, accuracy(y, [perceptron.predict(x) for x in X])) print(标准化后准确率, accuracy(y, [perceptron_scaled.predict(x) for x in X_scaled]))标准化后的数据通常能让梯度下降更快收敛特别是当不同特征的尺度差异很大时。6.3 感知机的局限性虽然感知机简单有效但也有明显局限只能处理线性可分数据这就是为什么我们选择Setosa和Versicolor对于非线性问题无能为力即使数据线性可分也可能有多个解感知机找到的只是其中之一这些局限催生了后续更强大的模型如支持向量机(SVM)和神经网络。

更多文章