对于一张输入的图片,该图片是栅格图像,也就是说图片分成一格一格,每一格代表一个像素,对于patch(图片块),我们按照块的大小,从上到下、从左到右对图片进行遍历,然后对每个图像块进行卷积运算。
如何卷积:
(1)对于单通道的进行卷积时,首先按照卷积核的规模比如说3x3,那么现在输入图像中画出3x3的窗口,然后该窗口和这个卷积核进行数乘运算,即对应位置元素相乘再相加,然后移动窗口不断遍历,最终得到输出,该输出的规格为:(输入规格-(卷积核规格-1))
(2)对于多通道图片进行卷积时,每一个通道都要对应1个卷积核,那么也就是说,输入图片有几个通道那么卷积核就应该有几个通道;对于一个多通道的卷积核和一个多通道的图片最终结果只能输出一个单通道的结果,那么要使得最终输出的结果为多通道,就要是卷积核个数等于输出的通道数。
Padding
使用Padding是希望最终经过卷积,输出的图像大小不变,那么可以通过Padding在图像外面添加n圈,默认方式是像素补0。一般来说,卷积核规模比如说是nxn的,那么图片就补充n/2圈(整除)。
下采样:
在卷积神经网络当中需要结合Convoluation和下采样,用的比较多的下采样是Max Pooling(最大池化层),使用下采样可以减小数据规模,经过max pooling后,图片的通道数不变,但是图像大小进行变化。
本次实现卷积神经网络的网络结构:
本次实现卷积神经网络的数据集采用的是Pytorch中自带的MNIST图像数据集,经过转化后,该图像呈1x28x28。
首先我们使用一层卷积层(卷积核:5x5),将输出的图像通道转化成10通道,规模为10x24x24;
然后再经过一层2x2的池化层,图像变为10x12x12;
再经过一层卷积层(卷积核:5x5),使得通道数变为20,此时图像为20x8x8;
经过一层2x2池化层,变为20x4x4;
最后再通过全连接层将最终的结果转为1维向量。
代码实现:
# -*- coding: utf-8 -*-
# @Time : 2022/1/28 14:24
# @Author : CH339
# @FileName: Test1_28_1.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/weixin_56068397/article/
'''
卷积神经网络
'''
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
batch_size = 64
# 将图片转换成pytorch当中的张量
transform = transforms.Compose([
# 转变成张量
transforms.ToTensor(),
# 进行标准化,转换成0-1分布
transforms.Normalize((0.1307,),(0.3081,))
])
# 构造训练集
train_dataset = datasets.MNIST(root='../dataset/mnist/',train=True,download=True,transform=transform)
train_loader = DataLoader(train_dataset,shuffle=True,batch_size=batch_size)
# 构造测试机
test_dataset = datasets.MNIST(root='../dataset/mnist/',train=False,download=True,transform=transform)
test_loader = DataLoader(test_dataset,shuffle=False,batch_size=batch_size)
# 编写模型类
# 首先需要将c*w*H转换成1阶向量
class ConvNet(torch.nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
# 卷积层
self.conv1 = torch.nn.Conv2d(1,10,kernel_size=5)
self.conv2 = torch.nn.Conv2d(10,20,kernel_size=5)
# 最大池化层
self.pool = torch.nn.MaxPool2d(2)
# 全连接层
self.linear = torch.nn.Linear(320,10)
def forward(self,x):
# 获取batch
batch = x.size(0)
x = F.relu(self.pool(self.conv1(x)))
x = F.relu(self.pool(self.conv2(x)))
# 转换成向量
x = x.view(batch,-1)
# 经过全连接层
x = self.linear(x)
return x
# 创建模型对象
model = ConvNet()
# 假如电脑有GPU环境,使用显卡来算
# 将模型和所有参数放入到CUDA当中
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)
# 创建损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(),lr=0.01,momentum=0.5)
# 定义训练函数
def train(epoch):
sum_loss = 0
for batch_index,data in enumerate(train_loader,0):
# 特征值和目标值
inputs,target = data
# 训练时将数据也放入到CUDA当中
inputs,target = inputs.to(device),target.to(device)
# 梯度清零
optimizer.zero_grad()
outputs = model(inputs)
# 计算损失
loss = criterion(outputs,target)
loss.backward()
# 权重更新
optimizer.step()
# 将损失累加
sum_loss += loss.item()
if batch_index%100 == 99:
# 每100次进行输出
print('[%d,%5d]loss:%.3f'%(epoch+1,batch_index+1,sum_loss/100))
# 重新将损失设为0
sum_loss = 0
# 定义测试函数
def test():
# 准确分类的数量
sum_correct = 0
total = 0
# 在测试的时候只需要计算即可,不需要进行反向传播
with torch.no_grad():
for data in test_loader:
images,label = data
# 将数据放入到CUDA当中
images,label = images.to(device),label.to(device)
# 获得估计结果
output = model(images)
_,predict = torch.max(output.data,dim=1)
total += label.size(0)
sum_correct += (predict==label).sum().item()
print('准确率:%d%%'%(100*sum_correct/total))
if __name__ == "__main__":
for epoch in range(10):
train(epoch)
test()
如果设备中有GPU,并且想要使用显卡加速训练过程,我们只需在代码当中设置是否使用GPU,此时必须将模型和数据所有模块当放入到CUDA当中。