PyTorch学习(2)-猫狗大战数据集分类识别-PyTorch代码实训

猫狗大战数据集分类识别-PyTorch代码实训

二分类任务

数据集文件目录结构图

pythonProject/
│
├── cat_recognition.py
│  
└── kagglecatsanddogs_5340/
    └── PetImages/
    	├── Cat/...
    	└── Dog/...

Cat和Dog文件夹中的图片的后缀均为.jpg

代码1(实现二分类问题)

import torch
import torch.nn
from torch.utils.data import Dataset, DataLoader, Subset, random_split
import torchvision.datasets
from torchvision import transforms, models
from PIL import Image
from torch import nn, optim
import matplotlib.pyplot as plt

transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, ), (0.5, ))
])

root_dir = './kagglecatsanddogs_5340/PetImages'

dataset = torchvision.datasets.ImageFolder(root=root_dir, transform=transform)

n_train = int(0.8 * len(dataset))
n_test = len(dataset) - n_train

train_dataset, test_dataset = random_split(dataset, [n_train, n_test])

# dataloader = DataLoader(dataset, batch_size=4, shuffle=True)

train_loader = DataLoader(dataset=train_dataset, batch_size=16, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=16, shuffle=True)

print("Finish Reading the Dataset")

# MyNet


class MyNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
        self.relu1 = nn.ReLU()
        self.maxpool1 = nn.MaxPool2d(kernel_size=2)
        self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)
        self.relu2 = nn.ReLU()
        self.maxpool2 = nn.MaxPool2d(kernel_size=2)
        self.fc1 = nn.Linear(in_features=32*56*56, out_features=512)
        self.relu_fc1 = nn.ReLU()
        self.fc2 = nn.Linear(in_features=512, out_features=2)

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.maxpool1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.maxpool2(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = self.relu_fc1(x)
        x = self.fc2(x)
        return x


model = MyNet()


# model = models.resnet50(pretrained=True)
# for param in model.parameters():
#     param.requires_grad = False
# model.fc = nn.Linear(2048, 2)

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params=model.parameters(), lr=1e-4)

# train


def train(epoch): # epoch: 方便打印
    running_loss = 0.0
    running_total = 0
    running_correct = 0
    for batch_idx, data in enumerate(train_loader, 0): # 给train_loader元素编号,从0开始
        inputs, targets = data # inputs和targets是“数组”的形式
        optimizer.zero_grad() # 消除优化器中原有的梯度

        outputs = model(inputs)
        loss = criterion(outputs, targets) # 对比输出结果和“答案”

        loss.backward()
        optimizer.step() # 优化网络参数

        running_loss += loss.item() # .item(): 取出tensor中特定位置的具体元素值并返回该值(Tensor to int or float)
        _, predicted = torch.max(outputs.data, dim=1) # 找到每个样本预测概率最高的类别的标签值(即预测结果)
        # dim=0计算tensor中每列的最大值的索引,dim=1表示每行的最大值的索引
        running_total += inputs.shape[0] # .shape[0]: 读取矩阵第一维度的长度
        running_correct += (predicted == targets).sum().item()

        if batch_idx % 300 == 299:
            print('[%d, %5d]: loss: %.3f , acc: %.2f %%'
                  % (epoch + 1, batch_idx + 1, running_loss / 300, 100 * running_correct / running_total))
            running_loss = 0.0
            running_total = 0
            running_correct = 0

# test *
def test(epoch):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    acc = correct / total
    print('[%d / %d]: Accuracy on test set: %.1f %% ' % (epoch + 1, 3, 100 * acc))
    return acc



# main

for epoch in range(3):
    train(epoch)
    acc_test = test(epoch)
    acc_list_test.append(acc_test)

print("----------Finish the model training process.----------")

结果1

(之前调整过epoch,且由于电脑性能限制,完成过多epoch的训练耗时较长,故仅进行了几轮训练)

在这里插入图片描述

代码2(用于理解dataloader内的结构)

# 理解dataloader

from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader

# 数据预处理
transform = transforms.Compose([transforms.Resize((224,224)), transforms.ToTensor()])

# 加载数据集
dataset = ImageFolder('./kagglecatsanddogs_5340/PetImages', transform=transform)

# 创建 DataLoader
loader = DataLoader(dataset, batch_size=4)

# 通过迭代的方式访问 DataLoader 中的元素
for i, (images, labels) in enumerate(loader):
    # if i == 0:  # 仅显示第一个批次的数据
        print(f"第{i + 1}个批次的图像张量:")
        print(images.shape)  # 显示图像张量的形状
        print("对应的标签:", labels)
    # break


# 获取数据集中的特定样本(假设索引为10)
sample_idx = 10
image, label = dataset[sample_idx]
print(f"索引 {sample_idx} 的图像张量:")
print(image.shape)
print("对应的标签:", label)

结果2

在这里插入图片描述

ImageFolder方法会自动读取文件目录中的图片,并将其打上标签:[‘0’, ‘1’]。

问题

loss的含义;loss值的大小怎么看?

损失函数用来优化模型。在此二分类任务中,采用了交叉熵损失函数。损失值越小,表示模型对类别预测的准确性越高。

在训练过程中,目标是通过优化算法使损失值逐渐减小。

较大的损失值可能表示模型尚未充分学习数据的特征,或者模型架构、超参数选择不合适。在训练过程中,需要检查和调整模型,以使损失值逐渐减小。

batch_size对于训练速度和训练模型分类正确率的影响?

增大batch_size可以减少迭代次数。对相同的数据量,处理速度比小的batch_size更快。

但过大的batch_size可能会让内存容量撑不住,同时对参数的修正会变缓。

总结:

  1. batch_size设的大一些,收敛得快,也就是需要训练的次数少,准确率上升的也很稳定,但是实际使用起来精度不高;
  2. batch_size设的小一些,收敛得慢,可能准确率来回震荡,因此需要把基础学习速率降低一些,但是实际使用起来精度较高。

如何设计神经网络的结构?

设计神经网络的思路:

  • 先设计一个过拟合的模型
  • 再消除过拟合带来的问题

对于datasets.[数据集名]的参数transform,.ToTensor()和.Resize()谁先谁后?有影响吗?

推荐的操作顺序是先进行Resize操作,然后再进行ToTensor操作。

如果先ToTensor后Resize,那么最终得到的张量尺寸是转换前的原始大小。因为调整大小操作通常需要基于图像的像素信息来进行,而且模型通常要输入固定大小的张量。

怎样认识CNN提取了猫狗图像中的哪些特征用于分类任务?

一种方法是打印feature map来可视化网络提取的特征。

当batch_size = 16时,对搭建的卷积网络中的几个层进行可视化:

  • conv1

在这里插入图片描述

  • maxpool1

在这里插入图片描述

  • conv2

在这里插入图片描述

归一化的作用?如何归一化?

归一化的作用:

  • 加速训练过程,加速收敛
  • 提高模型的稳定性
  • 改善模型的泛化能力

所以往往需要使用代码求解数据集的均值和标准差,用于归一化时设置参数。

Learning Rate的含义?

本质上是“步长”。

学习率 大学习率 小
学习速度
理想状态下的使用时间点刚开始训练时一定轮数过后(接近结束时)
不足有可能会出现震荡的情况容易过拟合;收敛速度慢

代码中为什么要声明全局变量(global)?

在Python中,如果想要在函数内部修改一个定义在函数外部的变量,需要使用 global 关键字来声明这个变量是全局的。否则,Python会认为是在函数内部创建了一个与全局变量同名的局部变量。

*什么是钩子函数?

钩子函数是一种回调机制,允许程序在执行的特定点插入用户定义的代码。

在PyTorch中,hook方法有四种:
torch.Tensor.register_hook()
torch.nn.Module.register_forward_hook()
torch.nn.Module.register_backward_hook()
torch.nn.Module.register_forward_pre_hook().

使用.register_forward_hook可以导出卷积特征图。

框选识别

这里使用opencv中提供的.CascadeClassifier()方法,引入haarcascade_frontalcatface.xml文件和haarcascade_frontalcatface_extended.xml文件,用于自动框选出图像中被识别出来的猫脸。

其中的.xml文件可以通过此网址下载:https://github.com/opencv/opencv/tree/master/data/haarcascades

import numpy as np
import cv2 

cat_cascade = cv2.CascadeClassifier('haarcascade_frontalcatface.xml')
cat_ext_cascade = cv2.CascadeClassifier('haarcascade_frontalcatface_extended.xml')

SF=1.05  # try different values of scale factor like 1.05, 1.3, etc
N=3 # try different values of minimum neighbours like 3,4,5,6

def processImage(image_dir,image_filename):
    # read the image
    img = cv2.imread(image_dir+'/'+image_filename)
    # convery to gray scale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # this function returns tuple rectangle starting coordinates x,y, width, height
    cats = cat_cascade.detectMultiScale(gray, scaleFactor=SF, minNeighbors=N)
    #print(cats) # one sample value is [[268 147 234 234]]
    cats_ext = cat_ext_cascade.detectMultiScale(gray, scaleFactor=SF, minNeighbors=N)
    #print(cats_ext)
    
    # draw a blue rectangle on the image
    for (x,y,w,h) in cats:
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)       
    # draw a green rectangle on the image 
    for (x,y,w,h) in cats_ext:
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
    
    # save the image to a file
    cv2.imwrite('out'+image_filename,img)
    
for idx in range(1,7):
    processImage('cats/',str(idx)+'.jpg')
    
processImage('.','dog.jpg')

运行结果如下:(准确率没有预期中高)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值