第二周:数据读取Dataloader,Dataset|图像预处理Transforms

基本概念

  • 数据
    • 数据收集:Img,Label
    • 数据划分:train valid test, valid用于挑选没有过拟合的模型,test 测试挑选出来的模型的性能
    • 数据读取:DataLoader
      • Sampler: 生成索引index
      • DataSet: 根据Img读取标签
    • 数据预处理:transforms
  • 模型
  • 损失函数
  • 优化器
  • 迭代训练

1. DataLoader

torch.utils.data.DataLoader(dataset,batch_size=1,num_works=0,shuffle=False,drop_last=False)

  • dataset:数据从哪里读取及如何读取
  • batchsize:批大小
  • num_works:是否多进程读取数据
  • drop_last:当样本数不能被batchsize整除时,是否舍弃最后一批数据

Epoch:所有训练样本都已输入到模型中1次
Itertion:一批样本输入到模型中
Batchsize:批大小,决定一个Epoch有多少个Iteration
例子
样本总数87,BatachSize 8
1 Epoch = 10 Iteration,如果drop_last=False
1 Epoch = 11 Iteration,如果drop_last=True

2. DataSets

torch.utils.data.Dataset
功能:Dataset抽象类,所有自定义的Dataset都需要继承它,并且复写
__getitem__(),接受一个索引,返回一个样本

class Dataset(object):
	def __getitem__(self,index):
		raise NotImplementedError
	def __add__(self,other):
		return ConcatDataset([self,other])

DataSet,DataLoader例子:

class RMBDataset(Dataset):
    def __init__(self, data_dir, transform=None):
        """
        rmb面额分类任务的Dataset
        :param data_dir: str, 数据集所在路径
        :param transform: torch.transform,数据预处理
        """
        self.label_name = {"1": 0, "100": 1}
        self.transform = transform
        self.data_info = self.get_img_info(data_dir)  
        # data_info存储所有图片路径和对应的标签,在DataLoader中通过index读取样本

    def __getitem__(self, index):
        path_img, label = self.data_info[index]
        img = Image.open(path_img).convert('RGB')     # 0~255
		
		# 如果初始化的时候制定了一种transform的方法
        if self.transform is not None:
            img = self.transform(img)   # 在这里做transform,转为tensor等等

        return img, label

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

    @staticmethod
    def get_img_info(data_dir):
        data_info = list()
        for root, dirs, _ in os.walk(data_dir):
            # 遍历类别
            for sub_dir in dirs:
                img_names = os.listdir(os.path.join(root, sub_dir))
                img_names = list(filter(lambda x: x.endswith('.jpg'), img_names))
                # 遍历图片
                for i in range(len(img_names)):
                    img_name = img_names[i]
                    path_img = os.path.join(root, sub_dir, img_name)
                    label = rmb_label[sub_dir]
                    data_info.append((path_img, int(label)))
        return data_info

# 构建MyDataset实例
train_data = RMBDataset(data_dir=train_dir, transform=train_transform)
# 构建DataLoader
train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
# data是一个含有input和labels的list
for i, data in enumerate(train_loader):

具体流程:
第一步:for i, data in enumerate(train_loader)
第二步:DataLoader类判断num_workers是否大于0,这里等于0,所以运行return self._get_iterator()
第三步:DataLoaderIter __next__函数

3. Transform

3.1 Transform的运行机制

torchvision:计算机视觉工具包,它包括

  • torchvision.transforms:常用的图像预处理方法
  • torchvision.datasets:常用数据集的dataset实现,MNIST,CIFAR-10,ImageNet等,即得到数据的方法
  • torchvision.model:常用的模型预训练,AlexNet,VGG,ResNet,GoogleLeNet等。

3.2 Transforms 例子

类似5年高考3年模拟😂.,模拟的就是transform的
常用的图像预处理方法:

  • 数据中心化
  • 数据标准化
  • 缩放
  • 裁剪
  • 旋转
  • 翻转
  • 填充
  • 噪声添加
  • 灰度变换

例子:

norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]
# 按照顺序对图像进行操作
train_transform = transforms.Compose([
    transforms.Resize((32, 32)), # 将图像进行缩放
    transforms.RandomCrop(32, padding=4), # 将图像进行裁剪
    transforms.ToTensor(), # 变成Tensor,值从0,255变为0,1
    transforms.Normalize(norm_mean, norm_std), # 将均值变为0,标准差变为1
])

# 去除了RandomCrop这个裁剪增强擦操作
valid_transform = transforms.Compose([
    transforms.Resize((32, 32)),
    transforms.ToTensor(),
    transforms.Normalize(norm_mean, norm_std),
])

# 将构建好的Transorm传入DataSet的初始化过程
# transform在Dataset的__getitem__方法中被调用
train_data = RMBDataset(data_dir=train_dir, transform=train_transform)
valid_data = RMBDataset(data_dir=valid_dir, transform=valid_transform)

# DataSet -> DataLoader
train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
valid_loader = DataLoader(dataset=valid_data, batch_size=BATCH_SIZE)

3.3 transforms.Normalize

transforms.Normalize(mean,std,inplace=False)

  • mean:各通道的均值
  • std:各通道的标准差
  • inplace:是否原地操作

Rq:

  • tensor.sub_中的_表示inplace操作。
  • 可以加速模型收敛

3.4 transfrom中其他数据增强方法

目的:让模型更具泛化能力
以下函数直接填在transforms.Compose()里面即可

transforms.CenterCrop(196)
将图像变为196196的,如果比原图大,则多出部分填充空白。196196的中心是。

transforms.RandomCrop(size,padding=None,pad)
从图片中随机裁剪出尺寸为size的图片。

  • size:如224意味着224*224
  • padding:设置填充大小
    • 当为a时,上下左右均填充 a 个像素
    • 当为(a, b)时,上下填充 b 个像素,左右填充 a 个像素
    • 当为(a, b, c, d) 时,左,上,右,下分别填充 a, b, c, d。
  • pad_if_need:如我们设置的size,则要把它改为True,否则会报错
  • fill:在paddig_mode为constant时,设置填充的像素值,如(R,G,B)=(255,0,0)
  • padding_mode:填充模式
    • constant:像素值由fill决定
    • edge:像素值由图像边缘像素决定
    • reflect:镜像填充,最后一个像素不镜像,如[1,2,3,4] -> [3,2,1,2,3,4,3,2]
    • symmetric:镜像填充,最后一个像素镜像,如[1,2,3,4] -> [2,1,1,2,3,4,4,3]

例子:
transfroms.RandomCrop(224,padding=16)
最后输出的图像为尺寸为224+16+16=256,然后我们随机在上面选取224*224的区域放上我们的裁剪过后的图像,剩下的就是padding。

transforms.RandomResizedCrop(size,scale=(0.08,1.0),ratio=(3/4,4/3),interpolation)

  • size:所需裁剪图片尺寸,最后resize到这个尺寸
  • scale:随机裁剪面积比例,默认是从0.08到1中间随机选取一个数字
  • ratio:随机长宽比,默认是(3/4,4/3),长比宽的大小
  • interpolation:插值方法
    • PIL.Image.NEAREST
    • PIL.Image.BILINEAR
    • PIL.Image.BICUBIC

transforms.FiveCrop(size)
功能:在图像的上下左右以及中心裁剪出尺寸为 size 的 5 张图片。
返回的是一个tuple,transform.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops]))

  • size: 所需裁剪图片尺寸

transforms.TenCrop(size,vertical_flip=False)
功能:在图像的上下左右以及中心裁剪出尺寸为 size 的 5 张图片, TenCrop 对这 5 张图片进行水平或者垂直镜像获得 10 张图片

  • vertical_flip = False,不进行垂直翻转,但进行水平翻转。

transforms.RandomHorizontalFlip(p)

  • 有p的概率进行水平翻转

transforms.RandomVerticalFlip(p)

  • 由p的概率进行垂直翻转

transforms.RondomRotation(degrees,resample=False,expand=False)

  • degrees:旋转角度。为a时,在(-a,a)之间选择旋转角度,为(a,b)时,在(a,b)之间选择旋转角度
  • resample:重采样方法
  • expand:是否扩大图片,以保持原图信息。因为旋转之后有可能超过原图的尺寸。如果要expand,需要resize,从而将所有图片的尺寸变为一样。expand是对于center计算的,如果旋转中心不是中心,不一定会完全保留所有的原图信息。
  • center:默认是中心,可以指定为左上角

3.5 图像变换

transform.Pad(padding,fill=0,padding_mode='constant')

  • padding,padding_mode,fill:见上面transforms.RandomCrop的介绍。

transforms.ColorJotter(brightness=0,contrast=0,saturation=0,hue=0)
功能:调整亮度、对比度、饱和度和色相

  • brightness: 亮度调整因子,当为a时,从[max(0,1-a),1+a]中随机选择,小于1变暗。当为(a,b)时,从[a,b]中随机选择
  • contrast:对比度参数,同brightness
  • saturation:饱和度,同brightness
  • hue:色相参数,若为a,从[-a,a]中选择,当为(a,b)时,从[a,b]中选择,最大区间为[-0.5,0.5]

转为灰度图
transforms.Grayscale(num_output_channels)
transforms.RandomGrayscale(num_output_channels,p=0.1)
功能:一定概率将图片转换为灰度图

  • num_output_channels:输出通道数1或者3

仿射变换
RandomAffine(degrees,translate=None,scale=None,shear=None,resample=False,fillcolor=0)
功能:对图像进行仿射变换,由5种基本原子变换构成,分别是旋转、平移、缩放、错切和翻转。

  • degrees: 中心旋转角度设置
  • translate: 平移区间设置,如(a,b),a设置宽(width),b设置高(height)。图像在宽维度平移的区间为-img_width*a < dx < img_width * a
  • scale:缩放比例(以面积为单位)
  • fill_color:填充颜色设置
  • shear:错切角度设置,有水平、垂直错切。水平错切类似于把一堆书水平摞在桌子上,然后每一本书相右都移动一点。
    • a,仅在x轴错切,错切角度在(-a,a)之间
    • (a,b),则a设置x轴角度,b设置y的角度
    • (a,b,c,d),则a,b设置x轴角度,c,d设置y轴角度。
  • resample:重采样方法,有NEAREST、BILINEAR、BICUBIC。

随机遮挡
RandomErasing(p=0.5,scale=(0.02,0.33),ratio=(0.3,3.3),value=0,inplace=False)
功能:对图像进行随机遮挡

  • p:概率值,执行该操作的概率
  • scale:遮挡区域的面积
  • ratio:遮挡区域长宽比
  • value:设置遮挡区域的值,如(R,G,B)或者(Gray),如果是一个字符串就是随机噪声(类似于电视雪花屏)
    ⚠️ 它接受的是一个Tensor,不像之前的函数是输入一个PIL Image。

transform.Lambda(lambd)
功能:用户自定义lambda方法

transforms.TenCrop(200,vertical_flip=False)
# crops是上一个函数的输出
# 我们把Stack一个Tensor的list (C,H,W)
# 然后stack成为一个(10,C,H,W)的Tensor
transforms.Lambda(lambda crops : torch.stack([transforms.Totensor()(crop) for crop in crops]))

3.6 随机挑选一些transform

transforms.RandomChoice([transforms1,transforms2,transforms3])
功能:从一系列transforms操作中随机挑选一个

transforms.RandomApply([transforms1,transforms2,transforms3])
功能:依据概率执行一组transforms操作

transforms.RandomOrder([transforms1,transforms2,transforms3])
功能:对一组transforms操作打乱顺序

3.7 自定义transforms

回顾:
transform方法是在Compose这个class里面的__call__函数被调用的。

class Compose(object):
	def __call__(self,img):
		for t in self.transforms:
			img = t(img)
		return img
  • 只能一个输入,一个输出
    Sol:
    通过类实现多参数传入:
class YourTransforms(object):
	def __init__(self,...):
		# 参数传入
	def __call__(self,img):
		# 实现自己要的功能
		return img

例子:自定义椒盐噪声
白点盐噪声,黑色椒噪声。
信噪比(Signal-Noise Rate, SNR):图像中图像像素的占比,1的话就是原图。

class AddPepperNoise(object):
	def __init__(self,snr,p=0.9):
		self.snr = snr
		self.p = p
	
	def __call__(self,img):
		if random.uniform(0,1)<self.p:
			img_ = np.array(img).copy()
			h,w,c = img_.shape
			signal_pct = self.snr
			noise_pct = 1-self.snr
			maksk = np.random.choice = np.random.choice((0,1,2),size=(h,w,1),p=[signal_pct,noise_pct/2.,noise_pct/2.])
			mask = np.repeat(mask,c,axis=2) #对另外两个通道也同样处理,即一个像素在一个通道上为椒噪声,在其他两个通道上也是椒噪声
			img_[mask == 1] = 255 # 盐噪声
			img_[mask == 2] = 0 # 椒噪声
			return Image.fromarray(img_.astype('uint8').convert('RGB'))
		else:
			return img
# 之后正常用AddPepperNoise(0.8,p=0.9)加入到tranform.Compose里面即可。
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值