1.nn.module
使用nn.module有很多的好处
1)可以使用使用现成的神经网络模块
nn.Linear nn.ReLU nn.Sigmoid nn.Conv2d nn.CosineEmbeddingLoss nn.Dropout
2)nn.Sequential方便的构造神经网络
self.model = nn.Sequential( nn.Linear(784, 200), nn.LeakyReLU(inplace=True), nn.Linear(200, 200), nn.LeakyReLU(inplace=True), nn.Linear(200, 10), nn.LeakyReLU(inplace=True), )
3)可以管理参数
# 构建两层全连接层 net = nn.Sequential( nn.Linear(4,2), nn.Linear(2,2), ) list(net.parameters())[0] # 直接查看第一层参数权重w # Parameter containing: # tensor([[-0.0824, 0.2460, -0.0154, 0.1714], # [ 0.1619, 0.1596, 0.0725, -0.1022]], requires_grad=True) list(net.parameters())[2] # 直接查看第二层参数权重w # Parameter containing: # tensor([[-0.5342, 0.0013], # [ 0.3502, -0.2241]], requires_grad=True) # list(net.parameters()) # 这句可以直接的查看全部的参数,且pytorch会给这些参数全部都赐予宇哥名字 dict(net.named_parameters()).items() # dict_items([( # '0.weight', Parameter containing: # tensor([[-0.0824, 0.2460, -0.0154, 0.1714], # [ 0.1619, 0.1596, 0.0725, -0.1022]], requires_grad=True)), # ('0.bias', Parameter containing: # tensor([-0.2492, -0.4491], requires_grad=True)), # ('1.weight', Parameter containing: # tensor([[ 0.0464, -0.1378], # [ 0.3071, 0.3216]], requires_grad=True)), # ('1.bias', Parameter containing: # tensor([0.1583, 0.4684], requires_grad=True))]) # 由于设置的是两层网络,所以会有两层权值w与偏置b,这些参数的shape都是与所设定的相匹配的 optimizer = optim.SGD(net.parameters(),lr=1e-3) # 由于pytorch帮助我们管理了参数,所以直接将net.parameters()直接传入优化器即可
4)可以得到网络结构的直系亲属
# 一个比较小的神经网络 class BasicNet(nn.Module): def __init__(self): super(BasicNet, self).__init__() self.net = nn.Linear(4, 3) def forward(self, x): return self.net(x) # 一个比较大的神经网络 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.net = nn.Sequential(BasicNet(), nn.ReLU(), nn.Linear(3, 2)) def forward(self, x): return self.net(x) # 也就是可以嵌套的定义神经网络
5)使用GPU加速
# GPU加速操作 device = torch.device('cuda') # 抽象出一个设备,一会来讲数据放在gpu上面 # device = torch.device('cpu') net = Net() net.to(device) # 需要注意,以上的两个net是同一样的内容,也就是对于一个网络来说返回的内容是一样的 # 但是,如果是对于一堆数据那返回内容的就是不一样的 # net.to(device) == net = net.to(device)
6)保存网络的中间状态
# save与load的操作 device = torch.device('cuda') net = Net() net.to(device) # 将训练过程中的中间数据参数net.state_dicta()保存在ckpt.mdl文件中 torch.save(net.state_dicta(),'ckpt.mdl') # 将ckpt.mdl文件中的参数解压出来然后加载load_state_dict都net网络中 net.load_state_dict(torch.load('ckpt.mdl')) # 有这个加载与保存参数的操作就不需要从0开始训练网络
7)训练或者测试过程状态的转换
# 状态的切换操作 # eg:Dropout与BatchNorm这样的操作,训练与测试的操作是不一样的 # 因此分别需要对参数状态进行切换 device = torch.device('cuda') net = Net() net.to(device) # 训练过程,进行train状态的切换 net.train() ... # 测试过程,进行test状态的切换 net.eval() ...
8)实现自定义的类
# 例子一: # 自定义的实现一个类函数 class Flatten(nn.Module): def __init__(self): super(Flatten, self).__init__() # 默认是将第一个维度保留下来,然好将后面的维度全部打平来操作 def forward(self, input): return input.view(input.size(0), -1) class TestNet(nn.Module): def __init__(self): super(TestNet, self).__init__() self.net = nn.Sequential(nn.Conv2d(1, 16, stride=1, padding=1), nn.MaxPool2d(2, 2), # 将数据打平 Flatten(), nn.Linear(1*14*14, 10)) def forward(self, x): return self.net(x) # 将自定义的类写在Sequential里面便可以一次性的进行forward操作
# 例子二: # 自定义实习类似nn.Linear的类操作 class MyLinear(nn.Module): def __init__(self, inp, outp): super(MyLinear, self).__init__() # requires_grad = True # 将w,b的参数自动的添加到nn.Parameter当中取,这样就可以自动的被optim.SGD优化 self.w = nn.Parameter(torch.randn(outp, inp)) self.b = nn.Parameter(torch.randn(outp)) def forward(self, x): x = x @ self.w.t() + self.b return x # 这个自定义的类与nn.Linear的功能是完全一样的
2.数据增强
常见的数据增强的手段有:
1)Flip翻转操作
2)Rotate旋转操作
3)Random Move & Crop
4)GAN
1)Flip操作
参考代码:
# 对训练集进行数据增强Filp操作 train_loader = torch.utils.data.DataLoader( datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([ # 进行水平方向的随机翻转 transforms.RandomHorizontalFlip(), # 进行竖直方向的随机翻转 transforms.RandomVerticalFlip(), transforms.ToTensor(), # transforms.Normalize((0.1307,), (0.3081,)) ])), batch_size=batch_size, shuffle=True)
2)Rotate操作
参考代码:
# 对训练集进行数据增强Rotate操作 train_loader = torch.utils.data.DataLoader( datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([ # 对图片进行一个随机的旋转角度,范围是[-15°-15°] transforms.RandomRotation(15), # 对图片进行一个随机的固定数值旋转角度,可以选择的角度在一个list中,eg:[90, 180, 270] transforms.RandomRotation([90, 180, 270]), transforms.ToTensor(), # transforms.Normalize((0.1307,), (0.3081,)) ])), batch_size=batch_size, shuffle=True)
3)Scale操作
参考代码:
# 对训练集进行数据增强Scale操作 train_loader = torch.utils.data.DataLoader( datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([ # 将原来的图片大小由28*28变成32*32 transforms.Resize([32, 32]), transforms.ToTensor(), # transforms.Normalize((0.1307,), (0.3081,)) ])), batch_size=batch_size, shuffle=True)
4)Crop Part操作
参考代码:
# 对训练集进行数据增强Crop操作,也就是随机的裁剪 train_loader = torch.utils.data.DataLoader( datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([ # 将原来的图片随机裁剪为28*28的大小 transforms.RandomCrop([28, 28]), transforms.ToTensor(), # transforms.Normalize((0.1307,), (0.3081,)) ])), batch_size=batch_size, shuffle=True)
尽管可以使用数据增强对图片进行很多的变换操作,但其分布不会提高很多,只会有一点的改进
5)多种操作结合处理
# 随机裁剪一个面积为原始面积10%到100%的区域,该区域的宽高比从0.5到2之间随机取值。 然后,区域的宽度和高度都被缩放到200像素。 shape_aug = torchvision.transforms.RandomResizedCrop( (32, 32), scale=(0.5, 1), ratio=(0.5, 2)) # 随机更改图像的亮度、对比度、饱和度、色调,随机值为原始图像的50%( 1−0.5 )到150%( 1+0.5 )之间 color_aug = torchvision.transforms.ColorJitter( brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5) # 数据增强 train_augs = torchvision.transforms.Compose([ # 水平翻转 torchvision.transforms.RandomHorizontalFlip(), # 竖直翻转 torchvision.transforms.RandomVerticalFlip(), # 颜色改变 color_aug, # 裁剪变化: shape_aug, # 格式转化 torchvision.transforms.ToTensor()]) test_augs = torchvision.transforms.Compose([ torchvision.transforms.ToTensor()]) root = "E:\学习\机器学习\数据集\cifar" batch_size = 64 tr_dataset = torchvision.datasets.CIFAR10(root=root, train=True, transform=train_augs, download=False) dataset = torchvision.datasets.CIFAR10(root=root, train=True, transform=test_augs, download=False) tr_dataloader = torch.utils.data.DataLoader(tr_dataset, batch_size=batch_size, shuffle=True) dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True) # 这里有一个问题,通过一些列的结构其实明没有增加样本量 # 这时候可以通过将数据增强后的数据再与元数据结合,label保持不变 # 再把扩充的数据丢到dataloader中进行加载,如下操作所示 data = tr_dataset + dataset loader = torch.utils.data.DataLoader(data, batch_size=batch_size, shuffle=True) print(len(tr_dataset), len(dataset), len(data), len(tr_dataloader), len(dataloader), len(loader))
输出结果为:50000 50000 100000 782 782 1563
可以看见,数据增强后的数据可以正常的加载,而且有效的扩充了样本数量
查看多种数据增加后的处理结果
def show_image(image, n_row, n_column): plt.figure(figsize=(12, 8), dpi=144) # 测试,显示一张图像 for i, img in enumerate(image): plt.subplot(n_row, n_column, i+1) plt.axis("off") plt.imshow(img.permute(1,2,0)) # plt.show() # time.sleep(1) # 输出转换后的图像数据 print("after transform: ") image,_ = iter(tr_dataloader).next() print(image.shape) show_image(image, 8, 8)