【零基础入门】Python机器视觉第四阶段:深度学习基础与PyTorch实战
【零基础入门】Python机器视觉第四阶段:深度学习基础与PyTorch实战
在前三个阶段,我们已经掌握了Python基础、OpenCV图像处理以及传统缺陷检测算法。从本阶段开始,我们将正式进入深度学习的世界。深度学习是当前机器视觉领域最强大的工具,它能够自动从数据中学习特征,解决传统方法难以处理的复杂问题。
本文将按照以下目录,带你系统学习PyTorch的核心概念,并通过实战案例掌握神经网络的搭建与训练。
一、本阶段学习目标
- 理解深度学习基本概念:张量、自动求导、神经网络结构
- 掌握PyTorch基础操作:张量创建、索引、运算、与NumPy互转
- 学会使用
torch.nn模块搭建全连接网络和卷积神经网络 - 掌握数据加载器
DataLoader的使用 - 能够训练一个简单的线性回归模型
- 能够在MNIST数据集上训练全连接网络,达到90%以上准确率
- 理解卷积神经网络原理,并在CIFAR-10数据集上搭建LeNet进行图像分类
二、深度学习核心概念速览(通俗版)
| 概念 | 通俗解释 | 对应PyTorch模块 |
|---|---|---|
| 张量 | 多维数组,类似于NumPy的ndarray,但可以在GPU上加速 | torch.Tensor |
| 自动求导 | 自动计算梯度,反向传播的核心 | torch.autograd |
| 神经网络层 | 对数据进行的线性变换(如全连接、卷积) | torch.nn.Linear, torch.nn.Conv2d |
| 激活函数 | 引入非线性,让网络能学习复杂模式 | torch.nn.ReLU, torch.nn.Sigmoid |
| 损失函数 | 衡量模型预测与真实值的差距 | torch.nn.MSELoss, torch.nn.CrossEntropyLoss |
| 优化器 | 根据梯度更新参数,使损失最小化 | torch.optim.SGD, torch.optim.Adam |
| 数据集 | 封装数据和标签的类 | torch.utils.data.Dataset |
| 数据加载器 | 批量加载数据,支持多线程 | torch.utils.data.DataLoader |
三、PyTorch基础
3.1 安装PyTorch
根据你的硬件选择安装命令:
# CPU版本(如果没有NVIDIA显卡)
pip install torch torchvision
# GPU版本(以CUDA 11.8为例)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
验证安装:
import torch
print(torch.__version__) # 版本号
print(torch.cuda.is_available()) # 是否有可用的GPU
3.2 张量(Tensor)操作
张量是PyTorch中的基本数据结构,与NumPy数组非常相似,但支持GPU加速和自动求导。
3.2.1 创建张量
import torch
import numpy as np
# 从列表创建
t1 = torch.tensor([1, 2, 3, 4])
print(t1) # tensor([1, 2, 3, 4])
# 创建全零张量(3行4列)
t2 = torch.zeros(3, 4)
print(t2)
# 创建全一张量(2x3)
t3 = torch.ones(2, 3)
print(t3)
# 创建随机张量(均匀分布[0,1))
t4 = torch.rand(3, 3)
print(t4)
# 创建随机张量(标准正态分布)
t5 = torch.randn(3, 3)
print(t5)
# 创建等差张量
t6 = torch.arange(0, 10, 2) # [0,2,4,6,8]
t7 = torch.linspace(0, 1, 5) # [0.0000, 0.2500, 0.5000, 0.7500, 1.0000]
# 创建单位矩阵
t8 = torch.eye(3)
print(t8)
3.2.2 张量属性
t = torch.randn(3, 224, 224) # 模拟一张3通道224x224的图像
print(f"形状: {t.shape}") # torch.Size([3, 224, 224])
print(f"维度: {t.ndim}") # 3
print(f"数据类型: {t.dtype}") # torch.float32
print(f"所在设备: {t.device}") # cpu
print(f"元素个数: {t.numel()}") # 3*224*224 = 150528
3.2.3 索引与切片(和NumPy完全一致)
t = torch.tensor([[1,2,3], [4,5,6], [7,8,9]])
print(t[0, 1]) # 2(第1行第2列)
print(t[:, 1]) # [2,5,8](所有行的第2列)
print(t[0:2, 1:3]) # [[2,3], [5,6]](行0-1,列1-2)
3.2.4 张量运算
a = torch.tensor([1,2,3])
b = torch.tensor([4,5,6])
print(a + b) # [5,7,9]
print(a * b) # [4,10,18]
print(a ** 2) # [1,4,9]
print(a + 10) # [11,12,13] 广播机制
# 矩阵乘法(二维)
A = torch.tensor([[1,2], [3,4]])
B = torch.tensor([[5,6], [7,8]])
C = torch.mm(A, B) # 矩阵乘法
print(C)
3.2.5 与NumPy相互转换
# NumPy -> Tensor
np_arr = np.array([1,2,3])
tensor_from_np = torch.from_numpy(np_arr)
# Tensor -> NumPy
tensor = torch.tensor([4,5,6])
np_arr_back = tensor.numpy()
3.3 自动求导(Autograd)
深度学习的核心是反向传播,PyTorch的autograd包可以自动计算梯度。
import torch
# 创建一个需要计算梯度的张量
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2 + 3 * x + 1 # y = x² + 3x + 1
# 反向传播,计算梯度
y.backward()
# 查看梯度:dy/dx = 2x + 3,当x=2时,梯度=7
print(x.grad) # tensor(7.)
# 更复杂的例子:矩阵
x = torch.tensor([[1.,2.], [3.,4.]], requires_grad=True)
y = (x ** 2).sum() # 所有元素的平方和
y.backward()
print(x.grad) # 2*x 的每个元素
3.4 构建第一个神经网络:线性回归
我们将用PyTorch实现一个简单的线性回归模型,拟合 y = 2x + 1。
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# ---------- 1. 生成训练数据 ----------
# 真实关系:y = 2x + 1 + 噪声
x_train = torch.rand(100, 1) * 10 # 100个0-10之间的随机数
y_train = 2 * x_train + 1 + torch.randn(100, 1) * 2
# ---------- 2. 定义模型 ----------
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(1, 1) # 输入1维,输出1维
def forward(self, x):
return self.linear(x)
model = LinearRegression()
print(model)
# ---------- 3. 定义损失函数和优化器 ----------
criterion = nn.MSELoss() # 均方误差损失
optimizer = optim.SGD(model.parameters(), lr=0.01) # 随机梯度下降
# ---------- 4. 训练 ----------
losses = []
for epoch in range(500):
# 前向传播
y_pred = model(x_train)
loss = criterion(y_pred, y_train)
# 反向传播
optimizer.zero_grad() # 清空之前的梯度
loss.backward() # 计算梯度
optimizer.step() # 更新参数
losses.append(loss.item())
if (epoch+1) % 100 == 0:
print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')
# ---------- 5. 可视化 ----------
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(losses)
plt.title('训练损失曲线')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.subplot(1,2,2)
with torch.no_grad():
y_pred = model(x_train)
plt.scatter(x_train.numpy(), y_train.numpy(), label='真实数据', alpha=0.6)
plt.scatter(x_train.numpy(), y_pred.numpy(), label='预测数据', alpha=0.6)
plt.legend()
plt.title('拟合结果')
plt.show()
# 测试
x_test = torch.tensor([[5.0]])
print(f'预测值: {model(x_test).item():.2f} (真实值: {2*5+1})')
3.5 数据加载器(DataLoader)
在实际项目中,数据通常不会一次性全部加载,而是**分批(batch)**加载,这样可以节省内存并加速训练。
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transforms
from torchvision.datasets import MNIST
# ---------- 使用内置数据集 ----------
# 定义数据预处理
transform = transforms.Compose([
transforms.ToTensor(), # 转为张量,像素值缩放到[0,1]
transforms.Normalize((0.1307,), (0.3081,)) # 标准化(MNIST数据集的均值和标准差)
])
# 下载训练集
train_dataset = MNIST(root='./data', train=True, download=True, transform=transform)
# 创建DataLoader
train_loader = DataLoader(
train_dataset,
batch_size=64, # 每批64张图片
shuffle=True, # 打乱数据
num_workers=2 # 使用2个进程加载数据
)
# 遍历DataLoader
for batch_idx, (data, target) in enumerate(train_loader):
print(f'Batch {batch_idx}: 数据形状 {data.shape}, 标签形状 {target.shape}')
# data.shape: [64, 1, 28, 28] (batch_size, 通道数, 高, 宽)
# target.shape: [64]
if batch_idx == 0:
break
# ---------- 自定义数据集 ----------
class MyImageDataset(Dataset):
"""自定义图像数据集"""
def __init__(self, image_paths, labels, transform=None):
self.image_paths = image_paths
self.labels = labels
self.transform = transform
def __len__(self):
return len(self.image_paths)
def __getitem__(self, idx):
# 加载第idx个样本
img_path = self.image_paths[idx]
label = self.labels[idx]
# 用OpenCV读取图片
import cv2
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 应用预处理
if self.transform:
img = self.transform(img)
return img, label
四、MNIST手写数字分类(全连接网络)
这是深度学习的“Hello World”项目,我们将用全连接网络对MNIST手写数字进行分类。
4.1 加载数据
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
4.2 定义网络
class SimpleMLP(nn.Module):
def __init__(self):
super(SimpleMLP, self).__init__()
self.flatten = nn.Flatten() # 将28x28展平为784
self.fc1 = nn.Linear(28*28, 128) # 第一隐藏层
self.fc2 = nn.Linear(128, 64) # 第二隐藏层
self.fc3 = nn.Linear(64, 10) # 输出层(10个类别)
self.relu = nn.ReLU()
def forward(self, x):
x = self.flatten(x) # [batch, 1, 28, 28] -> [batch, 784]
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x) # 输出10个值,对应10个类别的分数
return x
model = SimpleMLP()
print(model)
4.3 训练配置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
criterion = nn.CrossEntropyLoss() # 交叉熵损失(适用于多分类)
optimizer = optim.Adam(model.parameters(), lr=0.001)
4.4 训练函数
def train(epoch):
model.train()
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 % 100 == 0:
print(f'Train Epoch {epoch}: [{batch_idx*len(data)}/{len(train_loader.dataset)}] Loss: {loss.item():.6f}')
return total_loss / len(train_loader)
4.5 测试函数
def test():
model.eval()
correct = 0
total = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
pred = output.argmax(dim=1) # 取分数最高的类别
correct += (pred == target).sum().item()
total += target.size(0)
accuracy = 100. * correct / total
print(f'Test set: Accuracy: {correct}/{total} ({accuracy:.2f}%)')
return accuracy
4.6 开始训练
print("开始训练...")
for epoch in range(1, 6): # 训练5个epoch
loss = train(epoch)
acc = test()
print(f'Epoch {epoch}: 平均损失 {loss:.4f}, 准确率 {acc:.2f}%\n')
预期结果:5个epoch后,测试准确率应达到95%以上。
五、卷积神经网络(CNN)原理与实战
全连接网络对图像分类效果有限,因为它忽略了图像的空间结构。卷积神经网络通过卷积层和池化层,能够有效提取局部特征。
5.1 核心概念
| 层类型 | 作用 | 参数 | PyTorch实现 |
|---|---|---|---|
| 卷积层 | 用卷积核扫描图像,提取特征 | 输入通道、输出通道、核大小 | nn.Conv2d(in_ch, out_ch, kernel_size) |
| 池化层 | 下采样,降低维度 | 核大小、步长 | nn.MaxPool2d(kernel_size) |
| 全连接层 | 最后分类 | 输入特征数、输出类别数 | nn.Linear(in_features, out_features) |
| 激活函数 | 引入非线性 | - | nn.ReLU() |
5.2 在CIFAR-10上搭建LeNet
CIFAR-10数据集包含10类32x32的彩色图像:飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、卡车。
5.2.1 加载数据
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 归一化到[-1,1]
])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
trainloader = DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2)
testloader = DataLoader(testset, batch_size=100, shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
5.2.2 定义LeNet网络
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
# 卷积层1:输入3通道,输出6通道,卷积核5x5
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool1 = nn.MaxPool2d(2, 2)
# 卷积层2:输入6通道,输出16通道,卷积核5x5
self.conv2 = nn.Conv2d(6, 16, 5)
self.pool2 = nn.MaxPool2d(2, 2)
# 全连接层
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
self.relu = nn.ReLU()
def forward(self, x):
x = self.pool1(self.relu(self.conv1(x))) # [batch, 6, 14, 14]
x = self.pool2(self.relu(self.conv2(x))) # [batch, 16, 5, 5]
x = x.view(-1, 16 * 5 * 5) # 展平 [batch, 400]
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
model = LeNet()
print(model)
5.2.3 训练配置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
5.2.4 训练函数
def train(epoch):
model.train()
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data[0].to(device), data[1].to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 100 == 99: # 每100个batch打印一次
print(f'Epoch {epoch}, Batch {i+1}, Loss: {running_loss/100:.3f}')
running_loss = 0.0
5.2.5 测试函数
def test():
model.eval()
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data[0].to(device), data[1].to(device)
outputs = model(images)
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100. * correct / total
print(f'Test set: Accuracy: {correct}/{total} ({accuracy:.2f}%)')
return accuracy
5.2.6 开始训练
print("开始训练...")
for epoch in range(1, 11): # 训练10个epoch
train(epoch)
test()
print('-' * 50)
print("训练完成!")
预期结果:10个epoch后,测试准确率应达到50%-60%(LeNet对CIFAR-10的baseline)。可以通过加深网络、数据增强等方式提高准确率。
5.3 练习:尝试修改网络结构
# 练习1:增加卷积层数
class DeeperCNN(nn.Module):
def __init__(self):
super(DeeperCNN, self).__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.5)
def forward(self, x):
x = self.pool(torch.relu(self.bn1(self.conv1(x))))
x = self.pool(torch.relu(self.bn2(self.conv2(x))))
x = self.pool(torch.relu(self.bn3(self.conv3(x))))
x = x.view(-1, 128 * 4 * 4)
x = torch.relu(self.fc1(x))
x = self.dropout(x)
x = self.fc2(x)
return x
观察:网络越深,准确率不一定越高,可能出现过拟合(训练集准确率高,测试集低)。需要引入Dropout、Batch Normalization等技巧。
六、总结与下一步
通过本阶段的学习,你已经掌握了:
- ✅ PyTorch张量操作和自动求导
- ✅ 构建全连接网络解决回归和分类问题
- ✅ 使用数据加载器批量处理数据
- ✅ 在MNIST上训练分类器
- ✅ 理解卷积神经网络原理,并在CIFAR-10上实现LeNet
下一步:进入第五阶段——目标检测实战(YOLOv8),你将学会用最先进的目标检测框架训练自己的缺陷检测模型。
📚 参考文档链接
- PyTorch官方教程(中文) —— 60分钟入门、图像分类、目标检测等完整教程
- PyTorch官方文档 —— API参考手册
- PyTorch基础教程 - 廖雪峰 —— 中文入门教程
- CS231n 卷积神经网络课程(中文翻译) —— 斯坦福经典课程
- CIFAR-10 数据集介绍 —— 官方数据集页面
- LeNet论文原文 —— 卷积神经网络开山之作
如果在学习过程中遇到任何问题,欢迎随时交流!下一阶段我们将进入激动人心的目标检测实战。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)