深度学习课程作业实战:四大任务共用一条工程流水线
发布时间:2026/10/2 8:34:35来源:尧图网络
简介面向国科大深度学习课程四类常见作业覆盖手写数字体识别、猫狗分类、自动写诗与情感分析四个完整实验尤其适合计算机相关专业学生、课程设计者以及希望快速上手深度学习的初学者。资源包共76个文件以Python源码.py与编译产生的.pyc文件为主搭配实验报告PDF、说明文档txt、示例图片png及项目说明md压缩包整体约16.73MB目录按四个任务模块独立组织便于对照查找。目前已有468人浏览学习代码均经过测试运行成功可直接作为作业参考、毕设预研或教学演示材料。每个实验都包含主程序、数据加载、模型构建等环节README与实验报告给出了运行与设计思路两个图像任务覆盖数据预处理到模型评估的完整流程两个文本任务则涉及序列建模与文本分类能够帮助理解深度学习常见范式。若运行环境配置或代码调试遇到问题可联系作者获得答疑与远程教学支持。1. 国科大深度学习作业合集四个任务共用一条工程流水线学年末赶国科大深度学习课程作业最常见的组合就是手写数字体识别、猫狗分类、自动写诗、情感分析这四件事。看上去跨度很大两个图像任务两个文本任务但拆开看它们共用同一条工程流水线——数据加载、模型定义、损失函数、训练循环、结果可视化、文档整理。多数人不是被模型难倒的而是被环境的依赖冲突、数据集的目录混乱、报告里说不清实验设置拖垮的最后交上去的代码自己都跑不出原结果。这篇笔记把这四个作业当一套完整项目来做先给环境和数据打好地基再分别用卷积网络和循环网络把四个任务打通最后埋一份提交前的常见问题排查清单。新手照着复现熟手直接拿坑位清单对号入座。2. 环境与数据集准备用 conda 把四个任务的依赖一次装齐写课程作业最怕两件事一是环境装到一半想重来二是在自己电脑上能跑、交到实验室机器上立刻报错。深度学习环境配置这件事我的做法一直是统一用 conda 建独立环境python 解释器、PyTorch、torchvision、数据处理库全部锁在同一个环境里换机器时用 requirements 和环境导出文件恢复。四个任务里手写数字体识别和猫狗分类依赖 torchvision自动写诗和情感分析依赖 pandas 和分词工具用一套环境就能覆盖没必要开两个环境浪费磁盘。2.1 conda 环境与 PyTorch 安装先看机器再选版本先确认手上这台机器有没有 GPU、CUDA 版本是多少。别急着装Windows 或 Linux 下执行nvidia-smi一行命令就能看到顶部 CUDA Version。有 NVIDIA 显卡且驱动较新就装 CUDA 版 PyTorch没有 GPU 就直接装 CPU 版四个作业里除了猫狗分类大一点其余几个模型 CPU 都能跑只是慢。conda create -n dl-course python3.8 -y conda activate dl-course conda install pytorch torchvision torchaudio pytorch-cuda11.6 -c pytorch -c nvidia pip install pandas numpy matplotlib scikit-learn jupyter第一行创建 python 3.8 的环境3.8 对 torchvision 的兼容性比较稳后面换 3.9、3.10 也没问题但别直接装在 base 环境里否则和系统 Python 的包容易互相覆盖。第二行激活环境第三行让 conda 自动解析当前平台对应的 PyTorch 版本不手动指定版本号是为了避开官方仓库里某些特定版本被下架导致解析失败。最后一行补齐四个作业都要用的工具库pandas 负责情感分析的数据集读取matplotlib 画训练曲线scikit-learn 算指标。没 GPU 的机器把第三行换成conda install pytorch torchvision torchaudio cpuonly -c pytorch其余不用改。装完先验证环境python -c import torch, torchvision; print(torch.__version__, torchvision.__version__) python -c import torch; print(torch.cuda.is_available())提示conda 在默认源下解析依赖比较慢建议先配好国内镜像源再执行安装否则经常卡在 Solving environment 阶段。这一步属于深度学习环境配置的老生常谈但确实值得多花三分钟。2.2 四份训练数据的本地化准备目录结构决定代码简洁度深度学习实战项目案例里数据准备这一步最容易被低估。这四个作业的数据集来源和处理方式差别不小但统一原则是数据落在项目根目录的data/下按任务分子目录训练脚本只认固定路径不要每次手动改路径。手写数字体识别直接用 torchvision 内置的 MNIST 接口第一次运行downloadTrue会自动下载到本地60000 张训练图、10000 张测试图都是 28×28 灰度图基本不会失败from torchvision import datasets, transforms transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_set datasets.MNIST(root./data/mnist, trainTrue, transformtransform, downloadTrue) test_set datasets.MNIST(root./data/mnist, trainFalse, transformtransform, downloadTrue)关键是理解为什么 normalize 要写死这两个数MNIST 全量像素均值约 0.1307、标准差约 0.3081用数据集的全局统计做标准化比单独用 0.5 效果好模型收敛更快最终准确率也更高。作业里报告数据来源时直接写清楚 70000 张 28×28 灰度图、10 个类别即可。猫狗分类的数据是 Kaggle 的 Dogs vs Cats下载解压后train/目录里约一万两千五百张猫图和相同数量的狗图混在一起文件名带cat.和dog.前缀。必须先按torchvision.datasets.ImageFolder要求的目录结构整理否则没法直接读。整理脚本按 90%/10% 划分为训练和验证集mkdir -p data/dogcat/train/cat data/dogcat/train/dog mkdir -p data/dogcat/val/cat data/dogcat/val/dogimport os, shutil, random src raw_data/train # Kaggle 解压出来的原始目录 random.seed(0) files os.listdir(src) random.shuffle(files) train_ratio 0.9 for i, fname in enumerate(files): cls dog if fname.startswith(dog) else cat sub train if i len(files) * train_ratio else val shutil.copy(os.path.join(src, fname), os.path.join(data/dogcat, sub, cls, fname))这段脚本的关键是random.seed(0)必须放在打乱之前否则每次执行划分都不一样报告里写的实验设置就无法复现。整理完可以用find data/dogcat -type f | wc -l验证图片总数是否与原始数据一致。自动写诗和情感分析的数据也走同样的目录规范诗词是纯文本data/poems.txt情感分析是data/sentiment/train.csv所有路径统一英文避免后续在 Linux 服务器上踩编码坑。2.3 文档说明怎么写才不被老师打回README 与实验报告的分工标题里带着文档说明说明这份作业的要求不只是跑通代码还要把思路写清楚。我的经验是文档分成两层一层是给代码运行者看的README.md一层是给老师看的实验报告。目录结构尽量按功能组织四个任务各自的模型定义、训练脚本、数据目录分开互不干扰deeplearning-course/ ├── data/ │ ├── mnist/ │ ├── dogcat/ │ ├── sentiment/ │ └── poems/ ├── models/ │ ├── lenet.py │ ├── resnet_finetune.py │ ├── lstm_poet.py │ └── bilstm_sentiment.py ├── train_mnist.py ├── train_dogcat.py ├── train_poet.py ├── train_sentiment.py ├── requirements.txt ├── README.md └── report/ ├── report.md └── figures/README.md里只写四件事环境怎么装、四份数据从哪获取、每个训练脚本怎么执行、产物输出到哪里。实验报告则按固定结构组织我习惯用一张表格列出每个实验的配置老师扫一眼就能判断实验做没做规范。实验项模型结构优化器/学习率训练轮数验证指标结果文件手写数字识别LeNet-5Adam / 1e-312准确率 99.1%best_mnist.pt猫狗分类ResNet18 微调Adam / 1e-410准确率 96.8%best_dogcat.pt表格里的具体数字只是示意实际以你跑出来的为准但格式本身值得照抄。实验报告正文每章写清楚问题定义、数据清洗、模型选型理由、训练参数、结果分析与失败尝试。文档说明的关键不是字数多而是别人按文档能复现出你的结果这一点老师最看重。3. 手写数字识别与猫狗分类卷积基线与迁移学习的对照两个图像任务放在一起做能自然看到深度学习模型在图像识别上的两条主线从零训练的浅层 CNN 和基于预训练权重的迁移学习。手写数字体识别是 10 分类、单通道灰度图、数据量 60000 张LeNet 这类浅层网络就能到 99% 附近猫狗分类是二分类、三通道彩色图、单类一万两千多张如果从零训练一个 CNN效果会被数据量不足直接打回原形迁移学习就成了性价比最高的选择。这一章把两条路线分别跑通再给出一个两个任务通用的训练骨架。3.1 手写数字体识别用 LeNet-5 拿到 99% 基线MNIST 这个任务之所以经典是因为它让卷积神经网络的特征提取链路非常直观卷积层抓局部纹理、池化层降分辨率、全连接层做分类。LeNet-5 是最早为 28×28 手写数字设计的网络之一虽然也有更深网络可以套但课程作业里用 LeNet 更容易解释训练也快。import torch.nn as nn class LeNet5(nn.Module): def __init__(self): super().__init__() self.conv nn.Sequential( nn.Conv2d(1, 6, kernel_size5, padding2), nn.ReLU(), # 28x28 - 28x28 nn.MaxPool2d(2), # 28x28 - 14x14 nn.Conv2d(6, 16, kernel_size5), nn.ReLU(), # 14x14 - 10x10 nn.MaxPool2d(2), # 10x10 - 5x5 ) self.fc nn.Sequential( nn.Linear(16 * 5 * 5, 120), nn.ReLU(), nn.Linear(120, 84), nn.ReLU(), nn.Linear(84, 10), ) def forward(self, x): return self.fc(self.conv(x))第一层卷积加了padding2让 28×28 输入经过卷积后尺寸不变再池化到 14×14第二层卷积不加 padding14×14 经过 5×5 卷积变成 10×10再池化到 5×5最后全连接层输入维度就是16*5*5。这里最容易翻车的是维度算错每改一次输入尺寸都要重算建议在模型后加一行print(model(torch.randn(1, 1, 28, 28)).shape)验证。训练时用 DataLoader 封装数据优化器选 Adam、学习率 1e-3、batch size 64、交叉熵损失跑 12 个 epoch 左右CPU 上每个 epoch 也就一分钟级别。from torch.utils.data import DataLoader train_loader DataLoader(train_set, batch_size64, shuffleTrue) test_loader DataLoader(test_set, batch_size64, shuffleFalse)shuffleTrue只在训练集打开测试集保持顺序方便最后把预测结果和真实标签对齐画混淆矩阵。验证集准确率到 99% 附近后基本不需要再做数据增强MNIST 本身已经够干净。3.2 猫狗分类ResNet18 迁移学习与微调参数猫狗分类的难点不是网络多深而是单类样本量有限。ImageNet 预训练的 ResNet18 已经在百万张彩色图片上学过通用特征拿来在猫狗数据上微调比从头训练省十几个 epoch。加载时直接用官方权重import torchvision.models as models import torch.nn as nn model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) num_features model.fc.in_features model.fc nn.Linear(num_features, 2)官方权重第一次运行会下载到~/.cache/torch/hub/checkpoints/离线机器上跑要注意提前把权重文件准备好。替换model.fc时先取in_features避免硬编码 512换 ResNet34 时不用改第二处。数据预处理要跟着 ImageNet 的分布走训练时随机裁剪和水平翻转扩增验证时中心裁剪归一化的均值方差要用 ImageNet 的固定值输入统一缩放到 224×224from torchvision import datasets, transforms from torch.utils.data import DataLoader train_transform transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) train_set datasets.ImageFolder(data/dogcat/train, transformtrain_transform) train_loader DataLoader(train_set, batch_size32, shuffleTrue, num_workers2)ImageFolder要求二级目录结构data/dogcat/train/cat和data/dogcat/train/dog会自动生成类别索引。num_workers2让数据加载并行Windows 上如果报错就改成 0多进程在 Windows 下的兼容性偶尔有状况。训练时我的选择是全部微调而不是只训练fc层只冻结骨干训分类头虽然快但骨干网络的 BatchNorm 在训练模式下仍会更新统计量验证集表现会忽高忽低全部微调学习率不要给太大1e-4 比较稳batch size 32显存不够降到 16。3.3 训练循环、早停与日志记录两个图像任务共用的骨架图像分类任务的训练循环高度一致完全可以抽成一个函数模型、数据加载器、超参传进去自动完成训练、验证、保存最优权重。两个图像任务直接复用两个文本任务稍作修改也能用def train_model(model, train_loader, val_loader, epochs15, lr1e-3, save_pathbest.pt): optimizer torch.optim.Adam(model.parameters(), lrlr) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemax, patience2) criterion nn.CrossEntropyLoss() best_acc, best_state 0, None for epoch in range(1, epochs 1): model.train() total_loss, total_num 0, 0 for x, y in train_loader: x, y x.to(device), y.to(device) optimizer.zero_grad() loss criterion(model(x), y) loss.backward() optimizer.step() total_loss loss.item() * x.size(0) total_num x.size(0) val_acc evaluate(model, val_loader) scheduler.step(val_acc) print(fepoch {epoch:02d} loss {total_loss / total_num:.4f} val_acc {val_acc:.2%}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), save_path) print(fbest_acc {best_acc:.2%}, saved to {save_path})ReduceLROnPlateau监控验证集准确率连续两个 epoch 不涨就把学习率降一半比手动衰减省心早停逻辑体现在val_acc best_acc才保存权重最终保留的是验证集上最好的模型而不是最后一轮。配套的evaluate函数记得加torch.no_grad()和model.eval()def evaluate(model, val_loader): model.eval() correct 0 with torch.no_grad(): for x, y in val_loader: x, y x.to(device), y.to(device) correct (model(x).argmax(1) y).sum().item() return correct / len(val_loader.dataset)这里的device建议做成全局变量或者在train_model开头加一句device next(model.parameters()).device模型在哪个设备数据就往哪个设备上放不要在脚本里把cuda写死否则换 CPU 机器跑还得改代码。4. 自动写诗与情感分析LSTM 生成与分类的预处理分叉把两个 NLP 任务放在同一章是因为它们都用循环神经网络但训练目标和数据组织完全相反。自动写诗是字符级语言模型输入一段序列预测下一个字符属于生成任务情感分析是序列分类输入整段文本输出情感标签属于判别任务。很多人在后者卡住不是模型不会写而是没搞清楚生成要保留序列、分类要按长度对齐这条预处理分叉。这一章把两条路分别走一遍。4.1 自动写诗字符级 LSTM 与 temperature 采样自动写诗最常见的做法是字符级建模不分词把每个汉字当作一个类别词表大小就是诗中出现的去重字符数常见在 3000 到 5000 之间。字符级的好处是不依赖分词工具坏处是序列长、训练慢但对诗歌这种短文本完全够用。数据预处理把全部诗句连成一个长文本然后滑动窗口切样本def build_sequences(text, seq_len64): chars sorted(set(text)) char2idx {c: i for i, c in enumerate(chars)} idx2char {i: c for i, c in enumerate(chars)} sequences, targets [], [] for i in range(len(text) - seq_len - 1): seq text[i:i seq_len] target text[i seq_len] sequences.append([char2idx[c] for c in seq]) targets.append(char2idx[target]) return sequences, targets, char2idx, idx2char注意sorted(set(text))对字符排序保证每次运行得到同一个映射不会因为 set 的遍历顺序随机而让索引不稳定。模型用 Embedding 加两层 LSTM输入字符序列输出最后一个位置的词表分布class PoetLSTM(nn.Module): def __init__(self, vocab_size, embed_size128, hidden_size256, num_layers2): super().__init__() self.embed nn.Embedding(vocab_size, embed_size) self.lstm nn.LSTM(embed_size, hidden_size, num_layersnum_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, vocab_size) def forward(self, x): h, _ self.lstm(self.embed(x)) return self.fc(h[:, -1, :])h[:, -1, :]取每个序列最后一个有效位置的隐藏状态。训练时用 CrossEntropyLoss优化器 Adam、学习率 1e-3、batch size 128、epoch 20 左右。生成时不能直接取概率最高的字符否则会陷入高频字无限循环标准做法是用 temperature 调整概率分布后做多项式采样def generate(model, seed床前明月光, length64, temperature0.8): model.eval() chars list(seed) with torch.no_grad(): for _ in range(length): x torch.tensor([[char2idx[c] for c in chars[-seq_len:]]], devicedevice) logits model(x) / temperature probs torch.softmax(logits, dim-1).squeeze() next_idx torch.multinomial(probs, 1).item() chars.append(idx2char[next_idx]) return .join(chars)temperature 小于 1 会让高概率字符更突出典型取值 0.8大于 1 会增大随机性生成更像胡言乱语。chars[-seq_len:]保证输入窗口永远不超过训练窗口长度。作业报告里建议放一张对比表列出 temperature 分别取 0.2、0.8、1.2 时的生成片段一眼就能看出采样参数对输出的影响。4.2 情感分析BiLSTM 加预训练词向量做分类基线情感分析的输入是长度不等的文本输出一个标签。我习惯用双向 LSTM取最后一层两个方向的隐藏状态拼接后接分类头。双向的原因是情感判断常依赖后半句的转折词前向 LSTM 会漏掉这类信息。数据读取用 pandas预处理阶段把每条文本转成索引序列并统一截断到 200 个 tokenfrom collections import Counter tokenizer lambda s: s.lower().split() word_counter Counter() for t in texts: word_counter.update(tokenizer(t)) vocab {pad: 0, unk: 1} for word, _ in word_counter.most_common(20000): vocab[word] len(vocab) def encode(text, max_len200): tokens tokenizer(text)[:max_len] ids [vocab.get(w, vocab[unk]) for w in tokens] return ids [0] * (max_len - len(ids))高频词保留 20000 个其余映射到unk既控制词表规模又把生僻词噪声挡住。encode返回定长序列不足 200 部分用 0 填充。模型结构如下class BiLSTMClassifier(nn.Module): def __init__(self, vocab_size, embed_size100, hidden_size128): super().__init__() self.embed nn.Embedding(vocab_size, embed_size, padding_idx0) self.lstm nn.LSTM(embed_size, hidden_size, bidirectionalTrue, batch_firstTrue) self.fc nn.Linear(hidden_size * 2, 2) def forward(self, x): embedded self.embed(x) _, (h_n, _) self.lstm(embedded) out torch.cat((h_n[-2], h_n[-1]), dim1) return self.fc(out)padding_idx0让索引 0 对应的向量始终为零不参与更新也不会给 LSTM 引入噪声。取h_n[-2]和h_n[-1]分别是最后一层前向和后向的隐状态拼接后维度是hidden_size * 2。这里不要用h[:, -1, :]取最后时刻输出因为尾部全是 padding 位置。数据加载时需要在collate_fn里完成按批填充from torch.utils.data import Dataset, DataLoader class SentimentDataset(Dataset): def __init__(self, texts, labels): self.texts texts self.labels labels def __len__(self): return len(self.texts) def __getitem__(self, i): return encode(self.texts[i]), self.labels[i] def collate_batch(batch): xs, ys zip(*batch) xs [torch.tensor(x) for x in xs] xs torch.nn.utils.rnn.pad_sequence(xs, batch_firstTrue, padding_value0) return xs, torch.tensor(ys)pad_sequence会把一个 batch 内不同长度的序列对齐到该 batch 的最大长度比每批固定 200 更省内存也避免短文本被填充过多的无效 token。课程作业里 Embedding 随机初始化就能跑但要拿高分建议用 GloVe 或中文预训练词向量初始化效果提升明显如果后续做多模态情感分析这类进阶方向文本分类基线也是最先要立住的模块。4.3 生成与分类的预处理差异序列保留、定长填充与批处理把两个任务的 DataLoader 放一起对比会发现对齐逻辑完全不同这是新手最容易混的一处。自动写诗每个样本天然等长因为固定窗口截取batch 里所有样本序列长度一样不需要任何 padding。情感分析每条文本长度不同必须填充到同长度才能拼成 tensor而且不能简单在尾部补零后取最后时刻隐状态否则模型学到的大量位置信息被 padding token 稀释。对比项自动写诗情感分析建模粒度字符级词表约 3000-5000词级词表约 20000输入长度固定窗口 64按填充后最长样本对齐标签下一个字符的索引情感类别整数padding不需要需要用 padding_idx 屏蔽采样/推理temperature 多项式采样softmax 后取最大类别训练目标最大化序列似然最小化分类交叉熵损失函数都叫 CrossEntropyLoss但语义不同生成任务是在词表大小上做分类分类任务是在情感类别数上做分类。批处理时情感分析的collate_fn按批填充既控制内存又让每条样本的 padding 量最小。这两个任务做完四条预处理链路之间怎么组织代码基本就能形成一个可复用的参考。5. 作业提交前的常见问题排查5 个高发坑位与解法代码跑通不难难的是结果不符合预期时知道去哪查。这里集中写五个我见过最多的高发坑位每条按现象、原因、解决展开正好覆盖四个任务里最容易翻车的环节。5.1 手写数字识别准确率卡在 95% 以下上不去现象训练 loss 能降但验证集准确率卡在 90% 到 95% 之间怎么加深网络都上不去。原因最常见是数据输入没有归一化。transforms.ToTensor()会除以 255 把像素缩放到 0 到 1但有人图省事直接把手写数字图片原样放进 tensor输入范围是 0 到 255卷积层和激活层对这么大的数值非常敏感梯度更新不稳定。另一个原因是把验证集也做了随机增强导致验证集失去稳定性。解决确认 transform 链里必须有ToTensor()训练和验证的 transform 分开定义验证集只做ToTensor()和Normalize。排错时打印一次x.min(), x.max()如果最小值大于等于 0、最大值大于 1说明归一化链路有问题。MNIST 标准归一化参数在 2.2 节给过直接套用即可。另外把测试集shuffleFalse保证每次评估顺序一致。5.2 猫狗分类训练集 loss 抖动且验证集准确率飘忽现象训练集 loss 一路下降验证集准确率忽高忽低甚至出现训练准确率 99%、验证只有 88% 的过拟合特征。原因数据增强不够或者迁移学习中 BatchNorm 层没处理干净。常见做法是把 ResNet 骨干全部冻结只训练最后的 fc 层但冻结时模型里的 BatchNorm 在model.train()模式下仍会继续更新 running mean 和 running var和冻结目标矛盾导致验证时统计量漂移。解决要么全部微调学习率降到 1e-4要么在冻结骨干的同时把 BatchNorm 锁住推理模式手动遍历模块设置eval()for name, m in model.named_modules(): if isinstance(m, nn.BatchNorm2d): m.eval()这样训练循环里只有 fc 层参数更新BatchNorm 保持固定统计量曲线会平滑很多。同时把数据增强至少加上随机水平翻转和随机裁剪单靠预训练权重很难让模型泛化到陌生验证图。5.3 自动写诗生成的全是标点和的、了现象训练 loss 已经降得很低但生成文本被标点和的了这种高频虚词占满读起来不像诗。原因语言模型学到的是真实语料里的字频分布标点和虚词出现次数最多概率分布天然偏向它们。温度参数不合适会放大这个问题temperature 大于 1模型乱选字符小于 0.3又只会选概率最高的常见字陷入重复。解决把 temperature 设在 0.7 到 0.9 之间并在生成循环里对连续标点做抑制for _ in range(length): ... next_char idx2char[next_idx] if next_char in 。、 and chars[-1] in 。、: continue chars.append(next_char)如果还不行检查训练数据是不是整本诗集直接拼接诗与诗之间没有分隔。预处理时用换行符把每首诗分开训练样本不要跨两首诗拼接模型就不会把两首诗之间的残余信息当作上下文。5.4 情感分析训练中 loss 变成 NaN现象训练几个 batch 后 loss 变成 nan准确率骤降重启训练还会复现。原因最常见是学习率过大导致梯度爆炸双向 LSTM 层数稍深时梯度范数会指数放大。另一个隐蔽原因是文本里有空行或极端短文本分词后长度为 0长度 0 的序列进入 LSTM 得到无效隐状态反向传播一路带回 NaN。解决在collate_fn里过滤空文本长度小于 1 的样本直接剔除。同时加梯度裁剪限制梯度范数torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step()梯度裁剪必须放在loss.backward()之后、optimizer.step()之前。学习率从 5e-4 起步比较稳如果 NaN 依然复现逐项检查数据是否全是 UTF-8、标签是否为 0/1、embedding 是否设置了padding_idx0。5.5 Windows 下中文路径和模型保存格式的兼容问题现象代码在 Windows 上读取诗词 txt 报UnicodeDecodeError训练完保存的模型文件名带中文拷贝到 Linux 服务器上又加载不出来。原因Windows 默认编码是 GBK而多数诗歌和中文影评数据是 UTF-8PyTorch 保存的 checkpoint 本质是 zip 文件文件名里的中文在不同系统间解压可能乱码。这属于环境坑不是模型问题但交作业时极易被忽视。解决所有open()调用显式写encodingutf-8模型保存只给 ASCII 文件名加时间戳import time save_path fbest_{time.strftime(%m%d_%H%M)}.pt torch.save(model.state_dict(), save_path)数据目录统一用英文data/mnist、data/poems.txt不要用数据集/诗歌.txt一类的路径。提交前把 Windows 下的反斜杠路径全部改成/Python 在 Windows 和 Linux 上都能正确识别。6. 让作业有项目质感评估、可视化和随机种子课程作业和实际项目的差距往往不在模型结构而在三个地方评估指标是否完整、可视化是否让人一眼看懂、结果能不能复现。把这三件事补上作业就具备了项目质感。分类任务里手写数字识别和情感分析都要给出混淆矩阵不能只看准确率。准确率在类别不均衡时会骗人混淆矩阵里能看到模型把哪两类数字写岔、情感分析把负面误判成正面的比例。用 sklearn 一行出矩阵配 matplotlib 热力图存到report/figures/下老师打开报告就能看到完整证据链。生成任务则贴多组不同 temperature 下的采样结果列一个小表格对比低温保守、中温合理、高温发散比任何指标都直观。可视化的最低标准是两张图训练 loss 曲线和验证指标曲线。训练结束统一导出 PNG逐张解释曲线变化原因而不是贴图不解释。随机种子是复现的前提以前我交实验报告时只贴了最终结果老师按 README 跑出来数字对不上原因就是训练时没固定种子。现在我把这段放在每个训练脚本开头import random, numpy as np, torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark FalsedeterministicTrue让 cuDNN 选择确定性算法代价是训练略微变慢benchmarkFalse防止运行时动态选择最快卷积算法带来结果波动。固定种子后数据划分、模型初始化、batch 打乱顺序全部确定换机器重跑也能得到相近结果。这套作业做完四个任务的代码结构基本都是数据读取、模型定义、训练函数、推理脚本四层后续做毕业设计或更大项目时填新模型只是换模型类的问题。我自己的习惯是每完成一个作业就在 README 里追加一栏实验结果连同复现环境一起记录三个学期后再翻出来还能照着自己当时的文档跑通。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网