经典卷积神经网络 - LeNet

image-20231022164234916

该模型用于手写的数字识别。
LeNet模型包含了多个卷积层和池化层,以及最后的全连接层用于分类。其中,每个卷积层都包含了一个卷积操作和一个非线性激活函数,用于提取输入图像的特征。池化层则用于缩小特征图的尺寸,减少模型参数和计算量。全连接层则将特征向量映射到类别概率上。

MNISt数据集

50000个训练数据,10000个测试数据。图像大小为28x28,共10类(0~9)。

  • LeNet是早期成功的神经网络
  • 先使用卷积层来学习图片空间信息
  • 然后使用全连接层来转换到类别空间

对于padding

通用的卷积时padding 的选择

如卷积核宽高为3时 padding 选择1

如卷积核宽高为5时 padding 选择2

如卷积核宽高为7时padding选择3

至于选择填充多少像素,通常有两个选择,分别叫做Valid卷积和Same卷积。

Valid卷积意味着不填充,这样的话,如果你有一个 n × n n\times n n×n的图像,用一个 f × f f\times f f×f的过滤器卷积,它将会给你一个 ( n − f + 1 ) × ( n − f + 1 ) (n-f+1)\times (n-f+1) (nf+1)×(nf+1)维的输出。例如,有一个6×6的图像,通过一个3×3的过滤器,得到一个4×4的输出。

Same卷积意味你填充后,你的输出大小和输入大小是一样的。根据这个公式 n − f + 1 n-f+1 nf+1,当你填充 p p p个像素点,n就变成了 n + 2 p n+2p n+2p,最后公式变为 n + 2 p − f + 1 n+2p-f+1 n+2pf+1。因此如果你有一个 n × n n\times n n×n的图像,用 p p p个像素填充边缘,输出的大小就是这样的 ( n + 2 p − f + 1 ) × ( n + 2 p − f + 1 ) (n+2p−f+1)\times (n+2p−f+1) (n+2pf+1)×(n+2pf+1)。如果你想让 ( n + 2 p − f + 1 ) = n (n+2p−f+1)=n (n+2pf+1)=n的话,使得输出和输入大小相等,如果你用这个等式求解 p p p,那么 p = ( f − 1 ) / 2 p=(f-1)/2 p=(f1)/2。所以当 f f f是一个奇数的时候,只要选择相应的填充尺寸,你就能确保得到和输入相同尺寸的输出。

代码实现

LeNet(LeNet-5)由两个部分组成:卷积编码器和全连接层密集块。

model.py

from torch import nn

class Reshape(nn.Module):
    def forward(self,x):
        return x.reshape((-1,1,28,28))

class MyLeNet(nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        # 假如sigmoid激活函数后 损失不下降
        self.model = nn.Sequential(
            Reshape(),
            nn.Conv2d(1,6,kernel_size=5,padding=2),
            nn.Sigmoid(),
            nn.AvgPool2d(2,stride=2),
            nn.Conv2d(6,16,kernel_size=5),
            nn.Sigmoid(),
            nn.AvgPool2d(2,stride=2),
            nn.Flatten(),
            nn.Linear(16*5*5,120),
            nn.Linear(120,84),
            nn.Linear(84,10)
        )

    def forward(self,x):
        return self.model(x)

train.py

# 扫描数据次数
epochs = 20
# 分组大小
batch = 64
# 学习率
learning_rate = 0.05
# 训练次数
train_step = 0
# 测试次数
test_step = 0


# 定义图像转换
transform = transforms.Compose([
    transforms.ToTensor()
])
# 读取数据
train_dataset = datasets.MNIST(root="./dataset",train=True,transform=transform,download=True)
test_dataset = datasets.MNIST(root="./dataset",train=False,transform=transform,download=True)
# 加载数据
train_dataloader = DataLoader(train_dataset,batch_size=batch,shuffle=True,num_workers=0)
test_dataloader = DataLoader(test_dataset,batch_size=batch,shuffle=True,num_workers=0)
# 数据大小
train_size = len(train_dataset)
test_size = len(test_dataset)
print("训练集大小:{}".format(train_size))
print("验证集大小:{}".format(test_size))

# GPU
device = torch.device("mps" if torch.backends.mps.is_available() else "cpu")

# 创建网络
net = MyLeNet()
net = net.to(device)
# 定义损失函数
loss = nn.CrossEntropyLoss()
loss = loss.to(device)
# 定义优化器
optimizer = torch.optim.SGD(net.parameters(),lr=learning_rate)

writer = SummaryWriter("logs")
# 训练
for epoch in range(epochs):
    print("-------------------第 {} 轮训练开始-------------------".format(epoch))
    net.train()
    for data in train_dataloader:
        train_step = train_step + 1
        images,targets = data
        images = images.to(device)
        targets = targets.to(device)
        outputs = net(images)
        loss_out = loss(outputs,targets)
        optimizer.zero_grad()
        loss_out.backward()
        optimizer.step()

        if train_step%100==0:
            writer.add_scalar("Train Loss",scalar_value=loss_out.item(),global_step=train_step)
            print("训练次数:{},Loss:{}".format(train_step,loss_out.item()))

    # 测试
    net.eval()
    total_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in test_dataloader:
            test_step = test_step + 1
            images, targets = data
            images = images.to(device)
            targets = targets.to(device)
            outputs = net(images)
            loss_out = loss(outputs, targets)
            total_loss = total_loss + loss_out
            accuracy = (targets == torch.argmax(outputs,dim=1)).sum()
            total_accuracy = total_accuracy + accuracy
        # 计算精确率
        print(total_accuracy)
        accuracy_rate = total_accuracy / test_size

        print("第 {} 轮,验证集总损失为:{}".format(epoch+1,total_loss))
        print("第 {} 轮,精确率为:{}".format(epoch+1,accuracy_rate))
        writer.add_scalar("Test Total Loss",scalar_value=total_loss,global_step=epoch+1)
        writer.add_scalar("Accuracy Rate",scalar_value=accuracy_rate,global_step=epoch+1)
    torch.save(net,"./model/net_{}.pth".format(epoch+1))
    print("模型net_{}.pth已保存".format(epoch+1))
posted @ 2023-10-23 14:07  mango1698  阅读(14)  评论(0编辑  收藏  举报  来源