尧图精选

深度学习课程作业实战:CNN与LSTM四大典型任务的PyTorch实现

🕒 发布时间:2026/10/2 8:34:57 📁 来源:尧图网络
简介面向国科大深度学习课程的手写数字识别、猫狗分类、自动写诗与情感分析四项作业整合包适合人工智能、计算机等相关专业学生对照课程要求学习算法实现与调参思路也可用于课程设计或项目演示的前期参照。压缩包共76个文件以Python源码py/pyc为主附带实验报告PDF、README说明、训练数据npz与效果展示图片代码覆盖数据读取、网络构建、训练与推理等环节目录按四个作业分模块整理整体16.73MB便于按需检索。资源上线后已有468人学习下载。内容均经运行验证上传前完成测试课程答辩平均分达到96分除可直接复跑外也可作为修改扩展、毕设/课设演示或深度学习入门的一个完整样例。如果希望重点理解手写体识别、猫狗图像分类、文本生成与情感分类的工程实现这份资料能提供从数据到模型的完整路径配合实验报告和README可快速定位关键代码。1. 这份国科大深度学习作业包四个任务一次跑通期末季最崩溃的瞬间不是论文写不完而是深度学习课的老师在结课前两周甩过来四个作业手写数字体识别、猫狗分类、自动写诗、情感分析。每个都要交代码、交实验报告、还要现场跑给助教看。这份国科大深度学习课程作业资源正好把四个任务做成了四个独立子项目每个都有 main.py 主脚本和实验报告.pdf代码是跑通验证过才打包的。我拆完的结论是它适合两类人。第一类是计算机、人工智能相关专业的学生拿来当课程作业交或者毕设起步的脚手架第二类是刚入门深度学习、想用完整项目而不是零碎 demo 练手的人。四个任务刚好覆盖了深度学习最典型的两条主线——图像识别和文本处理跑完一遍你对模型训练流程的认知会完整很多。2. 手写数字体识别把第一个 CNN 跑通并看懂每个参数2.1 为什么手写数字必须第一个跑手写数字体识别在深度学习里的地位相当于编程课里的 Hello World。它用的是 MNIST 数据集28×28 的灰度图0 到 9 十个类别数据量小、类别均衡、单通道。我拆这个子项目的时候发现目录结构是 HandwrittenNumeralRecognition 下挂着 main.py、assets 和实验报告.pdf比较清爽。这个项目值得第一个跑的原因很实际它的训练时间短到可以在 CPU 上完成一个 epoch 用不了半分钟方便你反复改参数试错。更重要的是这个任务能把深度学习项目的标准四段流程完整走一遍数据加载、网络定义、训练循环、评估报告。后面猫狗分类和两个文本任务换的只是数据形态和网络结构骨架是完全一样的。先把这里跑通了后面就不会被框架细节卡住。实验报告.pdf 里如果写了 loss 曲线和测试集准确率那就是答辩时要讲的核心内容。2.2 main.py 里的数据加载与网络定义我一般会建议先看数据加载部分。MNIST 在 torchvision 里可以直接下载但要注意 transform 的写法这一步最容易出问题。常见实现是这样的import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets, transforms # 灰度图只有一个通道Normalize 的均值标准差都填 0.5 # 这样像素值从 [0, 1] 归一化到 [-1, 1]训练更容易收敛 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) ]) train_data datasets.MNIST( root./data, trainTrue, downloadTrue, transformtransform ) train_loader DataLoader(train_data, batch_size64, shuffleTrue) class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() # 输入 1 通道第一层卷积输出 32 个特征图 self.conv1 nn.Conv2d(1, 32, kernel_size3, padding1) # 第二层加深到 64 个特征图提取更高层特征 self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) # 28x28 经过两次池化变成 7x7所以全连接输入是 64*7*7 self.fc1 nn.Linear(64 * 7 * 7, 128) self.fc2 nn.Linear(128, 10) def forward(self, x): x torch.relu(self.conv1(x)) x torch.max_pool2d(x, 2) x torch.relu(self.conv2(x)) x torch.max_pool2d(x, 2) # 展平成向量再进全连接层 x x.view(x.size(0), -1) x torch.relu(self.fc1(x)) return self.fc2(x) # 输出 10 个类别的分数transform 里的 Normalize 是这节的重点。MNIST 是灰度图通道数是 1所以均值标准差只有一个值。看到(0.5,)这种写法不用疑惑它代表单通道。Normalize 的作用是把像素分布拉到接近零均值这样梯度更新更平稳不会出现某些权重更新太快、某些太慢的情况。如果你把 Normalize 去掉准确率通常会掉两三个点而且 loss 曲线会抖得厉害。网络结构上两层卷积加两个全连接是 MNIST 的黄金配置。28×28 的图分辨率低两层卷积足够提取数字的笔画特征了。关键细节是fc1的输入维度 64×7×7输入 28×28经过第一个池化变 14×14经过第二个池化变 7×7第二层卷积输出 64 个通道所以展平后是 64×7×73136。改网络结构时这个数字要跟着改不然维度对不上会直接报错。2.3 训练循环里值得动手改的三个参数main.py 里的训练循环基本都是标准写法但有几个参数是值得反复调的。第一个是 batch_size。我用 64显存小的机器用 32 也行。batch 太小梯度噪声大loss 曲线会像心电图batch 太大一个 epoch 的更新次数变少训练到同样效果需要更多轮数。第二个是学习率Adam 优化器下 0.001 是安全起点小于 0.0001 收敛会慢到让人怀疑人生大于 0.01 很容易在 loss 曲线上看到「先降后炸」的翻车现场。第三个是 epoch 数。MNIST 这个任务 5 个 epoch 就能到 99% 左右再多就容易过拟合。判断方法很简单训练集准确率还在涨、验证集准确率开始掉就是过拟合的信号这时候不是加 epoch而是要加 dropout 或者数据增强。提示跑这个项目时如果发现第一次下载 MNIST 特别慢确认 root 参数指向的 data 目录有写入权限。下载中断会导致文件损坏报错信息通常是 EOFError 或者 checksum mismatch这时候把 data 目录里没下完的文件删掉重新跑就行。实验报告.pdf 里通常会有 loss 曲线图和测试集准确率答辩的时候先讲网络结构再指着 loss 曲线说收敛过程最后报测试集准确率这套逻辑在四个项目里通用。3. 猫狗分类真实图片数据集的目录组织与过拟合排查3.1 猫狗分类和 MNIST 的三个本质区别猫狗分类用的是 Kaggle 上的 Dogs vs Cats 数据集虽然也是一个二分类任务但和 MNIST 比有三个本质区别不注意的话代码抄过来也跑不出效果。第一是图片是彩色的三通道而且尺寸不统一。MNIST 是标准的 28×28 灰度图猫狗图片是一堆几千像素的大图必须做 resize。第二是数据组织方式完全不同原始数据集是 cat.0.jpg、dog.0.jpg 这种平铺结构需要自己按目录拆分成 train、val、test 三份这个工作不做后面 DataLoader 根本不知道从哪里读数据。第三是猫和狗图片的内容差异大背景、光照、姿态都很复杂模型很容易学到背景特征而不是动物本身的特征表现就是训练集准确率很高一到验证集就掉。我拆 DogCat 子项目的时候注意到它的目录结构是 data、net、main.py 三个部分data 放原始图片net 单独放网络定义文件main.py 负责训练流程。这种把网络结构和训练逻辑分开的组织方式等到你要替换数据集或者换网络结构的时候会非常舒服不用在几百行代码里翻来找去。3.2 数据划分与预处理一键拆成 train/val/test猫狗分类的第一步是把平铺的图片拆到分类目录里。这个工作看着简单但手写很麻烦我一般会写一个划分脚本import os import shutil import random # 原始数据是 cat.0.jpg, dog.0.jpg 这种平铺结构 # 按 8:1:1 拆成 train / val / test分别用于训练、调参、最终评估 def split_data(src_dir, dst_dir, val_ratio0.1, test_ratio0.1): random.seed(42) # 固定随机种子保证每次拆分结果一致 for cls in [cat, dog]: files [f for f in os.listdir(src_dir) if f.startswith(cls .)] random.shuffle(files) # 必须先打乱否则前面全是猫后面全是狗 n len(files) val_n int(n * val_ratio) test_n int(n * test_ratio) for i, f in enumerate(files): if i n - val_n - test_n: sub train elif i n - test_n: sub val else: sub test dst os.path.join(dst_dir, sub, cls) os.makedirs(dst, exist_okTrue) shutil.copy(os.path.join(src_dir, f), os.path.join(dst, f)) print(split done: train/val/test 8/1/1)这里有几个关键点。固定随机种子是血泪经验不然每次运行划分结果都不一样实验不可复现。先 shuffle 再划分否则原始文件按 cat.0、cat.1 排列前 80% 可能是猫训练集里类别就偏了。val 和 test 的比例 0.1 是常见做法数据量大的时候可以放宽到 0.05数据量小的数据集不建议 test 比例超过 0.2不然训练数据不够。拆分完目录之后预处理用 PyTorch 的 ImageFolder 配合 transforms 就能读。resize 的目标尺寸我用 224×224 配合 ImageNet 的均值和标准差做归一化这是迁移学习常用的配置。如果你的机器是 CPU 训练可以改成 64×64速度快很多准确率也不会差太多这个取舍在课程作业阶段完全够用。数据增强加一个随机水平翻转就够猫狗分类用了因为猫狗图片不存在左右翻转后类别改变的问题。3.3 怎么判断模型真的学到了而不是背住了训练完看准确率是最容易踩坑的地方。只盯着训练集准确率看模型可能已经把训练图片背下来了换一批图片直接现原形。我判断有没有过拟合的习惯是画两条曲线训练集准确率和验证集准确率看它们的间距。如果训练集准确率已经到 95% 以上验证集只有 70%间距拉得很大这就是典型的过拟合。应对策略按优先级排先加数据增强随机裁剪加水平翻转能显著缓解再加 dropout在最后的全连接层之前加一个nn.Dropout(0.5)最后才考虑减小模型尺寸。反过来如果训练集准确率和验证集都只有 60%那是欠拟合模型容量不够要加深网络而不是加数据。有一个容易忽略的细节是 val 和 test 的职责划分。val 用于调参数比如决定要不要加 dropouttest 只在最终评估时跑一次。如果把 test 也用来调参那 test 的结果就等于 val 的结果最后报出来的准确率是虚高的。这一点在实验报告.pdf 里如果体现出来了答辩老师会认为你的实验设计是规范的。4. 自动写诗与情感分析NLP 任务里的生成与分类差异4.1 自动写诗把诗句变成序列用 LSTM 预测下一个字自动写诗和前面两个图像任务完全不是一个套路它属于序列生成任务。核心思想是把诗句看成一系列字符模型的任务是给定前面几个字预测下一个字是什么。AutoPoetry 子项目的目录是 main.py、data、utils、models 四个部分utils 大概率负责数据清洗和序列切分models 里是 LSTM 网络main.py 串起整个训练流程。这个任务在建模上有个关键步骤把文本转换成模型能吃的序列样本。我见到的常见做法是滑动窗口切分。举个例子语料里有一句「床前明月光」窗口大小设 4那就能切出「床前明月 → 光」这个样本前 4 个字是输入第 5 个字是标签。窗口滑动一个位置又得到「前明月光 → …」。切分的时候要注意语料的清洗标点符号、换行符、空格要去掉否则模型会学到输出一堆无用符号。模型结构上用 embedding 加 LSTM 是这类任务的标配。embedding 把每个字的 ID 映射成稠密向量LSTM 负责捕捉上下文关系最后接一个全连接层输出所有候选字的概率分布。训练时用交叉熵损失这和图像分类是一样的但生成和分类的本质区别在预测阶段分类是取最大概率的类别生成则是从这个概率分布里采样一个字符采样策略不同生成的诗风格完全不同。4.2 temperature 采样决定「保守」还是「放飞」写诗生成的质量很大程度取决于采样策略里的 temperature 参数。这个参数控制概率分布的形状temperature 越小分布越尖锐模型越倾向于选高概率的字生成结果保守、安全但容易重复temperature 越大分布越平坦低概率的字也有机会被选中生成结果更有惊喜但也更容易出现不通顺的句子。我拆的时候发现这个参数通常是个常量放在 main.py 顶部方便修改。核心采样逻辑长这样import math import random def sample_with_temperature(logits, temperature0.8): # logits 是模型输出的原始分数先除以 temperature 再算概率 scaled_logits [x / max(temperature, 1e-8) for x in logits] # 减去最大值防止 exp 溢出softmax 的数值稳定做法 max_logit max(scaled_logits) exp_logits [math.exp(x - max_logit) for x in scaled_logits] total sum(exp_logits) probs [x / total for x in exp_logits] # 按概率加权随机选一个下标而不是直接取最大值 r random.random() cumsum 0.0 for i, p in enumerate(probs): cumsum p if r cumsum: return i return len(probs) - 1温度和生成效果的关系我整理了一个参考区间。temperature 在 0.4 到 0.6 之间输出偏保守适合生成比较工整的句子但连续生成容易陷入重复字词的循环。0.8 到 1.0 是常见区间生成结果在通顺和创意之间比较平衡。1.2 以上概率分布趋近均匀模型开始「放飞」偶尔能出现惊艳的搭配但大部分时候输出是乱序的。从这个项目看如果生成的句子全是「的」「了」「吧」这种高频虚词大概率是 temperature 设太高了试试 0.6 以下。提示如果生成的文本里反复出现同一个字这是模型陷入了重复循环。除了调低 temperature还可以在采样时对已经出现过的字加一个惩罚权重常见的做法是把已生成字的 logits 统一减一个固定值比如 1.5让它们被选中的概率降下来。4.3 情感分析和写诗共用词表但输出是分类情感分析任务的建模思路和写诗共享很大一部分数据处理的代码但它们的学习目标是相反的。自动写诗学的是「给定上下文下一个字是什么」是生成式情感分析学的是「给定整段文本情感是积极还是消极」是判别式。SentimentAnalysis 子项目的目录结构和 AutoPoetry 几乎一样都是 main.py、data、utils、models这个结构在课程作业里可以直接互相参考。数据集方面常见的课程作业语料是影评、电商评论或者微博评论需要做的一件事是中文分词。英文按空格切就行中文必须用 jieba 这类工具否则「南京市长江大桥」这种词会被切得乱七八糟模型输入全是噪音。分词后构建词表给每个词分配一个 ID这是两个 NLP 任务共用的那套基础设施import jieba from collections import Counter def build_vocab(texts, max_size5000): # texts 是原始评论列表先分词再统计词频 counter Counter() for text in texts: counter.update(jieba.lcut(text)) # 只保留词频最高的 max_size 个词控制词表大小 vocab {w: i 2 for i, (w, _) in enumerate(counter.most_common(max_size))} vocab[pad] 0 # padding 专用补到定长 vocab[unk] 1 # 未登录词统一用这个 ID return vocab def encode_text(text, vocab, max_len128): # 把分好词的句子转成 ID 序列超过 max_len 截断不足补 0 ids [vocab.get(w, vocab[unk]) for w in jieba.lcut(text)] ids ids[:max_len] [0] * (max_len - len(ids)) return ids词表大小 max_size 设 5000 是平衡考虑。太小很多词变成unk模型丢失信息太大词表稀疏训练变慢而且低频词学不到有效表示。max_len 设 128 对短文本够用如果评论本身就几千个字要考虑截断策略我一般会先统计语料长度分布选一个能覆盖 90% 样本的长度。模型结构上embedding 加 LSTM 后取最后一个时刻的隐藏状态接一个全连接层输出 2 类概率损失函数用二分类交叉熵。和写诗最大的区别就是 LSTM 后面不再接采样而是接一个分类头。5. 避坑手记四个作业最容易翻车的五个细节5.1 MNIST 数据下载失败导致训练中断现象跑 HandwrittenNumeralRecognition 的 main.py第一次运行停在数据下载阶段报 EOFError 或校验和不匹配训练进程直接崩溃。原因datasets.MNIST的 download 参数设为 True但网络不稳定导致文件下载不完整PyTorch 在解压时发现文件损坏。还有个常见情况是 root 参数指向的目录没有写入权限下载失败后不会自动重试。解决把 data 目录下没下完的临时文件删掉手动下载四个数据文件放到 root/text 对应位置或者换一个网络稳定的时段重新执行。我习惯把数据下载和训练分开先跑一次下载脚本确认数据完整再跑训练主脚本这样不会每次都在同一个位置浪费时间。5.2 猫狗分类 loss 不收敛训练集准确率卡在 50%现象猫狗分类训练了十几个 epochloss 在 0.7 左右震荡训练集准确率在 50% 附近纹丝不动和随机猜没区别。原因最常见的翻车点是没做归一化。RGB 图片的像素值是 0 到 255 的整数直接送进模型每个特征的值域跨度过大梯度更新会非常不稳定。另一个原因是数据没打乱模型按顺序先看到一批全是猫再看到一批全是狗整体梯度被相互抵消。解决加上 transforms.Normalize用 ImageNet 的均值(0.485, 0.456, 0.406)和标准差(0.229, 0.224, 0.225)做标准化。同时检查 DataLoader 的 shuffle 参数训练集必须设置 shuffleTrue。确认这两项后loss 通常会在第一个 epoch 就开始下降。5.3 自动写诗生成的全是「的」「了」「吧」现象训练完成后让模型写诗输出的句子基本由高频虚词拼凑而成比如「的了了吧的了」偶尔夹带一两个实词完全不像是诗。原因温度参数设置过高概率分布太平坦高频虚词在所有位置都有一定概率被选中。还有一种原因是语料里虚词占比过高模型学到的最优策略就是输出虚词因为这样训练损失最低。解决把采样 temperature 降到 0.6 以下让高概率字更容易被选中。如果还是不行检查语料的清洗逻辑把高频的标点、停用词在预处理阶段过滤掉。另一个技巧是在训练时对重复出现的字做惩罚生成时如果某个字已经出现过就把它的 logits 降低这个策略在长文本生成里尤其有用。5.4 情感分析中文没分词词表里全是一个个单字现象情感分析训练完成后查看词表发现里面全是「我」「爱」「这」这种单字而不是「喜欢」「电影」这种双字词模型准确率明显偏低loss 降得很慢。原因构建词表之前没有运行 jieba 分词直接把整句文本按字符切分或者按空白切分。中文里很多语义完整的词是两个字以上按单字切分后词义被拆散模型学不到「不喜欢」这种整体否定含义。解决在 build_vocab 之前先 jieba.lcut 每个句子。注意 jieba 的词典对某些领域的词可能切不准比如「影评」「票房」这类词可能被切开可以在 jieba.load_userdict 里补充自定义词典。分词效果直接决定词表质量这一步值得手动检查几条语料的分词结果再继续。5.5 训练到一半内存爆掉进程直接被 kill现象四个项目里不管是图像还是文本任务训练到中途提示 OOM进程终止前面的训练时间全部白费。原因最常见的是 batch_size 设置过大显卡显存或 CPU 内存被占满。文本任务里还有一种隐蔽情况是文本序列长度没有截断同一个 batch 里有一条特别长的样本padding 后所有样本都被拉到它的长度内存瞬间暴涨。解决先把 batch_size 减半试跑一个 epoch确认内存占用后再逐步增大。文本任务里要严格设置 max_len 截断并且用 collate_fn 把同一批样本的 padding 长度统一到这一批的最大值而不是全局最大值。训练脚本里增加 checkpoint 保存逻辑每个 epoch 结束保存一次模型这样即使中途翻车也能从最近的存档继续不用重头再来。6. 从作业到毕设替换数据集、快速验证与答辩展示6.1 用消融实验把「玄学」变成确定性结论四个项目跑通之后如果打算把它改造成毕设起步第一步不是换更大的数据集而是做消融实验。消融实验的核心逻辑是每次只改一个变量看看它对结果的影响有多大。比如猫狗分类的 baseline 跑完准确率是 88%加随机水平翻转后到 91%把两层卷积加深到三层后到 92%把数据增强去掉后又掉回 89%。这一组对比实验做下来你就能明确说出「数据增强带来了 3 个点的提升加深网络带来了 1 个点」而不是笼统地说「模型效果不错」。我在做这个步骤时会维持一张对比表记录每个实验的配置、训练集准确率、验证集准确率和训练时长。这张表放到实验报告里比任何文字描述都有说服力。答辩老师问「你这个数据增强有用吗」你直接指表里的行回答比现场支支吾吾说「应该有用吧」强一百倍。做消融实验的过程还能帮你理解模型的边界哪些改动是有效的哪些改动是无效的这个判断力是课程作业阶段最值得练的技能。6.2 答辩展示的四个关键画面课程作业答辩时间通常只有五到十分钟画面比文字重要。我拆这份资源时总结出的展示顺序是网络结构图、训练曲线、预测样例、对比表各占一屏。网络结构图不要贴代码画一张示意图从输入到卷积、池化、全连接、输出让老师一眼看到模型设计。训练曲线展示 loss 和准确率两条线重点是强调收敛速度和最终值。预测样例是加分项手写数字识别就放十张测试图加预测结果自动写诗就放两首生成的诗一首 temperature 低、一首温度高直观展示参数影响。最后放对比表展示消融实验或者与 baseline 的差距。6.3 我固定下来的验证习惯从第一次被课程作业折磨开始我就养成了一个习惯拿到任何新数据集不急着调参先固定一个 10 个 epoch 的 baseline 跑通全流程记录准确率、loss、训练时间三个数字。之后每做一个改动只改一个变量重新记录三组数字。这个习惯帮我避开了无数次「改了好几个参数最后不知道是哪个起效」的困境。如果你也想把这个作业包变成自己的毕设起步建议从「把猫狗分类的数据集换成一个细分领域的小数据集」开始比如口腔疾病图像识别这种方向流程完全不用动只换数据和调整输出类别数一门心思把消融实验做扎实就是一份很完整的毕业设计初稿。希望这份拆解能帮你在课程作业和毕设起步的路上少走些弯路。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →