卷积神经网络(Convolutional Neural Network,CNN) | 是一种专门为处理具有网格结构数据的深度学习模型,例如图像、声音和文本。 |
卷积核 | 特征提取:识别和提取输入数据中的局部特征。通过在不同位置应用卷积核,网络能够捕捉到图像中的边缘、纹理、形状等特征。 降维:卷积核在应用过程中,会减少特征图(feature map)的尺寸。 参数共享:在卷积层中,同一卷积核会在整个输入数据上滑动,这意味着卷积核中的权重参数是共享的。 局部响应归一化(LRN):有助于提高特征图中的对比度,使得模型能够更好地识别图像中的关键特征。 平移不变性:卷积核的滑动窗口特性使得网络对输入数据的平移具有不变性,即网络不会因为输入数据的位置变化而改变其识别特征的能力。 |
卷积层 | 使用卷积核对输入数据进行滑动窗口处理,提取局部特征。 |
池化层 | 池化层位于卷积层之后,用于减小特征图的尺寸,减少参数数量,并减少过拟合的风险。 |
全连接层 | 其中每个神经元都与前一层中的所有神经元相连。全连接层通常位于网络的输出层,用于将前一层的特征映射到具体的输出类别或预测。在全连接层之后,通常会使用一个或多个激活函数来产生概率分布或类别预测。 |
CNN一般实现步骤:
- 数据加载与预处理。
- 模型搭建。
- 定义损失函数、优化器。
- 模型训练。
- 模型测试。
基于Pytorch框架实现CNN神经网络手写数字识别。
流程步骤:
- 导入相关库。
- 使用MNIST数据集,进行数据预处理。
- CNN模型搭建。
- 参数设置。
- 模型训练。
步骤1代码:
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
步骤2代码:
# 定义超参数
batch_size = 128 # 设置每个批次(batch)的样本数为128
# 对输入的数据进行标准化处理
# transforms.ToTensor() 将图像数据转换为 PyTorch 中的张量(tensor)格式,并将像素值缩放到 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)
batch_size = 128
: 设置每次迭代中从数据集中取样的样本数量。
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5], std=[0.5])])
: 定义一个转换器,它包含两个子转换器:transforms.ToTensor()
用于将图像数据转换为PyTorch的张量格式,并归一化像素值到0-1范围内;transforms.Normalize(mean=[0.5], std=[0.5])
用于进一步标准化像素值,使其均值为0.5,标准差为0.5。
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
: 创建一个MNIST数据集实例,用于训练。root='./data'
指定数据集下载和存储的路径;train=True
指定加载训练数据;transform=transform
指定之前定义的转换器;download=True
表示如果数据集不在指定路径下,则自动下载。
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, transform=transform, download=True)
: 创建一个MNIST数据集实例,用于测试。train=False
指定加载测试数据。
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True, num_workers=2)
: 创建一个数据加载器,用于训练模型。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)
: 创建一个数据加载器,用于测试模型。shuffle=False
指定在装载过程中不进行随机乱序。其他参数与训练数据加载器相同。
步骤3代码:
class CNN(nn.Module):
# 定义网络结构
def __init__(self):
super(CNN, self).__init__() # 调用父类的初始化方法
# 定义第一个卷积层
self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, # 输入通道数为1,输出通道数为16
kernel_size=5, stride=1, padding=2) # 卷积核大小为5x5,步长为1,填充为2
# 定义第一个ReLU激活函数
self.relu1 = nn.ReLU()
# 定义第一个池化层
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2) # 池化核大小为2x2,步长为2
# 定义第二个卷积层
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, # 输入通道数为16,输出通道数为32
kernel_size=5, stride=1, padding=2) # 卷积核大小为5x5,步长为1,填充为2
# 定义第二个ReLU激活函数
self.relu2 = nn.ReLU()
# 定义第二个池化层
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2) # 池化核大小为2x2,步长为2
# 定义第一个全连接层
self.fc1 = nn.Linear(in_features=7*7*32, out_features=256) # 输入特征数为7x7x32,输出特征数为256
# 定义第一个全连接层的ReLU激活函数
self.relu3 = nn.ReLU()
# 定义第二个全连接层
self.fc2 = nn.Linear(in_features=256, out_features=10) # 输入特征数为256,输出特征数为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
步骤4代码:
import torch.optim as optim
learning_rate = 0.001 # 学习率
# 定义损失函数,计算模型的输出与目标标签之间的交叉熵损失
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)
步骤5代码:
# 实例化CNN模型
model = CNN()
num_epochs = 10 # 定义迭代次数为10
# 如果可用的话使用 GPU 进行训练,否则使用 CPU 进行训练。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 将神经网络模型 model 移动到指定的设备上。
model = model.to(device)
total_step = len(train_loader)
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
# 将输入数据 images 和标签数据 labels 移动到指定的设备上。
images = images.to(device)
labels = labels.to(device)
optimizer.zero_grad() # 清空上一个batch的梯度信息
# 将输入数据 images 喂入神经网络模型 model 中进行前向计算,得到模型的输出结果 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')