CNN原理:
卷积神经网络(Convolutional Neural Network,CNN)是一种深度学习模型,主要用于处理和分析具有网格结构数据的任务,尤其是图像数据。CNN在图像处理领域取得了巨大成功,广泛应用于图像分类、目标检测、图像分割等任务。
CNN的主要特点:
- 卷积层(Convolutional Layer):
卷积层是CNN的核心组成部分,它通过在输入数据上滑动的一系列滤波器(也称为卷积核)来提取特征。每个滤波器都可以检测输入数据中的某种特定模式或特征,如边缘、纹理等。 - 池化层(Pooling Layer):
池化层用于降低特征图的空间维度,减少计算量并提高模型的鲁棒性。常用的池化操作包括最大池化(Max Pooling)和平均池化(Average Pooling)。
非线性激活函数:CNN中通常在卷积层之后使用非线性激活函数,如ReLU(Rectified Linear Unit),以引入非线性特性并增强模型的表达能力。 - 全连接层(Fully Connected Layer):
在CNN的末端通常包含一个或多个全连接层,用于将卷积层提取的特征映射转换为最终的预测结果。全连接层可以捕捉不同特征之间的复杂关系,并输出最终的分类或回归结果。
CNN的优势:
- 参数共享:在卷积层中,每个滤波器的参数都是共享的,这样可以大大减少模型的参数数量,降低过拟合的风险。
- 局部感受野:卷积操作只考虑输入数据的局部区域,从而使模型对平移、旋转和缩放等变换具有一定的不变性。
- 层级结构:CNN通常采用多层次的结构,逐渐提取更加抽象和高级的特征,使模型能够有效地理解和处理复杂的数据。
实现过程:
导入第三方库
import numpy as np
import torch
from torch import nn
from torchvision import datasets, transforms,utils
from PIL import Image
import matplotlib.pyplot as plt
import torchvision
定义超参数
# 定义超参数
batch_size = 128 # 每个批次(batch)的样本数
# 对输入的数据进行标准化处理
# transforms.ToTensor() 将图像数据转换为 PyTorch 中的张量(tensor)格式,并将像素值缩放到 0-1 的范围内。
# 这是因为神经网络需要的输入数据必须是张量格式,并且需要进行归一化处理,以提高模型的训练效果。
# transforms.Normalize(mean=[0.5],std=[0.5]) 将图像像素值进行标准化处理,使其均值为 0,标准差为 1。
# 输入数据进行标准化处理可以提高模型的鲁棒性和稳定性,减少模型训练过程中的梯度爆炸和消失问题。
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean=[0.5],std=[0.5])])
# 加载MNIST数据集
train_dataset = torchvision.datasets.MNIST(root='./data',
train=True,
transform=transform,
download=True)
test_dataset = torchvision.datasets.MNIST(root='./data',
train=False,
transform=transform,
download=True)
# 创建数据加载器(用于将数据分次放进模型进行训练)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True, # 装载过程中随机乱序
num_workers=2) # 表示2个子进程加载数据
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=2)
显示训练样本图片
for i in range(0,5):
oneimg,label = train_dataset[i]
grid = utils.make_grid(oneimg)
grid = grid.numpy().transpose(1,2,0)
std = [0.5]
mean = [0.5]
grid = grid * std + mean
# 可视化图像
plt.subplot(1, 5, i+1)
plt.imshow(grid)
plt.axis('off')
plt.show()
定义CNN结构
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, utils
class CNN(nn.Module):
# 定义网络结构
def __init__(self):
super(CNN, self).__init__()
# 图片是灰度图片,只有一个通道
self.conv1 = nn.Conv2d(in_channels=1, out_channels=16,
kernel_size=5, stride=1, padding=2)
self.relu1 = nn.ReLU()
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32,
kernel_size=5, stride=1, padding=2)
self.relu2 = nn.ReLU()
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.fc1 = nn.Linear(in_features=7*7*32, out_features=128)
self.relu3 = nn.ReLU()
self.fc2 = nn.Linear(in_features=128, out_features=10)
# 定义前向传播过程的计算函数
def forward(self, x):
# 第一层卷积、激活函数和池化
x = self.conv1(x)
x = self.relu1(x)
x = self.pool1(x)
# 第二层卷积、激活函数和池化
x = self.conv2(x)
x = self.relu2(x)
x = self.pool2(x)
# 将数据平展成一维
x = x.view(-1, 7*7*32)
# 第一层全连接层
x = self.fc1(x)
x = self.relu3(x)
# 第二层全连接层
x = self.fc2(x)
return x
# 实例化CNN模型
model = CNN()
learning_rate = 0.001 # 学习率
num_epochs = 10 # 定义迭代次数
# 定义损失函数,计算模型的输出与目标标签之间的交叉熵损失
criterion = nn.CrossEntropyLoss()
# 训练过程通常采用反向传播来更新模型参数,这里使用的是SDG(随机梯度下降)优化器
# momentum 表示动量因子,可以加速优化过程并提高模型的泛化性能。
#optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.9)
#也可以选择Adam优化方法
#optimizer = torch.optim.Adam(net.parameters(),lr=1e-2)
optimizer = optim.Adam(model.parameters(), lr=1e-3)
# 如果可用的话使用 GPU 进行训练,否则使用 CPU 进行训练。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 将神经网络模型 net 移动到指定的设备上。
model = model.to(device)
total_step = len(train_loader)
for epoch in range(num_epochs):
for i, (images,labels) in enumerate(train_loader):
images=images.to(device)
labels=labels.to(device)
optimizer.zero_grad() # 清空上一个batch的梯度信息
# 将输入数据 inputs 喂入神经网络模型 net 中进行前向计算,得到模型的输出结果 outputs。
outputs=model(images)
# 使用交叉熵损失函数 criterion 计算模型输出 outputs 与标签数据 labels 之间的损失值 loss。
loss=criterion(outputs,labels)
# 使用反向传播算法计算模型参数的梯度信息,并使用优化器 optimizer 对模型参数进行更新。
loss.backward()
# 更新梯度
optimizer.step()
# 输出训练结果
if (i+1) % 100 == 0:
print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, i+1, total_step, loss.item()))
print('Finished Training')
保存训练的模型
# 模型保存
PATH = './mnist_net.pth'
torch.save(model.state_dict(), PATH)
测试训练模型的结果
# 测试CNN模型
with torch.no_grad(): # 进行评测的时候网络不更新梯度
correct = 0
total = 0
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total))
训练结果
# 将 test_loader 转换为一个可迭代对象 dataiter
dataiter = iter(test_loader)
# 使用 next(dataiter) 获取 test_loader 中的下一个 batch 的图像数据和标签数据
images, labels = next(dataiter)
# print images
test_img = utils.make_grid(images)
test_img = test_img.numpy().transpose(1,2,0)
std = [0.5]
mean = [0.5]
test_img = test_img*std+0.5
plt.imshow(test_img)
plt.show()
plt.savefig('./mnist_net.png')
print('GroundTruth: ', ' '.join('%d' % labels[j] for j in range(128)))