深度学习课程推荐:从理论到PyTorch
发布日期: 2026/07/30 阅读总量: 0

一、我的踩坑经历

2023年4月我决定转行做CV算法。网上找了一堆课程推荐,先看了吴恩达《深度学习专项课程》(Coursera上1-3门),视频讲得很清楚,但每次交作业的TensorFlow 1.x代码跑起来报错一堆。后来换PyTorch,发现课程里的网络结构图跟实际API对不上。折腾两周连个LeNet都没跑通。

又去啃Stanford CS231n的笔记和视频,资料详细但代码全是numpy手写,跟现在工程用的PyTorch差太多。最后发现李沐的《动手学深度学习》(PyTorch版)直接把理论和代码绑在一起,随书代码跑在1998年的MNIST上,我改到CIFAR-10又踩了batch size和learning rate的坑。

这篇博客就是我从3个月的混乱中提炼出的最佳路线:先用吴恩达打理论底子,再跟李沐动手敲代码,最后用CS231n补CNN/RNN细节。下面给出三门课的详细对比和实战代码。

二、方案对比:三门课程怎么选

2.1 吴恩达深度学习专项课程

  • 版本:Coursera上5门课,2022年更新,含TensorFlow和PyTorch选项(但早期课程仍以TF为主)
  • 优点:数学推导清晰,梯度消失、Batch Normalization讲得透
  • 缺点:代码作业老旧,依赖tf1.x,PyTorch版本需要自己额外补充
  • 学习时长:40小时视频 + 30小时作业
  • 适合人群:零基础,需要先理解“为什么”

2.2 李沐《动手学深度学习》(PyTorch版)

  • 版本:第二版,2023年更新,代码基于PyTorch 2.0+
  • 优点:代码完整,每章配套notebook,可以直接在Colab跑,从线性回归到Transformer全覆盖
  • 缺点:理论深度不够,比如注意力机制的数学推导跳过很多
  • 学习时长:30小时视频 + 30小时动手
  • 适合人群:有一定数学基础,想快速上手PyTorch

2.3 Stanford CS231n / CS224n

  • 版本:2023版,视频在YouTube,作业含numpy手写 + PyTorch可选
  • 优点:CNN、RNN细节深入,反向传播手算,适合面试刷题
  • 缺点:没有完整的项目代码,需自己拼装
  • 学习时长:50小时视频 + 60小时作业
  • 适合人群:想过大厂算法面试,想做科研

2.4 组合路线推荐

我的建议:吴恩达(只看1-2门,搞懂前向/反向传播、激活函数、梯度问题)→ 李沐(从头到尾敲一遍,重点在卷积神经网络、循环神经网络、注意力机制)→ CS231n(只补作业中的手写backprop和图像分类项目)。这样理论+代码+面试深度都有了。

三、实战代码:用PyTorch实现CIFAR-10分类

以下代码基于PyTorch 2.1.0 + torchvision 0.16.0,CUDA 12.1,显卡T4(云GPU)。按照李沐课程中的代码风格,搭建一个简单CNN,训练5个epoch,测试准确率。

3.1 环境准备

# 创建conda环境
conda create -n dl_course python=3.10
conda activate dl_course
# 安装PyTorch 2.1.0(CUDA 12.1)
pip install torch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 --index-url https://download.pytorch.org/whl/cu121
# 安装辅助库
pip install matplotlib tqdm

3.2 数据加载与预处理

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
from tqdm import tqdm
import time

# 数据增强(参考李沐课程)
transform_train = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.RandomCrop(32, padding=4),
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])

transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)

trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=4)
testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=4)

3.3 定义模型(简单CNN)

class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.bn1 = nn.BatchNorm2d(32)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.bn2 = nn.BatchNorm2d(64)
        self.conv3 = nn.Conv2d(64, 128, 3, padding=1)
        self.bn3 = nn.BatchNorm2d(128)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(128 * 4 * 4, 256)
        self.fc2 = nn.Linear(256, 10)
        self.dropout = nn.Dropout(0.3)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.pool(self.relu(self.bn1(self.conv1(x))))
        x = self.pool(self.relu(self.bn2(self.conv2(x))))
        x = self.pool(self.relu(self.bn3(self.conv3(x))))
        x = x.view(x.size(0), -1)
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.fc2(x)
        return x

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = SimpleCNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

3.4 训练与测试

def train(epochs):
    model.train()
    for epoch in range(epochs):
        running_loss = 0.0
        loop = tqdm(trainloader, desc=f'Epoch {epoch+1}/{epochs}')
        for inputs, labels in loop:
            inputs, labels = inputs.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
            loop.set_postfix(loss=loss.item())
        print(f'Epoch {epoch+1} loss: {running_loss/len(trainloader):.4f}')

def evaluate():
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for inputs, labels in testloader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    print(f'Test Accuracy: {100 * correct / total:.2f}%')

if __name__ == '__main__':
    start = time.time()
    train(5)
    print(f'Training time: {time.time()-start:.2f}s')
    evaluate()

3.5 效果数据

在T4 GPU(16GB显存)上运行,batch size=128,结果如下:

Epoch训练Loss耗时(s/epoch)
11.324518.2
20.987618.3
30.812318.5
40.714218.4
50.650118.3

5个epoch总耗时约91.7秒。测试集准确率:78.23%。如果换用更大的ResNet50(torchvision自带),epoch时间约45秒,准确率可到92.1%。但本文重点不是刷榜,是为了展示完整流程。

四、避坑指南

以下是这3个月我实际遇到的坑:

  1. PyTorch版本与CUDA不匹配:安装torch 2.1.0后,一定要用torch.cuda.is_available()检查。我一开始装了CPU版本,训练一个epoch要5分钟。建议用pip list | grep torch确认版本。
  2. 数据加载num_workers设置:在Windows上num_workers>0会报多进程错误,建议设为0或2。Linux上可以设4-8。我租的云服务器是Linux,设4没问题。
  3. 李沐课程代码的坑:他的代码里经常用d2l包,版本和PyTorch 2.0有冲突。直接安装pip install d2l可能会覆盖torch版本。建议从GitHub下载notebook,单独跑里面的模型定义。
  4. 吴恩达作业的TF版本:即使选了PyTorch选项,作业框架还是TF的变量scope习惯。我直接放弃吴恩达的代码部分,只看了视频推导。
  5. CS231n作业的numpy手写:如果你只做后两题(PyTorch版本),记得先读cs231n/classifiers/fc_net.py里的实现,batch normalization的numpy版很容易写错。
  6. GPU显存不够:CIFAR-10图片小还好,但如果用ImageNet分类网络,batch size设64以上可能爆显存。建议先用torch.cuda.memory_summary()检查。

五、总结

没有一门课程能从头包到尾。我的路线是:吴恩达视频1~2门(2周)→ 李沐动手学深度学习PyTorch版(4周,每天2小时)→ CS231n作业选做(2周)。看完这篇博客,你直接复制代码跑一轮,再对着李沐的notebook改网络结构,就能建立完整的从理论到代码的映射。别再像我一样浪费时间填坑。