PytorchCIFAR-10分类(LeNet5)
⽂章⽬录
CSDN只作为查看⽹络结构,具体代码和结果展⽰请移步GitHub
1.数据读取
CIFAR-10 是由 Hinton 的学⽣ Alex Krizhevsky 和 Ilya Sutskever 整理的⼀个⽤于识别普适物体的⼩型数据集。⼀共包含 10 个类别的RGB 彩⾊图 ⽚:飞机( arplane )、汽车( automobile )、鸟类( bird )、猫( cat )、⿅( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。图⽚的尺⼨为 32×32 ,数据集中⼀共有 50000 张训练圄⽚和 10000 张测试图⽚。
与 MNIST 数据集中⽬⽐, CIFAR-10 具有以下不同点:
· CIFAR-10 是 3 通道的彩⾊ RGB 图像,⽽ MNIST 是灰度图像。
· CIFAR-10 的图⽚尺⼨为 32×32, ⽽ MNIST 的图⽚尺⼨为 28×28,⽐ MNIST 稍⼤。
· 相⽐于⼿写字符, CIFAR-10 含有的是现实世界中真实的物体,不仅噪声很⼤,⽽且物体的⽐例、 特征都不尽相同,这为识别带来很⼤困难。
⾸先使⽤torchvision加载和归⼀化我们的训练数据和测试数据。
1. torchvision这个东西,实现了常⽤的⼀些深度学习的相关的图像数据的加载功能,⽐如cifar10、Imagenet、Mnist等等的,保存
在torchvision.datasets模块中。
2. 同时,也封装了⼀些处理数据的⽅法。保存在ansforms模块中
3. 还封装了⼀些模型和⼯具封装在相应模型中,⽐如dels当中就包含了AlexNet,VGG,ResNet,SqueezeNet等模型。由于torchvision的datasets的输出是[0,1]的PILImage,所以我们先先归⼀化为[-1,1]的Tensor
⾸先定义了⼀个变换transform,利⽤的是上⾯提到的transforms模块中的Compose( )把多个变换组合在⼀起,可以看到这⾥⾯组合了ToTensor和Normalize这两个变换
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))前⾯的(0.5,0.5,0.5) 是 R G B 三个通道上的均值, 后⾯(0.5, 0.5, 0.5)是三个通道的标准差,注意通道顺序是 R G B ,⽤过opencv的同学应该知道openCV读出来的图像是 BRG顺序。这两个tuple数据是⽤来对RGB 图像做归⼀化的,如其名称 Normalize 所⽰这⾥都取0.5只是⼀个近似的操作,实际上其均值和⽅差并不是这么多,但是就这个⽰
例⽽⾔ 影响可不计。精确值是通过分别计算R,G,B三个通道的数据算出来的。
import torch
import torchvision
import torchvision.datasets as datasets
ansforms as transforms
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
# datasets.CIFAR10( )也是封装好了的,就在我前⾯提到的torchvision.datasets块中
trainset = datasets.CIFAR10(root='D:/CIFAR-10', train=True,download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,shuffle=True, num_workers=2)
# 对于测试集的操作和训练集⼀样,我就不赘述了
testset = torchvision.datasets.CIFAR10(root='D:/CIFAR-10', train=False,download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=128,shuffle=False, num_workers=2)
# 类别信息也是需要我们给定的
classes =('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')
2.定义⽹络(LeNet5)
⼿写字体识别模型LeNet5诞⽣于1994年,是最早的卷积神经⽹络之⼀。LeNet5通过巧妙的设计,利⽤卷积、参数共享、池化等操作提取特征,避免了⼤量的计算成本,最后再使⽤全连接神经⽹络进⾏分类识别,这个⽹络也是最近⼤量神经⽹络架构的起点。
[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-OBSpCaRR-1587021133464)
(attachment:image.png)]
LeNet-5 ⼀些性质:
1. 如果输⼊层不算神经⽹络的层数,那么 LeNet-5 是⼀个 7 层的⽹络。(有些地⽅也可能把 卷积和池化 当作⼀个 layer)(LeNet-5
名字中的“5”也可以理解为整个⽹络中含可训练参数的层数为 5。)
transform和convert的区别2. LeNet-5 ⼤约有 60,000 个参数。
3. 随着⽹络越来越深,图像的⾼度和宽度在缩⼩,与此同时,图像的 channel 数量⼀直在增加。
4. 现在常⽤的 LeNet-5 结构和 Yann LeCun 教授在 1998 年论⽂中提出的结构在某些地⽅有区别,⽐如激活函数的使⽤,现在⼀般使
⽤ ReLU 作为激活函数,输出层⼀般选择 softmax。
import torch
as nn
#若能使⽤cuda,则使⽤cuda
device = torch.device('cuda'if torch.cuda.is_available()else'cpu')
#定义⽹络
class LeNet5(nn.Module):# nn.Module是所有神经⽹络的基类,我们⾃⼰定义任何神经⽹络,都要继承nn.Module
def__init__(self):
super(LeNet5,self).__init__()
# 卷积层1,3通道输⼊,6个卷积核,核⼤⼩5*5
# 经过该层图像⼤⼩变为32-5+1,28*28
nn.Conv2d(in_channels=3,out_channels=6,kernel_size=5,stride=1, padding=0),
#激活函数
nn.ReLU(),
# 经2*2最⼤池化,图像变为14*14
nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
)
# 卷积层2,6输⼊通道,16个卷积核,核⼤⼩5*5
# 经过该层图像变为14-5+1,10*10
nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5,stride=1, padding=0),
nn.ReLU(),
# 经2*2最⼤池化,图像变为5*5
nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
)
self.fc = nn.Sequential(
# 接着三个全连接层
nn.Linear(16*5*5,120),
nn.ReLU(),
nn.Linear(120,84),
nn.ReLU(),
nn.Linear(84,10),
)
# 定义前向传播过程,输⼊为
def forward(self,x):
x = v1(x)
x = v2(x)
# nn.Linear()的输⼊输出都是维度为⼀的值,所以要把多维度的tensor展平成⼀维
x = x.view(x.size()[0],-1)
x = self.fc(x)
return x
net = LeNet5().cuda()
print("LeNet5 out: ", net)
3. 定义损失函数和优化器
pytorch将深度学习中常⽤的优化⽅法全部封装在torch.optim之中,所有的优化⽅法都是继承基类optim.Optimizier 损失函数是封装在神经⽹络⼯具箱nn中的,包含很多损失函数
import torch.optim as optim
#⽤到了神经⽹络⼯具箱 nn 中的交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 使⽤SGD(随机梯度下降)优化,学习率为0.001,动量为0.9
optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)
4.训练
from torch.autograd import Variable
plotloss =[]
plotauc =[]
for epoch in range(50):# 指定训练⼀共要循环⼏个epoch
sum_loss =0.0
correct =0.0
total =0.0
# 这⾥我们遇到了第⼀步中出现的trailoader,代码传⼊数据,enumerate是python的内置函数,既获得索引也获得数据for i,(images,labels)in enumerate(trainloader):
# data是从enumerate返回的data,包含数据和标签信息,分别赋值给inputs和labels
# data的结构是:[4x3x32x32的张量,长度4的张量],4是batch_size的数值
# 把input数据从tensor转为variable,variable才拥有梯度grad,输⼊模型训练都要转成Variable
if torch.cuda.is_available():
images=Variable(images).cuda()
labels=Variable(labels).cuda()
else:
images=Variable(images)
labels=Variable(labels)
# 将参数的grad值初始化为
<_grad()
# forward + backward + optimize
outputs = net(images)
# 将output和labels使⽤叉熵计算损失
loss = criterion(outputs, labels)
# 反向传播
loss.backward()
# ⽤SGD更新参数
optimizer.step()
# loss.item()转换为numpy
# loss本⾝为Variable类型,所以要使⽤loss.data[0]获取其Tensor,因为其为标量,所以取0
sum_loss += loss.item()
_, predicted = torch.max(outputs.data,1)
total += labels.size(0)# 更新测试图⽚的数量
correct +=(predicted == labels).sum()# 更新正确分类的图⽚的数量
# if i % 200 == 199:
print('[epoch:%d, iter:%d] Loss: %.03f | Acc: %.3f%% '
%(epoch +1,(i +1+ epoch *len(trainloader)), sum_loss /(i +1),100.* correct / total))
plotloss.append(sum_loss /(i +1))
plotauc.append(100.* correct / total)
print('Finished Training')
plt.subplot(2,1,1)
plt.plot(plotloss)
plt.subplot(2,1,2)
plt.plot(plotauc)
5.测试
# 定义2个存储每类中测试正确的个数的列表,初始化为0
class_correct =list(0.for i in range(10))
class_total =list(0.for i in range(10))
for data in testloader:
images, labels = data
images=Variable(images).cuda()
labels=Variable(labels).cuda()
outputs = net(images)
_, predicted = torch.max(outputs.data,1)
#4组(batch_size)数据中,输出于label相同的,标记为1,否则为0
c =(predicte
d == labels).squeeze()
for i in range(16):
label = labels[i]# 对各个类的进⾏各⾃累加
class_correct[label]+= c[i]
class_total[label]+=1
for i in range(10):
print('Accuracy of %5s : %2d %%'%(classes[i],100* class_correct[i]/ class_total[i]))
6.保存模型
pytorch保存模型的⽅式有两种:
(1)将整个⽹络都都保存下来
torch.save(model_object,'model.pkl')
model = torch.load('model.pkl')
这种⽅式再重新加载的时候不需要⾃定义⽹络结构,保存时已经把⽹络结构保存了下来,但是,再读取模型的时候还需要将以前的⽹络结构复制进来且⽐较死板(pycharm中不⽤),另外不能调整⽹络结构。
(2)仅保存和加载模型参数(推荐使⽤这样的⽅法)
GPU上保存,CPU上加载:
torch.save(model.state_dict(), PATH)
device = torch.device('cpu')
model = TheModelClass(*args,**kwargs)
model.load_state_dict(torch.load(PATH, map_location=device))
GPU上保存,GPU上加载:
torch.save(model.state_dict(), PATH)
device = torch.device("cuda")
model = TheModelClass(*args,**kwargs)
model.load_state_dict(torch.load(PATH))
<(device)
往模型中输⼊数据的时候不要忘记在任意tensor上调⽤input = (device)
这种⽅式再重新加载的时候需要⾃⼰定义⽹络,并且其中的参数名称与结构要与保存的模型中的⼀致(可以是部分⽹络,⽐如只使⽤VGG的前⼏层),相对灵活,便于对⽹络进⾏修改。
torch.save(net,'D:/CIFAR-10/model/LeNet5-128.pth')
7.预测
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论