Files
cs188/proj5/main.typ
2025-12-08 18:38:46 +08:00

279 lines
11 KiB
Typst
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#import "labtemplate.typ": *
#show: nudtlabpaper.with(
author: "程景愉",
id: "202302723005",
title: "机器学习",
training_type: "普通本科生",
grade: "2023级",
major: "网络工程",
department: "计算机学院",
advisor: "胡罡",
jobtitle: "教授",
lab: "306-707",
date: "2025.12.10",
header_str: "机器学习实验报告",
simple_cover: true,
)
#set page(header: [
#set par(spacing: 6pt)
#align(center)[#text(size: 11pt)[人工智能实验报告]]
#v(-0.3em)
#line(length: 100%, stroke: (thickness: 1pt))
],)
#show heading: it => if it.level == 1 [
#v(1em)
#set text(font: hei, size: 16pt)
#counter(heading).display()
#h(0.5em)
#it.body
#v(0.5em)
] else [
#v(0.8em)
#set text(font: "New Computer Modern", weight: "bold", style: "italic")
#counter(heading).display(it.numbering)
#h(0.5em)
#it.body
#v(0.5em)
]
#outline(title: "目录",depth: 2, indent: 1em)
#set enum(indent: 0.5em,body-indent: 0.5em,)
#pagebreak()
= 实验介绍
#para[
本项目是 CS188 课程的第五个项目主题为机器学习Machine Learning。实验的主要目的是通过实践操作熟悉机器学习的基本概念、神经网络的构建过程以及 PyTorch 深度学习框架的使用。在本项目中我们将从最基础的感知机开始逐步构建非线性回归模型、用于手写数字识别的多层感知机MLP以及卷积神经网络CNN。通过这些任务我们将深入理解神经网络的初始化、前向传播、损失计算、反向传播以及参数更新等核心机制。
]
= 实验内容
#para[
本次实验主要包含以下四个核心编程任务,所有代码实现均在 `models.py` 文件中进行:
]
+ *Q1: Perceptron (感知机)*:实现一个简单的二元分类感知机,理解基本的线性分类器更新规则。
+ *Q2: Non-linear Regression (非线性回归)*:构建一个两层的神经网络,用于拟合正弦函数 $sin(x)$,理解如何利用神经网络的非线性能力进行函数逼近。
+ *Q3: Digit Classification (数字分类)*:设计并训练一个多层全连接神经网络,对 MNIST 手写数字数据集进行分类,掌握分类任务的损失函数和模型评估方法。
+ *Q5: Convolutional Neural Networks (卷积神经网络)*:手动实现二维卷积操作,并基于此构建卷积神经网络,理解 CNN 在处理图像数据时的优势。
= 实验要求
#para[
根据项目指导文档,实验的具体要求如下:
]
+ *文件修改*:仅允许修改 `models.py` 文件,其余文件(如 `autograder.py` 等)需保持原样,以确保自动评分器正常运行。
+ *框架使用*:需熟练使用 PyTorch 提供的 `Tensor` 操作、`nn.Parameter``optim` 优化器等工具。
+ *性能达标*
- Q1感知机需在训练集上收敛准确率 100%)。
- Q2回归模型的平均训练损失MSE需低于 0.02。
- Q3数字分类模型在测试集上的准确率需达到 97% 以上。
- Q5CNN 模型在简化版数据集上的准确率需达到 80% 以上。
+ *禁止事项*Q3 的输出层不得使用 ReLU 激活函数;代码需通过自动评分器的技术正确性检查。
= 实验步骤与实现
== Q1: Perceptron (感知机)
*实现思路*
感知机是一个最简单的线性二分类模型。我们的目标是找到一个权重向量 $w$,使得对于输入 $x$,如果 $w dot x >= 0$ 则预测为类别 1否则为 -1。
1. *初始化*:使用 `torch.nn.Parameter` 初始化权重 `self.w`,维度为 `(1, dimensions)`,初始化为全 1 向量。
2. *计算得分*:计算输入 $x$ 与权重 $w$ 的点积。使用 `tensordot` 计算。
3. *预测与训练*`get_prediction` 根据得分符号返回 1 -1。`train` 方法中,我们遍历数据集。如果预测错误,则根据感知机规则更新权重:$w <- w + y dot x$
*核心代码* (`models.py`):
```python
class PerceptronModel(Module):
def __init__(self, dimensions):
super(PerceptronModel, self).__init__()
self.w = Parameter(ones(1, dimensions))
def run(self, x):
return tensordot(self.w, x, dims=2)
def train(self, dataset):
with no_grad():
dataloader = DataLoader(dataset, batch_size=1, shuffle=True)
while True:
converged = True
for batch in dataloader:
x = batch['x']
y = batch['label']
prediction = self.get_prediction(x)
if prediction != y.item():
self.w.data += y.item() * x
converged = False
if converged:
break
```
*测试指令*:
```fish
python autograder.py -q q1
```
== Q2: Non-linear Regression (非线性回归)
*实现思路*
为了拟合非线性的 $sin(x)$ 函数,我们需要引入非线性激活函数。我们构建了一个三层的神经网络:
- 第一层:线性层 `Linear(1, 256)` + ReLU。
- 第二层:线性层 `Linear(256, 256)` + ReLU。
- 第三层:线性层 `Linear(256, 1)`,输出预测值。
训练时使用 Adam 优化器和 MSE 损失函数。为了加速收敛,我们还引入了学习率调度器 (`StepLR`),每 250 step 将学习率衰减为原来的 0.1。
*核心代码* (`models.py`):
```python
class RegressionModel(Module):
def __init__(self):
super().__init__()
self.layer1 = Linear(1, 256)
self.layer2 = Linear(256, 256)
self.layer3 = Linear(256, 1)
def forward(self, x):
x = relu(self.layer1(x))
x = relu(self.layer2(x))
return self.layer3(x)
def train(self, dataset):
batch_size = 40
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
optimizer = optim.Adam(self.parameters(), lr=0.001)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=250, gamma=0.1)
for epoch in range(1000):
total_loss = 0
num_samples = 0
for batch in dataloader:
optimizer.zero_grad()
loss = self.get_loss(batch['x'], batch['label'])
loss.backward()
optimizer.step()
total_loss += loss.item() * len(batch['x'])
num_samples += len(batch['x'])
scheduler.step()
if total_loss / num_samples < 0.02:
break
```
*测试指令*:
```fish
python autograder.py -q q2
```
== Q3: Digit Classification (数字分类)
*实现思路*
针对 MNIST 28x28 的图像分类任务,我们构建了一个三层全连接网络:
- 输入层784 28*28 展平)。
- 隐藏层 1`Linear(784, 256)` + ReLU。
- 隐藏层 2`Linear(256, 128)` + ReLU。
- 输出层:`Linear(128, 10)`,直接输出 Logits。
使用 `cross_entropy` 损失函数和 Adam 优化器,批大小为 100训练 5 个 Epoch 即可达到目标准确率。
*核心代码* (`models.py`):
```python
class DigitClassificationModel(Module):
def __init__(self):
super().__init__()
input_size = 28 * 28
output_size = 10
self.layer1 = Linear(input_size, 256)
self.layer2 = Linear(256, 128)
self.layer3 = Linear(128, output_size)
def run(self, x):
x = relu(self.layer1(x))
x = relu(self.layer2(x))
return self.layer3(x)
def train(self, dataset):
batch_size = 100
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
optimizer = optim.Adam(self.parameters(), lr=0.001)
for epoch in range(5):
for batch in dataloader:
optimizer.zero_grad()
loss = self.get_loss(batch['x'], batch['label'])
loss.backward()
optimizer.step()
```
*测试指令*:
```fish
python autograder.py -q q3
```
== Q5: Convolutional Neural Networks (卷积神经网络)
*实现思路*
本任务的核心是手动实现二维卷积操作。`Convolve` 函数通过双重循环遍历输出矩阵的每个位置,计算输入子矩阵与卷积核的点积(使用 `tensordot`)。
基于此,我们构建了一个简单的 CNN
1. 卷积层:使用 `Convolve` 处理输入,卷积核大小 3x3输出特征图大小为 26x26。
2. 展平:将特征图展平为 676 维向量。
3. 全连接层 1`Linear(676, 100)` + ReLU。
4. 全连接层 2`Linear(100, 10)`。
*核心代码* (`models.py`):
```python
def Convolve(input: tensor, weight: tensor):
input_h, input_w = input.shape
weight_h, weight_w = weight.shape
output_h = input_h - weight_h + 1
output_w = input_w - weight_w + 1
Output_Tensor = torch.zeros((output_h, output_w))
for y in range(output_h):
for x in range(output_w):
sub_tensor = input[y:y+weight_h, x:x+weight_w]
Output_Tensor[y, x] = tensordot(sub_tensor, weight, dims=2)
return Output_Tensor
class DigitConvolutionalModel(Module):
def __init__(self):
super().__init__()
self.convolution_weights = Parameter(ones((3, 3)))
self.layer1 = Linear(26 * 26, 100)
self.layer2 = Linear(100, 10)
def forward(self, x):
x = x.reshape(len(x), 28, 28)
x = stack(list(map(lambda sample: Convolve(sample, self.convolution_weights), x)))
x = x.flatten(start_dim=1)
x = relu(self.layer1(x))
return self.layer2(x)
```
*测试指令*:
```fish
python autograder.py -q q5
```
= 实验结果
#para[
本实验所有代码均通过 `autograder.py` 的测试,各项指标均达到或超过实验要求。
]
+ *Q1 感知机*:模型能够迅速收敛,在训练数据上实现了 100% 的分类准确率。
+ *Q2 非线性回归*:训练后的模型 Loss 稳定在 0.02 以下,能够很好地拟合 $sin(x)$ 曲线。
+ *Q3 数字分类*:构建的 MLP 模型在 MNIST 测试集上的准确率超过 97%Staff 参考值为 98%),训练过程高效稳定。
+ *Q5 CNN*:手动实现的卷积层逻辑正确,通过了梯度检查。构建的 CNN 模型在测试数据集上准确率超过 80%,验证了卷积特征提取的有效性。
#figure(image("screenshot.png"))
#pagebreak()
= 实验总结
#para[
通过本次 Project 5 的实验,我收获颇丰:
]
#para[
首先,我熟练掌握了 *PyTorch* 框架的基础操作。从定义 `nn.Parameter` 到构建 `nn.Linear` 层,再到使用 `optim.Adam` 和各类 Loss 函数,我对深度学习模型的代码实现流程有了清晰的认识。
]
#para[
其次,我深入理解了*神经网络的内部机制*。通过 Q1我复习了线性分类器的原理通过 Q2我直观体会到了激活函数ReLU引入非线性的重要性——没有它多层网络也仅仅是线性变换的叠加通过 Q3我掌握了处理多分类问题的标准范式CrossEntropy + Softmax Logits
]
#para[
最后Q5 的手动实现卷积让我对 CNN 有了更底层的理解。以前只知道调用 API现在通过自己编写滑动窗口和点积运算深刻理解了权重共享和局部感知野的概念。这次实验不仅锻炼了编程能力也为后续深入学习深度学习打下了坚实基础。
]