【人工智能】【Python】单层感知机应用实验

1. 导入库

先导入需要的各类库

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
图片[1] - AI科研 编程 读书笔记 - 【人工智能】【Python】单层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

2. 数据处理

2.1.加载鸢尾花数据集。

2.2.按7:3比例划分训练/测试集。

2.3.用StandardScaler进行数据标准化。

2.4.格式转换为PyTorch张量。

# 加载鸢尾花数据集
iris = load_iris()
X = iris.data
y = iris.target
​
# 将问题转换为二分类:Setosa vs Non-Setosa
# 原始标签:0-Setosa, 1-Versicolour, 2-Virginica
# 我们将Versicolour和Virginica合并为一类(标签1)
y = (y > 0).astype(int) # Setosa->0, Non-Setosa->1
​
# 划分训练集和测试集 (70%训练,30%测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
​
# 数据标准化:加速模型收敛
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
​
# 将数据转换为PyTorch张量
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.float32)
​
# 为标签添加一个维度,使其形状从 (n_samples) 变为 (n_samples, 1),与模型输出匹配
y_train = y_train.view(-1, 1)
y_test = y_test.view(-1, 1)
图片[2] - AI科研 编程 读书笔记 - 【人工智能】【Python】单层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

3. 模型搭建

1.定义单层感知机的类结构。

2.创建对象并初始化模型。

class SingleLayerPerceptron(nn.Module):
    def __init__(self, input_dim):
        super(SingleLayerPerceptron, self).__init__()
        # 定义一个线性层 (input_dim -> 1)
        self.linear = nn.Linear(input_dim, 1)
        # 虽然原版感知机使用阶跃函数,但为了使用PyTorch的梯度下降,
        # 我们在输出层使用Sigmoid函数,它将输出压缩到(0,1),可以理解为概率
        self.sigmoid = nn.Sigmoid()
​
    def forward(self, x):
        # 前向传播:线性变换 + Sigmoid激活
        outputs = self.sigmoid(self.linear(x))
        return outputs
​
# 初始化模型
input_dim = 4 # 输入特征数
model = SingleLayerPerceptron(input_dim)
图片[3] - AI科研 编程 读书笔记 - 【人工智能】【Python】单层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

4. 定义损失函数与优化器

1.损失函数选择二元交叉熵损失(BCE)。

2.优化器选择随机梯度下降(SGD),设置学习率为0.01。

# 使用二元交叉熵损失 (Binary Cross Entropy)
# 它适用于二分类问题,并与Sigmoid激活函数配合良好。
criterion = nn.BCELoss()
# 使用随机梯度下降优化器 (SGD)
# 这与原始的感知机学习规则的思想一致。
optimizer = optim.SGD(model.parameters(), lr=0.01)
图片[4] - AI科研 编程 读书笔记 - 【人工智能】【Python】单层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

5. 模型训练

1.设定训练轮次为100。 2.单轮训练流程: 前向传播:用训练集数据计算模型输出,再通过损失函数计算损失值,将损失存入列表。 反向传播与优化:清空上一轮的梯度,避免梯度累加,然后通过loss.backward()反向计算梯度,最后用optimizer.step()根据梯度更新模型参数。 3.每10轮打印一次当前损失,训练结束后绘制“损失-轮次”曲线。

# 训练参数
num_epochs = 100
train_losses = []
for epoch in range(num_epochs):
    # 前向传播
    outputs = model(X_train)
    loss = criterion(outputs, y_train)
    train_losses.append(loss.item())
    # 反向传播和优化
    optimizer.zero_grad() # 清空上一步的梯度
    loss.backward()       # 反向传播,计算当前梯度
    optimizer.step()      # 根据梯度更新模型参数
    # 每10个epoch打印一次损失
    if (epoch+1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
​
# 绘制训练损失曲线
plt.plot(train_losses)
plt.title('Training Loss over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss (BCE)')
plt.show()
图片[5] - AI科研 编程 读书笔记 - 【人工智能】【Python】单层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

6. 模型评估

1.关闭梯度计算。

2.预测与标签转换。

3.计算和打印准确率,输出前10个样本的“真实标签-预测标签-预测概率”。

# 在测试集上进行预测
with torch.no_grad(): # 关闭梯度计算,节省内存和计算资源
    test_outputs = model(X_test)
    # 将Sigmoid输出概率转换为二进制标签 (>=0.5 -> 1, <0.5 -> 0)
    predicted_labels = (test_outputs >= 0.5).int()
    # 计算准确率
    accuracy = (predicted_labels == y_test).float().mean()
    print(f'Test Accuracy: {accuracy.item() * 100:.2f}%')
​
# 查看部分真实值和预测值以供分析
print("\nSample Predictions (True vs Predicted):")
for i in range(10):
    print(f"True: {y_test[i].item()}, Predicted: {predicted_labels[i].item()}, Prob: {test_outputs[i].item():.4f}")
图片[6] - AI科研 编程 读书笔记 - 【人工智能】【Python】单层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

7. 结果与思考

在第五小节中进行了模型训练,观察模型损失曲线,我看到随着训练轮数的增加,损失值从最初的较高水平逐步下降,从第 10 轮的 0.5736 一直降到第 100 轮的 0.3220,而且它下降的幅度在后期慢慢变小的。这说明单层感知机模型在不断学习。它通过梯度下降持续调整了线性层的各种参数,让预测结果与标签的差距不断缩小,并且模型正在逐渐收敛,参数调整的效果越来越稳定,没有出现明显的震荡或发散,证明当前的优化器(SGD)和学习率(0.01)是合适的,模型能有效学习到训练数据中的特征规律。

在第六小节中进行了模型测试,我发现模型在测试集上的准确率是100%,所以在 “区分 Setosa 和 Non-Setosa” 这个二分类任务中,利用数据集的4个特征(萼长、萼宽、瓣长、瓣宽),确实能找到一条线性决策边界把两类样本完全分开。这也就证明了 Setosa 类和 Non-Setosa 类在这4个特征构成的空间里是线性可分的,而单层感知机又作为线性分类器,可以正好完美适配这种分类任务,所以模型才能达到这么高的测试准确率。

尝试将问题改为区分Versicolour和Virginica:

# 只保留Versicolour(1)和Virginica(2)
mask = (y == 1) | (y == 2)
X = X[mask]
y = y[mask]
​
# 重新定义标签:Versicolour→0,Virginica→1
y = (y == 2).astype(int)
图片[7] - AI科研 编程 读书笔记 - 【人工智能】【Python】单层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

重新训练模型后,我发现测试准确率明显下降到90%。

这是因为 Versicolour 和 Virginica 两类鸢尾花在这 4 个特征构成的空间中不是线性可分的。它们的特征分布有较多重叠,不存在一条直线能将两类样本完美分开。 而我的单层感知机本质是线性分类器,它只能学习线性决策边界。面对这种非线性可分的任务,无论怎么调整参数,都无法找到一个理想的线性边界来准确区分两类样本,因此准确率必然会显著下降。

8. 实验总结

本次实验主要我在这个修改和对应的结果中有些收益,我认识到了数据的线性可分性对单层感知机这类线性分类器至关重要。也就是说当两类数据在特征空间中存在重叠、无法用直线划分时(如 Versicolour 和 Virginica),即使模型结构和训练流程不变,准确率也会大幅下降。这让我直观理解了线性分类器的本质局限,也明白了为什么需要多层结构和非线性激活函数来处理更复杂的数据关系。接下来要学习的多层感知机可能解决这个问题。

© 版权声明
THE END
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容