文章结构
-
自定义Dataset的基本结构
-
使用Torchvisiom进行类型转换
-
使用Torchvision的另一种方法
-
Incorporating Pandas
-
Incorporating Pandas with More Logic
-
使用Data Loader
自定义Dataset的基本结构
from torch.utils.data.dataset import Dataset
class MyCustomDataset(Dataset):
def __init__(self, ...):
# 填充
def __getitem__(self, index):
# 填充
return (img, label)
def __len__(self):
return count # 你有多少张图片
- 这是必须填充用来获得自定义数据集的框架。数据集必须包含以下函数,以便稍后由数据加载程序使用。
__init__() #函数是初始逻辑发生的地方,比如读取csv、分配转换等
__getitem__()#函数返回数据和标签。这个函数是从dataloader中被调用的,如下所示:
img, label = MyCustomDataset.__getitem__(99) # 有99个数据
- 因此,索引参数(index)是你要返回的第n个数据/图像(tensor)。
__len__()#返回你的样本数量
- 注意
__getitem__()
返回一个特殊的数据类型,比如tensor,numpy array等,如果不是这些类型,在data loader将会报错。
TypeError: batch must contain tensors, numbers, dicts or lists; found <class 'PIL.PngImagePlugin.PngImageFile'>
使用Torchvisiom进行类型转换
- 一般在
__init__()
里面都会写成transforms = None
,这是为了方便在调用dataset类的时候传入自定义的transforms
from torch.utils.data.dataset import Dataset
from torchvision import transforms
class MyCustomDataset(Dataset):
def __init__(self, ..., transforms=None):
# 填充
#...
self.transforms = transforms
def __getitem__(self, index):
# 填充
#...
data = # 从文件或者图像中读取的数据
if self.transforms is not None:
data = self.transforms(data)
# 如果转换变量不是空
# 按照传入的转换格式来转换数据
return (img, label)
def __len__(self):
return count
if __name__ == '__main__':
# 自定义transforms
transformations = transforms.Compose([transforms.CenterCrop(100), transforms.ToTensor()])
# 调用数据集
custom_dataset = MyCustomDataset(..., transformations)
使用Torchvision的另一种方法
- 如果不喜欢在外面自定义transforms,可以在dataset类里面定义好,不过这样降低了程序的可读性。
from torch.utils.data.dataset import Dataset
from torchvision import transforms
class MyCustomDataset(Dataset):
def __init__(self, ...):
# 填充
#...
# 单独定义转换
self.center_crop = transforms.CenterCrop(100)
self.to_tensor = transforms.ToTensor()
# 也可以组合定义
self.transformations = transforms.Compose([
transforms.CenterCrop(100),
transforms.ToTensor()])
def __getitem__(self, index):
# 填充
#...
data = # 从文件或者图像中读取的数据
#对应了在__init__()中定义的三个transforms
data = self.center_crop(data)
data = self.to_tensor(data)
data = self.trasnformations(data)
return (img, label)
def __len__(self):
return count
if __name__ == '__main__':
# 调用dataset
custom_dataset = MyCustomDataset(...)
Incorporating Pandas
- 假设,我们想通过pandas从csv文件中读取数据。第一个例子如下的csv文件,包含文件名和标签,和一个额外的操作指示器根据这个额外的操作标志我们对图像做一些操作。
File Name |
Label |
Extra Operation |
tr_0.png |
5 |
TRUE |
tr_1.png |
0 |
FALSE |
tr_2.png |
4 |
FALSE |
- 如果我们想建立一个自定义数据集,读取图像位置从这个csv文件,然后我们可以做如下操作
class CustomDatasetFromImages(Dataset):
def __init__(self, csv_path):
'''
Args:
csv_path (string): csv文件路径
img_path (string): 图片文件路径
transform: pytorch变换用于变换和张量转换
'''
# Transforms
self.to_tensor = transforms.ToTensor()
# 读取csv文件
self.data_info = pd.read_csv(csv_path, header=None)
# 第一列包含图像路径
self.image_arr = np.asarray(self.data_info.iloc[:, 0])
# 第二列是标签
self.label_arr = np.asarray(self.data_info.iloc[:, 1])
# 第三列是操作指示符
self.operation_arr = np.asarray(self.data_info.iloc[:, 2])
# 计算整个数据集的长度
self.data_len = len(self.data_info.index)
def __getitem__(self, index):
# 从pandas df获取图片文件名
single_image_name = self.image_arr[index]
# 打开图片
img_as_img = Image.open(single_image_name)
# 检查是否有操作
some_operation = self.operation_arr[index]
# 如果有操作的话
if some_operation:
# 对图像做一些操作
# ...
# ...
pass
# 把图像变换成张量
img_as_tensor = self.to_tensor(img_as_img)
# 根据裁剪的panda列获取图像的标签
single_image_label = self.label_arr[index]
return (img_as_tensor, single_image_label)
def __len__(self):
return self.data_len
if __name__ == "__main__":
# 调用 dataset
custom_mnist_from_images = CustomDatasetFromImages('../data/mnist_labels.csv')
Incorporating Pandas with More Logic
- 另一个从csv中读取图像的例子,其中每个像素的值都在一个列中。这时,只需要返回张量以及其标签。数据被分成像素。
Lbel |
pixel_1 |
pixel_2 |
... |
1 |
50 |
99 |
... |
0 |
21 |
223 |
... |
9 |
44 |
112 |
... |
... |
... |
... |
... |
class CustomDatasetFromCSV(Dataset):
def __init__(self, csv_path, height, width, transforms=None):
'''
Args:
csv_path (string): csv文件路径
height (int): 图片高度
width (int): 图片宽度
transform: pytorch transforms for transforms and tensor conversion
'''
self.data = pd.read_csv(csv_path)
self.labels = np.asarray(self.data.iloc[:, 0])
self.height = height
self.width = width
self.transforms = transform
def __getitem__(self, index):
single_image_label = self.labels[index]
# Read each 784 pixels and reshape the 1D array ([784]) to 2D array ([28,28])
img_as_np = np.asarray(self.data.iloc[index][1:]).reshape(28,28).astype('uint8')
# 将图像从numpy数组转换为PIL图像,模式“L”为灰度
img_as_img = Image.fromarray(img_as_np)
img_as_img = img_as_img.convert('L')
# 把图像变换成tensor
if self.transforms is not None:
img_as_tensor = self.transforms(img_as_img)
# 返回图片和标签
return (img_as_tensor, single_image_label)
def __len__(self):
return len(self.data.index)
if __name__ == "__main__":
transformations = transforms.Compose([transforms.ToTensor()])
custom_mnist_from_csv = CustomDatasetFromCSV('../data/mnist_in_csv.csv', 28, 28, transformations)
使用Data Loader
- 在pytorch中DataLoader只需要调用
__getitem__()
然后把他们打包成一个批次。我们也可以不使用Dataloader每调用__getitem()__
一次就把数据传入到模型(远没有使用DataLoader方便)。从上面的示例继续,如果我们假设有一个名为CustomDatasetFromCSV的自定义数据集,那么我们可以像这样调用DataLoader
if __name__ == "__main__":
# 定义 transforms
transformations = transforms.Compose([transforms.ToTensor()])
# 定义dataset
custom_mnist_from_csv = CustomDatasetFromCSV('../data/mnist_in_csv.csv',28, 28,transformations)
# 定义data loader
mn_dataset_loader = torch.utils.data.DataLoader(dataset=custom_mnist_from_csv,
batch_size=10,
shuffle=False)
for images, labels in mn_dataset_loader:
# 将数据送入模型
- DataLoader的第一个参数是数据集,从那里它调用该数据集的
__getitem__()
.batch_size确定一个批次传入的数据量,如果我们假设一张图片的tensor是[1*28*28] ---> [D:1,H:28,W:28]
那么用这个DataLoader返回的tensor是[10*1*28*28]
网友评论