PyTorch实现的猫狗识别系统——基于卷积神经网络【Python】

介绍

        文章内容来自我的《深度学习》课程作业实验报告。

        图像识别是在提取图像特征的基础上,对图像的各种不同模式目标和对象 进行识别的技术。本实验通过对 Kaggle 上的 Cats.vs.Dogs 图像数据集进行学习, 实现识别图片的猫狗类型。深度学习方法在图像识别中应用比较广泛。用于基于 卷积神经网络的图像识别方法有很多,常用的卷积神经网络模型有 LeNet、 AleXNet、GoogLeNet、VGGNet、ResNet 等模型。本实验使用 Kaggle 上的 Cats vs.Dogs 图像数据集的部分数据,采用了结构较为简单的 AleXNet 网络,并引入 了预训练模型,构建了算法构建一个卷积神经网络模型,解决了猫狗二分类图像识别问题。

解决方案

2.1 AlexNet 模型

        模型使用 AlexNet 模型进行猫狗分类。AlexNet 输入为 RGB 三通道的 224 × 224 × 3 大小的图像(也可填充为 227 × 227 × 3 )。AlexNet 共包含 5 个 卷积层(包含 3 个池化)和 3 个全连接层。其中,每个卷积层都包含卷积核、 偏置项、ReLU 激活函数和局部响应归一化(LRN)模块。第 1、2、5 个卷积层 后面都跟着一个最大池化层,后三个层为全连接层。最终输出层为 softmax,将 网络输出转化为概率值,用于预测图像的类别。模型结构如下:

2.2 损失函数

        使用交叉熵损失作为损失函数,计算公式如下:

2.3 评价指标

       使用精确度作为评价指标,计算公式如下:

其中:TP为真正例,TN为真负例,FP为假正例,FN为假负例。

实验结果和分析

3.1 数据集与工具

        Visual Studio Code

        PyTorch 1.13

        kaggle Dogs vs. Cats 数据集

3.2 训练过程及代码

       首先进行数据的预处理,将原始数据集中训练集里的猫狗图像人为重新划分训练集和测试集。其中,训练集2000张,测试集500张,猫狗各占50%。将数据的红绿蓝通道以均值0.485, 0.456, 0.406,标准差0.229, 0.224, 0.225进行归一化处理,并且依据概率对图片水平翻转,进行数据增强。

       构建模型AlexNet模型,这里使用了torchvision.models中的模型结构,参数初始化为AlexNet提供的参数,在此基础上进行微调,大大降低了训练时间。

# 加载模型
def get_model(path_state_dict, vis_model=False):
    """
    创建模型,加载参数
    :param path_state_dict:
    :return:
    """
    model = alexnet()
    pretrained_state_dict = torch.load(path_state_dict)
    model.load_state_dict(pretrained_state_dict)

    if vis_model:
        print(model)

    return model

获取AlexNet模型,并对其结构进行修改。因为是二分类问题,所以将最后输出的类别,从1000改为2。

# ============================ step 2/5 模型 ============================
    alexnet_model = get_model(path_state_dict, True)

    num_ftrs = alexnet_model.classifier._modules["6"].in_features
    alexnet_model.classifier._modules["6"] = nn.Linear(num_ftrs, num_classes)
    
    alexnet_model.to("cuda" if torch.cuda.is_available() else "cpu")

定义损失函数与优化器

# ============================ step 3/5 损失函数 ============================
    criterion = nn.CrossEntropyLoss()
    # ============================ step 4/5 优化器 ============================

    optimizer = torch.optim.SGD(alexnet_model.parameters(), lr=LR, momentum=0.9)  # 选择优化器

    scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=lr_decay_step, gamma=0.1)  # 设置学习率下降策略

训练与测试过程

# ============================ step 5/5 训练 ============================
    train_curve = []
    train_acc=[]
    valid_curve = []
    valid_acc=[]
    for epoch in range(start_epoch + 1, MAX_EPOCH):

        loss_mean = 0.
        correct = 0.
        total = 0.

        alexnet_model.train()
        for i, data in enumerate(train_loader):

            # forward
            inputs, labels = data
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = alexnet_model(inputs)

            # backward
            optimizer.zero_grad()
            loss = criterion(outputs, labels)
            loss.backward()

            # update weights
            optimizer.step()

            # 统计分类情况
            predicted = torch.argmax(outputs.data, 1)
            total += labels.size(0)
            correct += torch.sum(predicted == labels)

            # 打印训练信息
            loss_mean += loss.item()
            train_curve.append(loss.item())
            train_acc.append(correct / total)
            if (i+1) % log_interval == 0:
                loss_mean = loss_mean / log_interval
                print("Training:Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                    epoch, MAX_EPOCH, i+1, len(train_loader), loss_mean, correct / total))
                loss_mean = 0.

        scheduler.step()  # 更新学习率

        # validate the model
        if (epoch+1) % val_interval == 0:

            correct_val = 0.
            total_val = 0.
            loss_val = 0.
            alexnet_model.eval()
            with torch.no_grad():
                for j, data in enumerate(valid_loader):
                    inputs, labels = data
                    inputs, labels = inputs.to(device), labels.to(device)

                    bs, ncrops, c, h, w = inputs.size()     # [4, 10, 3, 224, 224
                    outputs = alexnet_model(inputs.view(-1, c, h, w))
                    outputs_avg = outputs.view(bs, ncrops, -1).mean(1)

                    loss = criterion(outputs_avg, labels)

                    predicted = torch.argmax(outputs_avg.data, 1)
                    total_val += labels.size(0)
                    correct_val += torch.sum(predicted == labels)

                    loss_val += loss.item()

                loss_val_mean = loss_val/len(valid_loader)
                valid_curve.append(loss_val_mean)
                valid_acc.append(correct_val / total_val)
                print("Valid:\t Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                    epoch, MAX_EPOCH, j+1, len(valid_loader), loss_val_mean, correct_val / total_val))
            alexnet_model.train()

3.3 结果与分析

        迭代过程中,交叉熵损失与精确度变化情况如上图所示。训练和测试均设置batch为64。在训练过程中,每一个iteration,交叉熵损失与准确度进行一次计算。测试过程中,每一个epoch,交叉熵损失与准确度进行一次计算。整个过程设置了epoch=3。在图中可以看出,在第一个epoch后,模型逐渐趋于稳定,并且没有出现明显的过拟合或欠拟合现象。在最后一个epoch后,交叉熵损失为0.0765,准确率为96.60%。

作者:来二斤胡萝卜_

物联沃分享整理
物联沃-IOTWORD物联网 » PyTorch实现的猫狗识别系统——基于卷积神经网络【Python】

发表回复