AlexNet第一个深度卷积神经网络代码实现(保姆级教程一定叫你跑起来)

本文代码如下,如果只想抄个模板可以直接拿去

from torchvision.datasets import CIFAR10
import torch
from torch import nn, optim # nn:神经网络模块,optim:优化器
from torch.optim.lr_scheduler import CosineAnnealingLR # 学习率衰减:导入余弦退火学习率调度器
from torch.utils.data import DataLoader  #用于加载数据的库
import numpy as np #用于数学计算的库
from tqdm import tqdm#进度条的库
import Ranger #Ranger优化器
from utils import train

def train(net, loss, train_dataloader, valid_dataloader, device, batch_size, num_epoch, lr, lr_min, optim='sgd',
          init=True, scheduler_type='Cosine'):  # 训练函数
    def init_xavier(m):  # 参数初始化
        # if type(m) == nn.Linear or type(m) == nn.Conv2d:#如果是全连接层或者卷积层
        if type(m) == nn.Linear:  # 如果是全连接层
            nn.init.xavier_normal_(m.weight)  # 权重初始化

    if init:  # 是否进行权重初始化
        net.apply(init_xavier)  # 应用权重初始化

    print('training on:', device)  # 打印训练设备
    net.to(device)  # 将网络模型放到训练设备上
    # 优化器选择
    if optim == 'sgd':  # sgd优化器
        optimizer = torch.optim.SGD((param for param in net.parameters() if param.requires_grad), lr=lr,
                                    # param.requires_grad:是否需要梯度
                                    weight_decay=0)  # weight_decay:权重衰减
    elif optim == 'adam':  # adam优化器
        optimizer = torch.optim.Adam((param for param in net.parameters() if param.requires_grad), lr=lr,
                                     # param.requires_grad:是否需要梯度
                                     weight_decay=0)
    elif optim == 'adamW':  # adamW优化器
        optimizer = torch.optim.AdamW((param for param in net.parameters() if param.requires_grad), lr=lr,
                                      # param.requires_grad:是否需要梯度
                                      weight_decay=0)
    elif optim == 'ranger':  # ranger优化器
        optimizer = Ranger((param for param in net.parameters() if param.requires_grad), lr=lr,
                           # param.requires_grad:是否需要梯度
                           weight_decay=0)
    if scheduler_type == 'Cosine':  # 余弦退火学习率调度器
        scheduler = CosineAnnealingLR(optimizer, T_max=num_epoch, eta_min=lr_min)
    # 用来保存每个epoch的Loss和acc以便最后画图
    train_losses = []  # 训练集Loss
    train_acces = []  # 训练集acc
    eval_acces = []  # 验证集acc
    best_acc = 0.0  # 最好的acc
    # 训练
    for epoch in range(num_epoch):  # 训练轮数

        print("——————第 {} 轮训练开始——————".format(epoch + 1))  # 打印训练轮数

        # 训练开始
        net.train()  # 训练模式
        train_acc = 0  # 训练集acc
        for batch in tqdm(train_dataloader, desc='训练'):  # tqdm:进度条
            imgs, targets = batch  # imgs:图片,targets:标签
            imgs = imgs.to(device)  # 将图片放到训练设备上
            targets = targets.to(device)  # 将标签放到训练设备上
            output = net(imgs)  # 输出

            Loss = loss(output, targets)  # 损失函数

            optimizer.zero_grad()  # 梯度清零
            Loss.backward()  # 反向传播
            optimizer.step()  # 优化器更新参数

            _, pred = output.max(1)  # 预测
            num_correct = (pred == targets).sum().item()  # 预测正确的数量
            acc = num_correct / (batch_size)  # acc
            train_acc += acc  # 训练集acc
        scheduler.step()  # 学习率调度器更新学习率
        print("epoch: {}, Loss: {}, Acc: {}".format(epoch, Loss.item(),
                                                    train_acc / len(train_dataloader)))  # 打印训练轮数、Loss和acc
        train_acces.append(train_acc / len(train_dataloader))  # 保存训练集acc
        train_losses.append(Loss.item())  # 保存训练集Loss

        # 测试步骤开始
        net.eval()  # 测试模式
        eval_loss = 0  # 验证集Loss
        eval_acc = 0  # 验证集acc
        with torch.no_grad():  # 不进行梯度计算
            for imgs, targets in valid_dataloader:  # 验证集
                imgs = imgs.to(device)  # 将图片放到训练设备上
                targets = targets.to(device)  # 将标签放到训练设备上
                output = net(imgs)  # 输出
                Loss = loss(output, targets)  # 损失函数
                _, pred = output.max(1)  # 预测
                num_correct = (pred == targets).sum().item()  # 预测正确的数量
                eval_loss += Loss  # 累加验证集Loss
                acc = num_correct / imgs.shape[0]  # acc
                eval_acc += acc  # 累加验证集acc

            eval_losses = eval_loss / (len(valid_dataloader))  # 整体验证集上的Loss
            eval_acc = eval_acc / (len(valid_dataloader))  # 整体验证集上的acc
            if eval_acc > best_acc:  # 如果acc大于最好的acc
                best_acc = eval_acc  # 更新最好的acc
                torch.save(net.state_dict(), 'best_acc.pth')  # 保存最好的acc
            eval_acces.append(eval_acc)  # 保存验证集acc
            print("整体验证集上的Loss: {}".format(eval_losses))  # 打印整体验证集上的Loss
            print("整体验证集上的正确率: {}".format(eval_acc))  # 打印整体验证集上的acc
    return train_losses, train_acces, eval_acces  # 返回训练集Loss、训练集acc、验证集acc

class AlexNet(nn.Module):
    def __init__(self):
        super().__init__()
        #第一层定义5*5卷积,输入的channels为3,输出的channels为64,步长为1,没有padding
        self.conv1 = nn.Sequential(
                nn.Conv2d(3, 64, 5),
                nn.ReLU(True))
        #第二层为3*3池化层 步长为2,没有padding
        self.max_pool1 = nn.MaxPool2d(3, 2)
        #第三层,定义5*5的卷积层,输入的channel是64,输出的channel为64,步长为1
        self.conv2 = nn.Sequential(
                nn.Conv2d(64, 64, 5, 1),
                nn.ReLU(True))
        #第四层,定义3*3的池化层,步长为2
        self.max_pool2 = nn.MaxPool2d(3, 2)
        #第五层,定义全连接层,输入1024,输出384
        self.fc1 = nn.Sequential(
                nn.Linear(1024, 384),
                nn.ReLU(True))
        #第六层,输入384,输出192
        self.fc2 = nn.Sequential(
                nn.Linear(384, 192),
                nn.ReLU(True))
        #第七层,输入192,输出10
        self.fc3 = nn.Linear(192, 10)
    def forward(self, x):
        x = self.conv1(x)
        x = self.max_pool1(x)
        x = self.conv2(x)
        x = self.max_pool2(x)
        # 将矩阵拉平
        x = x.view(x.shape[0], -1)
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        return x
alexnet = AlexNet()
#print(alexnet)
#定义输入(1,3,32,32)
# input_demo = Variable(torch.zeros(1, 3, 32, 32))
# output_demo = alexnet(input_demo)
# print(output_demo.shape)
def data_tf(x):
    x = np.array(x, dtype='float32') / 255
    x = (x - 0.5) / 0.5             #标准化
    x = x.transpose((2, 0, 1))      #变换位置,符合pytorch输入
    x = torch.from_numpy(x)
    return x
train_set = CIFAR10('./data', train=True, transform=data_tf,download=True)
train_data = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)
test_set = CIFAR10('./data', train=False, transform=data_tf)
test_data = torch.utils.data.DataLoader(test_set, batch_size=128, shuffle=False)
net = AlexNet().cuda()
print(len(test_data),len(test_data))
#im,label = train_data
print(train_data)
#print('原图像大小和标签:', im.size, label)
optimizer = torch.optim.SGD(net.parameters(), lr=1e-1)
criterion = nn.CrossEntropyLoss()

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") #判断是否有GPU
train(net,criterion,train_data, test_data, device, batch_size=64, num_epoch=20, lr=0.1, lr_min=1e-4, optim='sgd', init=False)

AlexNet网络介绍及原理

2012年,由Alex提出了一种使用卷积神经网络的方法,用0.85的准确率一举拿下当年的冠军,比亚军高出十个百分点,本文旨在介绍AlexNet模型,和代码的实现。
AlexNet采用cifar10数据集,用于对图片的分类。从下图可以看出模型是由卷积池化和几个全连接层
在这里插入图片描述
AlexNet使用了五个卷积层和三个全连接层。在次对原模型进行了简化,使用两个卷积层和三个全连接层,训练二十次。
卷积层1
输入RGB三通道,即 33232的图像,
使用64个大小为55的卷积核,步长为1,填充为0
输出 64
2828的特征图
Relu激活,
最大池化,核大小3
3,步长为22
输出64
1313的特征图
卷积层2
输入的特征图大小为64
1313
使用64个大小为5
5的卷积核,步长为1,填充为0
输出6499的特征图
Relu激活,
最大池化,核达小33,步长为22
输出6444的特征图
全连接层1
输入特征1024,输出特征384
Relu激活
全连接层2
输入特征384,输出特征192
Relu激活
全连接层192,输出特征10

AlexNet代码实现详解

导入相关的函数包

from torchvision.datasets import CIFAR10
import torch
from torch import nn, optim # nn:神经网络模块,optim:优化器
from torch.optim.lr_scheduler import CosineAnnealingLR # 学习率衰减:导入余弦退火学习率调度器
from torch.utils.data import DataLoader  #用于加载数据的库
import numpy as np #用于数学计算的库
from tqdm import tqdm#进度条的库
import Ranger #Ranger优化器

定义train训练方法,方便传入参数后直接训练,详细介绍已经注释出来

def train(net, loss, train_dataloader, valid_dataloader, device, batch_size, num_epoch, lr, lr_min, optim='sgd',
          init=True, scheduler_type='Cosine'):  # 训练函数
    def init_xavier(m):  # 参数初始化
        # if type(m) == nn.Linear or type(m) == nn.Conv2d:#如果是全连接层或者卷积层
        if type(m) == nn.Linear:  # 如果是全连接层
            nn.init.xavier_normal_(m.weight)  # 权重初始化

    if init:  # 是否进行权重初始化
        net.apply(init_xavier)  # 应用权重初始化

    print('training on:', device)  # 打印训练设备
    net.to(device)  # 将网络模型放到训练设备上
    # 优化器选择
    if optim == 'sgd':  # sgd优化器
        optimizer = torch.optim.SGD((param for param in net.parameters() if param.requires_grad), lr=lr,
                                    # param.requires_grad:是否需要梯度
                                    weight_decay=0)  # weight_decay:权重衰减
    elif optim == 'adam':  # adam优化器
        optimizer = torch.optim.Adam((param for param in net.parameters() if param.requires_grad), lr=lr,
                                     # param.requires_grad:是否需要梯度
                                     weight_decay=0)
    elif optim == 'adamW':  # adamW优化器
        optimizer = torch.optim.AdamW((param for param in net.parameters() if param.requires_grad), lr=lr,
                                      # param.requires_grad:是否需要梯度
                                      weight_decay=0)
    elif optim == 'ranger':  # ranger优化器
        optimizer = Ranger((param for param in net.parameters() if param.requires_grad), lr=lr,
                           # param.requires_grad:是否需要梯度
                           weight_decay=0)
    if scheduler_type == 'Cosine':  # 余弦退火学习率调度器
        scheduler = CosineAnnealingLR(optimizer, T_max=num_epoch, eta_min=lr_min)
    # 用来保存每个epoch的Loss和acc以便最后画图
    train_losses = []  # 训练集Loss
    train_acces = []  # 训练集acc
    eval_acces = []  # 验证集acc
    best_acc = 0.0  # 最好的acc
    # 训练
    for epoch in range(num_epoch):  # 训练轮数

        print("——————第 {} 轮训练开始——————".format(epoch + 1))  # 打印训练轮数

        # 训练开始
        net.train()  # 训练模式
        train_acc = 0  # 训练集acc
        for batch in tqdm(train_dataloader, desc='训练'):  # tqdm:进度条
            imgs, targets = batch  # imgs:图片,targets:标签
            imgs = imgs.to(device)  # 将图片放到训练设备上
            targets = targets.to(device)  # 将标签放到训练设备上
            output = net(imgs)  # 输出

            Loss = loss(output, targets)  # 损失函数

            optimizer.zero_grad()  # 梯度清零
            Loss.backward()  # 反向传播
            optimizer.step()  # 优化器更新参数

            _, pred = output.max(1)  # 预测
            num_correct = (pred == targets).sum().item()  # 预测正确的数量
            acc = num_correct / (batch_size)  # acc
            train_acc += acc  # 训练集acc
        scheduler.step()  # 学习率调度器更新学习率
        print("epoch: {}, Loss: {}, Acc: {}".format(epoch, Loss.item(),
                                                    train_acc / len(train_dataloader)))  # 打印训练轮数、Loss和acc
        train_acces.append(train_acc / len(train_dataloader))  # 保存训练集acc
        train_losses.append(Loss.item())  # 保存训练集Loss

        # 测试步骤开始
        net.eval()  # 测试模式
        eval_loss = 0  # 验证集Loss
        eval_acc = 0  # 验证集acc
        with torch.no_grad():  # 不进行梯度计算
            for imgs, targets in valid_dataloader:  # 验证集
                imgs = imgs.to(device)  # 将图片放到训练设备上
                targets = targets.to(device)  # 将标签放到训练设备上
                output = net(imgs)  # 输出
                Loss = loss(output, targets)  # 损失函数
                _, pred = output.max(1)  # 预测
                num_correct = (pred == targets).sum().item()  # 预测正确的数量
                eval_loss += Loss  # 累加验证集Loss
                acc = num_correct / imgs.shape[0]  # acc
                eval_acc += acc  # 累加验证集acc

            eval_losses = eval_loss / (len(valid_dataloader))  # 整体验证集上的Loss
            eval_acc = eval_acc / (len(valid_dataloader))  # 整体验证集上的acc
            if eval_acc > best_acc:  # 如果acc大于最好的acc
                best_acc = eval_acc  # 更新最好的acc
                torch.save(net.state_dict(), 'best_acc.pth')  # 保存最好的acc
            eval_acces.append(eval_acc)  # 保存验证集acc
            print("整体验证集上的Loss: {}".format(eval_losses))  # 打印整体验证集上的Loss
            print("整体验证集上的正确率: {}".format(eval_acc))  # 打印整体验证集上的acc
    return train_losses, train_acces, eval_acces  # 返回训练集Loss、训练集acc、验证集acc

定义深度学习网络模型

class AlexNet(nn.Module):
    def __init__(self):
        super().__init__()
        #第一层定义5*5卷积,输入的channels为3,输出的channels为64,步长为1,没有padding
        self.conv1 = nn.Sequential(
                nn.Conv2d(3, 64, 5,1),
                nn.ReLU(True))
        #第二层为3*3池化层 步长为2,没有padding
        self.max_pool1 = nn.MaxPool2d(3, 2)
        #第三层,定义5*5的卷积层,输入的channel是64,输出的channel为64,步长为1
        self.conv2 = nn.Sequential(
                nn.Conv2d(64, 64, 5, 1),
                nn.ReLU(True))
        #第四层,定义3*3的池化层,步长为2
        self.max_pool2 = nn.MaxPool2d(3, 2)
        #第五层,定义全连接层,输入1024,输出384
        self.fc1 = nn.Sequential(
                nn.Linear(1024, 384),
                nn.ReLU(True))
        #第六层,输入384,输出192
        self.fc2 = nn.Sequential(
                nn.Linear(384, 192),
                nn.ReLU(True))
        #第七层,输入192,输出10
        self.fc3 = nn.Linear(192, 10)
    def forward(self, x):
        x = self.conv1(x)
        x = self.max_pool1(x)
        x = self.conv2(x)
        x = self.max_pool2(x)
        # 将矩阵拉平
        x = x.view(x.shape[0], -1)
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        return x
alexnet = AlexNet()

定义数据格式转换方法

def data_tf(x):
    x = np.array(x, dtype='float32') / 255
    x = (x - 0.5) / 0.5             #标准化
    x = x.transpose((2, 0, 1))      #变换位置,符合pytorch输入
    #生成的数组转换为张量tensor
    x = torch.from_numpy(x)
    return x

下载数据集和训练集

#下载训练集
train_set = CIFAR10('./data', train=True, transform=data_tf,download=True)
#将训练集进行分组
train_data = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)
#下载测试集
test_set = CIFAR10('./data', train=False, transform=data_tf)
#将测试集合分组
test_data = torch.utils.data.DataLoader(test_set, batch_size=128, shuffle=False)

定义损失函数开始训练
#定义损失函数

criterion = nn.CrossEntropyLoss()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") #判断是否有GPU
train(net,criterion,train_data, test_data, device, batch_size=64, num_epoch=20, lr=0.1, lr_min=1e-4, optim='sgd', init=False)

运行结果如图
在这里插入图片描述
可以看到第二十次训练验证集合上正确率已经达到了百分之七十五以上。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值