目标
在 Linux + GTX 950M 上用 Miniconda 配置 PyTorch 环境
掌握
torch.nn、DataLoader、torch.no_grad()、模型保存/加载训练一个全连接网络(MLP)并在 MNIST 上达到约 97% 准确率
确保代码能在你的 GTX 950M(4GB,支持 CUDA 11.4)上运行
为什么使用 Miniconda?
轻量:仅几百 MB,不会像 Anaconda 那样占用 3~5 GB 磁盘空间
灵活:只安装你真正需要的包,环境干净
1. 环境配置步骤(Miniconda + PyTorch)
1.1 安装 Miniconda
终端执行:
# 下载 Miniconda 安装脚本(Linux x86_64)
wget -c https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
# 运行安装脚本
bash Miniconda3-latest-Linux-x86_64.sh
# 按提示操作:按 Enter 阅读协议,输入 yes 同意,默认安装路径即可,最后问是否初始化 conda 时选 yes安装完成后,关闭并重新打开终端,或执行 source ~/.bashrc,然后测试:
conda --version # 应显示版本号,如 26.3.21.2 创建专用环境(避免污染 base)
conda create -n dl_learning python=3.11 -y
conda activate dl_learning1.3 安装 PyTorch(先 CPU 版本验证,后续可升级到 GPU)
首先执行 nvidia-smi 查看显卡和驱动信息:

我的驱动已经升级到 CUDA 13.0(驱动版本 582.53),完全兼容目前所有主流 PyTorch 版本(无论使用 CUDA 11.8、12.x 还是未来更新的版本)。GTX 950M 虽然计算能力 5.0 稍老,但驱动足够新,因此可以放心使用 PyTorch 官方提供的 CUDA 12.x 预编译包,兼容性和性能都比 11.x 更好。
1.3.1 CPU 版本(仅使用 CPU 训练,不调用 GPU):
适用场景:只想快速验证代码逻辑,或暂时不想用 GPU。
特点:安装包小,无需 CUDA 驱动支持,但训练慢。
pip install torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cpu1.3.2 GPU 版本(使用你的 GPU 加速,推荐)
pip install torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cu124重要:CPU 和 GPU 版本不要混装在同一个环境中。如果你先装了 CPU 版,想换 GPU 版,请先用 pip uninstall torch torchvision torchaudio 卸载,再重新安装 GPU 版。
1.4 验证安装
import torch
print(torch.__version__) # 例如 2.5.1+cpu 或 2.5.1+cu124
print(torch.cuda.is_available()) # 若安装 CPU 版本,应显示 False;若安装 GPU 版本且驱动正常,应显示 True2. PyTorch 核心组件必学
在写代码前,先快速过一遍以下核心概念(每个都将在后面的完整代码中出现)。
3. 完整实验:全连接网络在 MNIST 上分类
3.1 导入库
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
# 检查设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")3.2 加载 MNIST 数据集
transform = transforms.Compose([
transforms.ToTensor(), # 将 PIL 图像或 numpy 数组转为 Tensor,并归一化到 [0,1]
transforms.Normalize((0.1307,), (0.3081,)) # MNIST 均值和标准差(预计算好的)
])
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, download=True, transform=transform)
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)说明:第一次运行会自动下载 MNIST 到
./data文件夹,约 11 MB。
3.3 定义全连接网络(MLP)
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
# 输入 28*28 = 784,输出 10 个类别
self.fc1 = nn.Linear(28*28, 512)
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, 10)
self.dropout = nn.Dropout(0.2) # 防止过拟合
def forward(self, x):
x = x.view(x.size(0), -1) # 展平:从 (batch,1,28,28) -> (batch,784)
x = F.relu(self.fc1(x))
x = self.dropout(x)
x = F.relu(self.fc2(x))
x = self.dropout(x)
x = self.fc3(x) # 最后一层不激活,因为 CrossEntropyLoss 自带 softmax
return x
model = MLP().to(device)
print(model)3.4 定义损失函数和优化器
criterion = nn.CrossEntropyLoss() # 适合多分类,内部融合了 LogSoftmax + NLLLoss
optimizer = optim.Adam(model.parameters(), lr=0.001)3.5 训练函数
def train(epoch):
model.train() # 启用 dropout 等训练专用层
total_loss = 0
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad() # 清零梯度
output = model(data) # 前向传播
loss = criterion(output, target) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数
total_loss += loss.item()
if batch_idx % 200 == 0:
print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} '
f'({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}')
avg_loss = total_loss / len(train_loader)
print(f'====> Epoch {epoch} Average loss: {avg_loss:.4f}')3.6 测试函数(使用 torch.no_grad())
def test():
model.eval() # 关闭 dropout 等
test_loss = 0
correct = 0
with torch.no_grad(): # 禁用梯度计算,节省显存和计算
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += criterion(output, target).item() # 累计损失
pred = output.argmax(dim=1, keepdim=True) # 取最大概率的类别
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader)
accuracy = 100. * correct / len(test_loader.dataset)
print(f'Test set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)\n')
return accuracy3.7 训练并保存模型
epochs = 10
best_acc = 0
for epoch in range(1, epochs + 1):
train(epoch)
acc = test()
# 保存最佳模型
if acc > best_acc:
best_acc = acc
torch.save(model.state_dict(), 'best_mnist_mlp.pth')
print(f"Saved new best model with accuracy {acc:.2f}%")
print(f"Training finished. Best accuracy: {best_acc:.2f}%")模型保存:
torch.save(model.state_dict(), 'best_mnist_mlp.pth')仅保存可学习参数(字典)。后续加载使用model.load_state_dict(torch.load('best_mnist_mlp.pth'))。
3.8 加载模型并预测单张图片(示例)
# 加载最佳模型
model.load_state_dict(torch.load('best_mnist_mlp.pth', map_location=device))
model.eval()
# 取测试集中第一张图片
test_iter = iter(test_loader)
images, labels = next(test_iter)
img = images[0].unsqueeze(0).to(device) # 增加 batch 维度
with torch.no_grad():
output = model(img)
pred = output.argmax(dim=1).item()
true_label = labels[0].item()
print(f"Predicted: {pred}, True label: {true_label}")
# 显示图片(若在 WSL2 中无 GUI,可跳过)
# plt.imshow(images[0].squeeze(), cmap='gray')
# plt.title(f'Pred: {pred}, True: {true_label}')
# plt.show()4. 结果与讨论
预期结果
在 CPU 上训练 10 个 epoch 大约需要 2~3 分钟(GTX 950M 上 GPU 加速约 30 秒)。
测试准确率通常可达 97% ~ 98%(全连接网络在 MNIST 上的上限约为 98.5%)。
关键问题自查
5. 下一步预告
第1周:CNN 博客
将全连接网络替换为 LeNet-5 或简单 CNN
学习卷积层、池化层、特征图可视化
在 CIFAR-10 数据集上训练并观察效果
在此之前,请确保你已经能完整跑通本文的所有代码,并理解每一行的作用。
6. 参考资料
博客后记:本文的完整代码可以在你的 GTX 950M 上直接运行。建议你将此博客作为自己实验笔记的开端,后续每个模型都写出类似的“可复现实验报告”。欢迎在评论区留下你训练得到的最高准确率!
评论区