ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

全连接神经网络入门:用Python实现Iris花卉分类实战

全连接神经网络入门:用Python实现Iris花卉分类实战 简介这是一份面向机器学习初学者的全连接神经网络MLP图像分类实战代码包以Iris花卉数据集为对象演示如何用NumPy从零搭建神经网络并配合scikit-learn完成数据加载与训练。代码覆盖网络层定义、前向计算、反向传播、损失计算和训练循环等核心模块使用计算图替代softmax函数以简化推导并采用SGDMomentum优化器提升收敛效果便于读者逐行理解MLP的底层机制与调参思路。资源共4个文件包含1个Python主程序、2张损失与计算图示意图以及1份PDF笔记压缩包仅765KB轻量便携Python代码可直接运行示意图辅助观察训练过程PDF则梳理了关键公式与实现细节。目前已有1852人学习下载适合正在学习深度学习基础、希望从理论走向代码实践的读者既可对照实验也可作为课程设计或入门练习的参考。1. 全连接神经网络真能分花卉先泼一盆冷水搜“全连接神经网络(MLP)实现花卉图像分类 Iris数据集 Python代码”的人十有八九是刚入门的Python学习者。这个标题里藏着一个很容易被忽略的坑Iris数据集根本不是图像数据。它是 150 条鸢尾花的测量记录每条只有花萼长、花萼宽、花瓣长、花瓣宽 4 个数值特征标签是山鸢尾、变色鸢尾、维吉尼亚鸢尾三种。所以这个项目在做的事是把 4 个数字喂给 MLP让它学会按花瓣尺寸把花的种类分开跟“图像分类”相差十万八千里。但这反而让它成了学习 MLP 最合适的起步题。图像动辄几百上千维网络结构、参数量、训练周期都被放大新手很难看清模型到底在学什么。而 Iris 只有 4 个输入和 3 个输出隐藏层放 8 个神经元就能跑出 95% 以上的准确率训练一次不到一秒内存占用可以忽略。你可以在它身上把数据预处理、前向传播、反向传播、调参、过拟合这些概念全部过一遍再带着理解去啃真正的大图分类。这篇文章就按这个路径讲从 MLP 原理到可复现的 Python 代码再到参数设置和常见的翻车现场。2. MLP 的结构与 Iris 数据真相为什么入门分类总选它2.1 从神经元到全连接层MLP 的分层打分机制全连接神经网络MLPMultilayer Perceptron的核心逻辑可以压缩成一句话输入特征进来经过一层层加权求和加激活函数最后输出每个类别的得分。每一层由若干神经元组成每个神经元做的事就是拿上一层的输出乘以权重、加上偏置再过激活函数。因为每个神经元都和上一层的所有神经元相连所以叫“全连接”。用 Iris 这个任务来数一下维度输入层 4 个神经元对应 4 个特征隐藏层若干神经元输出层 3 个神经元对应 3 个类别。中间层数越多、每层神经元越多模型的拟合能力越强但代价是参数量上升、训练变慢也更容易过拟合。对 Iris 这种 150 条数据的小样本任务一到两层隐藏层已经完全够用堆深度反而是给自己找麻烦。激活函数的存在是为了让模型具备非线性表达能力。如果没有激活函数多层全连接堆叠起来跟一层线性变换没有任何区别——矩阵连乘最后还是个线性变换学不出花瓣尺寸和品种之间的复杂边界。常见做法是在隐藏层用 ReLU输出层用 Softmax 把得分转成概率分布。ReLU 计算简单、能缓解梯度消失是现在训练 MLP 的默认选择。下面这段代码就是一个不含训练逻辑的纯结构示意用来展示每一层在做什么import torch.nn as nn class MLP(nn.Module): def __init__(self): super().__init__() # 输入4个特征 - 隐藏层8个神经元 self.fc1 nn.Linear(4, 8) # 隐藏层8个神经元 - 输出层3个类别 self.fc2 nn.Linear(8, 3) self.relu nn.ReLU() def forward(self, x): x self.relu(self.fc1(x)) x self.fc2(x) # 输出层后面接Softmax但交叉熵损失里自带 return x这里nn.Linear(4, 8)里的 4 是输入特征数8 是隐藏层神经元数。nn.Linear(8, 3)里的 3 是分类类别数。forward里第一层输出过了一遍 ReLU第二层没有再接激活函数因为后面配nn.CrossEntropyLoss()时它会内部先做 Softmax。很多初学者在这里多写一个nn.Softmax()结果也好但显得对 PyTorch 的交叉熵实现不熟悉。2.2 Iris 数据集拆解4 个数值特征不是图片Iris 数据集由统计学家 Ronald Fisher 在 1936 年整理150 条样本被均匀分成三类每类 50 条。每个样本包含 4 个特征单位是厘米sepal length花萼长、sepal width花萼宽、petal length花瓣长、petal width花瓣宽。标签是三类鸢尾花的名称。这个数据集最有意思的一点是花瓣长和花瓣宽的区分能力远强于花萼。山鸢尾的花瓣明显偏小另外两类花瓣偏大而变色鸢尾和维吉尼亚鸢尾在花瓣上有一部分重叠靠线性分类器会发生少量误判。这意味着如果你只用前两个特征训练准确率大概率在 80% 左右把 4 个特征全用上准确率能上到 95% 以上。我在给新人讲特征工程时经常拿这个例子说明模型能学的上限是由数据里的区分度决定的层数再多也补不回特征层面就丢掉的判别信息。用 pandas 加载后可以快速看一眼数据结构和类别分布这段代码在你自己的环境里可以直接跑import pandas as pd df pd.read_csv(https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data, headerNone, names[sepal_length, sepal_width, petal_length, petal_width, species]) print(df.head()) # 查看前5行 print(df[species].value_counts()) # 统计每个类别的样本数headerNone是因为原始文件没有列头列名由我们自己指定。value_counts()应该输出每个类别 50 条。如果你看到某个类别数量不对先检查下载的文件是否完整或者换用sklearn.datasets.load_iris()直接加载那个接口返回的是 numpy 数组省去处理 CSV 的麻烦。2.3 Softmax 与交叉熵让网络学会说“概率”输出层的 3 个原始得分叫 logits数值范围没有约束可能是正也可能是负。Softmax 做的事是把这 3 个数压成非负且总和为 1 的概率分布。比如 logits 是 [2.0, 1.0, 0.1]Softmax 之后可能变成 [0.52, 0.31, 0.17]含义就是模型认为这朵花有 52% 概率是山鸢尾。有了这个概率分布还需要一个损失函数来告诉模型错得有多离谱。最常用的是交叉熵损失Cross-Entropy Loss。对于一条真实标签为 y 的样本交叉熵计算的是模型预测概率和真实概率分布之间的距离模型越自信且越正确损失越小。如果模型对错误类别给出高置信度损失值会非常大梯度更新幅度也随之变大。训练过程就是不断拿一批样本算损失、求梯度、更新权重。在代码层面PyTorch 的nn.CrossEntropyLoss()已经把 Softmax 和损失计算合二为一所以模型前向传播的最后一层直接输出 logits 就行。很多新手在这里踩坑自己手动在forward里加了 Softmax又用了CrossEntropyLoss()等于做了两遍 Softmax虽然不报错但收敛速度和最终效果都会变差。3. 用 Python 跑通 MLP 花卉分类数据、模型、训练一条龙3.1 加载 Iris 数据用 scikit-learn 一行取数写代码之前先把环境准备好Python 3.8 以上装上numpy、pandas、torch、scikit-learn、matplotlib这几个库就行。训练一个 Iris 分类器不需要 GPUCPU 跑起来绰绰有余你甚至可以开个终端直接执行。数据加载我推荐用sklearn.datasets.load_iris()它返回一个 Bunch 对象data是 150×4 的特征矩阵target是 150 个 0/1/2 的整数标签target_names是对应的品种名。相比自己去下载 CSV这个接口不依赖网络也不用手动处理列名而且保证数据干净——对于教学场景屏蔽掉不必要的“脏活”能让注意力集中到网络本身。下面是数据加载和拆分的一小段注意看拆分参数from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split iris load_iris() X iris.data # 形状 (150, 4) y iris.target # 形状 (150,)取值 0、1、2 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) print(X_train.shape, X_test.shape)test_size0.2表示留出 20% 的数据做测试集剩下 120 条做训练集。stratifyy是分层抽样保证训练集和测试集里三类花的比例都接近 1:1:1。对于 Iris 这种每类只有 50 条的小数据分层抽样很重要——如果不分层随机拆分很可能让某一类在测试集里过多或过少导致评估结果波动很大。random_state42固定了随机种子让每次跑代码得到相同的拆分结果方便复现。我很建议做对比实验时固定这个值否则你今天跑 95%、明天跑 97%根本分不清是模型改好了还是数据拆分变了。3.2 标准化与数据拆分顺序错了会漏信息标准化StandardScaler是训练 MLP 前几乎必做的一步。原理很简单把每个特征减去均值、除以标准差让所有特征都落在均值 0、标准差 1 的分布里。为什么必须做因为 MLP 的权重更新依赖特征尺度如果某两个特征的数值范围差很多——比如花萼宽在 2~4 之间、花瓣长在 1~7 之间——梯度下降的过程中大尺度特征会主导权重更新模型需要更长的时间才能学到合适的权重甚至在某些学习率下根本无法收敛。标准化还有一个“顺序”上的讲究先拆分训练集和测试集再做标准化且只能拿训练集的均值和标准差去变换测试集。这是为了模拟真实场景——你在部署模型时没有一份“未来的测试集”让你提前统计它的均值。如果先对整个数据集做标准化再拆分训练集里其实已经混入了测试集的统计信息算是一种数据泄漏会让测试准确率偏高。下面是标准写法from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) # 在训练集上计算均值和标准差 X_test_scaled scaler.transform(X_test) # 用训练集的参数转换测试集fit_transform在训练集上做两件事拟合fit出均值和标准差然后转换transform数据。测试集只调用transform不重新 fit。如果你在这里写成了scaler.fit_transform(X_test)测试集的均值标准差就用错了而且scaler会在内部记住最后一次 fit 的参数后续模型保存和推理时容易出隐患。3.3 构建三层 MLPPyTorch 版最小模型模型结构在第二章已经展示过了这里直接把它和训练代码拼起来。下面的完整代码是这个项目的核心我把训练循环、损失计算、参数定义都放在一起方便你直接复制运行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 # 1. 加载数据 iris load_iris() X, y iris.data, iris.target # 2. 拆分与标准化 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 3. 转成 PyTorch Tensor X_train_t torch.tensor(X_train_scaled, dtypetorch.float32) y_train_t torch.tensor(y_train, dtypetorch.long) X_test_t torch.tensor(X_test_scaled, dtypetorch.float32) y_test_t torch.tensor(y_test, dtypetorch.long) # 4. 定义模型结构 class MLP(nn.Module): def __init__(self): super().__init__() self.fc1 nn.Linear(4, 8) self.fc2 nn.Linear(8, 3) self.relu nn.ReLU() def forward(self, x): x self.relu(self.fc1(x)) x self.fc2(x) return x model MLP() # 5. 定义损失函数和优化器 criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.01) # 6. 训练循环 epochs 200 for epoch in range(epochs): model.train() optimizer.zero_grad() outputs model(X_train_t) loss criterion(outputs, y_train_t) loss.backward() optimizer.step() if (epoch 1) % 50 0: print(fEpoch [{epoch1}/{epochs}], Loss: {loss.item():.4f})这段代码的关键逻辑都写在注释里了。第 6 步的训练循环是标准的 PyTorch 五件套zero_grad()清空上一次迭代的梯度前向传播算出输出计算损失backward()反向传播求出各参数梯度step()更新权重。如果不调zero_grad()梯度会在每次迭代时累加模型直接崩掉——这是新手常犯的第一个错误。optim.Adam(model.parameters(), lr0.01)里的lr0.01是学习率对 Iris 来说是个比较稳的选择。Adam 是自适应学习率的优化器对学习率的敏感度比纯 SGD 低很多所以 0.01 基本不会出问题。如果你想用 SGD学习率要调低一个量级比如 0.001 到 0.005这个差别后面会展开讲。3.4 训练循环、损失回传与结果评估模型训练完接下来要拿测试集评估它的泛化能力。评估要做两件事一是算出测试准确率二是看混淆矩阵搞清楚模型具体在哪些类别之间犯错。千万不要只看准确率——Iris 三分类的随机基线是 33%如果模型到了 95%光看数字是很漂亮但你不知道那 5% 的错全集中在哪一类上这会影响你对特征和模型的判断。完整评估代码如下from sklearn.metrics import confusion_matrix, classification_report model.eval() with torch.no_grad(): outputs model(X_test_t) _, predicted torch.max(outputs, 1) accuracy (predicted y_test_t).sum().item() / len(y_test_t) print(fTest Accuracy: {accuracy:.4f}) cm confusion_matrix(y_test, predicted.numpy()) print(Confusion Matrix:) print(cm) print(classification_report(y_test, predicted.numpy(), target_namesiris.target_names))model.eval()通知模型进入评估模式。这个操作在 MLP 上看起来没有实质性变化因为模型里没有 Dropout 或 BatchNorm但写成习惯在以后做更复杂的网络时能避免很多坑。torch.no_grad()显式告诉 PyTorch 不需要计算梯度评估时省内存、速度快几倍。torch.max(outputs, 1)的含义是在输出的第二维类别维上找最大值下标即模型认为最可能的类别。用random_state42拆分时这套模型跑出来的测试准确率应该在 93%97% 之间。混淆矩阵通常在 “变色鸢尾” 和 “维吉尼亚鸢尾” 之间有一两条对角外的值这是正常的因为这两类在花瓣尺寸上有重叠区域。真正的小概率事件是山鸢尾被分错——它的特征和其他两类差异太明显如果这都分错基本是标准化步骤出了问题。4. 参数调优学习率、隐藏层宽度、Epoch 怎么设4.1 学习率从 0.001 到 0.1 的试错区间学习率是训练 MLP 时影响最大的超参数。它决定了每次参数更新迈多大的步子学习率太大损失函数会在最优点附近来回震荡甚至发散学习率太小模型收敛得极慢训练几百轮还在原地踏步。以 Iris 为例我习惯先在 0.001、0.01、0.1 三档里快速试一遍。你会发现 0.01 通常表现很好几十个 epoch 后损失就降到 0.1 以下0.001 则需要更多的 epoch 才能追上而 0.1 会出现一个明显的特征——损失在前几个 epoch 里不降反升然后开始剧烈震荡。这是因为大学习率让权重更新跨过了最优区域模型在损失曲面里“弹来弹去”。这时把学习率调回 0.01重新训练问题立刻消失。下表是我在同样 200 个 epoch 下跑出来的经验参考值学习率优化器收敛情况测试准确率范围0.1Adam震荡明显40%~75%0.01Adam70轮内收敛93%~97%0.001Adam收敛偏慢93%~97%0.01SGD可能发散25%~70%0.001SGD200轮不够85%~93%SGD 在 0.01 下表现很差原因是它对参数尺度和数据分布更敏感学习率必须更保守。这也是为什么现在入门教程大多直接推荐 Adam——降低调参门槛先跑通再研究优化器的差异。4.2 隐藏层宽度与网络深度太窄欠拟合、太宽过拟合隐藏层的神经元数量决定了网络容量。Iris 数据集只有 4 个输入特征、3 个类别这种低维分类任务的决策边界并不复杂。隐藏层放 4 个神经元就能达到 90% 左右的准确率8 个神经元一般是最优区间16 个以上性能提升有限反而开始过度贴合训练集。如果你把隐藏层加到 32 个神经元再训 500 轮训练集准确率会到 100%但测试集准确率可能掉到 90% 以下——这就是过拟合。特征只有 4 个信息量有限模型把训练集里的噪声也当成了规律记下来。对 Iris 这种小数据正则化手段Dropout、L2的效果也不明显最直接的方法是减少神经元数量或提前停止训练。深度方面更不用贪。两层隐藏层的 MLP 在 Iris 上不会比一层显著更好反而训练时间翻倍、更容易过拟合。我的建议是固定一层隐藏层从 8 个神经元开始调如果测试准确率低于 93%再往 16 个加如果 8 个已经到 95% 以上就别折腾模型结构去检查数据处理和训练配置。4.3 Batch Size 与 EpochIris 小数据的特殊用法Batch Size 是指每次参数更新用多少条样本计算梯度。在 Iris 这个场景下训练集只有 120 条常见的几种设置是Batch Size 全部数据Full Batch每次更新都用全部 120 条算梯度梯度方向稳定收敛平滑适合小数据。Batch Size 1SGD每条样本更新一次梯度非常不稳定损失曲线上下乱跳但可能逃离局部最优。Batch Size 8 或 16Mini-Batch折中方案梯度带有一定噪声但波动可控。对 120 条数据来说我一般直接用 Full Batch即每次迭代用全部训练集计算梯度。这样做的好处是损失曲线单调下降方便你判断模型是否正常收敛。如果你看到曲线有锯齿状波动说明 Batch Size 设小了或者学习率偏大。用 Mini-Batch 时需要注意一个 epoch 是指把所有训练数据过一遍而不是只完成一次参数更新。Batch Size 越少一个 epoch 内参数更新的次数越多所以大 Batch 和小 Batch 做对比时要小心嘴上说“都训 100 个 epoch”背后的计算量其实不一样。Epoch 的设置则要结合早停Early Stopping。训练过程中每个 epoch 结束后都算一次测试集准确率如果连续 30 个 epoch 测试准确率没有提升就停止训练。这样既能省时间又能动态避开过拟合区间不用纠结“到底训 100 轮还是 200 轮”的问题。4.4 读懂损失曲线收敛、震荡、过拟合三种形态训练过程中把每个 epoch 的 loss 值和测试集准确率画成曲线是诊断模型状态的最有效手段。matplotlib 画两张子图就够了左边一张 loss-epoch 曲线右边一张测试准确率-epoch 曲线。我在调模型时九成的时间都花在盯这两张图上。三种常见形态分别对应三种问题。第一种loss 稳步下降最后趋于平缓测试准确率同步上升说明模型状态健康。第二种loss 下降几个 epoch 后又飙升或者呈现锯齿状震荡原因基本锁定在学习率过大或 Batch Size 太小。第三种训练集 loss 持续下降但测试准确率在某个点之后开始回落这是典型的过拟合信号——模型在记忆训练集而不是学习通用规律此时需要减少 epoch、减小网络容量或加入正则化。代码实现很简单把每个 epoch 的指标存进列表训练结束后统一绘制import matplotlib.pyplot as plt train_losses [] test_accs [] for epoch in range(epochs): model.train() optimizer.zero_grad() outputs model(X_train_t) loss criterion(outputs, y_train_t) loss.backward() optimizer.step() train_losses.append(loss.item()) model.eval() with torch.no_grad(): test_outputs model(X_test_t) _, preds torch.max(test_outputs, 1) acc (preds y_test_t).sum().item() / len(y_test_t) test_accs.append(acc) plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.plot(train_losses) plt.title(Training Loss) plt.subplot(1, 2, 2) plt.plot(test_accs) plt.title(Test Accuracy) plt.tight_layout() plt.show()这段代码不需要额外解释它是第三节训练循环的延伸。注意测试集准确率在每个 epoch 都算一次对 Iris 这种 30 条测试数据来说计算量几乎为零但对大模型来说每个 epoch 都评估会拖慢训练那时候改成每 5 或 10 个 epoch 评估一次更合理。5. 常见问题避坑排查MLP 训练 Iris 时的高频翻车点5.1 现象准确率卡在 33% 左右模型在“装死”这是我见过最多的问题。训练过程不报错loss 也下降但测试准确率只有三分之一——跟随机猜的概率几乎一样。原因大概率是标签和输出维度对不上模型输出是 3 个类别的 logits但y_train里的标签不是 0/1/2而是 1/2/3或者根本没转成整数标签而是字符串setosa。解决方法是打印一两行数据和标签看一眼。y.dtype、np.unique(y)这两条命令一执行问题立刻现形。标签必须是 0、1、2 这样的连续整数且 dtype 是int64或longCrossEntropyLoss()不允许字符串标签报错的话反而好排查怕就怕有些教程里先做了LabelEncoder或 One-Hot编码后维度没对上模型静默输出错误结果。5.2 现象Loss 变成 NaN训练直接中断Loss 出现nan的位置通常是第三、四个 epoch有时在第一个 epoch 就出现。两个常见原因一是原始数据里有缺失值或无穷大——Iris 数据集本身是干净的但如果你自己拼接了外部数据很可能带进来NaN二是学习率设置过大导致梯度更新后参数溢出计算过程中产生inf。排查顺序是先跑np.isnan(X).sum()和np.isfinite(X).all()检查输入数据再检查标准化是否执行——未标准化的数据配合大学习率loss 变成nan的概率极高最后把优化器换成 Adam、学习率降到 0.001 重跑一遍如果nan消失说明是优化器或学习率的问题。如果降到 0.0001 仍然nan那就是数据本身的问题跟模型无关。5.3 现象训练集 100%测试集只有 85%训练集准确率拉到 100% 并不难Iris 的 120 条训练数据太容易记住了。真正的问题是测试集准确率上不去。这是典型过拟合模型把训练集里每一条样本的独有噪声都学进去了遇到没见过的样本反而判断不准。对 Iris 最有效的解法不是加正则化而是减小模型容量。把隐藏层从 32 降到 8或者把 epoch 从 500 降到 100你会发现测试集准确率反而上升。另一个立竿见影的方法是用交叉验证比如 5-fold cross validation替代固定的单次拆分。sklearn 的cross_val_score可以自动完成多次拆分和评估得到的平均准确率远比你拍脑袋拆一次的结果有说服力。5.4 现象看到“花卉图像分类”就把 Iris 当成图片处理这个坑来自标题本身的误导。有人觉得既然是花卉分类就得用cv2.imread读图片再把图片像素矩阵转成向量喂给网络。但 Iris 数据集压根没有图片文件是 150 条纯数值记录。用图像处理流程走一遍第一行就报错因为read_csv读出来是表格数据。如果你要做真正的图像分类请换数据集CIFAR-10、MNIST、或者自己用torchvision.datasets.ImageFolder读取本地图片文件夹。Iris 只适合学 MLP 的数值输入输出流程别把图像那套 resize、归一化、通道变换的流程搬过来给 Iris 用那是用错工具。拿“花卉图像”作为搜索词找到 Iris 的情况非常普遍但数据集定义如此认清它的形态是第一步。5.5 现象先标准化再拆分测试集分数虚高把标准化放在数据拆分之前做是一个看起来很顺理成章、实际上漏了信息的做法。整个数据集 150 条的均值和标准差与训练集 120 条的统计值有细微差别拆分后测试集被“剧透”了整体统计信息测试准确率会比在生产环境中的真实表现高一点点。对 Iris 来说这个偏差不大大约 1 到 3 个百分点但这是一个习惯问题——如果你在真正的大数据集上这么做测试集分数会显著虚高误导你对模型实力的判断。正确姿势永远在同一行代码里决出先后先train_test_split再scaler.fit_transform(X_train)最后scaler.transform(X_test)。6. 从 Iris 到真图像MLP 做图像分类的最后一个技巧6.1 把图像拉平成向量单层 Flatten 的代价Iris 只有 4 个特征MLP 处理好这个表格分类问题并不难。但如果你真的想拿 MLP 做图像分类最常见的做法是用torch.flatten把图片从(batch, channels, height, width)变成(batch, channels * height * width)然后丢进全连接层。以 MNIST 的 28×28 单通道图片为例展平后每个样本是 784 维的向量隐藏层放 128 个神经元模型参数就是 784×128 128×3 这个量级。问题在于展平丢掉了空间结构。图像里相邻像素之间的局部相关性是最重要的信息而展平后的向量里任何两列之间的位置关系都是一样的模型学不出“左上角和右下角像素一起出现意味着什么”这种概念。所以 MLP 在 MNIST 上大约能做到 97%~98% 的准确率而 CNN 可以轻松到 99% 以上。这不是调参能补的差距是网络结构本身的局限。6.2 验证 MLP 是否学到位混淆矩阵读错误模式从 Iris 切换到图像后评估方式依然通用混淆矩阵、分类报告、损失曲线。但图像任务里混淆矩阵的价值更大因为它能告诉你模型在哪些“长得接近”的类别之间犯错。比如把 7 误判成 1、把 0 误判成 6这种错误有规律可循看过矩阵后你可以去翻被误判的样本图亲自感受模型为什么犹豫。我个人的习惯是训练完模型后挑出被分错的样本用 matplotlib 把图片和模型预测概率一起打印出来看一眼。这一步比盯损失曲线更能建立直觉——你会很快发现模型对某些形态的数字、某种光照条件下的图片系统性犯错。这个习惯从 Iris 开始就值得培养虽然错误样本只有一两朵花但打印出它们的原始特征值和预测概率你会对模型的学习边界有更具体的感知而不只是停留在“94% 准确率”这个抽象数字上。希望这篇实战笔记能帮你把第一个 MLP 项目干净利落地跑通少踩几个我当年翻过的车调参时心里更有数。本文还有配套的精品资源点击获取
返回列表