CNN中残差网络的使用
- K. He, X. Zhang, S. Ren and J. Sun, “Deep Residual Learning for Image Recognition,” in 2016 IEEE Conference on Computer Vision and Pattern Recognition (CVPR), Las Vegas, NV, USA, 2016 pp. 770-778.doi: 10.1109/CVPR.2016.90
创新点:
- 残差网络依旧让非线形层满足 (普通网络的映射关系),然后从输入直接引入一个短连接到非线形层的输出上,使得整个映射变为:
图中 。优势
- 一方面是残差网络更好的拟合分类函数以获得更高的分类精度,这个理论给了一个很合理的启发,就是原则上,带短连接的网络的拟合高维函数的能力比普通连接的网络更强。ResNet在增加网络深度的同时,却并没有增加网络的复杂度,而且效果远远好于其他如VGG,GoogleNet等网络。随着层数的增加,这一优势越发明显。
- 残差网络解决网络在层数加深时优化训练上的难题(短链接)。
- 恒等映射不会给模型带来额外的参数和计算量。
劣势:
介绍了两种 Shortcut connections,第一种用于在特征相加时维度相等的情况下,即恒等映射,显然,第二种用于不相等的情况。
- 恒等映射不会给模型带来额外的参数和计算量,不过,使用不含参的shortcut connections需要 x 和 F(x) 的维度相等,不然无法相加。
- 维度不相等时,需要进行匹配。
原因:
网络特性:在某些应用领域中,深层次的网络至关重要。但深层次的网络难以训练,易出现梯度消失或梯度爆炸(problem of vanishing/exploding gradients)。但是,这些问题已经通过 normalized initialization 和 intermediate normalization layers(即BN层)基本被解决了,从而使得网络可以达到数十层的深度,并且可以使用反向传播算法有效地训练。 但是,在更深的网路开始收敛时,暴露出一个被称为“退化“(degradation problem of training accuracy)的问题:随着网络深度增加,在训练集上的精度达到饱和,然后迅速下降。
如图所示x为输入,F(x)为普通神经网络的输出,x 一个恒等映射的快捷连接,ReLu是激活函数。那么为什么会出现退化问题呢,即更深的网络不能保证性能至少等价于较浅的网络呢?假设我们去掉快捷连接 x,我们要想保证 x 经过weight layer形成恒等映射,则F(x) =H(x)= x,此处的H(x)为网络希望拟合潜在的映射 。事实表明这个过程的优化是非常困难的,比如 x = 10,此时F(x) = 10.1,此时变化率为(10.1 - 10) / 10 = 1%;当我们加入了快捷连接之后 H(x)=F(x)+x则 H(x)=10.1,F(x) =10.1-10= 0.1,变化率为(0.1 - 10) / 10 ≈ 100%。这对于权值的调节是有很大好处的,迫使权重逼近于0,因此将F(x)优化为0,此时 H(x) =x ,完成恒等映射。现实训练中,权重不可能完全接近于 0,F(x) 只能逼近与 0。综上所述,当一个网络训练一定程度,准确度不在 提高时,我们可以链接残差网络,因为后面几乎是恒等映射,用此中方法误差不会比浅层网络大,网络还可以继续优化,提高网络性能。换句话说,我们加入快捷连接之后,使得原来的恒等映射变成了零映射,网络更容易优化。参考文章1
**场景特性:**目标检测、图像分割。
代码:
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets # 放置了许多常用数据集,包括手写数字识别
import torch.nn.functional as F
数据预处理
transform = transforms.Compose([
transforms.ToTensor(), # 转张量,将值缩放到[0,1]之间
transforms.Normalize((0.1307,),(0.3081,)) # 归一化,第一个为均值,第二个为方差
])
# 加载数据
train_dataset = datasets.MNIST(root= "E:/MNIST/mnist",
train=True, # 下载训练集
transform=transform, # 转张量,将值缩放到[0,1]之间.也可以写成transform = transforms.ToTensor()
download=True
)
test_dataset = datasets.MNIST(root= "E:/MNIST/mnist",
train=False, # 下载训练集
transform=transform, # 转张量,将值缩放到[0,1]之间
download=True
)
train_loader = DataLoader(dataset=train_dataset,
batch_size=64,
shuffle=True)
test_loader = DataLoader(dataset=test_dataset,
batch_size=64,
shuffle=False)
Residual Block
class ResidualBlock(nn.Module):
def __init__(self, channels):
super(ResidualBlock, self).__init__()
self.channels = channels
self.conv1 = nn.Conv2d(channels, channels, kernel_size=3, padding = 1)
self.conv2 = nn.Conv2d(channels, channels, kernel_size=3, padding = 1)
# 两个卷积层就使用一次残差网络。
def forward(self, x):
y = F.relu(self.conv1(x))
y = self.conv2(y)
return F.relu(x+y) # 先求和再激活。
搭建CNN模型
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(1, 16, kernel_size=5)
self.conv2 = nn.Conv2d(16, 32, kernel_size=5)
self.mp = nn.MaxPool2d(2)
self.rblock1 = ResidualBlock(16)
self.rblock2 = ResidualBlock(32)
self.fc = nn.Linear(512, 10)
def forward(self, x): # (batch_size, channel, W, H)
in_size = x.size(0) # batch_size
x = self.mp(F.relu(self.conv1(x)))
x = self.rblock1(x)
x = self.mp(F.relu(self.conv2(x)))
x = self.rblock2(x)
x = x.view(in_size, -1)
x = self.fc(x)
return x
model = Net()
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr= 0.01, momentum= 0.5)
训练函数
def train(epoch):
runing_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target =data
inputs, target = inputs.to(device), target.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = loss_fn(outputs, target)
loss.backward()
optimizer.step()
runing_loss += loss.item()
if batch_idx % 300 == 299:
print("[%d, %5d] loss: %.3f" % (epoch +1, batch_idx+1, runing_loss/300))
runing_loss = 0.0
测试函数
def test():
correct = 0
total =0
with torch.no_grad():
for data in test_loader:
images, labels =data
outputs = model(images)
_, predicted = torch.max(outputs.data, dim =1 ) # 返回两个值,第一个是最大值,第二个是最大值的索引。dim=1表示在列维度求以上结果,dim = 0表示在行维度求以上结果。
total += labels.size(0) # 每一个batch_size 中labels是一个(N,1)的元组,size(0)=N
correct +=(predicted == labels).sum().item() # 对的总个数
print("Accuracy on the test set %d %%" % (100*correct/total))
网络启动
if __name__=="__main__":
for epoch in range(10):
train(epoch)
if epoch % 2 ==0:
test()
[1, 300] loss: 0.535
[1, 600] loss: 0.165
[1, 900] loss: 0.121
Accuracy on the test set 96 %
[2, 300] loss: 0.085
[2, 600] loss: 0.088
[2, 900] loss: 0.077
[3, 300] loss: 0.060
[3, 600] loss: 0.060
[3, 900] loss: 0.060
Accuracy on the test set 98 %
[4, 300] loss: 0.046
[4, 600] loss: 0.052
[4, 900] loss: 0.047
[5, 300] loss: 0.039
转载:https://blog.csdn.net/qq_40211493/article/details/106762564