news 2026/1/26 4:29:18

吴恩达深度学习课程四:计算机视觉 第二周:经典网络结构 课后习题和代码实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
吴恩达深度学习课程四:计算机视觉 第二周:经典网络结构 课后习题和代码实践

此分类用于记录吴恩达深度学习课程的学习笔记。
课程相关信息链接如下:

  1. 原课程视频链接:[双语字幕]吴恩达深度学习deeplearning.ai
  2. github课程资料,含课件与笔记:吴恩达深度学习教学资料
  3. 课程配套练习(中英)与答案:吴恩达深度学习课后习题与答案

本篇为第四课第二周的课后习题和代码实践部分。

1. 理论习题

【中英】【吴恩达课后测验】Course 4 -卷积神经网络 - 第二周测验
还是先上一下链接,这周的理论习题涉及一些公式推导,但是在这篇博客里已经给出了很详细的过程。所以就不再展开这一部分了。
这周的代码演示内容较多,我们就把精力主要放在下面的实践内容中。

2. 代码实践

要提前说明的是,在吴恩达老师的课程中,本周编程作业的题目是Keras 入门与残差网络的搭建
不过需要注意:Keras 原本是一个独立的第三方库,但在 TensorFlow 2.x 中,它已被集成成为 TensorFlow 的高级 API
因此,在这位博主的 Keras入门与残差网络的搭建 博客中,部分导入语法在新版本中可能会报错,这只是由于版本更新导致,并不影响博客的核心内容,且残差网络的手工搭建部分仍然是正确且值得学习的。
我们在正文部分依旧还是用 PyTorch 来演示这周所学的内容。
因为内容较多,这里先简单列举一下:

  1. 使用 LeNet-5 进行手写数字图像识别
  2. 使用 AlexNet 进行手写数字图像识别,猫狗图像二分类
  3. 使用 VGG-16 进行猫狗图像二分类
  4. 使用 ResNet-18 进行猫狗图像二分类

2.1 使用 LeNet-5 进行手写数字图像识别

回看一下 LeNet-5 的网络结构:

我们在介绍它的时候就提到过,LeNet-5 的提出应用就是单通道的文档识别。
因此,我们就来看看这个二十多年前的模型在手写数字图像识别上的效果如何。
LeNet-5 的网络结构并不复杂,我们用 ReLU 替换了原始的 sigmoid/tanh,用CrossEntropyLoss 替代原始平方误差损失,这样在 PyTorch 中更适合现代训练。
最后代码如下:

/* by 01022.hk - online tools website : 01022.hk/zh/formatcss.html */ class LeNet5(nn.Module): def __init__(self, num_classes=10): super(LeNet5, self).__init__() self.conv1 = nn.Conv2d(1, 6, kernel_size=5) # 输入: 1x32x32 -> 输出: 6x28x28 self.pool = nn.AvgPool2d(kernel_size=2, stride=2) # 6x28x28 -> 6x14x14 self.conv2 = nn.Conv2d(6, 16, kernel_size=5) # 16x10x10 self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, num_classes) def forward(self, x): x = F.relu(self.conv1(x)) # 6x28x28 x = self.pool(x) # 6x14x14 x = F.relu(self.conv2(x)) # 16x10x10 x = self.pool(x) # 16x5x5 x = torch.flatten(x, 1) # 16*5*5=400 x = F.relu(self.fc1(x)) # 120 x = F.relu(self.fc2(x)) # 84 x = self.fc3(x) # 10 return x # 参数设置: criterion = nn.CrossEntropyLoss() #内置 softmax optimizer = optim.Adam(model.parameters(), lr=0.001) num_epochs = 10

现在,来看看 LeNet-5 在手写数字图像识别上的效果如何:

可以看到及效果极佳,仅仅 10 轮训练,训练准确率几乎达到 100%,测试准确率也接近 100%,并且损失仍在平稳下降。
原因一方面,LeNet-5 的卷积-池化结构能够有效提取手写数字的局部到全局特征;另一方面,MNIST 数据集相对简单、规范,使得小型模型也能快速收敛并取得高精度。

2.2 AlexNet

还是先回顾一下网络结构:

实际上,AlexNet 被已经被PyTorch内置了,我们可以比较方便的调用:

/* by 01022.hk - online tools website : 01022.hk/zh/formatcss.html */ # 使用内置 AlexNetmodel = models.alexnet(pretrained=False)

现在就来看看 AlexNet 在不同任务上的效果。

(1)使用 AlexNet 进行手写数字图像识别

这里先使用 AlexNet 跑 MNIST ,并不是追求更高精度,而是对比模型规模变大后,训练效率和收敛行为的变化。
因为 AlexNet 接受的输入为RGB图像,因此,在开始训练前,还需要对 MNIST 数据集进行预处理:

transform = transforms.Compose([ transforms.Resize((224, 224)), # AlexNet 需要 224x224 transforms.Grayscale(num_output_channels=3), # MNIST 是单通道,将其复制为 3 通道 transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ])

运行结果是这样的:

这个结果不出意料,AlexNet 在 LeNet-5 的基本逻辑上又通过网络深度增加了非线性表达能力,但相应的,也会增加每轮的平均训练时间
同时你会发现,在相同训练轮次下,AlexNet 的损失值相对 LeNet-5 略高,这并不意味着模型性能更差,而是由于其参数规模更大、优化难度更高,在有限轮次下尚未充分收敛。
从模型容量和表达能力上看,AlexNet 具备更高的性能上限,在更大数据规模或更充分训练条件下通常能取得更优结果。

(1)使用 AlexNet 进行猫狗图像二分类

我们先试试使用预训练的 AlexNet 来看看模型在这个任务上的上限。

AlexNetmodel = models.alexnet(pretrained=True)

结果如下:

现在,我们再试试从头开始训练:

AlexNetmodel = models.alexnet(pretrained=False)

结果如下:

仅仅不使用预训练参数,训练过程就明显变得困难、损失下降缓慢,前几层几乎学不到有意义的特征,表现出典型的梯度消失现象。
原因在于:AlexNet 参数规模大,但缺乏 BN 和残差等稳定训练的结构设计,在小数据集上很难把梯度有效传回前层。
因此,我们由此进行一些调试:

  1. 进行数据增强变相“增加数据量”,这一点在原论文也提到过。
  2. 缩小学习率,调试能否避免像这样一样“卡死”。
transform = transforms.Compose([ # (1)尺度裁剪 transforms.RandomResizedCrop( 224, scale=(0.8, 1.0), ratio=(0.9, 1.1) ), # (2)左右翻转 transforms.RandomHorizontalFlip(p=0.5), # (3)旋转 transforms.RandomRotation( degrees=10, interpolation=transforms.InterpolationMode.BILINEAR ), # (4)颜色抖动 transforms.ColorJitter( brightness=0.2, contrast=0.2, saturation=0.2, hue=0.05 ), transforms.ToTensor(), # (5)标准化 transforms.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] ), ]) optimizer = optim.Adam(model.parameters(), lr=0.0001)

现在再来看结果:

很明显有了好转,损失稳步下降,但是又出现了过拟合倾向。
我们先不着急继续优化,来看看别的模型的表现。

2.3 VGG-16

现在,保持其他所有参数不变,只改变模型为没有预训练的 VGG-16:

model = models.vgg16(pretrained=False)

再次运行,看看结果:

你会发现,在相同的参数设置下,AlexNet的验证准确率最高只能达到80%,而 VGG-16 却可以几乎稳定在80%以上。
简单总结来看:

  • 在相同训练设置下,VGG-16 比 AlexNet 更容易从头训练,训练过程也更稳定。
  • 原因在于 VGG-16 使用了统一的小卷积核结构,特征是逐层、渐进式学习的,梯度传播更顺畅。

因此,VGG-16 比 AlexNet 更好优化。
继续,最后登场的是 ResNet 。

2.4 ResNet-18

同样,我们先回顾一下它的网络结构:

我们对它的使用也并不陌生:

model = models.resnet18(pretrained=False) model.fc = nn.Linear(model.fc.in_features, 1)

同样保持其他参数不变,来看看运行结果:

在这个实验规模下,ResNet-18 的优势还未被完全放大,但它的训练过程已经表现出明显的稳定性优势,这正是残差结构的核心价值。

2.5 对比总结

模型结构特点训练难度实验表现核心结论
LeNet-5浅层网络,卷积 + 池化 + 全连接,参数量小非常容易在 MNIST 上快速收敛,准确率接近 100%结构简单但有效,适合小图像、低复杂度任务
AlexNet较深网络,大卷积核 + 大量全连接层,无 BN、无残差困难小数据集从头训练易梯度消失,需预训练或强正则表达能力强,但优化不稳定,强依赖数据规模与初始化
VGG-16统一小卷积核(3×3)堆叠,结构规则中等从头训练稳定,验证准确率明显高于 AlexNet结构“规整”比“更浅”更重要,更易优化
ResNet-18残差连接(skip connection),梯度直通最容易训练最稳定,小数据集也能正常收敛残差结构本质上解决了深层网络难训练问题

模型越往后发展,提升的重点不是“更强的表达能力”,而是“更容易被训练好”
当然这并不绝对,同样有可以兼顾二者的强大技术,我们之后就会了解到。
回到现在,LeNet-5 结构简单,在低复杂度任务上非常好训;AlexNet 虽然更深,但缺乏良好的优化设计,小数据集上反而容易卡住;VGG-16 用规则的小卷积堆叠,让网络更深却更稳定;而 ResNet-18 通过残差连接给梯度开了“直通车”,彻底缓解了深层网络难训练的问题。
实际应用中,我们也能发现,在小规模数据集中,预训练权重往往比模型深度更重要,而在从头训练时,结构是否好优化比参数量大小更关键

3.附录

3.1 训练代码 PyTorch版

import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms, models from torch.utils.data import DataLoader, random_split import matplotlib.pyplot as plt # 数据预处理 transform = transforms.Compose([ # (1)尺度裁剪 transforms.RandomResizedCrop( 224, scale=(0.8, 1.0), ratio=(0.9, 1.1) ), # (2)左右翻转 transforms.RandomHorizontalFlip(p=0.5), # (3)旋转 transforms.RandomRotation( degrees=10, interpolation=transforms.InterpolationMode.BILINEAR ), # (4)颜色抖动 transforms.ColorJitter( brightness=0.2, contrast=0.2, saturation=0.2, hue=0.05 ), # (5)转 Tensor transforms.ToTensor(), # (6)标准化 transforms.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] ), ]) dataset = datasets.ImageFolder(root='./cat_dog', transform=transform) train_size = int(0.8 * len(dataset)) val_size = int(0.1 * len(dataset)) test_size = len(dataset) - train_size - val_size train_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size]) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True) val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False) test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # ---------------------------------- # 重点:模型选择 # ---------------------------------- # model = models.alexnet(pretrained=False) # 使用 AlexNet # model = models.vgg16(pretrained=False) # 使用 VGG16 model = models.resnet18(pretrained=False) # 使用 ResNet18 # 替换最后一层分类器 AlexNet、VGG16 使用这行 # model.classifier[6] = nn.Linear(model.classifier[6].in_features, 1) # 替换最后一层分类器 ResNet18 使用这行 model.fc = nn.Linear(model.fc.in_features, 1) model = model.to(device) # 训练参数 criterion = nn.BCEWithLogitsLoss() optimizer = optim.Adam(model.parameters(), lr=0.0001) epochs = 80 train_losses = [] train_accs = [] val_accs = [] for epoch in range(epochs): model.train() epoch_train_loss = 0 correct_train = 0 total_train = 0 for images, labels in train_loader: images, labels = images.to(device), labels.to(device).float().unsqueeze(1) outputs = model(images) loss = criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() epoch_train_loss += loss.item() preds = (outputs > 0.5).int() correct_train += (preds == labels.int()).sum().item() total_train += labels.size(0) avg_train_loss = epoch_train_loss / len(train_loader) train_acc = correct_train / total_train train_losses.append(avg_train_loss) train_accs.append(train_acc) # 验证集 model.eval() correct_val = 0 total_val = 0 with torch.no_grad(): for images, labels in val_loader: images, labels = images.to(device), labels.to(device).float().unsqueeze(1) outputs = model(images) preds = (outputs > 0).int() correct_val += (preds == labels.int()).sum().item() total_val += labels.size(0) val_acc = correct_val / total_val val_accs.append(val_acc) print(f"轮次 [{epoch+1}/{epochs}] " f"训练损失: {avg_train_loss:.4f} " f"训练准确率: {train_acc:.4f} " f"验证准确率: {val_acc:.4f}") # 可视化 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False plt.figure(figsize=(10,5)) plt.plot(train_losses, label='训练损失') plt.plot(train_accs, label='训练准确率') plt.plot(val_accs, label='验证准确率') plt.legend() plt.grid(True) plt.show() # 测试集 model.eval() correct = 0 total = 0 with torch.no_grad(): for images, labels in test_loader: images, labels = images.to(device), labels.to(device).float().unsqueeze(1) outputs = model(images) preds = (outputs > 0).int() correct += (preds == labels.int()).sum().item() total += labels.size(0) print(f"测试准确率: {correct / total:.4f}")

3.2 训练代码 TF版

由于官方模型库并未提供 AlexNet 与 ResNet-18,我们分别采用手工实现(AlexNet)与结构等价模型(ResNet50)进行对比。

import tensorflow as tf from tensorflow.keras import layers, models import matplotlib.pyplot as plt data_augmentation = tf.keras.Sequential([ layers.Resizing(256, 256), layers.RandomCrop(224, 224), layers.RandomFlip("horizontal"), layers.RandomRotation(0.05), layers.RandomContrast(0.2), ]) train_ds = tf.keras.preprocessing.image_dataset_from_directory( "./cat_dog", validation_split=0.2, subset="training", seed=42, image_size=(224, 224), batch_size=32 ) val_ds = tf.keras.preprocessing.image_dataset_from_directory( "./cat_dog", validation_split=0.2, subset="validation", seed=42, image_size=(224, 224), batch_size=32 ) AUTOTUNE = tf.data.AUTOTUNE train_ds = train_ds.prefetch(AUTOTUNE) val_ds = val_ds.prefetch(AUTOTUNE) # ------------------------------- # 模型选择 # ------------------------------- # ---------- AlexNet ---------- # def build_alexnet(): # return models.Sequential([ # layers.Input(shape=(224, 224, 3)), # data_augmentation, # layers.Rescaling(1./255), # # layers.Conv2D(96, 11, strides=4, activation='relu'), # layers.MaxPooling2D(3, strides=2), # # layers.Conv2D(256, 5, padding='same', activation='relu'), # layers.MaxPooling2D(3, strides=2), # # layers.Conv2D(384, 3, padding='same', activation='relu'), # layers.Conv2D(384, 3, padding='same', activation='relu'), # layers.Conv2D(256, 3, padding='same', activation='relu'), # layers.MaxPooling2D(3, strides=2), # # layers.Flatten(), # layers.Dense(4096, activation='relu'), # layers.Dropout(0.5), # layers.Dense(4096, activation='relu'), # layers.Dropout(0.5), # layers.Dense(1) # ]) # model = build_alexnet() # ---------- VGG-16 ---------- # base_model = tf.keras.applications.VGG16( # include_top=False, # weights=None, # input_shape=(224, 224, 3) # ) # model = models.Sequential([ # layers.Input(shape=(224, 224, 3)), # data_augmentation, # layers.Rescaling(1./255), # base_model, # layers.Flatten(), # layers.Dense(4096, activation='relu'), # layers.Dropout(0.5), # layers.Dense(4096, activation='relu'), # layers.Dropout(0.5), # layers.Dense(1) # ]) # ---------- ResNet50 ---------- base_model = tf.keras.applications.ResNet50( include_top=False, weights=None, input_shape=(224, 224, 3) ) model = models.Sequential([ layers.Input(shape=(224, 224, 3)), data_augmentation, layers.Rescaling(1./255), base_model, layers.GlobalAveragePooling2D(), layers.Dense(1) ]) model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4), loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=["accuracy"] ) history = model.fit( train_ds, validation_data=val_ds, epochs=80 ) plt.figure(figsize=(10, 5)) plt.plot(history.history["loss"], label="训练损失") plt.plot(history.history["accuracy"], label="训练准确率") plt.plot(history.history["val_accuracy"], label="验证准确率") plt.legend() plt.grid(True) plt.show()
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/24 20:46:25

AnySoftKeyboard:自定义键盘的终极指南与开源解决方案

AnySoftKeyboard:自定义键盘的终极指南与开源解决方案 【免费下载链接】AnySoftKeyboard Android (f/w 2.1) on screen keyboard for multiple languages (chat https://gitter.im/AnySoftKeyboard) 项目地址: https://gitcode.com/gh_mirrors/an/AnySoftKeyboard…

作者头像 李华
网站建设 2026/1/24 20:46:23

Buster验证码插件:从零到商店的完整发布攻略

Buster验证码插件:从零到商店的完整发布攻略 【免费下载链接】buster Captcha solver extension for humans, available for Chrome, Edge and Firefox 项目地址: https://gitcode.com/gh_mirrors/bu/buster 你是否曾经想过,一款能够自动解决验证…

作者头像 李华
网站建设 2026/1/25 8:19:08

中国草地资源数据终极指南:1:100万精度完整解析

想要深入了解中国草地资源的分布与现状吗?这份1:100万精度草地资源数据集正是您需要的宝贵资料!无论您是从事生态研究、土地规划,还是教育资源开发,这个数据集都能为您提供权威可靠的地理信息支撑。 【免费下载链接】中国1100w草地…

作者头像 李华
网站建设 2026/1/24 20:46:19

如何快速搭建ESP32电子墨水日历:完整配置指南

如何快速搭建ESP32电子墨水日历:完整配置指南 【免费下载链接】portal_calendar A Portal themed e-ink calendar based on the ESP32 platform 项目地址: https://gitcode.com/gh_mirrors/po/portal_calendar Portal日历是一款基于ESP32平台的电子墨水日历&…

作者头像 李华
网站建设 2026/1/25 23:45:42

如何用FaceFusion镜像提升短视频内容创作效率?

如何选择合规高效的AI视频创作工具?在短视频内容爆发式增长的今天,创作者们面临的不仅是创意压力,还有制作效率与技术合规性的双重挑战。随着人工智能技术不断渗透到视频生产流程中,越来越多的AI工具开始被用于辅助剪辑、智能配音…

作者头像 李华
网站建设 2026/1/25 9:56:57

rembg图像抠图性能调优实战:从卡顿到丝滑的三步进阶

还在为rembg处理高分辨率图像时CPU跑满但效率低下的问题头疼吗?😫 作为一名AI开发者,我在处理4K产品图批量抠图时,发现即使设置了线程参数,性能依然原地踏步。经过深度源码剖析,终于找到了ONNX运行时线程亲…

作者头像 李华