CIFAR10数据集
????????CIFAR10数据集中有5万张训练集,1万张测试集。针对是十分类的问题,其中包括的类别如下图所示:
????????CIFAR10数据集中的图像都是3通道,尺寸为32*32。?
? ? ? ? 在PyTorch中,通过torchvision下的datasets来加以实现的,实例代码如下:
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
#root:数据集的根目录
#train:是否为训练集
#transform:可以将PIL和numpy格式的数据从[0,255]范围转换到[0,1]。原始数据的shape是(H x W x C),转换后shape会变为(C x H x W)
#download:是否下载
train_dataset = datasets.CIFAR10(root="data/CIFAR10",train=True,transform=transforms.ToTensor(),download=True)
test_dataset = datasets.CIFAR10(root="data/CIFAR10",train=False,transform=transforms.ToTensor(),download=True)
#训练集长度
print(len(train_dataset))
#测试集长度
print(len(test_dataset))
#数据集类别
print(train_dataset.classes)
#训练集最后一张图片的类别
print(train_dataset.targets[49999])
#训练集最后一张图片的形状
print(train_dataset.data[49999].shape)
#训练集最后一张图片的数据
print(train_dataset.data[49999])
#测试集最后一张图片的类别
print(test_dataset.targets[9999])
#测试集最后一张图片的形状
print(test_dataset.data[9999].shape)
#测试集最后一张图片的数据
print(test_dataset.data[9999])
#上述的ToTensor在dataloader中调用
train_dataloader = DataLoader(dataset=train_dataset,batch_size=5,shuffle=True)
for i,(img,tag) in enumerate(train_dataloader):
print(tag)
print(img.shape)
print(img)
break
神经网络设计
????????因为使用的数据集为CIFAR10数据集,最终做的还是一个分类问题。所以在神经网络中包含了卷积神经网络和全连接神经网络。使用全连接神经网络对最终的分类概率进行求解。
残差模块
????????在进行网络设计之前先设计出一个残差的模块。注意残差模块的输入通道和输出通道必须要是相等的。
class Res_Net(nn.Module):
def __init__(self,c_in,c_out,c):
super(Res_Net, self).__init__()
self.layer = nn.Sequential(
#输入通道,输出通道,卷积核尺寸,步长,padding,参数b
nn.Conv2d(c_in,c,3,1,padding=1,bias=True),
nn.ReLU(),
nn.Conv2d(c,c_out,3,1,padding=1,bias=True),
nn.ReLU()
)
def forward(self,x):
return self.layer(x) + x
网络设计
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
#卷积
self.conv_layer = nn.Sequential(
#将3通道的图片转化为64通道
nn.Conv2d(3,64,3,1,padding=1),
nn.ReLU(),
#最大池化
nn.MaxPool2d(2,2),
nn.ReLU(),
#残差模块
Res_Net(64,64,64),
nn.Conv2d(64,128,3,1),
nn.ReLU(),
Res_Net(128,128,128),
Res_Net(128,128,128),
nn.Conv2d(128,256,3,1),
nn.ReLU(),
Res_Net(256,256,256),
Res_Net(256,256,256),
Res_Net(256,256,256),
nn.Conv2d(256,512,3,1),
nn.ReLU(),
Res_Net(512,512,512)
)
#全连接
self.linear_layer = nn.Sequential(
#输入为卷积的输出
nn.Linear(512*10*10,1024),
nn.ReLU(),
#抑制全连接神经网络,减小运算量
nn.Dropout(0.5),
nn.Linear(1024,512),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(512,10),
#softmax激活,10个类别的真实概率
nn.Softmax(dim=1)
)
def forward(self,x):
conv_out = self.conv_layer(x)
linear_in = conv_out.reshape(-1,512*10*10)
linear_out = self.linear_layer(linear_in)
return linear_out
测试网络
if __name__ == '__main__':
net = Net()
print(net)
x = torch.randn(3,3,32,32)
result = net.forward(x)
print(result.shape)
print(result)
模型训练
import torch
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
from torch import nn
from torch.utils.tensorboard import SummaryWriter
from Net import Net
#如果有cuda,则用cuda训练,没有则使用cpu训练
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class Train():
def __init__(self):
#训练集
self.train_dataset = datasets.CIFAR10(root="data/CIFAR10",train=True,transform=transforms.ToTensor(),download=True)
#测试集
self.test_dataset = datasets.CIFAR10(root="data/CIFAR10",train=False,transform=transforms.ToTensor(),download=True)
#训练集加载器
self.train_dataloader = DataLoader(dataset=self.train_dataset,batch_size=500,shuffle=True)
#测试集加载器
self.test_dataloader = DataLoader(dataset=self.test_dataset,batch_size=100,shuffle=True)
#创建网络
self.net = Net()
#网络位置
self.net.to(DEVICE)
#优化器
self.opt = torch.optim.Adam(self.net.parameters())
#损失函数:均方差
self.loss_func = nn.MSELoss()
#SummaryWriter类可以在指定文件夹生成一个事件文件,这个事件文件可以对TensorBoard解析。
self.summaryWriter = SummaryWriter("logs")
def __call__(self, *args, **kwargs):
#训练轮次
for epoch in range(1000):
#每一轮次训练总损失
sum_loss = 0
#加载数据
for i,(img,tag) in enumerate(self.train_dataloader):
self.net.train()#训练模式
img,tag = img.to(DEVICE),tag.to(DEVICE)#将数据放在cuda上
out = self.net.forward(img)#计算结果
one_hot_tag = nn.functional.one_hot(tag,10).float()#制作one_hot标签
loss = self.loss_func(out,one_hot_tag)#计算损失
sum_loss = sum_loss + loss
self.opt.zero_grad()#清空梯度
loss.backward()#反向求导
self.opt.step()#更新参数
avg_loss = sum_loss/len(self.train_dataloader)
print("训练轮次:{}".format(epoch))
print("训练损失:{}".format(avg_loss))
#每一轮次测试总损失
sum_test_loss = 0
#每一轮次测试总分数
sum_score = 0
with torch.no_grad():#不进行梯度下降操作,节约空间
#加载数据
for i,(img,tag) in enumerate(self.test_dataloader):
self.net.eval()#测试模式
img,tag = img.to(DEVICE),tag.to(DEVICE)#将数据放在cuda上
test_out = self.net.forward(img)#计算结果
one_hot_tag = nn.functional.one_hot(tag,10).float()#制作one_hot标签
test_loss = self.loss_func(test_out,one_hot_tag)#计算损失
sum_test_loss = sum_test_loss + test_loss
out_label = torch.argmax(test_out,dim=1)
tag_label = torch.argmax(one_hot_tag,dim=1)
score = torch.mean(torch.eq(out_label,tag_label).float())#计算得分
sum_score = sum_score + score
avg_test_loss = sum_test_loss/len(self.test_dataloader)
avg_score = sum_score/len(self.test_dataloader)
print(" ")
print("测试损失:{}".format(avg_test_loss))
print("测试得分:{}".format(avg_score))
print("========================")
#保存训练参数
torch.save(self.net.state_dict(),f"weights/{epoch}.pt")
#训练损失可视化:图名 y值 x值
self.summaryWriter.add_scalars("loss",{"train_loss":avg_loss,"test_loss":avg_test_loss},epoch)
if __name__ == '__main__':
train = Train()
train()
? ? ? ? 这里面提到了SummaryWriter,这里介绍两个作图方法:add_scalar和add_scalars。首先在__init__中创建了SummaryWriter类的对象,通过这个对象来调用上述方法。两个方法的区别就是一个只有一个y值,而另外一个可以有多个y值(以字典的形式存在)
? ? ? ? 在运行项目后,找到项目所在的文件夹输入cmd
?进入命令行模式之后输入tensorboard --logdir=logs,回车后复制下面出现的网址即可。这里的logs就是在创建类对象时传入的参数,也就是文件存储的位置。
|