本帖最后由 cc1989summer 于 2024-10-4 00:29 编辑
上一篇我们学习了卷积神经网络(CNN)的概念与原理
https://bbs.eeworld.com.cn/thread-1295074-1-1.html
下面是运用卷积神经网络,进行经典的手写数字识别的过程。
输入是一张28*28像素的黑白照片。经过卷积、池化、激活、全链接,最后输出0~10个数字的概率。
但说了那么多,理论和实践总有隔阂,不如跑个例子实践实践,加深认识。
在开始Pytorch进行手写识别前,首先安装Python、Anaconda、与Pytorch,设置好虚拟环境。
详见:
https://bbs.eeworld.com.cn/thread-1294276-1-1.html
接下来安装Pycharm软件:(免费社区版)
https://www.jetbrains.com/pycharm/download/?section=windows
安装完成后需要关联好前面设置的虚拟环境。
Anaconda中设置好的虚拟环境:
该虚拟环境目录下对应的Python程序
Pycharm中关联好前面设置的虚拟环境
设置完成后,进行测试:
import torch
print(torch.__version__)
运行结果正常:
接下来安装几个基本的软件包。
pip install torch torchvision matplotlib
缺什么就在Pycharm命令行输入安装指令:
接下来我们步入正题,进行手写识别。
需要用到MNIST数据集
MNIST数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST)。训练集(training set)由来自250个不同人手写的数字构成,其中50%是高中学生,50%来自人口普查局(the Census Bureau)的工作人员。测试集(test set)也是同样比例的手写数字数据,但保证了测试集和训练集的作者集不相交。
MNIST数据集一共有7万张图片,其中6万张是训练集,1万张是测试集。每张图片是28 × 28 28\times 2828×28的0 − 9 0-90−9的手写数字图片组成。每个图片是黑底白字的形式,黑底用0表示,白字用0-1之间的浮点数表示,越接近1,颜色越白。
其实就是下面几个文件,接下来的例程会自动帮我们下载。
第一步,加载MNIST数据集
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
# 数据预处理:将图像转换为张量,并进行标准化
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
# 下载并加载 MNIST 训练集和测试集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# 加载数据集
train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)
# 查看数据集的大小
print(f"训练集大小: {len(train_dataset)}")
print(f"测试集大小: {len(test_dataset)}")
# 可视化部分样本
examples = enumerate(train_loader)
batch_idx, (example_data, example_targets) = next(examples)
plt.figure(figsize=(10, 3))
for i in range(6):
plt.subplot(1, 6, i + 1)
plt.imshow(example_data[i][0], cmap='gray')
plt.title(f"Label: {example_targets[i]}")
plt.axis('off')
plt.show()
以下是运行结果。
还可以查看更多。
第二步,构建卷积神经网络
import torch.nn as nn
import torch.nn.functional as F
# 定义 CNN 模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
# 卷积层1: 输入通道为1(灰度图),输出通道为16,卷积核大小为3x3
self.conv1 = nn.Conv2d(1, 16, kernel_size=3)
# 卷积层2: 输入通道为16,输出通道为32,卷积核大小为3x3
self.conv2 = nn.Conv2d(16, 32, kernel_size=3)
# 全连接层1: 输入为32*5*5(展平后的特征图),输出为128
self.fc1 = nn.Linear(32 * 5 * 5, 128)
# 全连接层2: 输入为128,输出为10(10个类别)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
# 卷积层 + ReLU + 最大池化层
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2(x), 2))
# 展平成一维向量
x = x.view(-1, 32 * 5 * 5)
# 全连接层 + ReLU
x = F.relu(self.fc1(x))
# 输出层
x = self.fc2(x)
return x
# 实例化模型
model = CNN()
print(model)
仔细研究代码,正是我们前面讲到的卷积层、池化、激活、全链接。
运行结果如下:
第三步,训练模型
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# 数据预处理:将图像转换为张量,并进行标准化
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
# 下载并加载 MNIST 训练集和测试集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# 加载数据集
train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)
# 定义 CNN 模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
# 卷积层1: 输入通道为1(灰度图),输出通道为16,卷积核大小为3x3
self.conv1 = nn.Conv2d(1, 16, kernel_size=3)
# 卷积层2: 输入通道为16,输出通道为32,卷积核大小为3x3
self.conv2 = nn.Conv2d(16, 32, kernel_size=3)
# 全连接层1: 输入为32*5*5(展平后的特征图),输出为128
self.fc1 = nn.Linear(32 * 5 * 5, 128)
# 全连接层2: 输入为128,输出为10(10个类别)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
# 卷积层 + ReLU + 最大池化层
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2(x), 2))
# 展平成一维向量
x = x.view(-1, 32 * 5 * 5)
# 全连接层 + ReLU
x = F.relu(self.fc1(x))
# 输出层
x = self.fc2(x)
return x
# 实例化模型
model = CNN()
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 将模型移动到 GPU(如果可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# 训练模型
epochs = 5
for epoch in range(epochs):
running_loss = 0.0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f"Epoch [{epoch + 1}/{epochs}], Loss: {running_loss / len(train_loader):.4f}")
print("训练完成!")
经过5次训练,损失函数越来越低,说明正确率提高了
第四步,测试模型
测试环节,我们可以自己画一个图去进行识别,也可以从训练集里抽一个图去测试。
# 从测试集中取出一个样本
example_data, example_target = next(iter(test_loader))
example_data = example_data.to(device)
# 使用模型进行预测
model.eval()
with torch.no_grad():
output = model(example_data)
# 可视化预测结果
plt.figure(figsize=(10, 3))
for i in range(6):
plt.subplot(1, 6, i + 1)
plt.imshow(example_data[i][0].cpu(), cmap='gray')
plt.title(f"预测: {torch.argmax(output[i]).item()}")
plt.axis('off')
plt.show()
运行结果如以下,百发百中有木有?
准确率98.93%
而如果我们把训练模型这段程序拿掉,正确率一下子就很低了,基本全错。
# 训练模型
epochs = 5
for epoch in range(epochs):
running_loss = 0.0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f"Epoch [{epoch + 1}/{epochs}], Loss: {running_loss / len(train_loader):.4f}")
print("训练完成!")
本次的分享就到这里。