一、LeNet-5

这个是n多年前就有的一个CNN的经典结构,主要是用于手写字体的识别,也是刚入门需要学习熟悉的一个网络。

原论文地址

输入:32*32的手写字体图片,这些手写字体包含0~9数字,也就是相当于10个类别的图片

输出:分类结果,0~9之间的一个数

因此我们可以知道,这是一个多分类问题,总共有十个类,因此神经网络的最后输出层必然是SoftMax问题,然后神经元的个数是10个。LeNet-5结构:

输入层:32*32的图片,也就是相当于1024个神经元C1层:paper作者,选择6个特征卷积核,然后卷积核大小选择55,这样我们可以得到6个特征图,然后每个特征图的大小为32-5+1=28,也就是神经元的个数为628*28=784。参考公式

S2层:这就是下采样层,也就是使用最大池化进行下采样,池化的size,选择(2,2),也就是相当于对C1层2828的图片,进行分块,每个块的大小为22,这样我们可以得到1414个块,然后我们统计每个块中,最大的值作为下采样的新像素,因此我们可以得到S1结果为:1414大小的图片,共有6个这样的图片。C3层:卷积层,这一层我们选择卷积核的大小依旧为55,据此我们可以得到新的图片大小为14-5+1=10,然后我们希望可以得到16张特征图。那么问题来了?这一层是最难理解的,我们知道S2包含:6张14 * 14大小的图片,我们希望这一层得到的结果是:16张1010的图片。这16张图片的每一张,是通过S2的6张图片进行加权组合得到的,具体是怎么组合的呢?问题如下图所示:

为了解释这个问题,我们先从简单的开始,我现在假设输入6特征图的大小是55的,分别用6个55的卷积核进行卷积,得到6个卷积结果图片大小为11,如下图所示:

为了简便起见,我这里先做一些标号的定义:我们假设输入第i个特征图的各个像素值为x1i,x2i……x25i,因为每个特征图有25个像素。因此第I个特征图经过55的图片卷积后,得到的卷积结果图片的像素值Pi可以表示成:

这个是卷积公式因此对于上面的P1~P6的计算方法,这个就是直接根据公式。然后我们把P1~P6相加起来,也就是:P=P1+P2+……P6把上面的Pi的计算公式,代入上式,那么我们可以得到:

P=WX

其中X就是输入的那6张55特征图片的各个像素点值,而W就是我们需要学习的参数,也就相当于6个55的卷积核,当然它包含着6*(5*5)个参数。因此我们的输出特征图就是:

Out=f(P+b)

这个就是从S2到C3的计算方法,其中b表示偏置项,f为激活函数。

我们回归到原来的问题:有6张输入1414的特征图片,我们希望用55的卷积核,然后最后我们希望得到一张10*10的输出特征图片?

根据上面的过程,也就是其实我们用55的卷积核去卷积每一张输入的特征图,当然每张特征图的卷积核参数是不一样的,也就是不共享,因此我们就相当于需要6(55)个参数。对每一张输入特征图进行卷积后,我们得到6张1010,新图片,这个时候,我们把这6张图片相加在一起,然后加一个偏置项b,然后用激活函数进行映射,就可以得到一张10*10的输出特征图了。

总之,C3层每个图片是通过S2图片进行卷积后,然后相加,并且加上偏置b,最后在进行激活函数映射得到的结果。

S4层:下采样层,比较简单,也是知己对C3的16张1010的图片进行最大池化,池化块的大小为22。因此最后S4层为16张大小为55的图片。至此我们的神经元个数已经减少为:165*5=400。C5层:我们继续用5*5的卷积核进行卷积,然后我们希望得到120个特征图。这样C5层图片的大小为5-5+1=1,也就是相当于1个神经元,120个特征图,因此最后只剩下120个神经元了。这个时候,神经元的个数已经够少的了,后面我们就可以直接利用全连接神经网络,进行这120个神经元的后续处理,上面的结构,只是一种参考,在现实使用中,每一层特征图需要多少个,卷积核大小选择,还有池化的时候采样率要多少,等这些都是变化的,这就是所谓的CNN调参,我们需要学会灵活多变。

代码部分

采用pytorch官网的数据集

#model.pyimport torch.nn as nnimport torch.nn.functional as FclassLeNet(nn.Module):def__init__(self):super(LeNet,self).__init__() self.covn1 = nn.Conv2d(3,16,5) self.pool1 = nn.MaxPool2d(2,2) self.conv2 = nn.Conv2d(16,32,5) self.pool2 = nn.MaxPool2d(2,2) self.fc1 = nn.Linear(32*5*5,120) self.fc2 = nn.Linear(120,84) self.fc3 = nn.Linear(84,10)defforward(self, x): x = F.relu(self.conv1(x))#input(3,32,32) output(16,28,28) x = self.pool1(x) #output(16,14,14) x = F.relu(self.conv2(x)) #output(32,10.10) x = self.pool2(x) #output(32,5,5) x = x.view(-1,32*5*5) #output(5*5*32) x = F.relu(self.fc1(x)) #output(120) x = F.relu(self.fc2(x)) #output(84) x = self.fc3(x) #output(10)return x# #model调试# import torch# #定义shape# input1 = torch.rand([32,3,32,32])# model = LeNet()#实例化# print(model)# #输入网络中# output = model(input1)

首先下载数据集

#train.pyimport torchimport torchvisionimport torch.nn as nn#from model import LeNetimport torch.optim as optimimport torchvision.transforms as transforms#预处理函数transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])# 50000张训练图片train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform))

或者在网盘下载链接:https://pan.baidu.com/s/1NBHp0SxEOJ5EIyYUsDHm_g提取码:qp3k

先小部分测试一下

#train.pyimport torchimport torchvisionimport torch.nn as nnfrom model import LeNetimport torch.optim as optimimport torchvision.transforms as transformsfrom torchvision import transforms, datasets, utilsimport matplotlib.pyplot as pltimport numpy as np#device : GPU or CPUdevice = torch.device("cuda:0"if torch.cuda.is_available() else"cpu")print(device)transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])# 50000张训练图片train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=False, transform=transform)train_loader = torch.utils.data.DataLoader(train_set, batch_size=36, shuffle=False, num_workers=0)# 10000张验证图片val_set = torchvision.datasets.CIFAR10(root='./data', train=False, download=False, transform=transform)val_loader = torch.utils.data.DataLoader(val_set, batch_size=4, shuffle=False, num_workers=0)val_data_iter = iter(val_loader)val_image, val_label = val_data_iter.next()print(val_image.size())print(train_set.class_to_idx)classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')#显示图像,之前需把validate_loader中batch_size改为4aaa = train_set.class_to_idxcla_dict = dict((val, key) for key, val in aaa.items())defimshow(img): img = img / 2 + 0.5# unnormalize npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.show()print(' '.join('%5s' % cla_dict[val_label[j].item()] for j in range(4)))imshow(utils.make_grid(val_image))

正式测试

#train.pyimport torchimport torchvisionimport torch.nn as nnfrom model import LeNetimport torch.optim as optimimport torchvision.transforms as transformsfrom torchvision import transforms, datasets, utilsimport matplotlib.pyplot as pltimport numpy as np#device : GPU or CPUdevice = torch.device("cuda:0"if torch.cuda.is_available() else"cpu")print(device)transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])# 50000张训练图片train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=False, transform=transform)train_loader = torch.utils.data.DataLoader(train_set, batch_size=36, shuffle=False, num_workers=0)# 10000张验证图片val_set = torchvision.datasets.CIFAR10(root='./data', train=False, download=False, transform=transform)val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000, shuffle=False, num_workers=0)val_data_iter = iter(val_loader)val_image, val_label = val_data_iter.next()print(val_image.size())# print(train_set.class_to_idx)# classes = ('plane', 'car', 'bird', 'cat',# 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')### #显示图像,之前需把validate_loader中batch_size改为4# aaa = train_set.class_to_idx# cla_dict = dict((val, key) for key, val in aaa.items())# def imshow(img):# img = img / 2 + 0.5 # unnormalize# npimg = img.numpy()# plt.imshow(np.transpose(npimg, (1, 2, 0)))# plt.show()## print(' '.join('%5s' % cla_dict[val_label[j].item()] for j in range(4)))# imshow(utils.make_grid(val_image))net = LeNet()net.to(device)loss_function = nn.CrossEntropyLoss()#定义优化器optimizer = optim.Adam(net.parameters(), lr=0.001)#训练过程for epoch in range(10): # loop over the dataset multiple times running_loss = 0.0#累加损失for step, data in enumerate(train_loader, start=0):# get the inputs; data is a list of [inputs, labels] inputs, labels = data#print(inputs.size(), labels.size())# zero the parameter gradients optimizer.zero_grad()#如果不清除历史梯度,就会对计算的历史梯度进行累加# forward + backward + optimize outputs = net(inputs.to(device)) loss = loss_function(outputs, labels.to(device)) loss.backward() optimizer.step()# print statistics running_loss += loss.item()if step % 500 == 499: # print every 500 mini-batcheswith torch.no_grad():#上下文管理器 outputs = net(val_image.to(device)) # [batch, 10] predict_y = torch.max(outputs, dim=1)[1] accuracy = (predict_y == val_label.to(device)).sum().item() / val_label.size(0) print('[%d, %5d] train_loss: %.3f test_accuracy: %.3f' % (epoch + 1, step + 1, running_loss / 500, accuracy)) running_loss = 0.0print('Finished Training')save_path = './Lenet.pth'torch.save(net.state_dict(), save_path)

进行预测

#predict.pyimport torchimport torchvision.transforms as transformsfrom PIL import Imagefrom model import LeNetimport matplotlib.pyplot as pltimport numpy as np#预处理transform = transforms.Compose([transforms.Resize((32, 32)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')net = LeNet()net.load_state_dict(torch.load('Lenet.pth'))im = Image.open('飞机.jpg') #测试飞机#im = Image.open('猫.jpg') #测试猫#im = Image.open('轮船.jpg') #测试狗#im = Image.open('狗。jpg’) #测试猫,可能出现错误im1 = transform(im) # [C, H, W]im = torch.unsqueeze(im1, dim=0) # [N, C, H, W]#显示图像defimshow(img): img = img / 2 + 0.5# unnormalize npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.show()imshow(im1)with torch.no_grad(): outputs = net(im) predict = torch.max(outputs, dim=1)[1].data.numpy() predict1 = torch.softmax(outputs, dim=1) print(predict1) print(classes[int(predict)])

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐