基于深度强化学习的绘画智能体 代码详解
Github源码链接
actor.py
- 导入库
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.utils.weight_norm as weightNorm
from torch.autograd import Variable
import sys
- 定义3*3的二维卷积模板
def conv3x3(in_planes, out_planes, stride=1):
return (nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False))
- nn.Conv2d()详解
1.kernel_size:卷积核的大小,一般我们会使用5x5、3x3这种左右两个数相同的卷积核,因此这种情况只需要写kernel_size = 5这样的就行了。如果左右两个数不同,比如3x5的卷积核,那么写作kernel_size = (3, 5),注意需要写一个tuple,而不能写一个列表(list)。 2.stride:步幅,卷积核两次卷积左上角点之间的距离;通俗讲就是卷积核每隔几个像素进行一次卷积操作 3.padding:=0时,不填充,原图与卷积核进行卷积;=1时,在原图四边填充一行(一列),具体填充的数据由padding_mode控制,一般填0) 4.bias:偏差,会在网络训练后向传播过程中,给输出加上一个学得的偏差
- 定义基础模块BasicBlock
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_planes, planes, stride=1):
super(BasicBlock, self).__init__()
self.conv1 = conv3x3(in_planes, planes, stride)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = conv3x3(planes, planes)
self.bn2 = nn.BatchNorm2d(planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != self.expansion * planes:
self.shortcut = nn.Sequential(
(nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False)),
nn.BatchNorm2d(self.expansion*planes)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += self.shortcut(x)
out = F.relu(out)
return out
def cfg(depth):
depth_lst = [18, 34, 50, 101, 152]
assert (depth in depth_lst), "Error : Resnet depth should be either 18, 34, 50, 101, 152"
cf_dict = {
'18': (BasicBlock, [2,2,2,2]),
'34': (BasicBlock, [3,4,6,3]),
'50': (Bottleneck, [3,4,6,3]),
'101':(Bottleneck, [3,4,23,3]),
'152':(Bottleneck, [3,8,36,3]),
}
return cf_dict[str(depth)]
- 定义Bottleneck模块。
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, in_planes, planes, stride=1):
super(Bottleneck, self).__init__()
self.conv1 = (nn.Conv2d(in_planes, planes, kernel_size=1, bias=False))
self.conv2 = (nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False))
self.conv3 = (nn.Conv2d(planes, self.expansion*planes, kernel_size=1, bias=False))
self.bn1 = nn.BatchNorm2d(planes)
self.bn2 = nn.BatchNorm2d(planes)
self.bn3 = nn.BatchNorm2d(self.expansion*planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != self.expansion*planes:
self.shortcut = nn.Sequential(
(nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False)),
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
out += self.shortcut(x)
out = F.relu(out)
return out
- 定义整个残差网络
class ResNet(nn.Module):
def __init__(self, num_inputs, depth, num_outputs):
super(ResNet, self).__init__()
self.in_planes = 64
block, num_blocks = cfg(depth)
self.conv1 = conv3x3(num_inputs, 64, 2)
self.bn1 = nn.BatchNorm2d(64)
self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=2)
self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
self.fc = nn.Linear(512, num_outputs)
def _make_layer(self, block, planes, num_blocks, stride):
strides = [stride] + [1]*(num_blocks-1)
layers = []
for stride in strides:
layers.append(block(self.in_planes, planes, stride))
self.in_planes = planes * block.expansion
return nn.Sequential(*layers)
def forward(self, x):
x = F.relu(self.bn1(self.conv1(x)))
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = F.avg_pool2d(x, 4)
x = x.view(x.size(0), -1)
x = self.fc(x)
x = torch.sigmoid(x)
return x
关于残差网络(Resnet)的理解
网络深度是影响深度卷积神经网络性能的一大因素,但是研究者发现当网络不断加深时,训练的结果并不好。这不是因为过拟合,因为过拟合的话应该是训练集上结果好,测试集不好,但深度网络出现的现象是训练集上的效果就不好。而且这种现象还会随着深度加深而变差。这并不符合逻辑,因为深层网络在训练时,可以是在浅层网络的函数上加上一个恒等变换。而深层网络显然没有把这种恒等变换学习到。因此,提出了Resnet。 在单纯的前向传播基础上深度残差网络增加了跃层连接(Skip Connection)。实践表明,深度残差网路可以有效地改善“深度”网络的性能。
参考知乎上的解释 这里我们以上图为例简单解释为什么看似简单的操作能够解决深度网络中梯度消失的问题:
假设网络输入为
x
x
x ,从
A
A
A到
D
D
D经历两次前向传播以及一次Skip Connection,
根据后向传播的链式法则,
而
所以:
此时,即使在A-B-C的后向传播中出现梯度衰减的情况,D处的梯度依然能够直接传递到A,也即是实现了梯度的跨层传播。
以及为了深入了解,再引用下简书上一个的回答
F是求和前网络映射,H是从输入到求和后的网络映射。 比如把5映射到5.1,那么引入残差前是F’(5)=5.1,引入残差后是H(5)=5.1, H(5)=F(5)+5, F(5)=0.1。这里的F’和F都表示网络参数映射,引入残差后的映射对输出的变化更敏感。比如s输出从5.1变到5.2,映射F’的输出增加了1/51=2%,而对于残差结构输出从5.1到5.2,映射F是从0.1到0.2,增加了100%。明显后者输出变化对权重的调整作用更大,所以效果更好。残差的思想都是去掉相同的主体部分,从而突出微小的变化,看到残差网络我第一反应就是差分放大器。
|