深度学习--BP实战猫狗分类数据集

运用BP模型实现猫狗数据集的分类

数据集下载

首先,我们要先下载好要分类的数据集,下载网址如下:

猫狗大作战数据集下载

该数据集是Kaggle在2013年公开的猫狗数据集,该数据集总共25000张图片,猫狗各12500张。

部分图片如下:

我们下载的只是最基本的图片数据,还需要自行的创建我们的数据集,一般而言我们都是通过创建类的方法来实现。

导入库

from PIL import Image                   # 这行代码从Pillow库中导入了Image模块,它提供了许多用于打开、操作和保存图像的函数。
import numpy as np
from torch.utils.data import Dataset    # Dataset类是torch.utils.data模块中的一个抽象类,用于表示一个数据集
from torchvision import transforms     
import os                               # os模块提供了与操作系统交互的函数,例如读取目录内容、检查文件是否存在等。
import torch
import matplotlib.pyplot as plt
import matplotlib
#设置字体为楷体
matplotlib.rcParams['font.sans-serif'] = ['KaiTi']

在这里,我们应该了解各个库的作用和用法,这些库在下面都会用到

自定义类来封装数据集

在这里,我们需要用到__init__,__len__,__getitem__三个魔术方法,下面,我们就先了解一下这三个方法。(有了解的可以直接跳过!!)

__init__

__init__方法,称为构造方法

特性:

在创建类对象时会自动执行,

在创建类对象时,会传入参数给__init__使用

下面是个简单的例子:

class student:
# 可写可不写
#     name=None
#     age=None
#     sex=None
 
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
 
stu=student("小红",18,"女")
print("信息添加成功")

简单了解一下就好

__len__

__len__ 是Python中的一个魔术方法,用于定义类的实例对象的长度。 该方法用于返回一个对象的长度,通常是容器类型的对象,如字符串、列表、元组和字典等。

__getitem__

 __getitem__方法用于获取序列对象中指定索引位置的元素,通常使用中括号 []运算符调用。 该方法接收一个索引作为参数,并返回序列对象中指定索引位置的元素。 如果指定的索引超出了序列对象的范围,应该抛出IndexError异常

接下来,我们来了解各个魔术方法下代码的含义

init部分

    def __init__(self,root_dir,lable_dir):
        self.root_dir=root_dir      # 文件主路径dataset/train
        self.label_dir=lable_dir    # 分路径 cat 和 dog
        self.path=os.path.join(self.root_dir,self.label_dir)  # 将文件路径的两部分连接起来,使path成为完整路径
        self.img_path=os.listdir(self.path)     # 查看
        self.transform = transforms.Compose([   # 包含:
            transforms.Resize((224,224)),       # 统一大小为224*224
            transforms.ToTensor()               # 转化为Tensor类型
        ])

self.img_path获取该完整路径下的图片(如dataset\train\cat目录下的图片) 

transforms.compose:

orchvision.transforms是pytorch中的图像预处理包。一般用Compose把多个步骤整合到一起

比如说:Resize和ToTensor

Resize它可以更改PIL类型的图片数据的大小,本质上其实更改的是像素多少

import torchvision.transforms as transforms
from PIL import Image
import numpy as np
# 定义图像的路径
path = "C:\\Users\\yangy\\Pictures\\Screenshots\\屏幕截图 2023-12-22 205435.jpg"
# 打开图像
image = Image.open(path)
# 将图像转换为NumPy数组,以便查看和打印原始数据
b = np.array(image)
# 创建一个Resize转换对象
resize_transform = transforms.Resize((224, 224))
# 应用Resize转换到PIL图像对象
resized_image = resize_transform(image)
# 将调整大小后的图像转换为NumPy数组(如果需要的话)
a = np.array(resized_image)
# 打印原始图像和调整大小后的图像的数据以及它们的形状
print('修剪之前:', b)
print('修剪之后:', a)
print('修剪之前的大小:', b.shape)
print("修剪之后的大小:", a.shape)
# 显示原始图像
image.show()
# 显示调整大小后的图像
resized_image.show()

 结果如下:

修剪之前: [[[  0   0   0 255]
  [  0   0   0 255]
  [  0   0   0 255]
  ...
  [  0   0   0 255]
  [  0   0   0 255]
  [  0   0   0 255]]

 [[  0   0   0 255]
  [  0   0   0 255]
  [  0   0   0 255]
  ...
  [  0   0   0 255]
  [  0   0   0 255]
  [  0   0   0 255]]

 [[237 237 237 255]
  [237 237 237 255]
  [237 237 237 255]
  ...
  [230 230 230 255]
  [230 230 230 255]
  [230 230 230 255]]

 ...

 [[255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]
  ...
  [255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]]

 [[255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]
  ...
  [255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]]

 [[255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]
  ...
  [255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]]]
修剪之后: [[[182 182 182 255]
  [182 182 182 255]
  [182 182 182 255]
  ...
  [178 178 178 255]
  [178 178 178 255]
  [178 178 178 255]]

 [[248 248 248 255]
  [248 248 248 255]
  [248 248 248 255]
  ...
  [236 236 236 255]
  [236 236 236 255]
  [236 236 236 255]]

 [[255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]
  ...
  [237 237 237 255]
  [237 237 237 255]
  [237 237 237 255]]

 ...

 [[255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]
  ...
  [255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]]

 [[255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]
  ...
  [255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]]

 [[255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]
  ...
  [255 255 255 255]
  [255 255 255 255]
  [255 255 255 255]]]
修剪之前的大小: (1446, 874, 4)
修剪之后的大小: (224, 224, 4)

图像对比:

 

让我们来看看os.listdir的用法:

len部分

    def __len__(self):
        ilen=len(self.img_path)
        return ilen         # 返回self.img_path列表的长度,即该数据集包含的图像数量。

getitem部分

 def __getitem__(self,item):
        img_name=self.img_path[item]                     # path路径中的第item个图像
        img_item_path=os.path.join(self.path,img_name)   # 该图像的路径
        img=Image.open(img_item_path)                    # 打开并读取图像
        img=self.transform(img)                          # 转化为(224*224)并转化为Tensor类型
        if self.label_dir=="cat":           # 如果是cat下的图片
            label=1                         # label为猫狗的二分类值,因为二分类不能用文字“猫,狗”表示,所以这里我们用1来表示猫,0来表示狗
        else:
            label=0
        return img,label  

注意:因为这里我们用img和label作为返回值,后续我们需要用两个参数来接收它

构建数据集

root_dir="dataset/train"
test_dir="D:\\猫狗数据集\\PetImages\\test"
lable_cat_dir="cat"
lable_dog_dir="dog"
cat_dataset=mydataset(root_dir,lable_cat_dir)
dog_dataset=mydataset(root_dir,lable_dog_dir)
train_dataset=cat_dataset+dog_dataset

cat_test=mydataset(test_dir,lable_cat_dir)
dog_test=mydataset(test_dir,lable_dog_dir)
test_dataset=cat_test+dog_test

在这里,root_dir使用的是从猫狗数据集中取出部分图片存放到dataset目录下的train之中,作为训练集。

test_dir是从猫狗数据集中直接取出

注意:进行数据处理的时候可能会出现图片数据损坏,或者图片数据的形状,维度不太对劲等问题,这个时候就需要我们仔细地进行图片清晰,我们可以只取部分图片,或利用os库里的库函数直接把错误的数据删除掉。

最终,将test_dataset作为测试集,而train_dataset作为训练集

构建BP模型

class BPnetwork(torch.nn.Module):
    def __init__(self):
        super(BPnetwork, self).__init__()
        self.linear1 = torch.nn.Linear(224 * 224*3, 128)
        self.ReLU1 = torch.nn.ReLU()

        self.linear2 = torch.nn.Linear(128, 64)
        self.ReLU2 = torch.nn.ReLU()

        self.linear3 = torch.nn.Linear(64, 2)
        self.softmax = torch.nn.LogSoftmax(dim=1)

    def forward(self, x):
        x=x.reshape(x.shape[0],-1)
        x = self.linear1(x)
        x = self.ReLU1(x)
        x = self.linear2(x)
        x = self.ReLU2(x)
        x = self.linear3(x)
        x = self.softmax(x)
        return x

其中,输入层为图片的大小,即224*224

self.linear为隐藏层的定义,linear1-linear2分别表示两个隐藏层,而linear3为输出层

self.ReLu为使用ReLu激活函数来将其激活

最后,使用LogSoftmax来进行归一化处理,来分为两类(1和0)

猫狗分类的大部分都和MINIST手写字体识别差不多

深度学习BP手写识别MINIST-CSDN博客

创建数据加载器

trainloader=torch.utils.data.DataLoader(train_dataset,batch_size=4, shuffle=True)
testloader=torch.utils.data.DataLoader(test_dataset,batch_size=4, shuffle=True)

训练模型

model=BPnetwork()
criterion = torch.nn.NLLLoss()
optimizer= torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

eopchs=25
for i in range(eopchs):
    sumloss=0
    for images, lables in trainloader:
        ypre=model(images)
        loss=criterion(ypre,lables)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        sumloss+=loss.item()
    print("Epoch {}, Loss: {}".format(i+1, sumloss/len(trainloader)))

在优化器部分,我们需要不断调整学习率,来得到最终结果,最终选择一个比较合适的值,0.01 

测试模型

examples=enumerate(testloader)
batch,(images,lables)=next(examples)

fig=plt.figure()
for i in range(4):
    t=torch.unsqueeze(images[i],dim=0)       # 增加一个维度,使t的形状与模型期望的形状相匹配
    logps=model(t)

    probab=list(logps.detach().numpy()[0])
    # logps.detach()从计算图中分离出logps,确保后续的操作不会影响到模型的梯度。
    # 接着,.numpy()将张量转换为NumPy数组。[0]取出第一个元素(因为t是一个批次大小为1的数据),
    # 最后list()将这个元素转换为一个列表。此时,probab是一个包含所有类别概率的列表。
    pred_label=probab.index(max(probab))   # 找出probab列表中概率最大的元素的索引,这个索引即代表模型预测的类别标签。
    img=torch.squeeze(images[i])           # 移除大小为1的维度,让它回到原来的形状
    img1=img.permute(1, 2, 0)              # 将图像的维度从(channels, height, width)调整为(height, width, channels)
    img1=img1.numpy()

    plt.subplot(2,2,i+1)          # 创建一个2*2的子图网格,并选择第i+1个子图作为当前绘图区域
    plt.tight_layout()            # 自动调整子图参数,使之填充整个图像区域并尽量减少重叠
    plt.imshow(img1,cmap='gray',interpolation='none')
    plt.title(f"预测值:{pred_label}")
    plt.xticks([])                # 设置x轴和y轴的刻度标签为空
    plt.yticks([])
plt.show()

最终结果:

Epoch 1, Loss: 0.720171856880188
Epoch 2, Loss: 0.7152694940567017
Epoch 3, Loss: 0.6765142321586609
Epoch 4, Loss: 0.6954864740371705
Epoch 5, Loss: 0.6259550094604492
Epoch 6, Loss: 0.6435211181640625
Epoch 7, Loss: 0.6113996803760529
Epoch 8, Loss: 0.5865052282810211
Epoch 9, Loss: 0.5092812538146972
Epoch 10, Loss: 0.43574718832969667
Epoch 11, Loss: 0.39420580863952637
Epoch 12, Loss: 0.4067960947751999
Epoch 13, Loss: 0.4550750508904457
Epoch 14, Loss: 0.37434772998094556
Epoch 15, Loss: 0.18671178817749023
Epoch 16, Loss: 0.15620937794446946
Epoch 17, Loss: 0.11713241040706635
Epoch 18, Loss: 0.07138810828328132
Epoch 19, Loss: 0.0491988904774189
Epoch 20, Loss: 0.040103929489851
Epoch 21, Loss: 0.027237643860280512
Epoch 22, Loss: 0.022758941538631915
Epoch 23, Loss: 0.01747293183580041
Epoch 24, Loss: 0.014283824525773526
Epoch 25, Loss: 0.012680502329021692

我们可以看出,随着轮数的增加,损失值逐渐减少

我们可以看出,预测值与真实值基本一致,但是由于我们的测试集数量少,加上测试集和训练集不同,预测的结果不是那么准确,也会出现误差

完整代码:

from PIL import Image                   # 这行代码从Pillow库中导入了Image模块,它提供了许多用于打开、操作和保存图像的函数。
import numpy as np
from torch.utils.data import Dataset    # Dataset类是torch.utils.data模块中的一个抽象类,用于表示一个数据集
from torchvision import transforms
import os                               # os模块提供了与操作系统交互的函数,例如读取目录内容、检查文件是否存在等。
import torch
import matplotlib.pyplot as plt
import matplotlib
#设置字体为楷体
matplotlib.rcParams['font.sans-serif'] = ['KaiTi']

# 自定义数据集
class mydataset(Dataset):
    def __init__(self,root_dir,lable_dir):
        self.root_dir=root_dir      # 文件主路径dataset/train
        self.label_dir=lable_dir    # 分路径 cat 和 dog
        self.path=os.path.join(self.root_dir,self.label_dir)  # 将文件路径的两部分连接起来
        self.img_path=os.listdir(self.path)     # 查看
        self.transform = transforms.Compose([   # 包含:
            transforms.Resize((224,224)),       # 统一大小为224*224
            transforms.ToTensor()               # 转化为Tensor类型
        ])
    def __len__(self):
        ilen=len(self.img_path)
        return ilen                            # 返回self.img_path列表的长度,即该数据集包含的图像数量。
    def __getitem__(self,item):
        img_name=self.img_path[item]                     # path路径中的第item个图像
        img_item_path=os.path.join(self.path,img_name)   # 该图像的路径
        img=Image.open(img_item_path)                    # 打开并读取图像
        img=self.transform(img)                          # 转化为(224*224)并转化为Tensor类型
        if self.label_dir=="cat":           # 如果是cat下的图片
            label=1                         # label为猫狗的二分类值,因为二分类不能用文字“猫,狗”表示,所以这里我们用1来表示猫,0来表示狗
        else:
            label=0
        return img,label
root_dir="dataset/train"
test_dir="D:\\猫狗数据集\\PetImages\\test"
lable_cat_dir="cat"
lable_dog_dir="dog"
cat_dataset=mydataset(root_dir,lable_cat_dir)
dog_dataset=mydataset(root_dir,lable_dog_dir)
train_dataset=cat_dataset+dog_dataset

cat_test=mydataset(test_dir,lable_cat_dir)
dog_test=mydataset(test_dir,lable_dog_dir)
test_dataset=cat_test+dog_test

class BPnetwork(torch.nn.Module):
    def __init__(self):
        super(BPnetwork, self).__init__()
        self.linear1 = torch.nn.Linear(224 * 224*3, 128)
        self.ReLU1 = torch.nn.ReLU()

        self.linear2 = torch.nn.Linear(128, 64)
        self.ReLU2 = torch.nn.ReLU()

        self.linear3 = torch.nn.Linear(64, 2)
        self.softmax = torch.nn.LogSoftmax(dim=1)

    def forward(self, x):
        x=x.reshape(x.shape[0],-1)
        x = self.linear1(x)
        x = self.ReLU1(x)
        x = self.linear2(x)
        x = self.ReLU2(x)
        x = self.linear3(x)
        x = self.softmax(x)
        return x

trainloader=torch.utils.data.DataLoader(train_dataset,batch_size=4, shuffle=True)
testloader=torch.utils.data.DataLoader(test_dataset,batch_size=4, shuffle=True)


model=BPnetwork()
criterion = torch.nn.NLLLoss()
optimizer= torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

eopchs=25
for i in range(eopchs):
    sumloss=0
    for images, lables in trainloader:
        ypre=model(images)
        loss=criterion(ypre,lables)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        sumloss+=loss.item()
    print("Epoch {}, Loss: {}".format(i+1, sumloss/len(trainloader)))

# 测试模型
examples=enumerate(testloader)
batch,(images,lables)=next(examples)

fig=plt.figure()
for i in range(4):
    t=torch.unsqueeze(images[i],dim=0)       # 增加一个维度,使t的形状与模型期望的形状相匹配
    logps=model(t)

    probab=list(logps.detach().numpy()[0])
    # logps.detach()从计算图中分离出logps,确保后续的操作不会影响到模型的梯度。
    # 接着,.numpy()将张量转换为NumPy数组。[0]取出第一个元素(因为t是一个批次大小为1的数据),
    # 最后list()将这个元素转换为一个列表。此时,probab是一个包含所有类别概率的列表。
    pred_label=probab.index(max(probab))   # 找出probab列表中概率最大的元素的索引,这个索引即代表模型预测的类别标签。
    img=torch.squeeze(images[i])           # 移除大小为1的维度,让它回到原来的形状
    img1=img.permute(1, 2, 0)              # 将图像的维度从(channels, height, width)调整为(height, width, channels)
    img1=img1.numpy()

    plt.subplot(2,2,i+1)          # 创建一个2*2的子图网格,并选择第i+1个子图作为当前绘图区域
    plt.tight_layout()            # 自动调整子图参数,使之填充整个图像区域并尽量减少重叠
    plt.imshow(img1,cmap='gray',interpolation='none')
    plt.title(f"预测值:{pred_label}")
    plt.xticks([])                # 设置x轴和y轴的刻度标签为空
    plt.yticks([])
plt.show()

### 回答1: 数据集是一个常用的图像分类数据集,用于训练计算机识别的图像。这个数据集通常由成千上万张已经标注好的的图像组成,用于训练机器学习模型。 在Matlab中,我们可以使用Image Labeler App来加载数据集,并进行标注。首先,我们需要创建一个图像标注工具对象,并指定图像文件夹的路径。然后,我们可以使用该工具加载图像,并逐一标注这些图像。我们可以选择在每张图像上绘制矩形来标注出的位置,或者直接为每个图像指定标签。 加载完数据集并完成标注后,我们可以使用其中的一部分数据来训练一个图像分类模型。在Matlab中,有许多深度学习工具箱和函数可用于训练图像分类模型,如AlexNet、VGGNet、ResNet等。我们可以选择一个合适的模型,并使用训练数据集对其进行训练。 训练完成后,我们可以使用测试数据集来评估已训练模型的性能。我们可以将测试图像输入到模型中,并获得每个图像属于的预测结果。根据这些预测结果,我们可以计算模型的准确率、召回率、F1分数等指标来评估模型的性能。 通过这样的过程,我们可以利用数据集在Matlab中训练并测试图像分类模型,从而实现对的自动识别。这种能力可以应用于各种实际场景,如智能安防系统、数字图书馆、医疗诊断等。 ### 回答2: 数据集是一个用于训练和测试机器学习模型的数据集,其中包含了的图像。这个数据集可用于开发和评估图像识别和分类算法。 在MATLAB中,可以使用Image Processing Toolbox来处理和分析数据集。首先,可以通过导入图像函数将数据集中的图像加载到MATLAB工作空间中。然后,可以使用各种图像处理函数来进行特征提取,例如提取边缘、纹理或颜色特征。 接下来,可以使用机器学习工具箱中的分类器来训练模型。有很多算法可以选择,如支持向量机(SVM),卷积神经网络(CNN)等。可以将数据集中的图像和标签对应起来,然后使用训练函数进行模型训练。 在训练完成后,可以使用测试数据集对模型进行评估。通过将测试图像输入到已训练的模型中,可以获得预测的标签。可以使用混淆矩阵、准确率、召回率和F1分数等评估指标来评估模型的性能。 最后,可以使用训练好的模型进行图像的分类。将新的图像输入到模型中,可以获得其预测的分类结果。 总之,通过MATLAB中的图像处理和机器学习工具,可以使用数据集来训练和评估图像分类模型,以实现对图像的自动识别。 ### 回答3: 数据集是一个用于训练和测试图像分类模型的数据集。在Matlab中,我们可以通过使用已有的数据集或自己创建一个数据集来进行相关实验。 首先,我们可以从互联网上下载已有的图片作为数据集。然后,我们可以使用Matlab的图像处理工具箱来对这些图片进行预处理,如调整大小、灰度化、增强对比度等。 接下来,我们可以利用Matlab的图像标签工具来手动或自动地为图片添加类别标签,将其分为两类。这些类别标签将在后续的训练和测试中起到关键作用。 然后,我们可以使用Matlab的机器学习工具箱来构建一个图像分类模型。我们可以选择使用传统的机器学习算法,如支持向量机(SVM)或随机森林(Random Forest),也可以使用深度学习方法,如卷积神经网络(CNN)。 在模型构建完成后,我们可以使用训练集的图像数据来训练模型,并使用测试集的图像数据来评估模型的性能表现。通过Matlab提供的训练函数和评估函数,我们可以得到模型的准确率、精确率、召回率等性能指标。 最后,根据模型的性能表现,我们可以对模型进行优化和调整,以提高其在图像分类任务上的性能。在优化过程中,我们可以尝试不同的特征提取方法、模型结构调整和超参数调整等技术手段。 总之,通过Matlab提供的图像处理和机器学习工具,我们可以方便地构建和训练图像分类模型,并对其性能进行评估和优化。这将为图像分类等相关任务提供有效的解决方案。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值