本篇文章只是一篇CIFAR-10数据集分类简介,是我的一门课程的大作业,从BP网络进行分类到CNN进行分类。
程序贴在最后!!!
目前主流的神经网络框架有Tensorflow,Pytorch,MXNET,Keras等。本次实验使用Pytroch深度学习框架。PyTorch看作加入了GPU支持的numpy,并且它是一个拥有自动求导功能的强大的深度神经网络。
上图是sigmoid激活函数。
计算量大,反向传播求误差梯度时,求导涉及除法。
上图是ReLU激活函数。
ReLU 的收敛速度会比 sigmoid 快很多。
如图,对三层BP网络的隐藏层分别设置为8,2048和10240个神经元。
对于隐藏层设置为8个神经元,神经元个数太少会导致网络表达能力太弱,无法对特征进行正确的表达。
对于隐藏层设置为10240个神经元,神经元个数太多不一定就能够对特征有正确的表达,而且神经元个数过大容易导致模型泛化能力较差。
分别选择512,2048,4096个神经元,可以看到512个神经元相对于2048和4096个神经元稍微差一点,而2048和4096个神经元效果相似。
如图所示,粉色和紫色分别表示4层BP网络和3层BP网络,他们使用Sigmoid激活函数。
层数的增加反而训练收敛速度变慢,看测试准确率几乎相似,,说明层数增加并没有对特征有个更好的非线性表示,这有可能是我参数没调好,也有可能是Sigmoid激活函数两端梯度为0,梯度不均匀,训练不稳定。
在上述实验基础上,将Sigmoid函数改为ReLU函数,可以看到,层数增加,模型的收敛速度更快,精度更高,能更好的表示非线性。
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(3, 8, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(8, 16, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.conv3 = nn.Sequential(
nn.Conv2d(16, 32, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.conv4 = nn.Sequential(
nn.Conv2d(32, 64, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.fc1 = nn.Sequential(
nn.Linear(64 * 2 * 2, 120),
nn.ReLU()
)
self.fc2 = nn.Sequential(
nn.Linear(120, 84),
nn.ReLU()
)
self.fc3 = nn.Linear(84, 10)
import torch
from torch.utils import data
import torch.nn as nn
from tqdm import tqdm
import wandb
import torchvision.transforms as transforms
import torchvision
class BP_model(nn.Module):
def __init__(self):
super(BP_model, self).__init__()
self.fc1 = nn.Linear(32 * 32 * 3, 2048) # 线性层
self.fc2 = nn.Linear(2048, 2048)
self.fc3 = nn.Linear(2048, 10)
def forward(self, x):
out = x.view(-1, 32 * 32 * 3) # 展平处理
out = nn.ReLU()(self.fc1(out))
out = nn.ReLU()(self.fc2(out))
out = self.fc3(out)
return out
class LeNet5(nn.Module):
def __init__(self):
super(LeNet5, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(3, 6, 5, 1, 2), # 卷积核 原来通道数 转换后通道数 卷积核大小 步长 填充
nn.ReLU(), # 激活函数
nn.MaxPool2d(2) # 最大池化层
)
self.conv2 = nn.Sequential(
nn.Conv2d(6, 16, 5, 1, 0),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.fc1 = nn.Sequential(
nn.Linear(16 * 6 * 6, 120),
nn.ReLU()
)
self.fc2 = nn.Sequential(
nn.Linear(120, 84),
nn.ReLU()
)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(-1, 16 * 6 * 6)
x = self.fc1(x)
x = self.fc2(x)
x = self.fc3(x)
return x
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(3, 8, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(8, 16, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.conv3 = nn.Sequential(
nn.Conv2d(16, 32, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.conv4 = nn.Sequential(
nn.Conv2d(32, 64, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
self.fc1 = nn.Sequential(
nn.Linear(64 * 2 * 2, 120),
nn.ReLU()
)
self.fc2 = nn.Sequential(
nn.Linear(120, 84),
nn.ReLU()
)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = self.conv4(x)
x = x.view(-1, 64 * 2 * 2)
x = self.fc1(x)
x = self.fc2(x)
x = self.fc3(x)
return x
if __name__ == '__main__':
wandb.init(project="BP")
wandb.config.dropout = 0.2
wandb.config.hidden_layer_size = 128
# 读取数据,数据预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]) # totensor是将0-255转到0-1,normalize是将0-1转换到-1-1
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=False, transform=transform) # 读取数据集,数据预处理
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,
shuffle=True, num_workers=1, pin_memory=True) # 打乱,包装成batchsize
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=128,
shuffle=False, num_workers=1, pin_memory=True)
device = torch.device("cuda:0") # 选择cpu或者GPU
# model = BP_model()
# model = LeNet5() # 用哪个模型,打开哪个,屏蔽另外两个
model = CNN()
model.to(device) # 选择cpu或者gpu
criterion = nn.CrossEntropyLoss() # 交叉熵损失
optimizer = torch.optim.SGD([{'params': model.parameters()}],
lr=0.005, weight_decay=5e-4, momentum=0.9) # 随机梯度优化策略
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=200) # CNN训练使用该学习率优化策略,bp网络也可以使用试试
for epoch in range(80):
# 训练
model.train()
for i, (img, label) in tqdm(enumerate(trainloader)):
img, label = img.to(device, non_blocking=True), label.to(device, non_blocking=True).long() # 加载数据
output = model(img) # 计算结果
loss = criterion(output, label) # 计算损失
optimizer.zero_grad()
loss.backward() # 反向传播
optimizer.step() # 优化器更新
iters = epoch * len(trainloader) + i
if iters % 10 == 0:
wandb.log({'loss': loss}) # 可视化
# 测试
accuracy = 0
model.eval()
for i, (img, label) in enumerate(testloader):
img, label = img.to(device), label.to(device).long()
output = model(img)
output = output.max(dim=1)[1] # 预测是哪一类
accuracy += (output==label).sum().item() # 准确率计算
accuracy = accuracy / 10000 # 准确率计算
print(accuracy)
wandb.log({'accuracy': accuracy}) # 可视化
scheduler.step() # 学习率更新