用Python和sklearn从零实现感知器算法:一个鸢尾花分类的保姆级教程
·
用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. 感知器算法原理与实现
感知器算法的核心思想很简单:找到一个超平面,能够将不同类别的数据点分开。具体来说:
- 初始化权重向量w(通常随机初始化)
- 对于每个训练样本:
- 计算预测值:sign(w·x)
- 如果预测错误,调整权重:w = w + η(y - ŷ)x
- 重复上述步骤,直到所有样本都被正确分类或达到最大迭代次数
让我们用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. 常见问题与调优技巧
在实际应用中,你可能会遇到以下问题:
-
模型不收敛 :
- 检查数据是否线性可分
- 尝试调整学习率(通常0.1-0.01之间)
- 增加最大迭代次数
-
过拟合 :
- 使用正则化(sklearn的Perceptron支持L2正则化)
- 增加训练数据量
- 减少特征数量
-
特征重要性 :
- 感知器的权重可以反映特征重要性
print("特征重要性:", sk_perceptron.coef_)
注意:感知器对特征缩放敏感,务必进行标准化或归一化处理。
7. 扩展到多类别分类
虽然感知器本质上是二分类器,但我们可以通过以下策略实现多分类:
- 一对多(One-vs-Rest) :为每个类别训练一个分类器
- 一对一(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)
在实际项目中,我发现数据预处理步骤往往比模型选择更重要。花时间理解数据分布、处理异常值、选择合适的特征,通常能带来比调参更大的性能提升。感知器虽然简单,但在线性可分问题上表现优异,而且训练速度快,是很好的基准模型。
更多推荐


所有评论(0)