一.前言:
大家想要完成图像分类的时候,大多是想对自己构造的数据集进行分类,此时我们需要载入自己的数据集。因此需要借助Dataset类完成。
整体流程:
1.1 图像数据
1.2 图像索引文件
1.3 使用Dataset构建数据集
1.4 使用DataLoader读取数据
二.这里展示一下图像文件及其标签
2.1索引式标签数据格式文件:
2.2文件目录组织形式
三.制作自己的数据集
在大家自制数据集的时候往往是一个文件夹内都为同一标签图像,这样也便于收集整理图像,但是在这里划分数据的训练和测试,于是大家需要制作图像索引
3.1图像索引制作流程:
3.1.1 输入文件夹地址(该地址下的图像为同一类的图像)
3.1.2 选择生成txt文件的地址以及txt的名称
3.1.3 生成txt文件
3.1.4 构建for循环便于生成图像
3.1.5 设置标签的名字
3.1.6 每一行的索引构成:图像名称 + 逗号(英)+ 图像标签
3.1.7 索引行写入txt中
3.2参考代码:
import os name = 9 img_path = './%s/' %name TxtName = './data%s .txt' % name f = open(TxtName, 'a') img_path_Line = os.listdir(img_path) for ImgName in img_path_Line: label = "%s" % name # 修改你标签的名字 Name_Label = ImgName + ',' + label print(Name_Label) f.write(Name_Label) # 格式化写入也可以 f.write('\n') # 显示写入换行 f.close()
3.3生成的样子:
这里我们需要将各data1-9汇总一下即可得到All.txt文件。
此时我们还需要将总文件划分训练和测试文件,二者公用一个数据集文件夹(datas),在txt中划分。
3.4划分训练测试代码:
# -*- coding:utf-8 -*- # 在txt文件中随机抽取行 import random import math All = open('All.txt', 'r', encoding='utf-8') # 要被抽取的文件All.txt,共5000行 newf = open('train.txt', 'w', encoding='utf-8') # 抽取的0.7倍写入train.txt AllNum = 5000 # 总图像数 SetTestNum = 0.7 # 设置比例 TestNum = math.floor(SetTestNum * AllNum) resultList = random.sample(range(0, AllNum), TestNum) # sample(x,y)函数的作用是从序列x中,随机选择y个不重复的元素 lines = All.readlines() for i in resultList: newf.write(lines[i]) All.close()
四.构建自己的Dataset
这里借鉴阿里天池云的教程,其步骤大致分如下几步:
4.1步骤4.1.1 初始化图像文件路径或文件名列表
4.1.2 根据索引index从文件中读取一个数据
4.1.3 预处理数据(可不要)
4.1.4 返回数据对
4.1.5 返回数据量
4.2轮子来了
from PIL import Image import torch from torch.utils.data import Dataset from torchvision import transforms ## class MnistDataset(Dataset): def __init__(self, image_path, image_label, transform=None): super(MnistDataset, self).__init__() self.image_path = image_path # 初始化图像路径列表 self.image_label = image_label # 初始化图像标签列表 self.transform = transform # 初始化数据增强方法 def __getitem__(self, index): """ 获取对应index的图像,并视情况进行数据增强 """ image = Image.open(self.image_path[index]) image = np.asarray(image) label = float(self.image_label[index]) if self.transform is not None: image = self.transform(image) return image, torch.tensor(label) def __len__(self): return len(self.image_path) def get_path_label(img_root, label_file_path): """ 获取数字图像的路径和标签并返回对应的列表 @ img_root:保存图像的根目路 @ label_file_path 保存图像标签的文件路径,。cvs 或 。txt @ return:图像的路径列表和对应的标签列表 """ data = pd.read_csv(label_file_path, names=['img', 'label']) print(data) data['img'] = data['img'].apply(lambda x: img_root + x) return data['img'].tolist(), data['label'].tolist()
在使用轮子的过程中对阿里天池云中做出了细微的更改:
if __name__ == '__main__': ImgPath = './datas/AllData/' # 获取训练集路径列表和标签列表 train_data_root = ImgPath train_label = './datas/train.txt' # 输入训练集的txt train_img_list, train_label_list = get_path_label(train_data_root, train_label) # 训练集dataset train_dataset = MnistDataset(train_img_list, train_label_list, transform=transforms.Compose([transforms.ToTensor()])) # 获取测试集路径列表和标签列表 test_data_root = ImgPath test_label = './datas/test.txt' # 输入测试集的txt test_img_list, test_label_list = get_path_label(test_data_root, test_label) # 训练集dataset test_dataset = MnistDataset(test_img_list, test_label_list, transform=transforms.Compose([transforms.ToTensor()]))
我们可以运行的看一下train_dataset:
从print中可以看见其基本结果为index编号+ img(名称) + label(名称)
img label 0 0f53988a-aa4c-11ec-98ee-0068ebc9b98d.jpg 5 1 12c269f2-aa4c-11ec-8c98-0068ebc9b98d.jpg 7 2 02bbb794-aa4c-11ec-a86d-0068ebc9b98d.jpg 2 3 0ad921d7-aa4c-11ec-aaf8-0068ebc9b98d.jpg 3 4 0f559308-aa4c-11ec-bfe7-0068ebc9b98d.jpg 5 ... ... ... 3495 14853acd-aa4c-11ec-9214-0068ebc9b98d.jpg 8 3496 0ad68b8d-aa4c-11ec-bc3f-0068ebc9b98d.jpg 3 3497 f80a329d-aa4b-11ec-8e14-0068ebc9b98d.jpg 1 3498 02b8fa4c-aa4c-11ec-9598-0068ebc9b98d.jpg 2 3499 0acc322b-aa4c-11ec-a3f3-0068ebc9b98d.jpg 3 [3500 rows x 2 columns] <__main__.MnistDataset object at 0x0000018075EFFFD0> img label 0 12c8a739-aa4c-11ec-92ee-0068ebc9b98d.jpg 7 1 0ad6d970-aa4c-11ec-a49a-0068ebc9b98d.jpg 3 2 f809bdba-aa4b-11ec-877b-0068ebc9b98d.jpg 1 3 1111623a-aa4c-11ec-a3f9-0068ebc9b98d.jpg 6 4 12c5c308-aa4c-11ec-9f4e-0068ebc9b98d.jpg 7 ... ... ... 1495 1640597c-aa4c-11ec-aa18-0068ebc9b98d.jpg 9 1496 0d2db729-aa4c-11ec-b385-0068ebc9b98d.jpg 4 1497 11188943-aa4c-11ec-8926-0068ebc9b98d.jpg 6 1498 0f508d5f-aa4c-11ec-996b-0068ebc9b98d.jpg 5 1499 147b5682-aa4c-11ec-bb98-0068ebc9b98d.jpg 8 [1500 rows x 2 columns] Process finished with exit code 0
此时扔未开始读取图像文件,目前做的是准备工作!
可以看一下咱们的图像的通道数、像素大小、标签:
for i in train_dataset: img, label = i print(img.size(), label)
输出节选:
torch.Size([1, 20, 20]) tensor(0.) torch.Size([1, 20, 20]) tensor(8.) torch.Size([1, 20, 20]) tensor(5.) torch.Size([1, 20, 20]) tensor(1.) torch.Size([1, 20, 20]) tensor(7.)
4.3使用DataLoader这里需要使用的是DataLoader函数,其内部常用参数使用:
dataset=train_dataset:输入自己要加载的数据set
batch_size=3:一个批量的大小
shuffle=True:是否打乱顺序
num_workers=4:是否使用多进程,0代表不使用
pin_memory=True:是否将数据保存在pin_memory区, pin_memory数据转移到Gpu中会快一些
drop_last=True:当为Ture时,dataset中的数据个数不是batch_size整数倍
#接上 # 开始加载自己的数据 train_loader = DataLoader(dataset=train_dataset, # 输入自己要加载的数据set batch_size=3, # 一个批量的大小 shuffle=True, # 是否打乱顺序 num_workers=4, # 是否使用多进程,0代表不使用 pin_memory=True, # 是否将数据保存在pin_memory区, pin_memory数据转移到Gpu中会快一些 drop_last=True) # 当为Ture时,dataset中的数据个数不是batch_size整数倍时,将多余出不足一个batch的数据丢弃 test_loader = DataLoader(dataset=test_dataset, # 输入自己要加载的数据set batch_size=3, # 一个批量的大小 shuffle=True, # 是否打乱顺序 num_workers=4, # 是否使用多进程,0代表不使用 pin_memory=True, # 是否将数据保存在pin_memory区, pin_memory数据转移到Gpu中会快一些 drop_last=True) # 当为Ture时,dataset中的数据个数不是batch_size整数倍时,将多余出不足一个batch的数据丢弃
五.开始训练
结合juejin.cn/post/707783… 中GPU版本的分类,对自己下载的数据集进行分类,这里需要注意的地方有两个:
5.1 导入的库需要齐全
5.2 在卷积网络中需要将in_c=改为:400(因为20201)
5.3 在训练代码中的label改为:label.long()
demo:
import time import pandas as pd import numpy as np import torch import torch.nn as nn import torch.backends.cudnn as cudnn from PIL import Image from torch import optim from torch.autograd import Variable from torch.utils.data import Dataset, DataLoader from torchvision import transforms class Net(nn.Module): def __init__(self, in_c=400, out_c=10): super(Net, self).__init__() # 定义全连接层 self.fc1 = nn.Linear(in_c, 512) # 定义激活层 self.act1 = nn.ReLU(inplace=True) self.fc2 = nn.Linear(512, 256) self.act2 = nn.ReLU(inplace=True) self.fc3 = nn.Linear(256, 128) self.act3 = nn.ReLU(inplace=True) self.fc4 = nn.Linear(128, out_c) def forward(self, x): x = self.act1(self.fc1(x)) x = self.act2(self.fc2(x)) x = self.act3(self.fc3(x)) x = self.fc4(x) return x ## class MnistDataset(Dataset): def __init__(self, image_path, image_label, transform=None): super(MnistDataset, self).__init__() self.image_path = image_path # 初始化图像路径列表 self.image_label = image_label # 初始化图像标签列表 self.transform = transform # 初始化数据增强方法 def __getitem__(self, index): """ 获取对应index的图像,并视情况进行数据增强 """ image = Image.open(self.image_path[index]) image = np.asarray(image) label = float(self.image_label[index]) if self.transform is not None: image = self.transform(image) return image, torch.tensor(label) def __len__(self): return len(self.image_path) def get_path_label(img_root, label_file_path): """ 获取数字图像的路径和标签并返回对应的列表 @ img_root:保存图像的根目路 @ label_file_path 保存图像标签的文件路径,。cvs 或 。txt @ return:图像的路径列表和对应的标签列表 """ data = pd.read_csv(label_file_path, names=['img', 'label']) data['img'] = data['img'].apply(lambda x: img_root + x) return data['img'].tolist(), data['label'].tolist() if __name__ == '__main__': t1 = time.time() # 搭建网络 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') net = Net() cudnn.benchmark = True net = net.to(device) ImgPath = './datas/AllData/' # 获取训练集路径列表和标签列表 train_data_root = ImgPath train_label = './datas/train.txt' # 输入训练集的txt train_img_list, train_label_list = get_path_label(train_data_root, train_label) # 训练集dataset train_dataset = MnistDataset(train_img_list, train_label_list, transform=transforms.Compose([transforms.ToTensor()])) """ for i in train_dataset: img, label = i print(img.size(), label) """ # 获取测试集路径列表和标签列表 test_data_root = ImgPath test_label = './datas/test.txt' # 输入测试集的txt test_img_list, test_label_list = get_path_label(test_data_root, test_label) # 训练集dataset test_dataset = MnistDataset(test_img_list, test_label_list, transform=transforms.Compose([transforms.ToTensor()])) # 开始加载自己的数据 train_data = DataLoader(dataset=train_dataset, # 输入自己要加载的数据set batch_size=16, # 一个批量的大小 shuffle=True, # 是否打乱顺序 num_workers=4, # 是否使用多进程,0代表不使用 pin_memory=True, # 是否将数据保存在pin_memory区, pin_memory数据转移到Gpu中会快一些 drop_last=True) # 当为Ture时,dataset中的数据个数不是batch_size整数倍时,将多余出不足一个batch的数据丢弃 test_data = DataLoader(dataset=test_dataset, # 输入自己要加载的数据set batch_size=8, # 一个批量的大小 shuffle=True, # 是否打乱顺序 num_workers=4, # 是否使用多进程,0代表不使用 pin_memory=True, # 是否将数据保存在pin_memory区, pin_memory数据转移到Gpu中会快一些 drop_last=True) # 当为Ture时,dataset中的数据个数不是batch_size整数倍时,将多余出不足一个batch的数据丢弃 # 定义损失函数 -- 交叉熵 criterion = torch.nn.CrossEntropyLoss().to(device) # 定义优化器 -- 随机梯度下降 optimizer = optim.SGD(net.parameters(), lr=0.01, weight_decay=0.00005) # 开始训练 losses = [] # 记录训练损失 acces = [] # 记录训练精度 eval_losses = [] # 记录测试损失 eval_acces = [] # 记录测试精度 nums_epoch = 20 # 训练次数 for epoch in range(nums_epoch): train_loss = 0 # 设置训练损失的初始值 train_acc = 0 # 设置训练精度的初始值 net.train() for batch, (img, label) in enumerate(train_data): img = img.reshape(img.size(0), -1) img = Variable(img) img = img.to(device) label = Variable(label) label = label.to(device) # 向前传播 out = net(img) loss = criterion(out, label.long()) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() # 记录误差 train_loss += loss.item() # 计算分类正确率 _, pred = out.max(1) num_correct = (pred == label.long()).sum().item() acc = num_correct / img.shape[0] if (batch + 1) % 200 == 0: print( '[INFO] Epoch-{}-Batch-{}: Train: Loss-{:.4f},Accuracy-{:.4f}'.format(epoch + 1, batch + 1, loss.item(), acc)) train_acc += acc losses.append(train_acc / len(train_data)) acces.append(train_acc / len(train_data)) eval_loss = 0 eval_acc = 0 # 测试集不训练 for img, label in test_data: img = img.reshape(img.size(0), -1) img = Variable(img) img = img.to(device) label = Variable(label) label = label.to(device) out = net(img) loss = criterion(out, label.long()) eval_loss += loss.item() _, pred = out.max(1) num_correct = (pred == label.long()).sum().item() acc = num_correct / img.shape[0] eval_acc += acc eval_losses.append(eval_loss / len(test_data)) eval_acces.append(eval_acc / len(test_data)) # 打印参数 set_epoch = epoch + 1 set_lossTrain = train_loss / len(train_data) set_AccTrain = train_acc / len(train_data) set_lossEval = eval_loss / len(test_data) set_AccEval = eval_acc / len(test_data) print('[INFO] Epoch-{}: Train: Loss-{:.4f},Accuracy-{:.4f} |Test:Loss-{:.4f}, Accuracy-{:.4f}'.format(set_epoch, set_lossTrain, set_AccTrain, set_lossEval, set_AccEval)) torch.save(net.state_dict(), 'saveNet.pth') t2 = time.time() t = t2 - t1 print(t)