本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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)