当前位置:首页 » 网络连接 » 残差网络采用跳跃连接
扩展阅读
无线网络租约延长24小时 2025-09-27 00:35:51

残差网络采用跳跃连接

发布时间: 2022-10-08 04:07:17

Ⅰ Resnet | Block的进化史——未完待续

https://arxiv.org/pdf/1512.03385.pdf

如上述图片所示,Resnet通过提出shortcut连接,解决了深度学习网络在训练过程中随着网络层数越来越多而导致的网络退化问题。这种shortcut的连接有两种形式,一种是完全的等价连接shortcut,另一种作为降采样block的shortcut,使得add操作可以在同等维度上进行降采样shortcut。另外考虑到耗时问题,为了减低更深的resnet网络的耗时,作者提出了一种Bottleneck结构,通过1*1卷积降低作用于3*3卷积的通道数,从而降低计算量和耗时。
对于shortcut的连接有点有两种我比较认可的解释:
1:跳跃连接实现了学习的灵活性,使得网络在训练的过程中可以自由的选择“更多进行卷积与非线性变换”还是“更多的倾向于什么都不做”,或者是两者结合。
2:跳跃连接使得网络刚开始训练时,由于恒等连接shortcut的存在使得网络从物理上变得更浅,网络训练更加容易。

https://arxiv.org/pdf/1603.05027.pdf
希望在训练的过程中,不论是前向还是反向阶段,信号可以直接从一个单元传递到其他任意一个单元,这种方式要比原始的Resnetblcok的性能更要好。

BN_after_addition:BNrelu的结果放在了恒等信号传递的路上,导致优化困难,阻碍了信息的传递。
ReLU_before_addition:将Relu放在信号传递的输出部分,导致最终的输出为非负值,这将限制网络特征的表达范围。
它的BN和ReLU全都放置在权重层的前面。
ReLU-only Pre-activation:ReLU层没有和BN层直连在一起,因此无法共享BN层带来的训练优势。
Pre-activation:这个方法最好。

https://arxiv.org/abs/2004.04989

1:提出了一种基于分段的残差学习网络结构,该方法为信息在网络各层间的传播提供了更好的途径,从而简化了学习过程。
2.更具原始的block和Pre-activation block存在的问题,作者提出了如下拓扑结构,该结构是一种分段的组织结构,细节为:
(1)把网络结构分为三个部分,四个主要stage和一个启动和结束阶段。
(2)四个主要阶段中每个stage都可以包含若干个Blocks,stage1,2,3,4分别有Block的个数为(3,4,6,3)。
(3)每个stage又分为三个部分,一个开始Block,若干个中间Block。比如以resnet50的情况下,有[1,2,4,1]对应stage的中间block。start,middle,end三种blcok的设计如图所示。

Ⅱ 什么是skip connection

skip connection 就是一种跳跃式传递。在ResNet中引入了一种叫resial network残差网络结构,其和普通的CNN的区别在于从输入源直接向输出源多连接了一条传递线,这是一种identity mapping,也就是所谓的恒等映射,用来进行残差计算。这叫是shortcut connection,也叫skip connection。其效果是为了防止网络层数增加而导致的梯度弥散问题与退化问题。

Ⅲ 自学围棋的AlphaGo Zero,你也可以造一个

  • 01

    遥想当年,AlphaGo的Master版本,在完胜柯洁九段之后不久,就被后辈AlphaGo Zero(简称狗零) 击溃了。

    从一只完全不懂围棋的AI,到打败Master,狗零只用了21天。

    而且,它不需要用人类知识来喂养,成为顶尖棋手全靠自学。

    如果能培育这样一只AI,即便自己不会下棋,也可以很骄傲吧。

    于是,来自巴黎的少年Dylan Djian (简称小笛) ,就照着狗零的论文去实现了一下。

    他给自己的AI棋手起名SuperGo,也提供了代码(传送门见文底) 。

    除此之外,还有教程——

    一个身子两个头

    智能体分成三个部分:

    一是特征提取器(Feature Extractor) ,二是策略网络(Policy Network) ,三是价值网络(Value Network) 。

    于是,狗零也被亲切地称为“双头怪”。特征提取器是身子,其他两个网络是脑子。

    特征提取器

    特征提取模型,是个残差网络 (ResNet) ,就是给普通CNN加上了跳层连接 (Skip Connection) , 让梯度的传播更加通畅。

    跳跃的样子,写成代码就是:

    1classBasicBlock(nn.Mole):

    2 """

    3 Basic resial block with 2 convolutions and a skip connection

    4 before the last ReLU activation.

    5 """

    6

    7def__init__(self, inplanes, planes, stride=1, downsample=None):

    8 super(BasicBlock, self).__init__()

    9

    10 self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=3,

    11 stride=stride, padding=1, bias=False)

    12 self.bn1 = nn.BatchNorm2d(planes)

    13

    14 self.conv2 = nn.Conv2d(planes, planes, kernel_size=3,

    15 stride=stride, padding=1, bias=False)

    16 self.bn2 = nn.BatchNorm2d(planes)

    17

    18

    19defforward(self, x):

    20 resial = x

    21

    22 out = self.conv1(x)

    23 out = F.relu(self.bn1(out))

    24

    25 out = self.conv2(out)

    26 out = self.bn2(out)

    27

    28 out += resial

    29 out = F.relu(out)

    30

    31returnout

    然后,把它加到特征提取模型里面去:

    1classExtractor(nn.Mole):

    2def__init__(self, inplanes, outplanes):

    3 super(Extractor, self).__init__()

    4 self.conv1 = nn.Conv2d(inplanes, outplanes, stride=1,

    5 kernel_size=3, padding=1, bias=False)

    6 self.bn1 = nn.BatchNorm2d(outplanes)

    7

    8forblockinrange(BLOCKS):

    9 setattr(self, "res{}".format(block),

    10 BasicBlock(outplanes, outplanes))

    11

    12

    13defforward(self, x):

    14 x = F.relu(self.bn1(self.conv1(x)))

    15forblockinrange(BLOCKS - 1):

    16 x = getattr(self, "res{}".format(block))(x)

    17

    18 feature_maps = getattr(self, "res{}".format(BLOCKS - 1))(x)

    19returnfeature_maps

    策略网络

    策略网络就是普通的CNN了,里面有个批量标准化(Batch Normalization) ,还有一个全连接层,输出概率分布。

    1classPolicyNet(nn.Mole):

    2def__init__(self, inplanes, outplanes):

    3 super(PolicyNet, self).__init__()

    4 self.outplanes = outplanes

    5 self.conv = nn.Conv2d(inplanes, 1, kernel_size=1)

    6 self.bn = nn.BatchNorm2d(1)

    7 self.logsoftmax = nn.LogSoftmax(dim=1)

    8 self.fc = nn.Linear(outplanes - 1, outplanes)

    9

    10

    11defforward(self, x):

    12 x = F.relu(self.bn(self.conv(x)))

    13 x = x.view(-1, self.outplanes - 1)

    14 x = self.fc(x)

    15 probas = self.logsoftmax(x).exp()

    16

    17returnprobas

    价值网络

    这个网络稍微复杂一点。除了标配之外,还要再多加一个全连接层。最后,用双曲正切 (Hyperbolic Tangent) 算出 (-1,1) 之间的数值,来表示当前状态下的赢面多大。

    代码长这样——

    1classValueNet(nn.Mole):

    2def__init__(self, inplanes, outplanes):

    3 super(ValueNet, self).__init__()

    4 self.outplanes = outplanes

    5 self.conv = nn.Conv2d(inplanes, 1, kernel_size=1)

    6 self.bn = nn.BatchNorm2d(1)

    7 self.fc1 = nn.Linear(outplanes - 1, 256)

    8 self.fc2 = nn.Linear(256, 1)

    9

    10

    11defforward(self, x):

    12 x = F.relu(self.bn(self.conv(x)))

    13 x = x.view(-1, self.outplanes - 1)

    14 x = F.relu(self.fc1(x))

    15 winning = F.tanh(self.fc2(x))

    16returnwinning

    未雨绸缪的树

    狗零,还有一个很重要的组成部分,就是蒙特卡洛树搜索(MCTS) 。

    它可以让AI棋手提前找出,胜率最高的落子点。

    在模拟器里,模拟对方的下一手,以及再下一手,给出应对之策,所以提前的远不止是一步。

    节点 (Node)

    树上的每一个节点,都代表一种不同的局势,有不同的统计数据:

    每个节点被经过的次数n,总动作值w,经过这一点的先验概率p,平均动作值q (q=w/n) ,还有从别处来到这个节点走的那一步,以及从这个节点出发、所有可能的下一步。

    1classNode:

    2def__init__(self, parent=None, proba=None, move=None):

    3 self.p = proba

    4 self.n = 0

    5 self.w = 0

    6 self.q = 0

    7 self.children = []

    8 self.parent = parent

    9 self.move = move

    部署 (Rollout)

    第一步是PUCT (多项式上置信树) 算法,选择能让PUCT函数 (下图) 的某个变体 (Variant)最大化,的走法。

    写成代码的话——

    1defselect(nodes, c_puct=C_PUCT):

    2 " Optimized version of the selection based of the PUCT formula "

    3

    4 total_count = 0

    5foriinrange(nodes.shape[0]):

    6 total_count += nodes[i][1]

    7

    8 action_scores = np.zeros(nodes.shape[0])

    9foriinrange(nodes.shape[0]):

    10 action_scores[i] = nodes[i][0] + c_puct * nodes[i][2] *

    11 (np.sqrt(total_count) / (1 + nodes[i][1]))

    12

    13 equals = np.where(action_scores == np.max(action_scores))[0]

    14ifequals.shape[0] > 0:

    15returnnp.random.choice(equals)

    16returnequals[0]

    结束 (Ending)

    选择在不停地进行,直至到达一个叶节点 (Leaf Node) ,而这个节点还没有往下生枝。

    1defis_leaf(self):

    2 """ Check whether a node is a leaf or not """

    3

    4returnlen(self.children) == 0

    到了叶节点,那里的一个随机状态就会被评估,得出所有“下一步”的概率。

    所有被禁的落子点,概率会变成零,然后重新把总概率归为1。

    然后,这个叶节点就会生出枝节 (都是可以落子的位置,概率不为零的那些) 。代码如下——

    1defexpand(self, probas):

    2 self.children = [Node(parent=self, move=idx, proba=probas[idx])

    3foridxinrange(probas.shape[0])ifprobas[idx] > 0]

    更新一下

    枝节生好之后,这个叶节点和它的妈妈们,身上的统计数据都会更新,用的是下面这两串代码。

    1defupdate(self, v):

    2 """ Update the node statistics after a rollout """

    3

    4 self.w = self.w + v

    5 self.q = self.w / self.nifself.n > 0else0

    1whilecurrent_node.parent:

    2 current_node.update(v)

    3 current_node = current_node.parent

    选择落子点

    模拟器搭好了,每个可能的“下一步”,都有了自己的统计数据。

    按照这些数据,算法会选择其中一步,真要落子的地方。

    选择有两种,一就是选择被模拟的次数最多的点。试用于测试和实战。

    另外一种,随机 (Stochastically) 选择,把节点被经过的次数转换成概率分布,用的是以下代码——

    1 total = np.sum(action_scores)

    2 probas = action_scores / total

    3 move = np.random.choice(action_scores.shape[0], p=probas)

    后者适用于训练,让AlphaGo探索更多可能的选择。

    三位一体的修炼

    狗零的修炼分为三个过程,是异步的。

    一是自对弈(Self-Play) ,用来生成数据。

    1defself_play():

    2whileTrue:

    3 new_player, checkpoint = load_player()

    4ifnew_player:

    5 player = new_player

    6

    7 ## Create the self-play match queue of processes

    8 results = create_matches(player, cores=PARALLEL_SELF_PLAY,

    9 match_number=SELF_PLAY_MATCH)

    10for_inrange(SELF_PLAY_MATCH):

    11 result = results.get()

    12 db.insert({

    13 "game": result,

    14 "id": game_id

    15 })

    16 game_id += 1

    二是训练(Training) ,拿新鲜生成的数据,来改进当前的神经网络。

    1deftrain():

    2 criterion = AlphaLoss()

    3 dataset = SelfPlayDataset()

    4 player, checkpoint = load_player(current_time, loaded_version)

    5 optimizer = create_optimizer(player, lr,

    6 param=checkpoint['optimizer'])

    7 best_player = deep(player)

    8 dataloader = DataLoader(dataset, collate_fn=collate_fn,

    9 batch_size=BATCH_SIZE, shuffle=True)

    10

    11whileTrue:

    12forbatch_idx, (state, move, winner)inenumerate(dataloader):

    13

    14 ## Evaluate a of the current network

    15iftotal_ite % TRAIN_STEPS == 0:

    16 pending_player = deep(player)

    17 result = evaluate(pending_player, best_player)

    18

    19ifresult:

    20 best_player = pending_player

    21

    22 example = {

    23 'state': state,

    24 'winner': winner,

    25 'move' : move

    26 }

    27 optimizer.zero_grad()

    28 winner, probas = pending_player.predict(example['state'])

    29

    30 loss = criterion(winner, example['winner'],

    31 probas, example['move'])

    32 loss.backward()

    33 optimizer.step()

    34

    35 ## Fetch new games

    36iftotal_ite % REFRESH_TICK == 0:

    37 last_id = fetch_new_games(collection, dataset, last_id)

    训练用的损失函数表示如下:

    1classAlphaLoss(torch.nn.Mole):

    2def__init__(self):

    3 super(AlphaLoss, self).__init__()

    4

    5defforward(self, pred_winner, winner, pred_probas, probas):

    6 value_error = (winner - pred_winner) ** 2

    7 policy_error = torch.sum((-probas *

    8 (1e-6 + pred_probas).log()), 1)

    9 total_error = (value_error.view(-1) + policy_error).mean()

    10returntotal_error

    三是评估(Evaluation) ,看训练过的智能体,比起正在生成数据的智能体,是不是更优秀了 (最优秀者回到第一步,继续生成数据) 。

    1defevaluate(player, new_player):

    2 results = play(player, opponent=new_player)

    3 black_wins = 0

    4 white_wins = 0

    5

    6forresultinresults:

    7ifresult[0] == 1:

    8 white_wins += 1

    9elifresult[0] == 0:

    10 black_wins += 1

    11

    12 ## Check if the trained player (black) is better than

    13 ## the current best player depending on the threshold

    14ifblack_wins >= EVAL_THRESH * len(results):

    15returnTrue

    16returnFalse

    第三部分很重要,要不断选出最优的网络,来不断生成高质量的数据,才能提升AI的棋艺。

    三个环节周而复始,才能养成强大的棋手。

    有志于AI围棋的各位,也可以试一试这个PyTorch实现。

    本来摘自量子位,原作 Dylan Djian。

    代码实现传送门:

    网页链接

    教程原文传送门:

    网页链接

    AlphaGo Zero论文传送门:

    网页链接

Ⅳ 论文阅读 | CVPR2017(Best Paper) | Densely Connected Convolutional Networks

大名鼎鼎的DenseNet,17年CVPR的best paper(当然有争议是后话),不得不读。黄高博士的扛鼎之作,之前在读他的Snapshot-Ensembles时感觉就很舒服,整个文章逻辑很清楚,实验对比做的也十分全面,相信这篇best paper更是没有问题,会给读者一种爽的感觉。

2019.2.20 2852次。绝对值很高,但相比其他经典网络,ResNet,GoogLeNet之类,有些差距。

本篇在16年8月挂到arXiv上,中了2017年CVPR,是继16年何大神的ResNet之后,第二个华人的best paper, 这里 有个作者本尊的talk,现场讲解。一作Gao Huang(黄高)05年北航的本科生(GPA第一),15年清华博士毕业(读了6年。。),后来在康奈尔待了3年做博后,此刻在清华作青椒,本篇是在康奈尔时的工作。二作刘壮(同等贡献)也是碉堡,现在在伯克利做博士生,之前是清华姚班的(13级),发这篇文章时还在清华,也就是说 本科生 。。。最近以一作的身份新发了一篇《Rethinking the Value of Network Pruning》,中了19年的ICLR,同时也是18年NIPS的best paper award。。这个世界太疯狂了,这都不是潜力股了,而是才华横溢溢的不行了。

官方实现在这里: https://github.com/liuzhuang13/DenseNet

黄高个人主页在这里: http://www.gaohuang.net/

刘壮个人主页在这里: https://liuzhuang13.github.io/

先前的研究中说明只要网络包含短路连接,基本上就能更深,更准确,更有效的训练。本文基于这个观察,引入了密集卷积网络(DenseNet),它以前馈方式将每个层连接到所有层。传统的卷积网络L层有L个连接,而DenseNet有 个直接连接。对于每一层,它前面所有层的特征图都当作输入,而其本身的特征图作为所有后面层的输入(短路连接被发挥到极致,网络中每两层都相连)。DenseNet具有几个引入注目的优点: 可以缓解梯度消失问题,加强特征传播,鼓励特征重用,并大幅减少参数数量。

随着CNN变得越来越深,一个新的研究问题出现了:随着输入信息或梯度通过多层,它在到达网络结尾(或开始)处就消失了。ResNets和Highway Networks通过恒等连接将信号从一层传输到下一层。Stochastic depth通过在训练期间随机丢弃层来缩短ResNets,以得到更好的信息和梯度流。FractalNets重复组合几个并行层序列和不同数量的卷积块,以获得较深的标准深度,同时在网络中保持许多短路径。尽管上述方法的网络结构都有所不同,但它们有一个共同特征:创建从早期层到后期层的短路径。

本文提出一个简单的连接模式:为了确保网络中各层之间的最大信息流, 将所有层(匹配特征图大小)直接相互连接 。为了保持前向传播性质,每个层从所有前面的层获得附加输入,并将其自身特征图传递给所有后续层。

至关重要的是,与ResNets相比,在传递给下一层之前, 不是通过求和来合并特征,而是通过concat来合并特征 。因此, 层有 个输入,包括所有先前卷积块的特征图。其特征图被传递到后续所有 层。这在L层网络中引入了 个连接,而不是传统架构的L个连接。正是因为这种密集连接模式,所以称本文方法为密集连接网络( Dense Convolutional Network DenseNet)。

相比传统卷积网络,这种密集连接模式有有一点可能违反直觉的是,它需要更少的参数,因为无需重新学习冗余的特征图。本文提出的DenseNet架构显式区分了添加到网络的信息和保留的信息。DenseNet的层非常窄(如每层只有12个滤波器),只给网络的"集体知识"增加一小组特征图,并保持其余的特征图不变。

除了更好的参数利用率之外,DenseNet的一大优势是它改善了整个网络中的信息流和梯度,使得网络更易于训练。每层都可以直接访问损失函数和原始输入信号的梯度( 我屮,这不就是GoogLeNet当时为解决梯度消失而在中间层引入分类器那种ugly办法的替代吗 ),从而导致隐式的深度监督。这有助于训练更深的网络。

与DenseNet相似的级联结构早在1989年就提出来了。。Adanet的提出差不多是与DenseNet并行的,跨层连接也相似(话说竞争真激烈。。)

本文作者提出的另一个网络Stochastic depth说明并非所有层都需要,在深度残差网络中存在大量冗余的层。本文的部分灵感也来源于此。

相比从极深或极宽的架构中提取表示能力,DenseNet是通过 特征重用 来利用网络的潜力,得到易于训练和高参数效率的压缩模型。相比从不同层拼接特征的Inception网络,DenseNet更简单有效(看来Inception因其结构复杂性没少被批判)。

定义 为单张输入图像,网络由 层组成,每一层实现非线性变换 ,其中 为层的索引号。 可以是BN,ReLU,Pooling,Conv等操作的复合函数,定义 层的输出为 。

传统的层连接: 。ResNets增加了跳跃连接: 。ResNets的一个优势是梯度可以通过恒等函数直接从后面的层流向前面的层。然而,恒等函数和 的输出通过加法合并,有可能会阻碍网络的信息流。

本文引入与ResNets不同的连接模式:从任意层到所有后续层的直接连接(图1)。结果就是,第 层接收所有之前层的特征图作为输入: 。为了便于实现,concat 的多个输入为单一张量。

受ResNet v2启发,定义 为三个连续运算的复合函数:BN,ReLU,3 x 3 Conv

当特征图的大小改变时,concat运算是不可能的,然鹅,卷积网络的一个关键组成部分就是下采样层,通过它可以改变特征图大小。为了便于在架构中进行下采样,将网络划分为多个密集连接的密集块(dense blocks),如图2所示。

将密集块之间的层称为过渡层(transition layers),它们进行卷积和池化。本文实验中的过渡层由BN,1 x 1卷积和 2 x 2平均池化组成。

如果每个函数 生成 个特征图,它后面跟着的 层有 个输入特征图,其中 是输入层的通道数。DenseNet和现有网络架构的一个重要区别是DenseNet可以有非常窄的层,如 。本文将超参数 定义为网络的成长率(growth rate)。对此的一种解释是,每一层都可以访问其块中所有前面的特征图,即,网络的‘集体知识’。可以将特征图视为网络的全局状态。每一层增加自己的 个特征图到这个状态。成长率反映了每层由多少新信息对全局状态有贡献。全局状态一旦写入,就可以被网络中的任何地方访问,而不像传统网络那样,无需从一层复制到另一层。(全文精华应该就是这一段了)

1x1 conv非常有用(提升计算效率),本文也大用特用。本文定义DenseNet-B的 为 BN-ReLU-Conv(1x1)-BN-ReLU-Conv(3x3)

为了使模型更紧凑,可以减少过渡层的特征图数量。如果密集块包含 个特征图,定义接下来的过渡层生成 个特征图,其中 表示压缩率。定义 的DenseNet为DenseNet-C,本位实验中设置为 。当同时使用瓶颈层和压缩过渡层时,定义模型为DenseNet-BC。

非ImageNet数据集采用同一个架构,由3个密集块构成。ImageNet的架构如表1所示

CIFAR SVHN ImageNet

所有网络都用SGD。

CIFAR和SVHN的batch size为64,epoch分别为300和40,初始学习率为0.1,在50%和75%的epoch时分别除10。

ImageNet的batch size为256,90个epoch,初始学习率为0.1,在30和60epoch时分别除10。

weight decay为 ,动量为0.9。用He初始化。

对于CIFAR和SVHN,还在每个卷积层后接了dropout层(除第一个卷积层外),丢失率为0.2。

看表2的最后一行

DenseNet可以利用更大更深模型表示能力的增长。

如图4所示

主要用DenseNet-BC和ResNet作比较。

表面上看,DenseNets和ResNets没什么不同,两个式子的差别仅仅是输入从加法变为concat,然而,这种看似很小的修改导致两种网络架构的行为明显不同。

因为鼓励特征重用,所以得到更紧凑的模型。

如图4所示。

对DenseNets准确率提升的一种解释是各个层通过短路连接从损失函数接收额外的监督(某种深度监督)。DenseNets用隐式的方式执行相似的深度监督:网络顶部的单个分类器通过最多两到三个过渡层为所有层提供直接监督。 然而,由于在所有层之间共享相同的损失函数,因此DenseNets的损失函数和梯度基本上不那么复杂。

和随机深度的对比,随机深度有点类似DenseNet:如果所有中间层都随机丢弃,那么在相同的池化层之间的任意两层都有可能直接连接。

DenseNet就是好,就是好啊就是好。在遵循简单的连接规则的同时,DenseNets自然地整合了恒等映射,深度监督和多样化深度的属性。

又是一篇没有什么数学公式的paper,越来越感觉深度学习像物理,很多结果都是基于做实验得到的。通过对实验的观察对比分析,找出实验中的缺陷不足,从而去改进,然后发paper。黄高博士的写作套路还是非常讨喜的,特别是开头的地方,娓娓道来,一步一步告诉你为什么要这么做,为什么要引入这一步。此外,DenseNets和作者本人的工作‘随机深度’也有千丝万缕的关系,看来功夫做扎实了,沿着一条道路是可以出一系列成果的。

这是个好问题。。是要进一步衍生ResNet吗?

提出密集连接结构,将ResNet的跳跃连接发扬光大为两两连接

效果比ResNet还好,通过减少滤波器个数(文中称作成长率),参数量也下来了

感觉效果提升并没有那么明显,被后续出来的ResNeXt超过了

各种网络结构的实现: https://towardsdatascience.com/history-of-convolutional-blocks-in-simple-code-96a7ddceac0c

黄高本人视频讲解: https://zhuanlan.hu.com/p/53417625

作者本人的解答: CVPR 2017最佳论文作者解读:DenseNet 的“what”、“why”和“how”

DenseNet的3个优势:

https://towardsdatascience.com/history-of-convolutional-blocks-in-simple-code-96a7ddceac0c

Ⅳ 十分钟一起学会ResNet残差网络

深度卷积网络自然的整合了低中高不同层次的特征,特征的层次可以靠加深网络的层次来丰富。从而,在构建卷积网络时,网络的深度越高,可抽取的特征层次就越丰富。所以一般我们会倾向于使用更深层次的网络结构,以便取得更高层次的特征。但是在使用深层次的网络结构时我们会遇到两个问题,梯度消失,梯度爆炸问题和网络退化的问题。

但是当使用更深层的网络时,会发生梯度消失、爆炸问题,这个问题很大程度通过标准的初始化和正则化层来基本解决,这样可以确保几十层的网络能够收敛,但是随着网络层数的增加,梯度消失或者爆炸的问题仍然存在。

还有一个问题就是网络的退化,举个例子,假设已经有了一个最优化的网络结构,是18层。当我们设计网络结构的时候,我们并不知道具体多少层次的网络时最优化的网络结构,假设设计了34层网络结构。那么多出来的16层其实是冗余的,我们希望训练网络的过程中,模型能够自己训练这五层为恒等映射,也就是经过这层时的输入与输出完全一样。但是往往模型很难将这16层恒等映射的参数学习正确,那么就一定会不比最优化的18层网络结构性能好,这就是随着网络深度增加,模型会产生退化现象。它不是由过拟合产生的,而是由冗余的网络层学习了不是恒等映射的参数造成的。

ResNet是在2015年有何凯明,张翔宇,任少卿,孙剑共同提出的,ResNet使用了一个新的思想,ResNet的思想是假设我们涉及一个网络层,存在最优化的网络层次,那么往往我们设计的深层次网络是有很多网络层为冗余层的。那么我们希望这些冗余层能够完成恒等映射,保证经过该恒等层的输入和输出完全相同。具体哪些层是恒等层,这个会有网络训练的时候自己判断出来。将原网络的几层改成一个残差块,残差块的具体构造如下图所示:

可以看到X是这一层残差块的输入,也称作F(x)为残差,x为输入值,F(X)是经过第一层线性变化并激活后的输出,该图表示在残差网络中,第二层进行线性变化之后激活之前,F(x)加入了这一层输入值X,然后再进行激活后输出。在第二层输出值激活前加入X,这条路径称作shortcut连接。

我们发现,假设该层是冗余的,在引入ResNet之前,我们想让该层学习到的参数能够满足h(x)=x,即输入是x,经过该冗余层后,输出仍然为x。但是可以看见,要想学习h(x)=x恒等映射时的这层参数时比较困难的。ResNet想到避免去学习该层恒等映射的参数,使用了如上图的结构,让h(x)=F(x)+x;这里的F(x)我们称作残差项,我们发现,要想让该冗余层能够恒等映射,我们只需要学习F(x)=0。学习F(x)=0比学习h(x)=x要简单,因为一般每层网络中的参数初始化偏向于0,这样在相比于更新该网络层的参数来学习h(x)=x,该冗余层学习F(x)=0的更新参数能够更快收敛,如图所示:

假设该曾网络只经过线性变换,没有bias也没有激活函数。我们发现因为随机初始化权重一般偏向于0,那么经过该网络的输出值为[0.6 0.6],很明显会更接近与[0 0],而不是[2 1],相比与学习h(x)=x,模型要更快到学习F(x)=0。

并且ReLU能够将负数激活为0,过滤了负数的线性变化,也能够更快的使得F(x)=0。这样当网络自己决定哪些网络层为冗余层时,使用ResNet的网络很大程度上解决了学习恒等映射的问题,用学习残差F(x)=0更新该冗余层的参数来代替学习h(x)=x更新冗余层的参数。

这样当网络自行决定了哪些层为冗余层后,通过学习残差F(x)=0来让该层网络恒等映射上一层的输入,使得有了这些冗余层的网络效果与没有这些冗余层的网络效果相同,这样很大程度上解决了网络的退化问题。

我们发现很深的网络层,由于参数初始化一般更靠近0,这样在训练的过程中更新浅层网络的参数时,很容易随着网络的深入而导致梯度消失,浅层的参数无法更新。

可以看到,假设现在需要更新 参数因为随机初始化偏向于0,通过链式求导我们会发现, 相乘会得到更加接近于0的数,那么所求的这个 的梯度就接近于0,也就产生了梯度消失的现象。

ResNet最终更新某一个节点的参数时,由于 ,由于链式求导后的结果如图所示,不管括号内右边部分的求导参数有多小,因为左边的1的存在,并且将原来的链式求导中的连乘变成了连加状态(正是 ),都能保证该节点参数更新不会发生梯度消失或梯度爆炸现象。

这样ResNet在解决了阻碍更深层次网络优化问题的两个重要问题后,ResNet就能训练更深层次几百层乃至几千层的网络并取得更高的精确度了。

这里是应用了ResNet的网络图,这里如果遇到了h(x)=F(x)+x中x的维度与F(x)不同的维度时,我们需要对identity加入Ws来保持Ws*x的维度与F(x)的维度一致。

x与F(x)维度相同时:

x与F(x)维度不同时:

下边是ResNet的网络结构图:

使用1*1卷积减少参数和计算量:

如果用了更深层次的网络时,考虑到计算量,会先用1 * 1的卷积将输入的256维降到64维,然后通过1*1恢复。这样做的目的是减少参数量和计算量。

左图是ResNet34,右图是ResNet50/101/152。这一个模块称作building block,右图称之为bottleneck design。在面对50,101,152层的深层次网络,意味着有很大的计算量,因此这里使用1 * 1卷积先将输入进行降维,然后再经过3 * 3卷积后再用 卷积进行升维。使用1*1卷积的好处是大大降低参数量计算量。

通过上述的学习,你应该知道了,现如今大家普遍认为更好的网络是建立在更宽更深的网络基础上,当你需要设计一个深度网络结构时,你永远不知道最优的网络层次结构是多少层,一旦你设计的很深入了,那势必会有很多冗余层,这些冗余层一旦没有成功学习恒等变换 ,那就会影响网络的预测性能,不会比浅层的网络学习效果好从而产生退化问题。

ResNet的过人之处,是他很大程度上解决了当今深度网络头疼的网络退化问题和梯度消失问题。使用残差网络结构 代替原来的没有shortcut连接的 ,这样更新冗余层的参数时需要学习 比学习 要容易得多。而shortcut连接的结构也保证了反向传播更新参数时,很难有梯度为0的现象发生,不会导致梯度消失。

这样,ResNet的构建,使我们更朝着符合我们的直觉走下去,即越深的网络对于高级抽象特征的提取和网络性能更好,不用在担心随着网络的加深发生退化问题了。

近段时间,准备持续发表一些CNN常见的网络模型讲解。好了,今天的十分钟就带你一起学会ResNet,下次的十分钟我们再见。

Ⅵ 图像分割模型U—Net融合浅层特征的方式是什么

摘要 提出一种将残差结构与U-Net网络融合的视盘分割方法。残差模块的跳跃连接能将浅层特征传递给更深一层网络,实现浅层特征的重复使用,增强了图像细节学习。将该方法在两个公开数据集Messidor和Kaggle上进行验证,在干扰较多的Kaggle数据集上,其AUC和MAP分别达到0.952 1和0.838 8,证明该方法可同时学习图像细节特征和全局结构特征,能更好地区分眼底视盘与亮病灶