《人工智能概论》实验3 知识点复习提纲
第一部分:数据集与输入预处理
1. Fashion-MNIST 数据集
-
训练集:60,000 张灰度图像,测试集:10,000 张。
-
图像尺寸:28 × 28 像素,单通道(灰度)。
-
类别数:10 类(T恤、裤子、套衫、裙子、外套、凉鞋、衬衫、运动鞋、包、踝靴)。
-
输入特征数:28 × 28 = 784。
2. 数据预处理(transform)
-
transforms.ToTensor():将 PIL 图像或 numpy 数组转换为[0,1]范围的张量,形状变为(C, H, W)。 -
transforms.Normalize((0.5,), (0.5,)):将每个像素x变为(x - 0.5)/0.5,即从[0,1]映射到[-1,1]。
第二部分:多层感知机(MLP)基础
1. 为什么需要 nn.Flatten()?
-
全连接层(
nn.Linear)要求输入是 二维:[batch, features]。 -
原始图像形状:
[batch, 1, 28, 28]→ 必须“拉直”为[batch, 784]。 -
Flatten()从第1维开始展平(保留 batch 维)。
2. 全连接层的参数量计算(必考)
-
权重数量 =
in_features × out_features -
偏置数量 =
out_features -
该层总参数量 =
in_features × out_features + out_features -
示例:
nn.Linear(784, 256)参数量 =784×256 + 256 = 200,960
3. 激活函数(重点对比)
| 名称 | 公式 | 导数 | 输出范围 | 优点 | 缺点(考点) |
|---|---|---|---|---|---|
| ReLU | max(0, x) |
0 (x<0), 1 (x≥0) | [0, ∞) |
计算快,正区间梯度不消失,稀疏性 | 神经元“死亡”(梯度为0) |
| Sigmoid | 1/(1+e^{-x}) |
σ(x)(1-σ(x)) ≤ 0.25 |
(0,1) |
平滑,易解释 | 梯度饱和(易消失),输出非零均值 |
| Tanh | (e^x-e^{-x})/(e^x+e^{-x}) |
1 - tanh²(x) ≤ 1 |
(-1,1) |
零中心,比 Sigmoid 稍好 | 仍有梯度饱和问题 |
梯度消失严重程度:Sigmoid > Tanh > ReLU(ReLU 几乎不消失)
收敛速度:ReLU 最快(梯度不衰减),Sigmoid/Tanh 很慢(饱和区梯度极小)。
4. 无激活函数的后果
-
多个线性层堆叠等价于一个线性层(矩阵乘法结合律)。
-
无法学习非线性关系 → 等价于线性回归/线性分类器,准确率大幅下降。
第三部分:模型定义方式
nn.Sequential 定义方式
nn.Sequential 是一种容器类,允许以顺序方式快速堆叠多个网络层。其特点是:
- 代码简洁:按顺序逐层定义,适用于线性结构模型。
- 自动前向传播:层之间按添加顺序自动执行前向计算,无需手动编写
forward方法。
示例代码:
import torch.nn as nn
model = nn.Sequential(
nn.Flatten(), # 将输入展平为向量
nn.Linear(784, 256), # 全连接层,输入784维,输出256维
nn.ReLU(), # 激活函数
nn.Linear(256, 10) # 输出层,10分类
)
nn.Module 子类定义方式
通过继承 nn.Module 类自定义模型,灵活性更高:
- 复杂结构支持:可定义分支、循环或条件逻辑。
- 需手动实现
forward:必须显式编写前向传播逻辑。
示例代码:
class CustomModel(nn.Module):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten()
self.fc1 = nn.Linear(784, 256)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(256, 10)
def forward(self, x):
x = self.flatten(x)
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
model = CustomModel()
核心区别
-
灵活性
nn.Sequential:仅适合线性堆叠层。nn.Module:可自由定义复杂计算图(如残差连接、多分支)。
-
代码控制
nn.Sequential:隐式处理前向传播,无法介入中间过程。nn.Module:需显式编写forward,可插入调试逻辑或自定义操作。
-
适用场景
nn.Sequential:快速原型设计或简单模型。nn.Module:需定制化逻辑的复杂模型。
-
参数管理
- 两者均通过
parameters()方法管理参数,无本质差异。
- 两者均通过
选择依据:根据模型复杂度和是否需要灵活控制前向传播逻辑决定。
第四部分:训练流程(通用训练函数)
1. 损失函数与优化器
-
多分类损失:
nn.CrossEntropyLoss()(内部 = LogSoftmax + NLLLoss) -
优化器:
optim.Adam(常用)或optim.SGD
2. 每个 Epoch 的训练步骤
-
model.train()→ 启用 Dropout/BatchNorm 训练模式 -
遍历 DataLoader:
-
optimizer.zero_grad()清空梯度 -
outputs = model(images)前向传播 -
loss = criterion(outputs, labels)计算损失 -
loss.backward()反向传播 -
optimizer.step()更新参数
-
3. 验证(测试)步骤
-
model.eval()→ 禁用 Dropout,固定 BatchNorm -
with torch.no_grad():→ 不记录梯度,节省内存 -
计算准确率:
-
_, predicted = torch.max(outputs, 1)获取预测类别(dim=1表示类别维度) -
correct += (predicted == labels).sum().item() -
最终准确率 =
100 * correct / total
-
4. 为什么验证时要用 eval() + no_grad()?
-
eval():保证 Dropout 不随机丢弃神经元,BatchNorm 使用全局统计量,结果稳定。 -
no_grad():不构建计算图,防止内存爆炸,且避免意外修改梯度。
第五部分:网络深度与性能
1. 单隐藏层 vs 多隐藏层
-
理论:单隐藏层 MLP(足够宽)可逼近任何连续函数(通用近似定理)。
-
实践:多隐藏层通常用更少参数达到更高精度,能学习层次化特征。
-
过深的问题:梯度消失/爆炸、过拟合、训练困难。
2. 改进准确率的方法(考点)
-
增加隐藏层或神经元数量
-
调整学习率、使用更好的优化器(Adam)
-
增加训练轮数
-
数据增强
-
正则化(Dropout, BatchNorm)
-
使用更合适的激活函数(ReLU系列)
附加
激活函数知识点
一、为什么要用激活函数?
-
如果没有激活函数(或只用线性激活),无论多少层神经网络,最终都等价于一个线性模型,无法解决非线性问题(如分类、图像识别)。
-
激活函数引入非线性,让神经网络能够拟合任意复杂的函数。
二、三大常用激活函数详解
1. ReLU(Rectified Linear Unit)—— 最常用
| 属性 | 内容 |
|---|---|
| 公式 | ReLU(x)=max(0,x) |
| 图像 | 一条折线:x<0 时 y=0(水平线);x≥0 时 y=x(斜率为1的直线) |
| 取值范围 | [0,+∞) |
| 导数 | 0(x<0);1(x>0);x=0 处不可导(通常取0或1) |
| 优点 | 计算简单,正区间梯度不消失,收敛快 |
| 缺点 | 神经元“死亡”:如果输入一直是负数,该神经元永远输出0,梯度为0,不再更新 |
手绘图像描述:
-
坐标系:横轴 x,纵轴 y
-
从 (-∞,0) 到 (0,0) 是一条沿着 x 轴的直线
-
从 (0,0) 出发,向右上方45°角直线(y=x)
2. Sigmoid(逻辑函数)—— 用于二分类输出层
| 属性 | 内容 |
|---|---|
| 公式 | σ(x)=1/(1+e^-x) |
| 图像 | S 形曲线(Sigmoid 意为 S 形) |
| 取值范围 | (0,1) |
| 特殊值 | σ(0)=0.5;σ(1)≈0.731;σ(-1)≈0.269;σ(10)≈0.99995;σ(-10)≈0.00005 |
| 导数 | σ′(x)=σ(x)(1−σ(x))σ′(x)=σ(x)(1−σ(x));最大值在 x=0 处,值为 0.25 |
| 优点 | 平滑、可导、输出可解释为概率 |
| 缺点 | 梯度饱和(两端导数→0),梯度消失严重,计算较慢 |
手绘图像描述:
-
一条平滑的 S 形曲线
-
当 x→ -∞ 时,y→0(趋近但不等于0)
-
当 x→ +∞ 时,y→1
-
过点 (0, 0.5),中心对称
3. Tanh(双曲正切)—— 零中心,比 Sigmoid 好一点
| 属性 | 内容 |
|---|---|
| 公式 | tanh(x)=(e^x-e^-x)/(e^x+e^-x) |
| 图像 | S 形曲线,但通过原点,关于原点中心对称 |
| 取值范围 | (−1,1) |
| 特殊值 | tanh(0)=0;tanh(1)≈0.762;tanh(-1)≈-0.762;tanh(2)≈0.964 |
| 导数 | tanh′(x)=1−tanh2(x)tanh′(x)=1−tanh2(x);最大值在 x=0 处,值为 1 |
| 优点 | 零中心(输出有正有负),梯度比 Sigmoid 大 |
| 缺点 | 仍有梯度饱和问题 |
手绘图像描述:
-
S 形曲线,过 (0,0)
-
x→ -∞ 时 y→ -1;x→ +∞ 时 y→ +1
-
比 Sigmoid 更陡峭
图像可以参考:深度学习中常见的10种激活函数(Activation Function)总结_激活函数有哪些-CSDN博客
三、对比总结表
| 激活函数 | 公式 | 取值范围 | 图像形状 | 主要缺点 |
|---|---|---|---|---|
| ReLU | max(0,x) | [0, +∞) | 折线(左平右斜) | 神经元死亡 |
| Sigmoid | 1/(1+e^-x) | (0,1) | S 形 | 梯度饱和,非零中心 |
| Tanh | (e^x-e^-x)/(e^x+e^-x) | (-1,1) | S 形(过原点) | 梯度饱和 |
多层感知机(MLP)的结构、图示与代码实现
一、多层感知机的结构
多层感知机(Multi-Layer Perceptron, MLP)是一种前馈神经网络,由以下三部分组成:
-
输入层:接收原始特征向量(如 28×28 图像展平后的 784 维)。
-
隐藏层:一个或多个全连接层,每层后通常跟非线性激活函数(如 ReLU、Sigmoid、Tanh)。
-
输出层:根据任务输出(回归:1个神经元;分类:C个神经元,常用 Softmax)。
结构示例(MNIST分类,输入784,隐藏层256,输出10):
输入层(784) → 全连接(784→256) → ReLU → 全连接(256→10) → 输出(10)
如果有多个隐藏层:
输入层 → FC1 → ReLU → FC2 → ReLU → ... → 输出层
二、图示呈现(三种方式)
方式1:文字描述(适合卷面答题)
一个三层MLP:输入层有 784 个神经元,第一个隐藏层有 256 个神经元,第二个隐藏层有 128 个神经元,输出层有 10 个神经元。每层之间全连接,隐藏层之后使用 ReLU 激活函数。
方式2:ASCII 草图(适合文本环境)
[输入层] [隐藏层1] [隐藏层2] [输出层] o o o o o o o o o → o → o → o o o o o o o o o ... ... ... ... 784个 256个 128个 10个
或者更简洁的流程图:
x (batch,784) → Linear(784,256) → ReLU → Linear(256,128) → ReLU → Linear(128,10) → 输出
方式3:Python 代码生成网络结构图(使用 torchsummary 或 plot_model)
import torch
import torch.nn as nn
from torchsummary import summary
class MLP(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 10)
self.relu = nn.ReLU()
def forward(self, x):
x = x.view(x.size(0), -1) # 展平
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
model = MLP()
print(model)
# 输出模型结构文本
summary(model, (1, 28, 28)) # 需要安装 torchsummary
《人工智能概论》实验3 考试题
(总分100分,建议完成时间80分钟)
一、单选题(每题3分,共15分)
-
关于 Fashion-MNIST 数据集,下列说法正确的是( )
A. 图像尺寸为 32×32,彩色图
B. 图像尺寸为 28×28,灰度图
C. 共有 100 个类别
D. 图像已被展平为 784 维向量存储 -
以下哪个激活函数最容易导致梯度消失问题?( )
A. ReLU
B. LeakyReLU
C. Sigmoid
D. Tanh -
一个 MLP 模型定义为:
nn.Sequential(nn.Flatten(), nn.Linear(784,128), nn.ReLU(), nn.Linear(128,10))。该模型的隐藏层(即第一个 Linear 层)的参数量(权重 + 偏置)是( )
A.128×10 + 10
B.784×128 + 128
C.784×128 + 128 + 128×10 + 10
D.784×128 -
在训练函数中,验证阶段使用
model.eval()和with torch.no_grad():的主要原因是( )
A. 提高训练速度
B. 防止梯度更新模型参数,并关闭 Dropout/BatchNorm 的训练行为
C. 清空 GPU 显存
D. 切换到数据并行模式 -
如果 MLP 的所有隐藏层都去掉激活函数(即只有线性层),则整个网络等价于( )
A. 一个线性模型
B. 一个深层非线性模型
C. 一个无法训练的模型
D. 一个卷积神经网络
二、填空题(每空2分,共20分)
-
Fashion-MNIST 训练集中共有 ______ 张图像,每张图像展平后的特征数为 ______。
-
nn.Sequential模型定义时,nn.Flatten()的作用是将形状(batch, 1, 28, 28)转换为(batch, ______)。 -
在
train_model函数中,计算测试准确率的代码torch.max(outputs, 1)中的1表示在 ______ 维度上取最大值,返回的第二个值是 ______。 -
在
nn.Module子类定义中,__init__方法用于 ______,forward方法用于 ______。 -
激活函数 ReLU 在输入为负数时输出为 ______,在输入为正数时输出等于 ______。
三、判断题(正确打“√”,错误打“×”,每题2分,共10分)
-
( )Fashion-MNIST 数据集中的图像经过
ToTensor()后,像素值范围变为[-1,1]。 -
( )
nn.CrossEntropyLoss内部已经包含 softmax,因此模型输出层不需要再添加nn.Softmax。 -
( )增加隐藏层数量一定会提高模型在测试集上的准确率。
-
( )ReLU 激活函数的输出范围是
(0,1)。 -
( )在训练过程中,每轮 epoch 结束后都应该调用
model.train()来继续下一轮训练。
四、简答题(共35分)
以下题目均来自实验指导书中的思考题,请结合复习提纲作答。
简答题1(8分,来自任务一)
(1)在 Fashion-MNIST 任务中,为什么模型的第一步需要 nn.Flatten()?
(2)nn.Sequential 方式与 nn.Module 子类方式相比,各自的优点是什么?
(3)nn.Module 子类中 __init__ 和 forward 的功能分别是什么?
简答题2(9分,来自任务二)
(1)单隐藏层 MLP 和多隐藏层 MLP 在 Fashion-MNIST 上通常哪个测试准确率更高?为什么?
(2)隐藏层越多越好吗?请说明理由。
(3)除了增加隐藏层,还有哪些方法可以改进模型准确率?(至少写出3种)
简答题3(10分,来自任务三)
(1)三种激活函数(ReLU、Sigmoid、Tanh)中,哪个收敛最快?哪个最终准确率最高?
(2)为什么 Sigmoid 和 Tanh 的训练速度通常比 ReLU 慢?
(3)如果去掉所有隐藏层后的激活函数,模型会变成什么?还能达到同样的准确率吗?为什么?
(4)请用文字描述实验任务三中模型的结构(输入层 → 隐藏层 → 激活函数 → 隐藏层 → 激活函数 → 输出层),并注明每一层的神经元数量。
简答题4(8分,来自通用训练函数)
(1)在训练函数中,测试准确率 acc 是如何计算的?请写出详细计算步骤。
(2)为什么验证(测试)时必须使用 model.eval() 和 with torch.no_grad():?如果不使用会有什么后果?
五、代码填空题(每空2分,共20分)
请根据上下文填写正确的代码。
代码填空1(模型定义,6空)
# Sequential 方式
model_seq = nn.Sequential(
nn.Flatten(),
nn.Linear(______, 256), # 空1: 输入维度
nn.ReLU(),
nn.Linear(256, ______) # 空2: 输出维度
)
# Module 子类方式
class MLP(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.fc1 = nn.Linear(______, ______) # 空3、空4
self.act = nn.ReLU()
self.fc2 = nn.Linear(______, ______) # 空5、空6
self.flatten = nn.Flatten()
def forward(self, x):
x = self.flatten(x)
x = self.fc1(x)
x = self.act(x)
x = self.fc2(x)
return x
代码填空2(训练函数部分,4空)
def train_model(model, epochs=10, lr=0.001):
criterion = nn.______() # 空7: 多分类损失函数
optimizer = optim.______(model.parameters(), lr=lr) # 空8: 优化器(常用)
for epoch in range(epochs):
model.______() # 空9: 设置为训练模式
for images, labels in trainloader:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
model.______() # 空10: 设置为评估模式
# ... 验证代码 ...
参考答案与超详细解析(小白必读)
一、单选题
1. 答案:B
解析:Fashion-MNIST 是 28×28 灰度图,训练集 60000 张,测试集 10000 张,共 10 类。图像存储为 28×28 矩阵,不是展平的。
2. 答案:C
解析:Sigmoid 的导数最大为 0.25,且当输入绝对值大时导数趋近 0 → 梯度消失最严重。Tanh 稍好(导数最大 1),ReLU 在正区导数恒为 1,几乎不消失。
3. 答案:B
解析:题目问的是“隐藏层”,即第一个 Linear(784,128)。参数量 = 输入特征数 × 输出特征数 + 输出特征数(偏置)= 784×128 + 128。选项 C 是全部层参数量,不符合题意。
4. 答案:B
解析:eval() 关闭 Dropout 并固定 BatchNorm;no_grad() 禁止梯度计算,防止内存浪费和无意的参数更新。
5. 答案:A
解析:无激活函数时,多层线性变换的复合仍是线性变换(矩阵乘法结合律),等价于单个线性层。
二、填空题
6. 答案:60000;784
解析:官方训练集大小 60000,28×28=784。
7. 答案:784
解析:Flatten 保留 batch,将 1×28×28 展平为 784。
8. 答案:类别(或特征、第1维);预测的类别索引
解析:torch.max(outputs, 1) 在维度1(类别)上取最大值的索引,即预测类别。
9. 答案:定义网络层;定义前向传播逻辑
解析:标准概念。
10. 答案:0;输入本身
解析:ReLU(x)=max(0,x)。
三、判断题
11. 答案:×
解析:ToTensor() 将像素从 [0,255] 转为 [0,1],Normalize((0.5,),(0.5,)) 才转到 [-1,1]。
12. 答案:√
解析:CrossEntropyLoss 内部已包含 softmax(log-softmax)。
13. 答案:×
解析:过深易过拟合或梯度消失,测试准确率可能下降。
14. 答案:×
解析:ReLU 输出 [0, +∞)。
15. 答案:×
解析:model.train() 只需在每个 epoch 开始前调用一次(通常在循环开始处),不是每轮结束后。
四、简答题(详细解析)
简答题1
(1) 因为全连接层要求输入是二维 [batch, features],原始图像是四维 [batch, channels, height, width],Flatten 将其变为 [batch, 784]。
(2) Sequential:简洁,适合线性堆叠;Module 子类:灵活,可定义复杂前向逻辑。
(3) __init__:实例化各层对象;forward:定义数据流过这些层的顺序。
简答题2
(1) 通常多隐藏层更高,因为多层非线性可学习层次化特征。
(2) 不是。过深会导致梯度消失、过拟合、训练困难。
(3) 调整学习率、增加神经元、数据增强、正则化(Dropout)、换优化器(Adam)等。
简答题3
(1) ReLU 收敛最快,最终准确率通常也是 ReLU 最高。
(2) Sigmoid/Tanh 在饱和区梯度极小,参数更新慢;ReLU 正区梯度恒为1。
(3) 退化为线性模型,无法学习非线性关系,准确率大幅下降。
(4) 输入层(784) → 全连接层(256) → ReLU → 全连接层(128) → ReLU → 全连接层(10) → 输出。
简答题4
(1) 对每个 batch,用 torch.max(outputs,1) 取预测类别,与标签比较,累加正确数和总数,最后 100 * correct / total。
(2) eval() 保证 Dropout/BatchNorm 行为正确;no_grad() 禁用梯度追踪,节省内存且防止误更新。不写则验证结果不稳定且浪费资源。
五、代码填空题答案
空1:784
空2:10
空3:input_dim(或 784)
空4:hidden_dim(或 256)
空5:hidden_dim(或 256)
空6:output_dim(或 10)
空7:CrossEntropyLoss
空8:Adam(或 SGD,但 Adam 更常用)
空9:train()
空10:eval()
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)