【7】nn.module使用与数据增强

191 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

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)

在这里插入图片描述