《人工智能从入门到精通》第五篇:计算机视觉基础与图像识别实战

一、章节引言:从“看到图像”到“理解图像”
💡 学习目标:理解计算机视觉的核心概念与发展历程,掌握图像的基本操作(读取、变换、增强),实现图像识别的常用算法(CNN、ResNet、Inception),完成真实场景下的图像分类任务(CIFAR-10数据集),对比不同网络结构的性能表现。
💡 重点提示:本章节是AI应用层的核心领域——计算机视觉是当前AI领域最活跃的研究方向之一,所有AI应用(如人脸识别、自动驾驶、医疗影像诊断)都是基于计算机视觉实现的,务必理解图像识别的原理并通过代码验证。
在第4篇我们完成了深度学习基础与神经网络实现,实现了手写数字识别任务(MNIST数据集)。但MNIST数据集是一个非常简单的数据集(28×28像素的灰度图像,数字0~9),而真实场景下的图像识别任务要复杂得多(彩色图像、多类别的物体、不同的光照和角度)。
什么是计算机视觉?
计算机视觉是AI的一个分支,它让计算机能够看到图像并理解图像。计算机视觉的核心流程是:
接下来,我们将分五个模块推进:计算机视觉核心概念与发展历程 → 图像的基本操作 → 图像识别的常用算法 → 真实场景下的图像分类任务(CIFAR-10数据集)→ 网络结构优化与性能对比
二、模块1:计算机视觉核心概念与发展历程
2.1 核心概念
2.1.1 图像的表示
图像在计算机中是以数字矩阵的形式表示的:
- 灰度图像:只有一个通道,每个像素值在0~255之间(0表示黑色,255表示白色)
- 彩色图像:有三个通道(RGB),每个通道的像素值在0~255之间(R表示红色,G表示绿色,B表示蓝色)
# 图像表示代码实现
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# 读取灰度图像
gray_image = Image.open("./data/gray_image.jpg").convert("L")
gray_image_array = np.array(gray_image)
# 读取彩色图像
color_image = Image.open("./data/color_image.jpg")
color_image_array = np.array(color_image)
# 可视化图像
plt.figure(figsize=(15, 5))
# 灰度图像
plt.subplot(1, 2, 1)
plt.imshow(gray_image_array, cmap="gray")
plt.title("灰度图像")
plt.axis("off")
# 彩色图像
plt.subplot(1, 2, 2)
plt.imshow(color_image_array)
plt.title("彩色图像")
plt.axis("off")
plt.tight_layout()
plt.show()
# 打印图像矩阵的形状
print(f"灰度图像矩阵的形状:{gray_image_array.shape}")
print(f"彩色图像矩阵的形状:{color_image_array.shape}")
2.1.2 图像的特征
图像的特征是图像的不变属性——它们不会随着图像的缩放、旋转、平移而改变。常用的图像特征有:
- 边缘特征:图像中像素值发生剧烈变化的区域(如物体的轮廓)
- 轮廓特征:物体的外部边界
- 纹理特征:图像的图案和纹理(如木材的纹理、金属的纹理)
- 颜色特征:图像的颜色分布
2.2 发展历程
计算机视觉的发展可以分为以下几个阶段:
三、模块2:图像的基本操作
3.1 图像的读取与保存
# 图像读取与保存代码实现
from PIL import Image
import matplotlib.pyplot as plt
# 读取图像
image = Image.open("./data/color_image.jpg")
# 保存图像
image.save("./data/saved_image.jpg")
# 可视化图像
plt.imshow(image)
plt.title("原图像")
plt.axis("off")
plt.show()
3.2 图像的变换
# 图像变换代码实现
from PIL import Image
import matplotlib.pyplot as plt
# 读取图像
image = Image.open("./data/color_image.jpg")
# 图像缩放
resized_image = image.resize((200, 200))
# 图像旋转
rotated_image = image.rotate(45)
# 图像翻转
flipped_image = image.transpose(Image.FLIP_LEFT_RIGHT)
# 可视化图像
plt.figure(figsize=(15, 5))
# 原图像
plt.subplot(1, 4, 1)
plt.imshow(image)
plt.title("原图像")
plt.axis("off")
# 缩放后的图像
plt.subplot(1, 4, 2)
plt.imshow(resized_image)
plt.title("缩放后的图像(200×200)")
plt.axis("off")
# 旋转后的图像
plt.subplot(1, 4, 3)
plt.imshow(rotated_image)
plt.title("旋转后的图像(45度)")
plt.axis("off")
# 翻转后的图像
plt.subplot(1, 4, 4)
plt.imshow(flipped_image)
plt.title("翻转后的图像(水平翻转)")
plt.axis("off")
plt.tight_layout()
plt.show()
3.3 图像的增强
# 图像增强代码实现
from PIL import Image, ImageEnhance
import matplotlib.pyplot as plt
# 读取图像
image = Image.open("./data/color_image.jpg")
# 亮度增强
brightness_enhancer = ImageEnhance.Brightness(image)
brightened_image = brightness_enhancer.enhance(1.5) # 亮度增加50%
# 对比度增强
contrast_enhancer = ImageEnhance.Contrast(image)
contrasted_image = contrast_enhancer.enhance(1.5) # 对比度增加50%
# 饱和度增强
saturation_enhancer = ImageEnhance.Color(image)
saturated_image = saturation_enhancer.enhance(1.5) # 饱和度增加50%
# 可视化图像
plt.figure(figsize=(15, 5))
# 原图像
plt.subplot(1, 4, 1)
plt.imshow(image)
plt.title("原图像")
plt.axis("off")
# 亮度增强后的图像
plt.subplot(1, 4, 2)
plt.imshow(brightened_image)
plt.title("亮度增强后的图像(1.5倍)")
plt.axis("off")
# 对比度增强后的图像
plt.subplot(1, 4, 3)
plt.imshow(contrasted_image)
plt.title("对比度增强后的图像(1.5倍)")
plt.axis("off")
# 饱和度增强后的图像
plt.subplot(1, 4, 4)
plt.imshow(saturated_image)
plt.title("饱和度增强后的图像(1.5倍)")
plt.axis("off")
plt.tight_layout()
plt.show()
四、模块3:图像识别的常用算法
4.1 算法1:卷积神经网络(CNN)
4.1.1 原理简介
卷积神经网络是专门用于图像识别任务的深度学习算法,它通过卷积操作和池化操作提取图像的特征。
卷积操作
卷积操作是对图像进行局部区域的加权求和,它的数学公式可以表示为:
(f∗g)(i,j)=∑m=−kk∑n=−kkf(i+m,j+n)g(m,n) (f * g)(i,j) = \\sum_{m=-k}^{k} \\sum_{n=-k}^{k} f(i+m,j+n) g(m,n) (f∗g)(i,j)=m=−k∑kn=−k∑kf(i+m,j+n)g(m,n)
其中:
- fff:输入图像
- ggg:卷积核(滤波器)
- kkk:卷积核的大小(通常为3×3、5×5)
池化操作
池化操作是对图像进行降采样,它的作用是:
常用的池化操作有:
- 最大池化:取局部区域的最大值
- 平均池化:取局部区域的平均值
4.1.2 代码实现
# 卷积神经网络代码实现(CIFAR-10数据集)
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.datasets as datasets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
# 1. 数据准备
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
train_dataset = datasets.CIFAR10(root="./data", train=True, transform=transform, download=True)
test_dataset = datasets.CIFAR10(root="./data", train=False, transform=transform, download=True)
batch_size = 64
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
# 2. 网络结构设计(卷积神经网络)
class ConvolutionalNeuralNetwork(nn.Module):
def __init__(self, output_dim):
super(ConvolutionalNeuralNetwork, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1, padding=1)
self.relu1 = nn.ReLU()
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)
self.relu2 = nn.ReLU()
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1)
self.relu3 = nn.ReLU()
self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)
self.fc1 = nn.Linear(128 * 4 * 4, 512)
self.relu4 = nn.ReLU()
self.dropout = nn.Dropout(p=0.5)
self.fc2 = nn.Linear(512, output_dim)
self.softmax = nn.Softmax(dim=1)
def forward(self, x):
out = self.conv1(x)
out = self.relu1(out)
out = self.pool1(out)
out = self.conv2(out)
out = self.relu2(out)
out = self.pool2(out)
out = self.conv3(out)
out = self.relu3(out)
out = self.pool3(out)
out = out.view(out.size(0), –1)
out = self.fc1(out)
out = self.relu4(out)
out = self.dropout(out)
out = self.fc2(out)
out = self.softmax(out)
return out
# 初始化神经网络、损失函数和优化器
output_dim = 10
model = ConvolutionalNeuralNetwork(output_dim)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 3. 模型训练
num_epochs = 20
total_step = len(train_loader)
losses = []
accuracies = []
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 记录损失值
losses.append(loss.item())
# 每100步打印一次训练信息
if (i + 1) % 100 == 0:
print(f"Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{total_step}], Loss: {loss.item():.4f}")
# 计算训练集准确率
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in train_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100 * correct / total
accuracies.append(accuracy)
print(f"Epoch [{epoch + 1}/{num_epochs}], Train Accuracy: {accuracy:.2f}%")
model.train()
# 4. 模型评估
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_accuracy = 100 * correct / total
print(f"Test Accuracy: {test_accuracy:.2f}%")
# 5. 可视化训练过程
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(losses, color="#FF6B6B", linewidth=2)
plt.xlabel("Step")
plt.ylabel("Loss")
plt.title("训练过程中的损失值变化")
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.plot(accuracies, color="#4ECDC4", linewidth=2)
plt.xlabel("Epoch")
plt.ylabel("Accuracy (%)")
plt.title("训练过程中的准确率变化")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 6. 可视化测试集预测结果
classes = ("plane", "car", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck")
random_indices = np.random.randint(0, len(test_dataset), 10)
plt.figure(figsize=(15, 5))
for i, index in enumerate(random_indices):
image, label = test_dataset[index]
image = image.unsqueeze(0)
with torch.no_grad():
output = model(image)
_, predicted = torch.max(output.data, 1)
plt.subplot(2, 5, i + 1)
plt.imshow(np.transpose(image.squeeze(0).numpy(), (1, 2, 0)))
plt.title(f"Predicted: {classes[predicted.item()]}\\nActual: {classes[label]}")
plt.axis("off")
plt.tight_layout()
plt.show()
结果分析:
- 卷积神经网络在CIFAR-10数据集上的测试准确率约为75%
- 训练过程中的损失值逐渐下降,准确率逐渐上升
- 模型对大部分图像的预测是正确的,但对一些模糊的图像或相似的物体可能会预测错误
4.2 算法2:ResNet(残差网络)
4.2.1 原理简介
ResNet是2015年ImageNet图像识别竞赛的冠军算法,它通过残差连接解决了深度神经网络的梯度消失和梯度爆炸问题。
残差连接
残差连接的数学公式可以表示为:
H(x)=F(x)+x H(x) = F(x) + x H(x)=F(x)+x
其中:
- H(x)H(x)H(x):输出
- F(x)F(x)F(x):残差函数
- xxx:输入
残差连接的作用是:
4.2.2 代码实现
# ResNet代码实现(CIFAR-10数据集)
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.datasets as datasets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
# 1. 数据准备(与卷积神经网络相同)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
train_dataset = datasets.CIFAR10(root="./data", train=True, transform=transform, download=True)
test_dataset = datasets.CIFAR10(root="./data", train=False, transform=transform, download=True)
batch_size = 64
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
# 2. 网络结构设计(ResNet-18)
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_channels, out_channels, stride=1):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != self.expansion * out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(self.expansion * out_channels)
)
def forward(self, x):
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += self.shortcut(x)
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, block, layers, output_dim):
super(ResNet, self).__init__()
self.in_channels = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.layer1 = self._make_layer(block, 64, layers[0], stride=1)
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512 * block.expansion, output_dim)
self.softmax = nn.Softmax(dim=1)
def _make_layer(self, block, out_channels, blocks, stride=1):
strides = [stride] + [1] * (blocks – 1)
layers = []
for stride in strides:
layers.append(block(self.in_channels, out_channels, stride))
self.in_channels = out_channels * block.expansion
return nn.Sequential(*layers)
def forward(self, x):
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = self.avg_pool(out)
out = out.view(out.size(0), –1)
out = self.fc(out)
out = self.softmax(out)
return out
def resnet18(output_dim):
return ResNet(BasicBlock, [2, 2, 2, 2], output_dim)
# 初始化神经网络、损失函数和优化器
output_dim = 10
model = resnet18(output_dim)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 3. 模型训练
num_epochs = 20
total_step = len(train_loader)
losses = []
accuracies = []
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 记录损失值
losses.append(loss.item())
# 每100步打印一次训练信息
if (i + 1) % 100 == 0:
print(f"Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{total_step}], Loss: {loss.item():.4f}")
# 计算训练集准确率
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in train_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100 * correct / total
accuracies.append(accuracy)
print(f"Epoch [{epoch + 1}/{num_epochs}], Train Accuracy: {accuracy:.2f}%")
model.train()
# 4. 模型评估
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_accuracy = 100 * correct / total
print(f"Test Accuracy: {test_accuracy:.2f}%")
# 5. 可视化训练过程
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(losses, color="#FF6B6B", linewidth=2)
plt.xlabel("Step")
plt.ylabel("Loss")
plt.title("训练过程中的损失值变化")
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.plot(accuracies, color="#4ECDC4", linewidth=2)
plt.xlabel("Epoch")
plt.ylabel("Accuracy (%)")
plt.title("训练过程中的准确率变化")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 6. 可视化测试集预测结果
classes = ("plane", "car", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck")
random_indices = np.random.randint(0, len(test_dataset), 10)
plt.figure(figsize=(15, 5))
for i, index in enumerate(random_indices):
image, label = test_dataset[index]
image = image.unsqueeze(0)
with torch.no_grad():
output = model(image)
_, predicted = torch.max(output.data, 1)
plt.subplot(2, 5, i + 1)
plt.imshow(np.transpose(image.squeeze(0).numpy(), (1, 2, 0)))
plt.title(f"Predicted: {classes[predicted.item()]}\\nActual: {classes[label]}")
plt.axis("off")
plt.tight_layout()
plt.show()
结果分析:
- ResNet-18在CIFAR-10数据集上的测试准确率约为85%,比卷积神经网络高
- 训练过程中的损失值和准确率的变化趋势与卷积神经网络相似
- 模型对图像的预测更准确,因为它解决了深度神经网络的梯度消失和梯度爆炸问题
五、模块4:真实场景下的图像分类任务(CIFAR-10数据集)
5.1 数据集简介
CIFAR-10数据集是深度学习领域最经典的彩色图像数据集之一,它包含60000个训练样本和10000个测试样本,每个样本都是32×32像素的彩色图像(10个类别:plane、car、bird、cat、deer、dog、frog、horse、ship、truck)。
5.2 代码实现(与模块3相同)
六、模块5:网络结构优化与性能对比
6.1 网络结构优化
为了提高模型的性能,我们可以对网络结构进行以下优化:
6.2 数据增强技术实现
# 数据增强技术代码实现
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.datasets as datasets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
# 1. 数据准备(使用数据增强技术)
transform_train = transforms.Compose([
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
transform_test = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
train_dataset = datasets.CIFAR10(root="./data", train=True, transform=transform_train, download=True)
test_dataset = datasets.CIFAR10(root="./data", train=False, transform=transform_test, download=True)
batch_size = 64
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
# 2. 网络结构设计(ResNet-18)
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_channels, out_channels, stride=1):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != self.expansion * out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(self.expansion * out_channels)
)
def forward(self, x):
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += self.shortcut(x)
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, block, layers, output_dim):
super(ResNet, self).__init__()
self.in_channels = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.layer1 = self._make_layer(block, 64, layers[0], stride=1)
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512 * block.expansion, output_dim)
self.softmax = nn.Softmax(dim=1)
def _make_layer(self, block, out_channels, blocks, stride=1):
strides = [stride] + [1] * (blocks – 1)
layers = []
for stride in strides:
layers.append(block(self.in_channels, out_channels, stride))
self.in_channels = out_channels * block.expansion
return nn.Sequential(*layers)
def forward(self, x):
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = self.avg_pool(out)
out = out.view(out.size(0), –1)
out = self.fc(out)
out = self.softmax(out)
return out
def resnet18(output_dim):
return ResNet(BasicBlock, [2, 2, 2, 2], output_dim)
# 初始化神经网络、损失函数和优化器
output_dim = 10
model = resnet18(output_dim)
criterion = nn.CrossEntropyLoss()
optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.0001)
# 学习率调度策略(余弦退火)
from torch.optim.lr_scheduler import CosineAnnealingLR
scheduler = CosineAnnealingLR(optimizer, T_max=20)
# 3. 模型训练
num_epochs = 20
total_step = len(train_loader)
losses = []
accuracies = []
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 记录损失值
losses.append(loss.item())
# 每100步打印一次训练信息
if (i + 1) % 100 == 0:
print(f"Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{total_step}], Loss: {loss.item():.4f}")
# 学习率调度
scheduler.step()
# 计算训练集准确率
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in train_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100 * correct / total
accuracies.append(accuracy)
print(f"Epoch [{epoch + 1}/{num_epochs}], Train Accuracy: {accuracy:.2f}%")
model.train()
# 4. 模型评估
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_accuracy = 100 * correct / total
print(f"Test Accuracy: {test_accuracy:.2f}%")
# 5. 可视化训练过程
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(losses, color="#FF6B6B", linewidth=2)
plt.xlabel("Step")
plt.ylabel("Loss")
plt.title("训练过程中的损失值变化")
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.plot(accuracies, color="#4ECDC4", linewidth=2)
plt.xlabel("Epoch")
plt.ylabel("Accuracy (%)")
plt.title("训练过程中的准确率变化")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 6. 可视化测试集预测结果
classes = ("plane", "car", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck")
random_indices = np.random.randint(0, len(test_dataset), 10)
plt.figure(figsize=(15, 5))
for i, index in enumerate(random_indices):
image, label = test_dataset[index]
image = image.unsqueeze(0)
with torch.no_grad():
output = model(image)
_, predicted = torch.max(output.data, 1)
plt.subplot(2, 5, i + 1)
plt.imshow(np.transpose(image.squeeze(0).numpy(), (1, 2, 0)))
plt.title(f"Predicted: {classes[predicted.item()]}\\nActual: {classes[label]}")
plt.axis("off")
plt.tight_layout()
plt.show()
结果分析:
- 使用数据增强技术和学习率调度策略后,ResNet-18在CIFAR-10数据集上的测试准确率约为90%
- 训练过程中的损失值和准确率的变化趋势与之前相似,但测试准确率有所提高
- 模型对图像的预测更准确,因为数据增强技术增加了训练样本的多样性,防止了模型过拟合
6.3 性能对比
根据CIFAR-10数据集的结果,我们对不同网络结构的性能进行对比:
| 卷积神经网络 | 否 | 否 | 75% | 600 | 0.5 |
| ResNet-18 | 否 | 否 | 85% | 1200 | 11 |
| ResNet-18 | 是 | 是 | 90% | 1500 | 11 |
结论:使用数据增强技术和学习率调度策略后,ResNet-18的性能得到了显著提升。虽然训练时间比之前长,但测试准确率提高了5个百分点。
七、章节总结与后续学习指南
✅ 本章节核心收获:
💡 后续学习方向:
⚠️ 学习建议:
- 务必动手运行每一行代码——计算机视觉是实操性极强的学科,看10遍代码不如自己运行1遍
- 遇到问题优先查阅官方文档(PyTorch官网https://pytorch.org/ )和Stack Overflow
- 养成代码注释和版本管理的好习惯,为后续复杂计算机视觉项目打下基础



