基于Pytorch的深度学习实战项目-------------------猫狗分类

Pytorch实战训练第一篇-------猫狗分类

0、数据集准备

本次数据集使用Kaggle上的开源数据集,这是网站链接:猫狗数据集
本次数据集共:857.48MB,分为train和test两个文件夹,其中train中有25000张图片,test中有12500张图片。其中有一点需要注意的是,这个train文件夹中的猫和狗的照片没有分开打包,所以我们需要进一步对train中的图片进行处理。

1、对train中的数据进行分割

  • 获取目录下的所有.jpg文件,并转化为一个列表
files = glob.glob(os.path.join(path, "*.jpg"))
  • 对图片进行随机排序,以保证对train文件进行划分为训练集和验证集时具有随机性
num_of_images = len(files)
shuffle = np.random.permutation(num_of_images)  # 对图片进行随机排序
  • 对数据进行划分,前2000张图片为验证集,后23000图片为训练集。并放入对应的目录中,同时在train和valid目录中再分别创建两个目录,分别为cat和dog,将数据分别放入对应的目录中。分割
# 划分验证集
for i in shuffle[:2000]:
    folder = files[i].split('\\')[-1].split('.')[0]  # 看属于哪个文件,就创建哪个文件,第一个创建完成之后,其他相同的就直接放进去
    image = files[i].split('\\')[-1]  # 图片原文件名
    print(folder, image)
    os.rename(files[i], os.path.join(path, "valid", folder, image))

# 划分训练集
for i in shuffle[2000:]:
    folder = files[i].split("\\")[-1].split(".")[0]
    image = files[i].split("\\")[-1]
    os.rename(files[i], os.path.join(path, "train", folder, image))
  • 数据分割源码:
import numpy as np
import os
import glob

# 文件路径
path = "./archive/dc/train/"

# 预处理,把图片按照名称分类
files = glob.glob(os.path.join(path, "*.jpg"))

print("一共{}张图片".format(len(files)))  # 一共25000张图片

num_of_images = len(files)
shuffle = np.random.permutation(num_of_images)  # 对图片进行随机排序
# 创建目录
os.mkdir(os.path.join(path, "train"))
os.mkdir(os.path.join(path, "valid"))

for t in ["train/", "valid/"]:
    for folder in ["dog/", "cat/"]:
        os.mkdir(os.path.join(path, t, folder))

# 划分验证集
for i in shuffle[:2000]:
    folder = files[i].split('\\')[-1].split('.')[0]  # 看属于哪个文件,就创建哪个文件,第一个创建完成之后,其他相同的就直接放进去
    image = files[i].split('\\')[-1]  # 图片原文件名
    print(folder, image)
    os.rename(files[i], os.path.join(path, "valid", folder, image))

# 划分训练集
for i in shuffle[2000:]:
    folder = files[i].split("\\")[-1].split(".")[0]
    image = files[i].split("\\")[-1]
    os.rename(files[i], os.path.join(path, "train", folder, image))

2、定义网络模型

  • 这个神经网络是我在网上随便找了一个框架进行了搭建,顺便稍微修改了一下。训练之后的模型框架如下图所示。在这顺便给大家推荐一个非常好用的查看神经网络模型的网址:NETRON,该网页的详细说明可以查看这篇文章:NETRON说明神经网络模型
  • 因为最后进行Flatten()的时候,数据大小需要计算,比较麻烦,这里给大家介绍一个小技巧,可以将Flatten()后面的线性层注销,然后用一个数据进行测试,这样就可以直接得到数据的大小。
if __name__ =="__main__":
    model=Model()
    input=torch.ones((32,3,224,224))
    output=model(input)
    print(output.shape)

数据大小

  • 神经网络模型搭建源码:
from torch.nn import Sequential,Conv2d,MaxPool2d,Linear,BatchNorm2d,ReLU,Flatten,BatchNorm1d
from torch import nn
import torch

# 搭建神经网络
class Model(nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        self.model=Sequential(
            Conv2d(3, 32, (7,7),stride=(2,2),padding=2),
            BatchNorm2d(32),
            ReLU(),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            BatchNorm2d(32),
            ReLU(),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            BatchNorm2d(64),
            ReLU(),
            MaxPool2d(2),
            Flatten(),
            Linear(10816, 64),  # 易错,展开后长度发生改变
            BatchNorm1d(64),
            ReLU(),
            Linear(64, 2)
        )
    def forward(self,x):
        output=self.model(x)
        return output

if __name__ =="__main__":
    model=Model()
    input=torch.ones((32,3,224,224))
    output=model(input)
    print(output.shape)

3、训练模型

  • 定义训练模型所用设备,我这里使用的是GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("使用{}运行".format(device))
  • 数据转化。
    因为Pytorch需要传入Tensor类型的数据,所以需要进行相关处理,并且数据集中的图片大小不一,需要进行大小转换,这里还用到的归一化,方便计算,其中的mean和std数值是常用于处理图片数据的,可以理解为默认是这个值。
data_transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    # Image数据常用归一化数值
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
  • 数据集准备和加载。
    因为数据类型是图片,并且在相对应的目录下,这里使用ImageFolder,直接可以加载为dataset,并且其目录对应下的文件自动打上目录名标签。例如:dog目录下的文件全部为dog标签。这里使用DataLoader加载数据,batch_size设置为32,这个参数根据你电脑配置进行对应的设置,设置过大会导致负载报错。
# 数据集准备  ImageFolder可以加载root下的所有文件,返回一个数据集对象
train = ImageFolder(train_path, data_transform)
valid = ImageFolder(valid_path, data_transform)
print(train.classes)  # ['cat', 'dog']
print(train.class_to_idx)  # {'cat': 0, 'dog': 1}  顺序取决于文件夹的顺序

# DataLoader加载数据
train_data = DataLoader(train, batch_size=32, shuffle=True, drop_last=True)
valid_data = DataLoader(valid, batch_size=32, shuffle=True, drop_last=True)
  • 参数设置、损失函数及优化器设置。
    需要注意的是这里的模型实例化、损失函数均需要转化为你所要运行的device状态(我这里是cuda),否则就会报错。
# 模型实例化
model = Model()
model.to(device)

# 参数设置
# 学习率
learning_rate = 1e-2
# 训练次数
total_train_step = 0
# 测试次数
total_valid_step = 0
# 训练轮数
epoch = 20

# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn.to(device)

# 优化器
optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)
  • 模型训练及保存最后一轮训练结果,其中进行数据训练时,需要将imgs和targets转换到device状态。
imgs = imgs.to(device)
targets = targets.to(device)
  • train_model源码:
import torch
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
from model import *

# 定义训练的设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("使用{}运行".format(device))

# 数据路径
train_path = "./archive/dc/train/train"
valid_path = "./archive/dc/train/valid"

# 数据转换
data_transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    # Image数据常用归一化数值
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 数据集准备  ImageFolder可以加载root下的所有文件,返回一个数据集对象
train = ImageFolder(train_path, data_transform)
valid = ImageFolder(valid_path, data_transform)
print(train.classes)  # ['cat', 'dog']
print(train.class_to_idx)  # {'cat': 0, 'dog': 1}  顺序取决于文件夹的顺序

# 查看数据集大小
train_data_size = len(train)
valid_data_size = len(valid)
print("训练集大小为:{}".format(train_data_size))
print("测试集大小为:{}".format(valid_data_size))

# DataLoader加载数据
train_data = DataLoader(train, batch_size=32, shuffle=True, drop_last=True)
valid_data = DataLoader(valid, batch_size=32, shuffle=True, drop_last=True)

# 模型实例化
model = Model()
model.to(device)

# 参数设置
# 学习率
learning_rate = 1e-2
# 训练次数
total_train_step = 0
# 测试次数
total_valid_step = 0
# 训练轮数
epoch = 20

# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn.to(device)

# 优化器
optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)

for i in range(epoch):
    print("============第{}轮训练开始==========".format(i + 1))

    # 训练开始
    model.train()
    for data in train_data:
        imgs, targets = data
        imgs = imgs.to(device)
        targets = targets.to(device)
        outputs = model(imgs)
        # print(imgs)
        # print(targets)
        # 计算损失值
        loss = loss_fn(outputs, targets)

        # 优化器优化模型
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_train_step = total_train_step + 1
        # if total_train_step%100==0:
        print({"训练次数:{},Loss:{}".format(total_train_step, loss.item())})

    # 验证开始
    model.eval()
    total_valid_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in valid_data:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            outputs = model(imgs)
            loss = loss_fn(outputs, targets)
            total_valid_loss = total_valid_loss + loss
            accuracy = (outputs.argmax(1) == targets).sum()
            total_accuracy = total_accuracy + accuracy
        print("验证集上的Loss为:{}".format(total_valid_loss))
        print("验证集上的准确率为 {}".format(total_accuracy / valid_data_size))

    # 保存最后一轮的训练结果
    if i == epoch - 1:
        torch.save(model, "model{}.pth".format(i + 1))
        print("模型已保存!")

4、测试模型及将分类后的图片加载到对应的目录下

  • 定义键值函数
    因为之前训练时 0 表示cat,1表示dog,模型预测最终也只能输出0或1,我需要将分类后的图片加载到对应目录下,所以我需要进行键值转换。
def get_Key(num):
    if num == 0:
        return "cat"
    else:
        return "dog"
  • 定义数据类型
    由于我需要的到文件的路径来进行文件路径的修改,以达到最终放入对应目录的效果,所以我在这定义了一个新的数据类型,返回图片信息和文件路径。
# 定义数据类
class CustomImageFolder():
    def __init__(self, root_dir, transform=None):
        self.root_dir = root_dir
        self.transform = transform
        self.image_paths = os.listdir(root_dir)  # 存储所有的文件名

    def __len__(self):
        return len(self.image_paths)

    def __getitem__(self, idx):
        img_path = os.path.join(self.root_dir, self.image_paths[idx])
        image = Image.open(img_path).convert(("RGB"))
        if self.transform:
            image = self.transform(image)
        return image, img_path
  • 测试源码:
import os
from torchvision import transforms
from model import *
from PIL import Image
from torch.utils.data import DataLoader, TensorDataset


def get_Key(num):
    if num == 0:
        return "cat"
    else:
        return "dog"


# 定义数据类
class CustomImageFolder():
    def __init__(self, root_dir, transform=None):
        self.root_dir = root_dir
        self.transform = transform
        self.image_paths = os.listdir(root_dir)  # 存储所有的文件名

    def __len__(self):
        return len(self.image_paths)

    def __getitem__(self, idx):
        img_path = os.path.join(self.root_dir, self.image_paths[idx])
        image = Image.open(img_path).convert(("RGB"))
        if self.transform:
            image = self.transform(image)
        return image, img_path


# 数据类型转换
data_trans = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 模型实例化
model = torch.load("./model5.pth", map_location="cuda")

if __name__ == '__main__':

    # 定义文件路径
    dir_pth = "./archive/dc/test"

    # 创建一个数据集
    dataset = CustomImageFolder(root_dir=dir_pth, transform=data_trans)
    data_size = len(dataset)
    print("测试集大小为:{}".format(data_size))  # 测试集大小为:2000

    # 加载数据集
    dataloader = DataLoader(dataset, batch_size=32, shuffle=True, drop_last=True, num_workers=4)

    # 创建文件夹
    os.mkdir(os.path.join(dir_pth, "cat"))
    os.mkdir(os.path.join(dir_pth, "dog"))

    for i, batch in enumerate(dataloader):
        inputs, paths = batch[0].to("cuda"), batch[1]  # inputs是图像数据,paths是对应路径
        outputs = model(inputs)
        for output, path in zip(outputs, paths):
            result = output.argmax().item()
            class_name = get_Key(result)
            if result == 0:
                image_name = path.split("\\")[-1]
                os.rename(path, os.path.join(dir_pth, "cat", image_name))
            else:
                image_name = path.split('\\')[-1]
                os.rename(path, os.path.join(dir_pth, "dog", image_name))
        print("第{}轮已结束".format(i + 1))

5、总结

  • 网络模型不够优秀,最终的验证集正确率只有85%左右(没附图主要是因为我忘记截图了。。。)
  • 在测试时,将数据进行了分类,最终可以看到对应目录下的图片,由于正确率确实不够高,导致还是会有肉眼可见的分类错误。分了两个目录,还有多余的是因为设置了drop_last=True(还有一个原因是中途调试了一下,导致部分数据被我删了,实际测试数据集大小好像是8500)。
    在这里插入图片描述
  • 光cat这个目录第一页就已经能看到dog了。。。。
    在这里插入图片描述
  • 此次实战中没有对损失率及准确率进行画图,下次可以考虑加上,毕竟看起来6一点。
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyTorch是一个基于Python的科学计算库,它提供了强大的GPU加速功能,并且是深度学习项目中常用的框架之一。在PyTorch项目实践中,以下是一些常见的步骤和方法: 1. 安装PyTorch和相关库:首先,确保你已经安装了合适版本的PyTorch。你可以通过pip安装指定版本的PyTorch,根据你的需求选择是否需要CPU或GPU版本,以及是否需要安装其他依赖库。具体的安装命令可以参考和。 2. 数据准备与加载:在PyTorch项目实践中,通常需要准备和加载数据。你可以使用PyTorch的数据加载工具,例如torchvision中的datasets和DataLoader,来加载和预处理数据。这些工具可以帮助你将数据集转换为PyTorch可用的形式,例如张量。 3. 构建模型:在PyTorch中,模型可以通过定义一个继承自nn.Module的类来创建。在这个类中,你可以定义模型的结构和操作。你可以使用PyTorch提供的各种模块,例如全连接层、卷积层、循环神经网络等,来构建你的模型。 4. 定义损失函数和优化器:在PyTorch项目实践中,你需要选择合适的损失函数来度量你的模型的性能。PyTorch提供了各种常见的损失函数,例如交叉熵损失、均方误差损失等。此外,你还需要选择一个优化器来更新模型的参数。常用的优化器包括随机梯度下降(SGD)、Adam等。 5. 训练模型:一旦你定义好了模型、损失函数和优化器,你可以开始训练你的模型了。在训练过程中,你需要将输入数据喂给模型,计算输出并与真实标签进行比较,然后计算损失并反向传播更新模型的参数。由于PyTorch已经为你提供了自动求导的功能,你只需要使用backward()方法来计算梯度,并使用optimizer.step()方法来更新参数。 6. 评估模型:在训练完成后,你需要评估你的模型在新数据上的性能。你可以使用一些评价指标,例如准确率、精确率、召回率等来评估你的模型的性能。 综上所述,这些步骤和方法是PyTorch项目实践中常见的一些内容。当然,具体的项目实践可能会因项目的需求而有所不同。希望这些信息对你有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值