pytorch 的数据加载到模型的操作顺序是这样的:
1. 创建一个 Dataset 对象
2. 创建一个 DataLoader 对象
3. 循环这个 DataLoader 对象,将img, label加载到模型中进行训练
数据加载
我们拿到手的数据大致分为一下三种:
- 标签在文件夹上的数据
- 标签在图片名上的数据
- 标签与名称储存在csv文件中
一、标签在文件夹上的数据集
from torch.utils.data import DataLoader
import torch
from torchvision import transforms, datasets
data_transform = transforms.Compose([
transforms.Resize(32), #缩放图片,保持长宽比不变,最短边为32像素
transforms.CenterCrop(32), #从图片中间开始切割出32*32大小的图片
transforms.ToTensor(), #将图片转换成Tensor,归一化至[0,1]
#标准化至[-1,1],规定均值和标准差
transforms.Normalize(mean=[0.492, 0.461, 0.417],std=[0.256, 0.248, 0.251])
])
hymenoptera_dataset = datasets.ImageFolder(root="./train", transform=data_transform)
#数据加载,每次传入4张图片进行训练,随机打乱
dataset_loader = torch.utils.data.DataLoader(hymenoptera_dataset,batch_size=4,shuffle=True)
二、标签在图片名上的数据
import os
from torch.utils.data import DataLoader,Dataset
from torchvision import transforms
from PIL import Image
class MyDataset(Dataset): #继承Dataset
def __init__(self,path_dir,transform=None): #初始化一些属性
self.path_dir = path_dir #文件路径
self.transform = transform #对图形进行处理
self.images = os.listdir(self.path_dir) #把路径下的所有文件放在一个列表中
def __len__(self): #返回整个数据集的大小
return len(self.images)
def __getitem__(self, index): #根据索引index返回图像及标签
image_index = self.images[index] #根据索引获取图像文件名称
img_path = os.path.join(self.path_dir,image_index) #获取图像的路径或目录
img = Image.open(img_path).convert('RGB') #读取图像
#根据目录名称获取图像标签(cat或dog)
label = img_path.split('//')[-1].split('.')[0]
#把字符转换为数字cat-0,dog-1
label = 1 if 'dog' in label else 0
if self.transform is not None:
img = self.transform(img)
return img,label
transform = transforms.Compose([
transforms.Resize(32), # 缩放图片,保持长宽比不变,最短边为32像素
transforms.CenterCrop(32), # 从图片中间开始切割出32*32大小的图片
transforms.ToTensor(), # 将图片转换成Tensor,归一化至[0,1]
#标准化至[-1,1],规定均值和标准差
transforms.Normalize(mean=[0.492, 0.461, 0.417], std=[0.256, 0.248, 0.251])
])
dataset = MyDataset(r"C:\Users\牛牛\Desktop\机器学习\标签在图片名上的数据集\data\train", transform=transform)
#使用DataLoader加载数据
dataloader = DataLoader(dataset, batch_size=4, shuffle=True)
数据集的划分
将加载的数据划分为训练集、验证集和测试集
标签在图片名上的数据/标签在文件夹上的数据集
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
from torchvision import transforms as T
from torch.utils.data import DataLoader, Dataset
import torch
from PIL import Image
class MyDataset(Dataset): # 继承Dataset
def __init__(self, root, transforms=None, train=True, test=False): # 初始化一些属性
self.test = test # 将test变量的赋值
imgs = [os.path.join(root, img) for img in os.listdir(root)] # 数据的路径列表
if self.test:
imgs = sorted(imgs, key=lambda x: int(x.split('.')[-2].split('\\')[-1]))
# 对测试集的数据进行排序
else:
imgs = sorted(imgs, key=lambda x: int(x.split('.')[-2]))
# 对非测试集的数据进行排序
# 排序的目的是便于后续的分割
imgs_num = len(imgs) # 获取数据的长度便于切分数据集
if self.test:
self.imgs = imgs # 将测试集的数据直接导入
elif train:
self.imgs = imgs[:int(0.7 * imgs_num)] # 将train中数据的70%给train
else:
self.imgs = imgs[int(0.7 * imgs_num):] # 剩下的30%做验证集
if transforms is None: # 对数据进行增强处理
normalize = T.Normalize(mean=[0.488, 0.455, 0.417],
std=[0.261, 0.255, 0.257])
if self.test or not train:
self.transforms = T.Compose([
T.Resize(28),
T.CenterCrop(28),
T.ToTensor(),
normalize
])
else:
self.transforms = T.Compose([
T.Resize(28),
T.CenterCrop(28),
T.RandomHorizontalFlip(),
T.ToTensor(),
normalize
])
def __len__(self): # 返回整个数据集的大小
return len(self.imgs)
def __getitem__(self, index): # 根据索引index返回图像及标签
img_path = self.imgs[index]
if self.test:
label = int(self.imgs[index].split('.')[-2].split('\\')[-1])
# 获取测试集文件名的部分作为标签
else:
label = 1 if 'dog' in img_path.split('\\')[-1] else 0
# 获取train中文件名中的标签并进行数字化,dog为1,cat为0
data = Image.open(img_path)
data = self.transforms(data)
return data, label
train_data = MyDataset(r"C:\Users\牛牛\Desktop\机器学习\标签在图片名上的数据集\data\train", train=True)
val_data = MyDataset(r"C:\Users\牛牛\Desktop\机器学习\标签在图片名上的数据集\data\train", train=False)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=4, shuffle=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=4, shuffle=True)
训练集验证集准备好后我们可以验证一下:
#训练集的数据
import torchvision
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# 显示图像
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# 随机获取部分训练数据
dataiter = iter(train_loader)
images, labels = dataiter.next()
# 显示图像
imshow(torchvision.utils.make_grid(images))
# 打印标签
print(' '.join('%s' % ["小狗" if labels[j].item()==1 else "小猫" for j in range(4)]))
输出:
#验证集的数据
import torchvision
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# 显示图像
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# 随机获取部分训练数据
dataiter = iter(val_loader)
images, labels = dataiter.next()
# 显示图像
imshow(torchvision.utils.make_grid(images))
# 打印标签
print(' '.join('%s' % ["小狗" if labels[j].item()==1 else "小猫" for j in range(4)]))
输出:
三、标签与名称储存在csv文件中
还有一种特殊的就是标签与名称的对应关系都储存在一个csv文件中:
其中id指的是图片的名字,breed指的是狗的品种,可以看到狗的品种非常的多,所以需要将每一个种类的数据照片放到对应种类命名的文件夹中,代码如下:
import math
import os
import shutil
from collections import Counter
data_dir = r"C:\Users\牛牛\Desktop\机器学习\猫狗识别_csv" #数据集的根目录
label_file = 'labels.csv' #根目录中csv的文件名加后缀
train_dir = 'train' #根目录中的训练集文件夹的名字
test_dir = 'test' #根目录中的测试集文件夹的名字
input_dir = 'train_valid_test' #用于存放拆分数据集的文件夹的名字,可以不用先创建,会自动创建
batch_size = 4 #送往训练的一批次中的数据集的个数
valid_ratio = 0.1 #将训练集拆分为90%为训练集10%为验证集
def reorg_dog_data(data_dir, label_file, train_dir, test_dir, input_dir,
valid_ratio):
# 读取训练数据标签,label.csv文件读取标签以及对应的文件名。
with open(os.path.join(data_dir, label_file), 'r') as f:
# 跳过文件头行(栏名称)。
lines = f.readlines()[1:]
tokens = [l.rstrip().split(',') for l in lines]
idx_label = dict(((idx, label) for idx, label in tokens))
labels = set(idx_label.values())
num_train = len(os.listdir(os.path.join(data_dir, train_dir)))#获取训练集的数量便于数据集的分割
# 训练集中数量最少一类的狗的数量。
min_num_train_per_label = (
Counter(idx_label.values()).most_common()[:-2:-1][0][1])
# 验证集中每类狗的数量。
num_valid_per_label = math.floor(min_num_train_per_label * valid_ratio)
label_count = dict()
def mkdir_if_not_exist(path):#判断是否有存放拆分后数据集的文件夹,没有就创建一个
if not os.path.exists(os.path.join(*path)):
os.makedirs(os.path.join(*path))
# 整理训练和验证集,将数据集进行拆分复制到预先设置好的存放文件夹中。
for train_file in os.listdir(os.path.join(data_dir, train_dir)):
idx = train_file.split('.')[0]
label = idx_label[idx]
mkdir_if_not_exist([data_dir, input_dir, 'train_valid', label])
shutil.copy(os.path.join(data_dir, train_dir, train_file),
os.path.join(data_dir, input_dir, 'train_valid', label))
if label not in label_count or label_count[label] < num_valid_per_label:
mkdir_if_not_exist([data_dir, input_dir, 'valid', label])
shutil.copy(os.path.join(data_dir, train_dir, train_file),
os.path.join(data_dir, input_dir, 'valid', label))
label_count[label] = label_count.get(label, 0) + 1
else:
mkdir_if_not_exist([data_dir, input_dir, 'train', label])
shutil.copy(os.path.join(data_dir, train_dir, train_file),
os.path.join(data_dir, input_dir, 'train', label))
# 整理测试集,将测试集复制存放在新建路径下的unknown文件夹中。
mkdir_if_not_exist([data_dir, input_dir, 'test', 'unknown'])
for test_file in os.listdir(os.path.join(data_dir, test_dir)):
shutil.copy(os.path.join(data_dir, test_dir, test_file),
os.path.join(data_dir, input_dir, 'test', 'unknown'))
#载入数据,进行数据的拆分
reorg_dog_data(data_dir, label_file, train_dir, test_dir, input_dir,valid_ratio)
当我们运行完程序后,可以看到:
运行代码之后我们创建了train_valid_test文件夹,测试集是我们要去测试的数据,没有标签,所以测试集图像都保存在unknown文件夹中,训练集、验证集和训练验证集文件夹下将每一个种类的数据照片放到了对应种类命名的文件夹中,此时就相当于标签在文件夹上,接下来的方法就与第一种猫狗二分类标签在文件夹上的方法类似,只不过这个是多分类,至此我们完成了数据的拆分
数据拆分后便可以进行数据的加载与训练集验证集的划分了,使用ImageFolder进行数据集的加载,在加载前需要进行数据强化函数的定义,也可以在未进行transforms处理之前对加载的数据集进行查验
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
import torch
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
# transform_train = transforms.Compose([
# # 随机对图像裁剪出面积为原图像面积0.08~1倍、且高和宽之比在3/4~4/3的图像,再放缩为高和宽均为224像素的新图像
# transforms.RandomResizedCrop(28, scale=(0.08, 1.0),
# ratio=(3.0/4.0, 4.0/3.0)),
# # 以0.5的概率随机水平翻转
# transforms.RandomHorizontalFlip(),
# # 随机更改亮度、对比度和饱和度
# transforms.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4),
# transforms.ToTensor(),
# # 对各个通道做标准化,(0.485, 0.456, 0.406)和(0.229, 0.224, 0.225)是在ImageNet上计算得的各通道均值与方差
# transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # ImageNet上的均值和方差
# ])
# # 在测试集上的图像增强只做确定性的操作
# transform_test = transforms.Compose([
# transforms.Resize(28),
# # 将图像中央的高和宽均为224的正方形区域裁剪出来
# transforms.CenterCrop(28),
# transforms.ToTensor(),
# transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
# ])
#也可以使用之前的transforms的函数
data_transform = transforms.Compose([
transforms.Resize(32), # 缩放图片(Image),保持长宽比不变,最短边为32像素
transforms.CenterCrop(32), # 从图片中间切出32*32的图片
transforms.ToTensor(), # 将图片(Image)转成Tensor,归一化至[0, 1]
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])# 标准化至[-1, 1],规定均值和标准差
#然后开始加载数据集
new_data_dir=r"C:\Users\牛牛\Desktop\机器学习\猫狗识别_csv\train_valid_test"
train_ds = datasets.ImageFolder(root=os.path.join(new_data_dir, 'train'),transform=data_transform)
valid_ds = datasets.ImageFolder(root=os.path.join(new_data_dir, 'valid'),transform=data_transform)
train_loader = torch.utils.data.DataLoader(train_ds,batch_size=4,shuffle=True)
valid_loader = torch.utils.data.DataLoader(valid_ds,batch_size=4,shuffle=True)
可以检查一下我们经过划分和处理过后的数据:
#验证数据
for img, label in valid_ds: #也可更改为训练集数据
print("图像img的形状{},标签label的值{}".format(img.shape, label))
print("图像数据预处理后:\n",img)
break
输出:
图像img的形状torch.Size([3, 32, 32]),标签label的值0
图像数据预处理后:
tensor([[[-1.9295, -1.8953, -1.9638, ..., 1.8550, 1.9235, 1.9407],
[-1.8268, -1.9124, -1.8953, ..., 1.8379, 1.9235, 1.9407],
[-1.2103, -1.9124, -1.8439, ..., 1.8550, 1.9749, 1.9749],
...,
[-0.7993, -1.6213, -1.6898, ..., -0.5767, -0.5767, -0.6281],
[-0.4911, -1.0219, -1.7412, ..., -0.5767, -0.6452, -0.6794],
[-0.4739, -0.6281, -1.5185, ..., -0.5253, -0.6965, -0.7479]],
[[-1.8256, -1.8256, -1.8782, ..., 1.3782, 1.5707, 1.3957],
[-1.7206, -1.8431, -1.8081, ..., 1.4307, 1.6583, 1.3957],
[-1.1604, -1.8431, -1.7556, ..., 1.4482, 1.7283, 1.4132],
...,
[-0.9678, -1.6681, -1.6856, ..., -1.0728, -1.0728, -1.1253],
[-0.8277, -1.2129, -1.7381, ..., -1.0728, -1.1429, -1.1604],
[-0.8277, -0.8978, -1.6155, ..., -1.0203, -1.1779, -1.2479]],
[[-1.5081, -1.4907, -1.5430, ..., 1.0888, 1.2805, 0.9319],
[-1.4559, -1.5430, -1.4907, ..., 1.1759, 1.4200, 0.9668],
[-1.0898, -1.5604, -1.4559, ..., 1.1759, 1.4722, 0.9319],
...,
[-1.1770, -1.4907, -1.4210, ..., -1.1770, -1.1770, -1.2119],
[-1.1944, -1.3513, -1.5779, ..., -1.1770, -1.2293, -1.1944],
[-1.1247, -1.1247, -1.5430, ..., -1.1073, -1.2467, -1.2293]]])
import matplotlib.pyplot as plt
import numpy as np
import torchvision
# 显示图像
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# 随机获取部分训练数据
dataiter = iter(train_loader) #换此处的值即可切换数据集
images, labels = dataiter.next()
# 显示图像
imshow(torchvision.utils.make_grid(images))
# 打印标签
print(labels[0],labels[1],labels[2],labels[3])
输出: