pytorch基础
数据导入
数据增强
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import torch from torchvision import transforms from PIL import Image
transform = transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.RandomVerticalFlip(), transforms.RandomRotation(30), transforms.RandomResizedCrop(224), transforms.ColorJitter(brightness=0.5, contrast=0.5), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])
image = Image.open('path_to_your_image.jpg') transformed_image = transform(image)
|
常见操作
transforms.RandomHorizontalFlip(p=0.5):随机以概率p水平翻转
transforms.RandomVerticalFlip(p=0.5):随机以概率p垂直翻转
transforms.RandomRotation(degrees):随机旋转,角度范围是 -degrees 到 degrees
transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(3/4, 4/3),interpolation=PIL.Image.BILINEAR):随机裁剪并缩放图像到指定大小,scale表示裁剪区域相对于原始图像的比例,ratio表示裁剪区域的宽高比,interpolation表示插值方法。
transforms.RandomCrop(size,padding=None,pad_if_needed=False,fill=0,padding_mode='constant')
size:裁剪的大小
padding:设置填充大小。
- 当为 a 时,上下左右均填充 a 个像素。
- 当为 (a, b) 时, 上下填充 b 个像素, 左右填充 a 个像素。
- 当为 (a, b, c, d) 时,左、上、右、下分别填充 a、b、c、d 个像素。
pad_if_needed:若图像小于设定 size,则填充,此时该项需要设置为 True。
padding_mode:填充模式,有 4 种模式:
- 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]。
- fill:padding_mode = ‘constant’ 时,设置填充的像素值。
transforms.Pad(padding,fill=0,padding_mode='constant')
padding:设置填充大小。
- 当为 a 时,上下左右均填充 a 个像素。
- 当为 (a, b) 时, 上下填充 b 个像素, 左右填充 a 个像素。
- 当为 (a, b, c, d) 时,左、上、右、下分别填充 a、b、c、d 个像素。
fill:padding_mode = ‘constant’ 时,设置填充的像素值。
padding_mode:填充模式,有 4 种模式:
- 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]。
transforms.ColorJitter(brightness, contrast, saturation, hue):调整亮度、对比度、饱和度、色相
transforms.Grayscale(num_output_channels=1):将图像转换为灰度图像,num_output_channels=1 表示转换为单通道灰度图像,num_output_channels=3 表示转换为三通道灰度图像(每个像素的三个通道值相同)。
transforms.RandomGrayscale( num_output_channels,p=0.1):随机以概率p将图像转换为灰度图像
transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False):随机擦除,p表示以概率p执行擦除操作,scale表示擦除区域的面积占比,ratio表示擦除区域的宽高比,value表示擦除区域的填充值,inplace表示是否在原图像上进行操作。
transforms.ToTensor():转为Tensor
transforms.Normalize(mean, std):归一化,mean和std分别为每个通道的均值和标准差
应用
这些变换操作应用于数据集(如ImageFolder),然后在训练时使用DataLoader来加载图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| from torchvision import datasets from torch.utils.data import DataLoader
dataset = datasets.ImageFolder(root='path_to_dataset', transform=transform)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
for images, labels in dataloader: # 在这里可以使用增强后的图像进行训练 pass
|
imgaug
相比于torchvision.transforms,它提供了更多的数据增强方法
官方例程
官方文档
github
安装
1 2
| conda config --add channels conda-forge conda install imgaug
|
简易使用
- 导入
imagaug不能进行图像的IO操作,建议使用imageio进行读入,如果使用的是opencv进行文件读取的时候,需要进行手动改变通道,将读取的BGR图像转换为RGB图像。除此以外,当我们用PIL.Image进行读取时,因为读取的图片没有shape的属性,所以我们需要将读取到的img转换为np.array()的形式再进行处理。
1 2
| img = imageio.imread("./Lenna.jpg")
|
- 定义增强方法
imgaug包含了许多从Augmenter继承的数据增强的操作,需要利用imgaug.augmenters.Sequential()来构造我们数据增强的pipline
1 2 3 4 5
| iaa.Sequential(children=None, # Augmenter集合 random_order=False, # 是否对每个batch使用不同顺序的Augmenter list name=None, deterministic=False, random_state=None)
|
1 2 3 4 5 6 7 8 9
| aug_seq = iaa.Sequential([ iaa.Affine(rotate=(-25,25)), iaa.AdditiveGaussianNoise(scale=(10,60)), iaa.Crop(percent=(0,0.2)) ])
image_aug = aug_seq(image=img) ia.imshow(image_aug)
|
- 针对一个batch的图片进行增强时,只需要将待处理的图片放在一个
list中,并将函数的image改为images即可进行数据增强操作
1 2 3 4 5 6 7 8 9 10
| aug_seq = iaa.Sequential([ iaa.Affine(rotate=(-25, 25)), iaa.AdditiveGaussianNoise(scale=(10, 60)), iaa.Crop(percent=(0, 0.2)) ])
images_aug = aug_seq.augment_images(images = images)
ia.imshow(np.hstack(images_aug))
|
- 对批次的图片分部分处理,可以通过imgaug.augmenters.Sometimes()对batch中的一部分图片应用一部分Augmenters,剩下的图片应用另外的Augmenters。
1 2 3 4 5 6 7
| iaa.Sometimes(p=0.5, # 代表划分比例 then_list=None, # Augmenter集合。p概率的图片进行变换的Augmenters。 else_list=None, #1-p概率的图片会被进行变换的Augmenters。注意变换的图片应用的Augmenter只能是then_list或者else_list中的一个。 name=None, deterministic=False, random_state=None)
|
自定义块
1 2 3 4 5 6 7 8 9 10 11 12 13
| import torch from torch import nn from torch.nn import functional as F
class MLP(nn.Module): def __init__(self): super().__init__() self.hidden = nn.Linear(20, 256) self.out = nn.Linear(256, 10) def forward(self, X): return self.out(F.relu(self.hidden(X)))
|
参数管理
将神经网络(nn.Module)看作是一个参数容器,它包含了所有的参数(权重和偏置),以及一些方法(如前向传播)。
- 参数访问
net.state_dict():返回一个字典,包含了模型的所有参数(权重和偏置),还可以利用net[n]来提取第n层的参数
net[n].weight:第n层的权重,net[n].weight.data:第n层的权重数据
net[n].bias:第n层的偏置,net[n].bias.data:第n层的偏置数据
net.parameters():返回一个迭代器,包含了模型的所有参数
- 参数初始化
1 2 3 4 5 6
| def init_normal(m): if type(m) == nn.Linear: nn.init.normal_(m.weight, mean=0, std=0.01) nn.init.zeros_(m.bias)
net.apply(init_normal)#遍历所有层,对每个层应用init_normal函数
|
1 2 3 4 5 6
| def init_constant(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 1) nn.init.zeros_(m.bias)
net.apply(init_constant)
|
可以对不同层应用不同的初始化函数: 1 2 3 4 5 6 7 8 9 10 11
| def init_xavier(m): if type(m) == nn.Linear: nn.init.xavier_uniform_(m.weight) nn.init.zeros_(m.bias)
def init_42(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 42) nn.init.zeros_(m.bias) net[0].apply(init_xavier) net[1].apply(init_42)
|
- 参数绑定
1 2 3 4 5
| shared = nn.Linear(8, 8) net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), shared, nn.ReLU(), shared, nn.ReLU(), nn.Linear(8, 1))
|
shared层的参数在不同层之间共享,即shared层的参数在不同层之间是指向同一个内存地址的
自定义层
可以把层作为组件嵌套进更复杂的模型
1 2 3 4 5 6
| class MyLinear(nn.Module): def __init__(self): super().__init__() def forward(self, X): return X - X.mean()
|
带参数的层:
1 2 3 4 5 6 7 8
| class MyLinear(nn.Module): def __init__(self, in_features, out_features): super().__init__() self.weight = nn.Parameter(torch.randn(in_features, out_features)) self.bias = nn.Parameter(torch.zeros(out_features)) def forward(self, X): return X @ self.weight + self.bias
|
自定义模型:
1 2 3 4 5 6 7 8 9 10 11
| class MyMLP(nn.Module): def __init__(self): super().__init__() self.linear = MyLinear(20, 256) self.out = nn.Linear(256, 10)
def forward(self, X): X = self.linear(X) return self.out(F.relu(X)) # return self.out(X) net = MyMLP()
|
保存与加载
- 加载和保存模型参数
torch.save(net.state_dict(), 'net.params'):保存模型参数
net.load_state_dict(torch.load('net.params')):加载模型参数
- 加载和保存整个模型
torch.save(net, 'net.pt'):保存整个模型
net = torch.load('net.pt'):加载整个模型
net.eval():将模型设置为评估模式,即关闭dropout和batch normalization等层的训练模式
net.train():将模型设置为训练模式,即开启dropout和batch normalization等层的训练模式