使用DataLoader加载本地数据

发布于:2025-09-03 ⋅ 阅读:(23) ⋅ 点赞:(0)

目录

一.食物分类案例

1..整合训练集测试集文档

2.导入相关的库

3.设置图片数据的格式转换

3.数据处理

4.数据打包

5.定义卷积神经网络

6.创建模型

7.训练和测试方法定义

8.损失函数和优化器

9.训练模型,测试准确率

10.测试模型


之前我们DataLoader加载的Minist手写数字集是已经封装处理好的数据,所以我们可以直接使用,现在我们学习如何使用DataLoader加载本地数据

一.食物分类案例

1..整合训练集测试集文档

我们可以看到在food_dataset文件夹有训练集和测试集两个文件夹分别存放不同食物文件夹共20种食物的照片

为了方便后面模型的对数据的读取和训练,我们将每个图片的地址和标签(标签可以自己定义)以空格分开都写在一个txt文件中,形如:

定义一个函数用来完成填写txt文件

需要注意的是os.walk()函数实在path里面漫步进入文件后还会出来进入下一个文件夹

第一次循环进入到train文件中directories为各食物名称列表,len不为0,赋值给dirs方便后面命名标签值

第二次循环时,root已经进入到第一个食物文件夹,遍历该文件夹内的所有食物照片得到路径,对root进行split()方便我们得到食物名now_dir[-1]后,再用dirs.index(now_dir[-1])获取该食物在dires中的下标值作为标签,最后将路径与标签写入文件

遍历完一个食物文件后顺便在字典中保存对应的食物和其标签

import os
dire={}
def train_test_file(root,dir):
    f_out=open(dir+'.txt','w')
    path=os.path.join(root,dir)
    for root,directories,files in os.walk(path):
        if len(directories)!=0:
            dirs=directories
        else:
            now_dir=root.split('\\')
            for file in files:
                path=os.path.join(root,file)
                f_out.write(path+' '+str(dirs.index(now_dir[-1]))+'\n')
            dire[dirs.index(now_dir[-1])]=now_dir[-1]
    f_out.close()

最后就是调用该函数,传入相关路径即可得到对应train.txt和test.txt

root=r'.\food_dataset'
train_dir='train'
test_dir='test'
train_test_file(root,train_dir)
train_test_file(root,test_dir)

2.导入相关的库

import torch
from torch.utils.data import Dataset,DataLoader
import numpy as np
from PIL import Image
from torchvision import transforms

3.设置图片数据的格式转换

用字典来保存训练集和测试集的相应格式转换

transforms.Compose()是将一些格式的转换组合在一起相当于一个容器

由于每个照片的大小都可能不同会影响到后面全连接层的展开输入总个数,所以这里必须统一大小

还需要将数据转化为Tensor张量类型

data_transforms={
    'train':
        transforms.Compose([
        transforms.Resize([256,256]),
        transforms.ToTensor()
    ]),
    'valid':
        transforms.Compose([
        transforms.Resize([256,256]),
        transforms.ToTensor()
    ])
}

3.数据处理

由于我们使用的是自己的数据,所以我们必须要让我们的数据集可以通过[]即索引来获取,这样DataLoader才能拿到数据并打包

定义一个类来实现上述要求,传入文件路径和上述的图片数据转换方式即可

在初始化方法__inint__中,完成一些共享空间self赋值后,我们将传入文件中的路径和标签分别保存在存储路径和存储标签的列表中

__len__方法也是必不可少的,DataLoader打包数据前会先检查总数据的大小长度够不够再完成打包

__getitem__则是我们通过索引获取信息的关键方法,只要使用索引就会调用该方法,我们在该方法中通过传进来的索引我们可以通过之前的存储列表获取图片的路径和标签,并用PIL库的Image.open()方法读取图片后根据初始化时传进来的转化格式进行转换,标签则用torch.from_numpy()方法也转化为tensor张量,最后返回图片数据和标签

class food_dataset(Dataset):#能通过索引的方式返回图片数据和标签结果
    def __init__(self,file_path,transform=None):
        self.file_path=file_path
        self.imgs_paths=[]
        self.labels=[]
        self.transform=transform
        with open(self.file_path) as f:
            samples=[x.strip().split(' ') for x in f.readlines()]
            for img_path,label in samples:
                self.imgs_paths.append(img_path)
                self.labels.append(label)
    def __len__(self):
        return len(self.imgs_paths)
    def __getitem__(self, idx):
        image=Image.open(self.imgs_paths[idx])
        if self.transform:
            image=self.transform(image)

        label=self.labels[idx]
        label=torch.from_numpy(np.array(label,dtype=np.int64))#label也转化为tensor
        return image,label

创建该类的对象,传入训练集和测试集的路径得到可以被DataLoader打包的数据

train_data=food_dataset(file_path='./train.txt',transform=data_transforms['train'])
test_data=food_dataset(file_path='./test.txt',transform=data_transforms['valid'])

4.数据打包

由于图片大小比较大我们就将一个图片数据打包成一个批次

train_loader=DataLoader(train_data,batch_size=1,shuffle=True)
test_loader=DataLoader(test_data,batch_size=1,shuffle=True)

device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'Using {device} device')

5.定义卷积神经网络

只需注意必须继承nn.Module类,和我们此次训练的是彩色图片,所以第一个in_channel输入通道数是3而不是之前手写数字灰度图的1,最后全连接层的输出通道是20因为我们有20种食物

from  torch import  nn
class CNN(nn.Module):
    def __init__(self):
        super().__init__()
        #nn.Sequential()是将网络层组合在一起,内部不能写函数
        self.conv1=nn.Sequential(#1*3*256*256
            nn.Conv2d(in_channels=3,#输入通道数
                      out_channels=8,
                      kernel_size=5,
                      stride=1,
                      padding=2),#1*8*256*256
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)#1*8*128*128
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(8,16,5,1,2),#1*16*128*128
            nn.ReLU(),
            nn.Conv2d(16,32,5,1,2),#1*32*128*128
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)##1*32*64*64
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(32,64,5,1,2),#1*64*64*64
            nn.ReLU(),
            nn.Conv2d(64, 64, 5, 1, 2),#1*64*64*64
            nn.ReLU()
        )
        self.flatten=nn.Flatten()
        self.out=nn.Linear(64*64*64,20)

    def forward(self,x):
        x=self.conv1(x)
        x=self.conv2(x)
        x=self.conv3(x)
        # x=x.view(x.size(0),-1)
        x=self.flatten(x)
        output=self.out(x)
        return output

6.创建模型

model=CNN().to(device)

7.训练和测试方法定义

与之前手写数字的方法并无任何不同

def train(dataloader,model,loss_fn,optimizer):
    model.train()
    batch_size_num=1
    for X,y in dataloader:
        X,y=X.to(device),y.to(device)
        pred=model(X)
        loss=loss_fn(pred,y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        loss_value=loss.item()
        if batch_size_num % 100 == 0:
            print(f'loss:{loss_value:>7f} [number:{batch_size_num}]')
        batch_size_num += 1
def test(dataloader,model,loss_fn):
        model.eval()
        len_data=len(dataloader.dataset)
        correct,loss_sum=0,0
        num_batch=0
        with torch.no_grad():
            for X, y in dataloader:
                X, y = X.to(device), y.to(device)
                pred = model(X)
                loss_sum += loss_fn(pred, y).item()
                correct+=(pred.argmax(1)==y).type(torch.float).sum().item()
                num_batch+=1
            loss_avg=loss_sum/num_batch
            accuracy=correct/len_data
            print(f'Accuracy:{100 * accuracy}%\nLoss Avg:{loss_avg}')
            return pred.argmax(1)

8.损失函数和优化器

多分类问题选择交叉熵损失函数

loss_fn=nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters(),lr=0.0001)

9.训练模型,测试准确率

设置20轮训练

epochs=20
for i in range(epochs):
    print(f'==========第{i + 1}轮训练==============')
    train(train_loader, model, loss_fn, optimizer)
    print(f'第{i + 1}轮训练结束')

test(test_loader,model,loss_fn)

准确率很低,后续会改进

10.测试模型

自己输入一个照片的路径通过模型来判断类别,我们需要将照片数据用上面的格式转化处理,需要注意的是我们必须手动为其添加batch维度,因为这里没用Dataloader加载数据不会自动添加batch维度

pred.argmax(1).item()是获取前向传播之后输出的最大概率的标签,.item()是将其转化为可读的形式

path=input('请输入一个图片地址: ')
image=Image.open(path)
image=data_transforms['valid'](image)
image=image.unsqueeze(0).to(device)#添加batch维度
# 注意使用DataLoader加载数据时,它会自动为批量数据添加 batch 维度
model.eval()
with torch.no_grad():
    pred=model.forward(image)
    label=pred.argmax(1).item()
    print('该图片是: '+dire[label])


网站公告

今日签到

点亮在社区的每一天
去签到