PyTorch 卷积神经网络
8. 卷积神经网络
卷积
卷积实则是一种特征映射,解决了在线性全连接层下的参数维度过大的问题。同时卷积也来源于人类视觉局部性的感受区域。 参数共享是卷积神经网络的一个重要特征,也极大地减少了参数维度。
卷积一次来源于信号处理上两个函数之间的卷积运算,操作可视化后类似。
卷积神经网络
notation
- Input channels
- Kernel channels: 2ch
- Kernel size
- Stride 步幅。
- Padding 填充。
multi-Kernel
LeNet-5
卷积层的作用效果
从图片来看卷积层的作用效果,浅层次的卷积越能捕捉图片细节特征,往后就可以捕捉图片的局部特征。
nn.Conv2d
#Class API in_channels, out_channels(number of kernel)
layer = nn.Conv2d(2, 16, kernel_size=3, stride=1, padding=0)
x = torch.rand(1, 2, 28, 28)
out = layer.forward(x)
out.shape # torch.Size([1, 16, 26, 26])
out_ = layer(x) #python provide magic to call __call__
out_.shape # torch.Size([1, 16, 26, 26])
layer.weight.shape # torch.Size([16, 2, 3, 3])
layer.bias.shape #torch.Size([16])
F.conv2d
#Functional API
x = torch.rand(1, 3, 28, 28)
w = torch.rand(16, 3, 5, 5)
b = torch.rand(16)
out = F.conv2d(x, w, b, stride=1, padding=1)
out.shape #torch.Size([16, 3, 26, 26])
池化层
pooling
pooling is also called down sample.
池化层为了缩减数据维度。
pooling 策略:
- Max Pooling.
- Avg Pooling.
layer = nn.Conv2d(2, 16, kernel_size=3, stride=1, padding=1)
x = torch.rand(1, 2, 28, 28)
out = layer(x) #python provide magic to call __call__
out.shape # torch.Size([1, 16, 28, 28])
# Class API
layerPooling = nn.AvgPool2d(2, stride=4)
outPClassAPI = layerPooling(out)
outPClassAPI.shape # torch.Size([1, 16, 7, 7])
# Functional API
outPFuncAPI = F.max_pool2d(out, 2, stride=4)
outPFuncAPI.shape # torch.Size([1, 16, 7, 7])
upsample
upsample 可以看做是图片的放大。
layer = nn.Conv2d(2, 16, kernel_size=3, stride=1, padding=1)
x = torch.rand(1, 2, 28, 28)
out = layer(x) #python provide magic to call __call__
out.shape # torch.Size([1, 16, 28, 28])
out = F.interpolate(out, scale_factor=2, mode='nearest')
out.shape # torch.Size([1, 16, 56, 56])
ReLu
layer = nn.Conv2d(2, 16, kernel_size=3, stride=1, padding=1)
x = torch.rand(1, 2, 28, 28)
out = layer(x) #python provide magic to call __call__
out.shape # torch.Size([1, 16, 28, 28])
out.min() # tensor(-1.2756, grad_fn=<MinBackward1>)
layer = nn.ReLU(inplace=True)
out = layer(out)
out.shape # torch.Size([1, 16, 28, 28])
out.min() tensor(0., grad_fn=<MinBackward1>)
batch norm
Batch normalization 广泛应用于深度学习,CNN,RNN.
使用原因
通过 BatchNorm 极大地避免使用 Sigmoid 函数时的梯度弥散情况,加快迭代收敛速度。
feature scale
更利于搜索最优解
image Normlization
normlize = transforms.Normalize(mean=[0.485, 0.456, 0.406]
std=[0.229, 0.224, 0.225]) #RGB
Batch Normlization
常见的 norm 类型:
可以看出来 Batch Normlization 只保留 channel 的统计数据 mean 以及 varience.
Batch Normlization 只会平移和缩放数据。
x = torch.rand(100, 16, 784)
layer = nn.BatchNorm1d(16)
out = layer(x)
out.shape # torch.Size([100, 16, 784])
layer.running_mean.shape # torch.Size([16])
layer.running_var.shape # torch.Size([16])
Class Variables, 查看类变量
vars(layer)
# 'affine': True, otherwise weight=1, bias=0 not update
# 'training': True, not test mode
Batch Normlization 在 training 以及 test 行为不一样。
因为在test时, 数据没有 Batch 统计单个数据 没有意义,因此只会使用 全局的 mean 以及 varience .且 weight 以及 bias 是不需要更新的。
layer.eval() # 'training': False,
hatx = torch.rand(1, 16, 7, 7)
test = layer(hatx)
使用效果
使用优势
- 加快收敛速度
- 更好的效果
- 模型更健壮
- 稳定性
- 学习率过大
经典的神经网络
ImageNet dataset 224x224
LeNet-5
Yann LeCun
上世纪80年代产物,用于手写字体识别,精度优秀。
AlexNet
Geoffrey Hinton
- GTX 580
- 3GB x 2
- 11 x 11
- 8 layers
1x1 convolution
- less computation 2. c in => c out
GoogLeNet
综合不同 size’s kernel 的感受野。
虽然深度学习随着网络层数的增加精度上有了明显的提升,但也不能无限制的增加
网络层数,否则 training 难度增加,精度不升反降。ResNet 很好地解决了这一问题。
ResNet
深度残差网络
提出短路层,寻找梯度回传的捷径。将新的 Unit 进行叠加形成 ResNet.
新的 Unit 单元,shape 不能变。将短路权利交给网络。
为什么叫残差(Residual)?
层数性能比较:
DenseNet
nn.Module 类
当我们自己定义网络层时,必须继承 nn.Module 父类。常用的线性层,卷积层等 PyTorch 官方已经写好。
class MyLinear(nn.Module):
def __init__(self, inp, outp):
super(MyLinear, self).__init__()
# requires_grad = True
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
Magic
- Every Layer is nn.Module
- nn.Linear
- nn.BatchNorm2d
- nn.Conv2d
- nn.Module is nested in nn.Module
使用 nn.Module 的好处:
- 可以使用大量现成的网络层。
• Linear
• ReLu
• Sigmoid
• Conv2d
• ConvTransposed2d
• Dropout
• etc. - Container 容器
• net(x)
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),
)
- 有效管理参数
net.parameters() # iterator
net.named_parameters # auto-named weight and bias iterator
- Modules
• modules: all nodes
• children: direct children
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)
modules: net Sequential(
(0): BasicNet(
(net): Linear(in_features=4, out_features=3, bias=True)
)
(1): ReLU()
(2): Linear(in_features=3, out_features=2, bias=True)
)
modules: net.0 BasicNet(
(net): Linear(in_features=4, out_features=3, bias=True)
)
modules: net.0.net Linear(in_features=4, out_features=3, bias=True)
modules: net.1 ReLU()
modules: net.2 Linear(in_features=3, out_features=2, bias=True)
- GPU 加速
device = torch.device('cuda')
net = Net()
net.to(device)
- 保存与加载中间数据
net.load_state_dict(torch.load('ckpt.mdl'))
...train...
torch.save(net.state_dict(), 'ckpt.mdl')
- train&test 状态切换方便。
# train
net.train()
...
# test
net.eval()
- 实现我们自己的类。
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)
- 自己实现的线性层
class MyLinear(nn.Module):
def __init__(self, inp, outp):
super(MyLinear, self).__init__()
# requires_grad = True
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
数据增强
确保深度学习的深度网络有良好的表现,防止过拟合的重要标准就是要使用的是大数据集。
Flip
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
])),
Rotation
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.RandomRotation(15),
transforms.RandomRotation([90, 180, 270]),
]))
Scale
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.Resize([32, 32]),
])),
Crop part
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.RandomCrop([28, 28]),
])),
Noise
pytorch 没有 加入高斯白噪点的 API,可人为加上高斯分布图片实现 图片的随机噪声。