在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

PyTorch教程-8.6. 殘差網(wǎng)絡(luò) (ResNet) 和 ResNeXt

jf_pJlTbmA9 ? 來(lái)源:PyTorch ? 作者:PyTorch ? 2023-06-05 15:44 ? 次閱讀

隨著我們?cè)O(shè)計(jì)越來(lái)越深的網(wǎng)絡(luò),了解添加層如何增加網(wǎng)絡(luò)的復(fù)雜性和表現(xiàn)力變得勢(shì)在必行。更重要的是設(shè)計(jì)網(wǎng)絡(luò)的能力,其中添加層使網(wǎng)絡(luò)嚴(yán)格更具表現(xiàn)力而不僅僅是不同。為了取得一些進(jìn)展,我們需要一點(diǎn)數(shù)學(xué)知識(shí)。

import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

from mxnet import init, np, npx
from mxnet.gluon import nn
from d2l import mxnet as d2l

npx.set_np()

import jax
from flax import linen as nn
from jax import numpy as jnp
from d2l import jax as d2l

import tensorflow as tf
from d2l import tensorflow as d2l

8.6.1. 函數(shù)類

考慮F,特定網(wǎng)絡(luò)架構(gòu)(連同學(xué)習(xí)率和其他超參數(shù)設(shè)置)可以達(dá)到的功能類別。也就是說(shuō),對(duì)于所有 f∈F存在一些參數(shù)集(例如,權(quán)重和偏差),可以通過(guò)在合適的數(shù)據(jù)集上進(jìn)行訓(xùn)練來(lái)獲得。讓我們假設(shè)f?是我們真正想要找到的“真實(shí)”功能。如果它在F,我們的狀態(tài)很好,但通常我們不會(huì)那么幸運(yùn)。相反,我們將嘗試找到一些fF?這是我們最好的選擇 F. 例如,給定一個(gè)具有特征的數(shù)據(jù)集 X和標(biāo)簽y,我們可以嘗試通過(guò)解決以下優(yōu)化問(wèn)題來(lái)找到它:

(8.6.1)fF?=defargminf?L(X,y,f)subject tof∈F.

我們知道正則化 (Morozov,1984 年,Tikhonov 和 Arsenin,1977 年)可以控制復(fù)雜度F并實(shí)現(xiàn)一致性,因此更大的訓(xùn)練數(shù)據(jù)通常會(huì)帶來(lái)更好的效果fF?. 唯一合理的假設(shè)是,如果我們?cè)O(shè)計(jì)一個(gè)不同的、更強(qiáng)大的架構(gòu)F′我們應(yīng)該取得更好的結(jié)果。換句話說(shuō),我們期望fF′? 比“更好”fF?. 然而,如果 F?F′甚至不能保證這會(huì)發(fā)生。實(shí)際上,fF′?可能會(huì)更糟。如圖 8.6.1所示,對(duì)于非嵌套函數(shù)類,較大的函數(shù)類并不總是向“真實(shí)”函數(shù)靠攏f?. 例如,在圖 8.6.1的左側(cè),雖然F3更接近f?比F1,F6 遠(yuǎn)離并且不能保證進(jìn)一步增加復(fù)雜性可以減少距離f?. 對(duì)于嵌套函數(shù)類,其中 F1?…?F6在圖 8.6.1右側(cè),我們可以從非嵌套函數(shù)類中避免上述問(wèn)題。

poYBAGR9NdSABwXfAAGkEZU56U8741.svg

圖 8.6.1對(duì)于非嵌套函數(shù)類,更大(用面積表示)的函數(shù)類并不能保證更接近“真實(shí)”函數(shù)(f?). 這不會(huì)發(fā)生在嵌套函數(shù)類中。

因此,只有當(dāng)較大的函數(shù)類包含較小的函數(shù)類時(shí),我們才能保證增加它們會(huì)嚴(yán)格增加網(wǎng)絡(luò)的表達(dá)能力。對(duì)于深度神經(jīng)網(wǎng)絡(luò),如果我們可以將新添加的層訓(xùn)練成恒等函數(shù) f(x)=x,新模型將與原始模型一樣有效。由于新模型可能會(huì)得到更好的解決方案來(lái)擬合訓(xùn)練數(shù)據(jù)集,因此添加的層可能更容易減少訓(xùn)練錯(cuò)誤。

這是He等人提出的問(wèn)題。( 2016 )在處理非常深的計(jì)算機(jī)視覺(jué)模型時(shí)考慮。他們提出的殘差網(wǎng)絡(luò)( ResNet )的核心思想是,每個(gè)附加層都應(yīng)該更容易地包含身份函數(shù)作為其元素之一。這些考慮相當(dāng)深刻,但它們導(dǎo)致了一個(gè)非常簡(jiǎn)單的解決方案,即殘差塊。憑借它,ResNet 在 2015 年贏得了 ImageNet 大規(guī)模視覺(jué)識(shí)別挑戰(zhàn)賽。該設(shè)計(jì)對(duì)如何構(gòu)建深度神經(jīng)網(wǎng)絡(luò)產(chǎn)生了深遠(yuǎn)的影響。例如,殘差塊已添加到循環(huán)網(wǎng)絡(luò)中 (Kim等人,2017 年,普拉卡什等。, 2016 年)。同樣,Transformers (Vaswani等人,2017 年)使用它們有效地堆疊多層網(wǎng)絡(luò)。它也被用于圖神經(jīng)網(wǎng)絡(luò) (Kipf 和 Welling,2016 年),并且作為一個(gè)基本概念,它已被廣泛用于計(jì)算機(jī)視覺(jué) (Redmon 和 Farhadi,2018 年,Ren等人,2015 年)。請(qǐng)注意,殘差網(wǎng)絡(luò)早于高速公路網(wǎng)絡(luò) (Srivastava等人,2015 年)這有一些共同的動(dòng)機(jī),盡管沒(méi)有圍繞身份函數(shù)進(jìn)行優(yōu)雅的參數(shù)化。

8.6.2. 殘差塊

讓我們關(guān)注神經(jīng)網(wǎng)絡(luò)的局部部分,如圖 8.6.2所示。表示輸入x. 我們假設(shè)我們想要通過(guò)學(xué)習(xí)獲得的期望底層映射是f(x), 用作頂部激活函數(shù)的輸入。左邊虛線框內(nèi)的部分必須直接學(xué)習(xí)映射f(x). 右邊虛線框內(nèi)的部分需要學(xué)習(xí)殘差映射 g(x)=f(x)?x,這就是殘差塊的名稱來(lái)源。如果身份映射 f(x)=x是所需的底層映射,殘差映射相當(dāng)于g(x)=0從而更容易學(xué)習(xí):我們只需要將虛線框內(nèi)的上層權(quán)重層(例如全連接層和卷積層)的權(quán)重和偏差推到零即可。右圖說(shuō)明了 ResNet的殘差塊,其中實(shí)線承載層輸入x加法運(yùn)算符稱為殘差連接(或快捷連接)。使用殘差塊,輸入可以通過(guò)跨層的殘差連接更快地向前傳播。事實(shí)上,殘差塊可以被認(rèn)為是多分支 Inception 塊的一個(gè)特例:它有兩個(gè)分支,其中一個(gè)是恒等映射。

pYYBAGR9NdeAOMD1AAI9tco820o210.svg

圖 8.6.2在一個(gè)規(guī)則塊中(左),虛線框內(nèi)的部分必須直接學(xué)習(xí)映射f(x). 在殘差塊中(右),虛線框內(nèi)的部分需要學(xué)習(xí)殘差映射 g(x)=f(x)?x, 進(jìn)行身份映射f(x)=x更容易學(xué)習(xí)。

ResNet 完全遵循 VGG 的3×3卷積層設(shè)計(jì)。殘差塊有兩個(gè)3×3具有相同輸出通道數(shù)的卷積層。每個(gè)卷積層后跟一個(gè)批量歸一化層和一個(gè) ReLU 激活函數(shù)。然后,我們跳過(guò)這兩個(gè)卷積操作,將輸入直接添加到最終的 ReLU 激活函數(shù)之前。這種設(shè)計(jì)要求兩個(gè)卷積層的輸出必須與輸入具有相同的形狀,這樣才能將它們相加。如果我們想改變頻道的數(shù)量,我們需要引入一個(gè)額外的 1×1卷積層將輸入轉(zhuǎn)換為加法運(yùn)算所需的形狀。讓我們看看下面的代碼。

class Residual(nn.Module): #@save
  """The Residual block of ResNet models."""
  def __init__(self, num_channels, use_1x1conv=False, strides=1):
    super().__init__()
    self.conv1 = nn.LazyConv2d(num_channels, kernel_size=3, padding=1,
                  stride=strides)
    self.conv2 = nn.LazyConv2d(num_channels, kernel_size=3, padding=1)
    if use_1x1conv:
      self.conv3 = nn.LazyConv2d(num_channels, kernel_size=1,
                    stride=strides)
    else:
      self.conv3 = None
    self.bn1 = nn.LazyBatchNorm2d()
    self.bn2 = nn.LazyBatchNorm2d()

  def forward(self, X):
    Y = F.relu(self.bn1(self.conv1(X)))
    Y = self.bn2(self.conv2(Y))
    if self.conv3:
      X = self.conv3(X)
    Y += X
    return F.relu(Y)

class Residual(nn.Block): #@save
  """The Residual block of ResNet models."""
  def __init__(self, num_channels, use_1x1conv=False, strides=1, **kwargs):
    super().__init__(**kwargs)
    self.conv1 = nn.Conv2D(num_channels, kernel_size=3, padding=1,
                strides=strides)
    self.conv2 = nn.Conv2D(num_channels, kernel_size=3, padding=1)
    if use_1x1conv:
      self.conv3 = nn.Conv2D(num_channels, kernel_size=1,
                  strides=strides)
    else:
      self.conv3 = None
    self.bn1 = nn.BatchNorm()
    self.bn2 = nn.BatchNorm()

  def forward(self, X):
    Y = npx.relu(self.bn1(self.conv1(X)))
    Y = self.bn2(self.conv2(Y))
    if self.conv3:
      X = self.conv3(X)
    return npx.relu(Y + X)

class Residual(nn.Module): #@save
  """The Residual block of ResNet models."""
  num_channels: int
  use_1x1conv: bool = False
  strides: tuple = (1, 1)
  training: bool = True

  def setup(self):
    self.conv1 = nn.Conv(self.num_channels, kernel_size=(3, 3),
               padding='same', strides=self.strides)
    self.conv2 = nn.Conv(self.num_channels, kernel_size=(3, 3),
               padding='same')
    if self.use_1x1conv:
      self.conv3 = nn.Conv(self.num_channels, kernel_size=(1, 1),
                 strides=self.strides)
    else:
      self.conv3 = None
    self.bn1 = nn.BatchNorm(not self.training)
    self.bn2 = nn.BatchNorm(not self.training)

  def __call__(self, X):
    Y = nn.relu(self.bn1(self.conv1(X)))
    Y = self.bn2(self.conv2(Y))
    if self.conv3:
      X = self.conv3(X)
    Y += X
    return nn.relu(Y)

class Residual(tf.keras.Model): #@save
  """The Residual block of ResNet models."""
  def __init__(self, num_channels, use_1x1conv=False, strides=1):
    super().__init__()
    self.conv1 = tf.keras.layers.Conv2D(num_channels, padding='same',
                      kernel_size=3, strides=strides)
    self.conv2 = tf.keras.layers.Conv2D(num_channels, kernel_size=3,
                      padding='same')
    self.conv3 = None
    if use_1x1conv:
      self.conv3 = tf.keras.layers.Conv2D(num_channels, kernel_size=1,
                        strides=strides)
    self.bn1 = tf.keras.layers.BatchNormalization()
    self.bn2 = tf.keras.layers.BatchNormalization()

  def call(self, X):
    Y = tf.keras.activations.relu(self.bn1(self.conv1(X)))
    Y = self.bn2(self.conv2(Y))
    if self.conv3 is not None:
      X = self.conv3(X)
    Y += X
    return tf.keras.activations.relu(Y)

此代碼生成兩種類型的網(wǎng)絡(luò):一種是我們?cè)趹?yīng)用 ReLU 非線性之前將輸入添加到輸出 use_1x1conv=False,另一種是我們通過(guò)1×1添加前的卷積。 圖 8.6.3說(shuō)明了這一點(diǎn)。

pYYBAGR9NdmAMEv-AAICnd0x3iQ460.svg

圖 8.6.3 ResNet 塊有無(wú)1×1卷積,將輸入轉(zhuǎn)換為加法運(yùn)算所需的形狀。

現(xiàn)在讓我們看一下輸入和輸出形狀相同的情況,其中1×1不需要卷積。

blk = Residual(3)
X = torch.randn(4, 3, 6, 6)
blk(X).shape

torch.Size([4, 3, 6, 6])

blk = Residual(3)
blk.initialize()
X = np.random.randn(4, 3, 6, 6)
blk(X).shape

(4, 3, 6, 6)

blk = Residual(3)
X = jax.random.normal(d2l.get_key(), (4, 6, 6, 3))
blk.init_with_output(d2l.get_key(), X)[0].shape

(4, 6, 6, 3)

blk = Residual(3)
X = tf.random.normal((4, 6, 6, 3))
Y = blk(X)
Y.shape

TensorShape([4, 6, 6, 3])

我們還可以選擇在增加輸出通道數(shù)量的同時(shí)將輸出高度和寬度減半。在這種情況下,我們使用 1×1卷積通過(guò)use_1x1conv=True. 這在每個(gè) ResNet 塊的開頭派上用場(chǎng),可以通過(guò)strides=2.

blk = Residual(6, use_1x1conv=True, strides=2)
blk(X).shape

torch.Size([4, 6, 3, 3])

blk = Residual(6, use_1x1conv=True, strides=2)
blk.initialize()
blk(X).shape

(4, 6, 3, 3)

blk = Residual(6, use_1x1conv=True, strides=(2, 2))
blk.init_with_output(d2l.get_key(), X)[0].shape

(4, 3, 3, 6)

blk = Residual(6, use_1x1conv=True, strides=2)
blk(X).shape

TensorShape([4, 3, 3, 6])

8.6.3. ResNet模型

ResNet 的前兩層與我們之前描述的 GoogLeNet 相同:7×7具有 64 個(gè)輸出通道且步幅為 2 的卷積層之后是3×3 步幅為2的最大池化層。不同之處在于ResNet中每個(gè)卷積層之后添加的批量歸一化層。

class ResNet(d2l.Classifier):
  def b1(self):
    return nn.Sequential(
      nn.LazyConv2d(64, kernel_size=7, stride=2, padding=3),
      nn.LazyBatchNorm2d(), nn.ReLU(),
      nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

class ResNet(d2l.Classifier):
  def b1(self):
    net = nn.Sequential()
    net.add(nn.Conv2D(64, kernel_size=7, strides=2, padding=3),
        nn.BatchNorm(), nn.Activation('relu'),
        nn.MaxPool2D(pool_size=3, strides=2, padding=1))
    return net

class ResNet(d2l.Classifier):
  arch: tuple
  lr: float = 0.1
  num_classes: int = 10
  training: bool = True

  def setup(self):
    self.net = self.create_net()

  def b1(self):
    return nn.Sequential([
      nn.Conv(64, kernel_size=(7, 7), strides=(2, 2), padding='same'),
      nn.BatchNorm(not self.training), nn.relu,
      lambda x: nn.max_pool(x, window_shape=(3, 3), strides=(2, 2),
                 padding='same')])

class ResNet(d2l.Classifier):
  def b1(self):
    return tf.keras.models.Sequential([
      tf.keras.layers.Conv2D(64, kernel_size=7, strides=2,
                  padding='same'),
      tf.keras.layers.BatchNormalization(),
      tf.keras.layers.Activation('relu'),
      tf.keras.layers.MaxPool2D(pool_size=3, strides=2,
                   padding='same')])

GoogLeNet 使用由 Inception 塊組成的四個(gè)模塊。然而,ResNet 使用了四個(gè)由殘差塊組成的模塊,每個(gè)模塊使用了幾個(gè)具有相同輸出通道數(shù)的殘差塊。第一個(gè)模塊中的通道數(shù)與輸入通道數(shù)相同。由于已經(jīng)使用了步幅為 2 的最大池化層,因此沒(méi)有必要減少高度和寬度。在每個(gè)后續(xù)模塊的第一個(gè)殘差塊中,通道數(shù)與前一個(gè)模塊相比增加了一倍,高度和寬度減半。

@d2l.add_to_class(ResNet)
def block(self, num_residuals, num_channels, first_block=False):
  blk = []
  for i in range(num_residuals):
    if i == 0 and not first_block:
      blk.append(Residual(num_channels, use_1x1conv=True, strides=2))
    else:
      blk.append(Residual(num_channels))
  return nn.Sequential(*blk)

@d2l.add_to_class(ResNet)
def block(self, num_residuals, num_channels, first_block=False):
  blk = nn.Sequential()
  for i in range(num_residuals):
    if i == 0 and not first_block:
      blk.add(Residual(num_channels, use_1x1conv=True, strides=2))
    else:
      blk.add(Residual(num_channels))
  return blk

@d2l.add_to_class(ResNet)
def block(self, num_residuals, num_channels, first_block=False):
  blk = []
  for i in range(num_residuals):
    if i == 0 and not first_block:
      blk.append(Residual(num_channels, use_1x1conv=True,
                strides=(2, 2), training=self.training))
    else:
      blk.append(Residual(num_channels, training=self.training))
  return nn.Sequential(blk)

@d2l.add_to_class(ResNet)
def block(self, num_residuals, num_channels, first_block=False):
  blk = tf.keras.models.Sequential()
  for i in range(num_residuals):
    if i == 0 and not first_block:
      blk.add(Residual(num_channels, use_1x1conv=True, strides=2))
    else:
      blk.add(Residual(num_channels))
  return blk

然后,我們將所有模塊添加到 ResNet。這里,每個(gè)模塊使用兩個(gè)殘差塊。最后,就像 GoogLeNet 一樣,我們添加了一個(gè)全局平均池化層,然后是全連接層輸出。

@d2l.add_to_class(ResNet)
def __init__(self, arch, lr=0.1, num_classes=10):
  super(ResNet, self).__init__()
  self.save_hyperparameters()
  self.net = nn.Sequential(self.b1())
  for i, b in enumerate(arch):
    self.net.add_module(f'b{i+2}', self.block(*b, first_block=(i==0)))
  self.net.add_module('last', nn.Sequential(
    nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(),
    nn.LazyLinear(num_classes)))
  self.net.apply(d2l.init_cnn)

@d2l.add_to_class(ResNet)
def __init__(self, arch, lr=0.1, num_classes=10):
  super(ResNet, self).__init__()
  self.save_hyperparameters()
  self.net = nn.Sequential()
  self.net.add(self.b1())
  for i, b in enumerate(arch):
    self.net.add(self.block(*b, first_block=(i==0)))
  self.net.add(nn.GlobalAvgPool2D(), nn.Dense(num_classes))
  self.net.initialize(init.Xavier())

@d2l.add_to_class(ResNet)
def create_net(self):
  net = nn.Sequential([self.b1()])
  for i, b in enumerate(self.arch):
    net.layers.extend([self.block(*b, first_block=(i==0))])
  net.layers.extend([nn.Sequential([
    # Flax does not provide a GlobalAvg2D layer
    lambda x: nn.avg_pool(x, window_shape=x.shape[1:3],
               strides=x.shape[1:3], padding='valid'),
    lambda x: x.reshape((x.shape[0], -1)),
    nn.Dense(self.num_classes)])])
  return net

@d2l.add_to_class(ResNet)
def __init__(self, arch, lr=0.1, num_classes=10):
  super(ResNet, self).__init__()
  self.save_hyperparameters()
  self.net = tf.keras.models.Sequential(self.b1())
  for i, b in enumerate(arch):
    self.net.add(self.block(*b, first_block=(i==0)))
  self.net.add(tf.keras.models.Sequential([
    tf.keras.layers.GlobalAvgPool2D(),
    tf.keras.layers.Dense(units=num_classes)]))

每個(gè)模塊有 4 個(gè)卷積層(不包括 1×1卷積層)。與第一個(gè)一起 7×7卷積層和最后的全連接層,一共18層。因此,該模型通常被稱為 ResNet-18。通過(guò)在模塊中配置不同數(shù)量的通道和殘差塊,我們可以創(chuàng)建不同的 ResNet 模型,例如更深的 152 層 ResNet-152。雖然 ResNet 的主要架構(gòu)與 GoogLeNet 相似,但 ResNet 的結(jié)構(gòu)更簡(jiǎn)單,也更容易修改。所有這些因素導(dǎo)致了 ResNet 的快速和廣泛使用。圖 8.6.4描繪了完整的 ResNet-18。

poYBAGR9NdyAUEOHAAFd5cJ1snI428.svg

圖 8.6.4 ResNet-18 架構(gòu)。

在訓(xùn)練 ResNet 之前,讓我們觀察輸入形狀在 ResNet 的不同模塊之間是如何變化的。與之前的所有架構(gòu)一樣,分辨率會(huì)降低,而通道數(shù)量會(huì)增加,直到全局平均池化層聚合所有特征為止。

class ResNet18(ResNet):
  def __init__(self, lr=0.1, num_classes=10):
    super().__init__(((2, 64), (2, 128), (2, 256), (2, 512)),
            lr, num_classes)

ResNet18().layer_summary((1, 1, 96, 96))

Sequential output shape:   torch.Size([1, 64, 24, 24])
Sequential output shape:   torch.Size([1, 64, 24, 24])
Sequential output shape:   torch.Size([1, 128, 12, 12])
Sequential output shape:   torch.Size([1, 256, 6, 6])
Sequential output shape:   torch.Size([1, 512, 3, 3])
Sequential output shape:   torch.Size([1, 10])

class ResNet18(ResNet):
  def __init__(self, lr=0.1, num_classes=10):
    super().__init__(((2, 64), (2, 128), (2, 256), (2, 512)),
            lr, num_classes)

ResNet18().layer_summary((1, 1, 96, 96))

Sequential output shape:   (1, 64, 24, 24)
Sequential output shape:   (1, 64, 24, 24)
Sequential output shape:   (1, 128, 12, 12)
Sequential output shape:   (1, 256, 6, 6)
Sequential output shape:   (1, 512, 3, 3)
GlobalAvgPool2D output shape:    (1, 512, 1, 1)
Dense output shape: (1, 10)

class ResNet18(ResNet):
  arch: tuple = ((2, 64), (2, 128), (2, 256), (2, 512))
  lr: float = 0.1
  num_classes: int = 10

ResNet18(training=False).layer_summary((1, 96, 96, 1))

Sequential output shape:   (1, 24, 24, 64)
Sequential output shape:   (1, 24, 24, 64)
Sequential output shape:   (1, 12, 12, 128)
Sequential output shape:   (1, 6, 6, 256)
Sequential output shape:   (1, 3, 3, 512)
Sequential output shape:   (1, 10)

class ResNet18(ResNet):
  def __init__(self, lr=0.1, num_classes=10):
    super().__init__(((2, 64), (2, 128), (2, 256), (2, 512)),
            lr, num_classes)

ResNet18().layer_summary((1, 96, 96, 1))

Sequential output shape:   (1, 24, 24, 64)
Sequential output shape:   (1, 24, 24, 64)
Sequential output shape:   (1, 12, 12, 128)
Sequential output shape:   (1, 6, 6, 256)
Sequential output shape:   (1, 3, 3, 512)
Sequential output shape:   (1, 10)

8.6.4. 訓(xùn)練

我們像以前一樣在 Fashion-MNIST 數(shù)據(jù)集上訓(xùn)練 ResNet。ResNet 是一個(gè)非常強(qiáng)大和靈活的架構(gòu)。捕獲訓(xùn)練和驗(yàn)證損失的圖說(shuō)明了兩個(gè)圖之間的顯著差距,訓(xùn)練損失明顯較低。對(duì)于這種靈活性的網(wǎng)絡(luò),更多的訓(xùn)練數(shù)據(jù)將在縮小差距和提高準(zhǔn)確性方面提供顯著的好處。

model = ResNet18(lr=0.01)
trainer = d2l.Trainer(max_epochs=10, num_gpus=1)
data = d2l.FashionMNIST(batch_size=128, resize=(96, 96))
model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn)
trainer.fit(model, data)

pYYBAGR9Nd6AY_5sAAGKAbS5afA220.svg

model = ResNet18(lr=0.01)
trainer = d2l.Trainer(max_epochs=10, num_gpus=1)
data = d2l.FashionMNIST(batch_size=128, resize=(96, 96))
trainer.fit(model, data)

poYBAGR9NeGAZKshAAGIgZn8cJU302.svg

model = ResNet18(lr=0.01)
trainer = d2l.Trainer(max_epochs=10, num_gpus=1)
data = d2l.FashionMNIST(batch_size=128, resize=(96, 96))
trainer.fit(model, data)

pYYBAGR9NeOAGdpfAAGIaQJ1PQQ165.svg

trainer = d2l.Trainer(max_epochs=10)
data = d2l.FashionMNIST(batch_size=128, resize=(96, 96))
with d2l.try_gpu():
  model = ResNet18(lr=0.01)
  trainer.fit(model, data)

poYBAGR9NeWAPB1XAAFovnrEzQc063.svg

8.6.5. ResNeXt

在 ResNet 的設(shè)計(jì)中遇到的挑戰(zhàn)之一是給定塊內(nèi)的非線性和維度之間的權(quán)衡。也就是說(shuō),我們可以通過(guò)增加層數(shù)或增加卷積寬度來(lái)增加更多的非線性。另一種策略是增加可以在塊之間傳輸信息的通道數(shù)量。不幸的是,后者會(huì)帶來(lái)二次懲罰,因?yàn)閿z取的計(jì)算成本ci 渠道和發(fā)射co渠道正比于 O(ci?co)(參見(jiàn)我們?cè)?第 7.4 節(jié)中的討論)。

我們可以從圖 8.4.1的 Inception 塊中得到一些啟發(fā) ,它有信息在不同的組中流經(jīng)塊。將多個(gè)獨(dú)立組的思想應(yīng)用于圖 8.6.3的 ResNet 塊導(dǎo)致了 ResNeXt 的設(shè)計(jì)(Xie et al. , 2017)。與 Inception 中變換的大雜燴不同,ResNeXt 在所有分支中采用 相同的變換,從而最大限度地減少了對(duì)每個(gè)分支進(jìn)行手動(dòng)調(diào)整的需要。

poYBAGR9NeiASf3WAARy5TTS_W0346.svg

圖 8.6.5 ResNeXt 塊。分組卷積的使用g 團(tuán)體是g比密集卷積快 1 倍。當(dāng)中間通道的數(shù)量增加時(shí),它是一個(gè)瓶頸殘差塊 b小于c.

從中分解一個(gè)卷積ci到co頻道進(jìn)入其中一個(gè)g大小組ci/g產(chǎn)生g 尺寸輸出co/g非常恰當(dāng)?shù)胤Q為分組卷積。計(jì)算成本(按比例)從 O(ci?co)到 O(g?(ci/g)?(co/g))=O(ci?co/g),即它是g倍更快。更好的是,生成輸出所需的參數(shù)數(shù)量也從 ci×co矩陣到g更小的矩陣 (ci/g)×(co/g), 又是一個(gè)g次減少。在下文中,我們假設(shè)兩者ci和co被整除g.

這種設(shè)計(jì)中唯一的挑戰(zhàn)是,兩者之間沒(méi)有信息交換g團(tuán)體。圖 8.6.5的 ResNeXt 塊 以兩種方式修正了這一點(diǎn):分組卷積與3×3內(nèi)核夾在兩個(gè)中間1×1卷積。第二個(gè)在改回頻道數(shù)量方面起著雙重作用。好處是我們只支付O(c?b)費(fèi)用1×1內(nèi)核,可以湊合O(b2/g)費(fèi)用 3×3內(nèi)核。類似于第 8.6.2 節(jié)中的殘差塊實(shí)現(xiàn),殘差連接被替換(因此泛化)為1×1卷積。

圖 8.6.5中的右圖提供了生成的網(wǎng)絡(luò)塊的更簡(jiǎn)潔的摘要。它還將在第 8.8 節(jié)中的通用現(xiàn)代 CNN 設(shè)計(jì)中發(fā)揮重要作用 。請(qǐng)注意,分組卷積的想法可以追溯到 AlexNet 的實(shí)現(xiàn) (Krizhevsky等人,2012 年)。當(dāng)在內(nèi)存有限的兩個(gè) GPU 上分配網(wǎng)絡(luò)時(shí),該實(shí)現(xiàn)將每個(gè) GPU 視為自己的通道,沒(méi)有任何不良影響。

該類的以下實(shí)現(xiàn)ResNeXtBlock作為參數(shù)groups(g), 與bot_channels(b) 中間(瓶頸)渠道。最后,當(dāng)我們需要減少表示的高度和寬度時(shí),我們添加一個(gè)步幅2通過(guò)設(shè)置。use_1x1conv=True, strides=2

class ResNeXtBlock(nn.Module): #@save
  """The ResNeXt block."""
  def __init__(self, num_channels, groups, bot_mul, use_1x1conv=False,
         strides=1):
    super().__init__()
    bot_channels = int(round(num_channels * bot_mul))
    self.conv1 = nn.LazyConv2d(bot_channels, kernel_size=1, stride=1)
    self.conv2 = nn.LazyConv2d(bot_channels, kernel_size=3,
                  stride=strides, padding=1,
                  groups=bot_channels//groups)
    self.conv3 = nn.LazyConv2d(num_channels, kernel_size=1, stride=1)
    self.bn1 = nn.LazyBatchNorm2d()
    self.bn2 = nn.LazyBatchNorm2d()
    self.bn3 = nn.LazyBatchNorm2d()
    if use_1x1conv:
      self.conv4 = nn.LazyConv2d(num_channels, kernel_size=1,
                    stride=strides)
      self.bn4 = nn.LazyBatchNorm2d()
    else:
      self.conv4 = None

  def forward(self, X):
    Y = F.relu(self.bn1(self.conv1(X)))
    Y = F.relu(self.bn2(self.conv2(Y)))
    Y = self.bn3(self.conv3(Y))
    if self.conv4:
      X = self.bn4(self.conv4(X))
    return F.relu(Y + X)

class ResNeXtBlock(nn.Block): #@save
  """The ResNeXt block."""
  def __init__(self, num_channels, groups, bot_mul,
         use_1x1conv=False, strides=1, **kwargs):
    super().__init__(**kwargs)
    bot_channels = int(round(num_channels * bot_mul))
    self.conv1 = nn.Conv2D(bot_channels, kernel_size=1, padding=0,
                strides=1)
    self.conv2 = nn.Conv2D(bot_channels, kernel_size=3, padding=1,
                strides=strides, groups=bot_channels//groups)
    self.conv3 = nn.Conv2D(num_channels, kernel_size=1, padding=0,
                strides=1)
    self.bn1 = nn.BatchNorm()
    self.bn2 = nn.BatchNorm()
    self.bn3 = nn.BatchNorm()
    if use_1x1conv:
      self.conv4 = nn.Conv2D(num_channels, kernel_size=1,
                  strides=strides)
      self.bn4 = nn.BatchNorm()
    else:
      self.conv4 = None

  def forward(self, X):
    Y = npx.relu(self.bn1(self.conv1(X)))
    Y = npx.relu(self.bn2(self.conv2(Y)))
    Y = self.bn3(self.conv3(Y))
    if self.conv4:
      X = self.bn4(self.conv4(X))
    return npx.relu(Y + X)

class ResNeXtBlock(nn.Module): #@save
  """The ResNeXt block."""
  num_channels: int
  groups: int
  bot_mul: int
  use_1x1conv: bool = False
  strides: tuple = (1, 1)
  training: bool = True

  def setup(self):
    bot_channels = int(round(self.num_channels * self.bot_mul))
    self.conv1 = nn.Conv(bot_channels, kernel_size=(1, 1),
                strides=(1, 1))
    self.conv2 = nn.Conv(bot_channels, kernel_size=(3, 3),
                strides=self.strides, padding='same',
                feature_group_count=bot_channels//self.groups)
    self.conv3 = nn.Conv(self.num_channels, kernel_size=(1, 1),
                strides=(1, 1))
    self.bn1 = nn.BatchNorm(not self.training)
    self.bn2 = nn.BatchNorm(not self.training)
    self.bn3 = nn.BatchNorm(not self.training)
    if self.use_1x1conv:
      self.conv4 = nn.Conv(self.num_channels, kernel_size=(1, 1),
                    strides=self.strides)
      self.bn4 = nn.BatchNorm(not self.training)
    else:
      self.conv4 = None

  def __call__(self, X):
    Y = nn.relu(self.bn1(self.conv1(X)))
    Y = nn.relu(self.bn2(self.conv2(Y)))
    Y = self.bn3(self.conv3(Y))
    if self.conv4:
      X = self.bn4(self.conv4(X))
    return nn.relu(Y + X)

class ResNeXtBlock(tf.keras.Model): #@save
  """The ResNeXt block."""
  def __init__(self, num_channels, groups, bot_mul, use_1x1conv=False,
         strides=1):
    super().__init__()
    bot_channels = int(round(num_channels * bot_mul))
    self.conv1 = tf.keras.layers.Conv2D(bot_channels, 1, strides=1)
    self.conv2 = tf.keras.layers.Conv2D(bot_channels, 3, strides=strides,
                      padding="same",
                      groups=bot_channels//groups)
    self.conv3 = tf.keras.layers.Conv2D(num_channels, 1, strides=1)
    self.bn1 = tf.keras.layers.BatchNormalization()
    self.bn2 = tf.keras.layers.BatchNormalization()
    self.bn3 = tf.keras.layers.BatchNormalization()
    if use_1x1conv:
      self.conv4 = tf.keras.layers.Conv2D(num_channels, 1,
                        strides=strides)
      self.bn4 = tf.keras.layers.BatchNormalization()
    else:
      self.conv4 = None

  def call(self, X):
    Y = tf.keras.activations.relu(self.bn1(self.conv1(X)))
    Y = tf.keras.activations.relu(self.bn2(self.conv2(Y)))
    Y = self.bn3(self.conv3(Y))
    if self.conv4:
      X = self.bn4(self.conv4(X))
    return tf.keras.activations.relu(Y + X)

它的使用與前面討論的完全相似ResNetBlock。例如,當(dāng)使用 ( ) 時(shí),輸入和輸出的形狀相同。或者,設(shè)置 輸出高度和寬度的一半。use_1x1conv=False, strides=1use_1x1conv=True, strides=2

blk = ResNeXtBlock(32, 16, 1)
X = torch.randn(4, 32, 96, 96)
blk(X).shape

torch.Size([4, 32, 96, 96])

blk = ResNeXtBlock(32, 16, 1)
blk.initialize()
X = np.random.randn(4, 32, 96, 96)
blk(X).shape

(4, 32, 96, 96)

blk = ResNeXtBlock(32, 16, 1)
X = jnp.zeros((4, 96, 96, 32))
blk.init_with_output(d2l.get_key(), X)[0].shape

(4, 96, 96, 32)

blk = ResNeXtBlock(32, 16, 1)
X = tf.random.normal((4, 96, 96, 32))
Y = blk(X)
Y.shape

TensorShape([4, 96, 96, 32])

8.6.6. 總結(jié)與討論

嵌套函數(shù)類是可取的,因?yàn)樗鼈冊(cè)试S我們?cè)谠黾尤萘繒r(shí)獲得更強(qiáng)大的函數(shù)類,而不是細(xì)微不同的函數(shù)類。實(shí)現(xiàn)這一點(diǎn)的一種方法是允許附加層簡(jiǎn)單地將輸入傳遞到輸出。殘余連接允許這樣做。因此,這改變了形式為簡(jiǎn)單函數(shù)的歸納偏差 f(x)=0看起來(lái)像簡(jiǎn)單的功能 f(x)=x.

殘差映射可以更容易地學(xué)習(xí)身份函數(shù),例如將權(quán)重層中的參數(shù)推為零。我們可以通過(guò)殘差塊來(lái)訓(xùn)練有效的深度神經(jīng)網(wǎng)絡(luò)。輸入可以通過(guò)跨層的剩余連接更快地向前傳播。因此,我們可以訓(xùn)練更深層次的網(wǎng)絡(luò)。例如,最初的 ResNet 論文( He et al. , 2016 )允許多達(dá) 152 層。殘差網(wǎng)絡(luò)的另一個(gè)好處是它允許我們添加層,初始化為恒等函數(shù),在培訓(xùn)過(guò)程。畢竟,層的默認(rèn)行為是讓數(shù)據(jù)不加改變地通過(guò)。在某些情況下,這可以加速超大型網(wǎng)絡(luò)的訓(xùn)練。

在殘差連接之前,引入了帶有門控單元的旁路路徑,以有效地訓(xùn)練超過(guò) 100 層的高速公路網(wǎng)絡(luò) (Srivastava等人,2015 年)。使用身份函數(shù)作為繞過(guò)路徑,ResNet 在多個(gè)計(jì)算機(jī)視覺(jué)任務(wù)上表現(xiàn)非常出色。殘差連接對(duì)后續(xù)深度神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)產(chǎn)生了重大影響,包括卷積和順序性質(zhì)。正如我們稍后將介紹的,Transformer 架構(gòu) (Vaswani等人,2017 年)采用殘差連接(連同其他設(shè)計(jì)選擇),并且在語(yǔ)言、視覺(jué)、語(yǔ)音和強(qiáng)化學(xué)習(xí)等不同領(lǐng)域普遍存在。

ResNeXt 是卷積神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)如何隨著時(shí)間的推移而演變的一個(gè)例子:通過(guò)更節(jié)儉地計(jì)算并與激活的大小(通道數(shù))進(jìn)行權(quán)衡,它允許以更低的成本更快、更準(zhǔn)確的網(wǎng)絡(luò). 查看分組卷積的另一種方法是考慮卷積權(quán)重的塊對(duì)角矩陣。請(qǐng)注意,有很多這樣的“技巧”可以提高網(wǎng)絡(luò)的效率。例如,ShiftNet (Wu等人,2018 年)模仿了 3×3卷積,簡(jiǎn)單地通過(guò)向通道添加移位激活,提供增加的功能復(fù)雜性,這次沒(méi)有任何計(jì)算成本。

到目前為止我們討論的設(shè)計(jì)的一個(gè)共同特征是網(wǎng)絡(luò)設(shè)計(jì)是相當(dāng)手動(dòng)的,主要依靠設(shè)計(jì)者的獨(dú)創(chuàng)性來(lái)找到“正確的”網(wǎng)絡(luò)超參數(shù)。雖然顯然可行,但就人力時(shí)間而言,它也非常昂貴,并且無(wú)法保證結(jié)果在任何意義上都是最佳的。在 第 8.8 節(jié)中,我們將討論一些以更自動(dòng)化的方式獲得高質(zhì)量網(wǎng)絡(luò)的策略。特別是,我們將回顧導(dǎo)致 RegNetX/Y 模型的 網(wǎng)絡(luò)設(shè)計(jì)空間的概念(Radosavovic等人,2020 年)。

8.6.7. 練習(xí)

圖 8.4.1中的 Inception 塊 和殘差塊的主要區(qū)別是什么?他們?nèi)绾卧谟?jì)算、準(zhǔn)確性和他們可以描述的函數(shù)類別方面進(jìn)行比較?

參考 ResNet 論文( He et al. , 2016 )中的表 1 來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)的不同變體。

對(duì)于更深的網(wǎng)絡(luò),ResNet 引入了一個(gè)“瓶頸”架構(gòu)來(lái)降低模型的復(fù)雜性。嘗試實(shí)施它。

在ResNet的后續(xù)版本中,作者將“卷積、批量歸一化和激活”結(jié)構(gòu)改為“批量歸一化、激活和卷積”結(jié)構(gòu)。自己進(jìn)行此改進(jìn)。參見(jiàn)He等人的圖 1 。( 2016 )了解詳情。

為什么我們不能無(wú)限制地增加函數(shù)的復(fù)雜性,即使函數(shù)類是嵌套的?

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • pytorch
    +關(guān)注

    關(guān)注

    2

    文章

    808

    瀏覽量

    13686
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    深度學(xué)習(xí)與圖神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)分享:CNN經(jīng)典網(wǎng)絡(luò)之-ResNet

    深度學(xué)習(xí)與圖神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)分享:CNN 經(jīng)典網(wǎng)絡(luò)之-ResNet resnet 又叫深度
    的頭像 發(fā)表于 10-12 09:54 ?1227次閱讀
    深度學(xué)習(xí)與圖神經(jīng)<b class='flag-5'>網(wǎng)絡(luò)</b>學(xué)習(xí)分享:CNN經(jīng)典<b class='flag-5'>網(wǎng)絡(luò)</b>之-<b class='flag-5'>ResNet</b>

    什么是深度收縮網(wǎng)絡(luò)

       深度收縮網(wǎng)絡(luò)是深度網(wǎng)絡(luò)的一種新的升級(jí)版本,其實(shí)是深度
    發(fā)表于 11-26 06:33

    由多模塊組成的多窗口網(wǎng)絡(luò)優(yōu)化模型

      基于卷積神經(jīng)網(wǎng)絡(luò)的單圖像超分辨率模型網(wǎng)絡(luò)結(jié)構(gòu)過(guò)深,導(dǎo)致高頻信息丟失以及模型體積龐大等問(wèn)題。提出一種由多個(gè)模塊構(gòu)成的多窗口
    發(fā)表于 03-10 16:31 ?3次下載
    由多<b class='flag-5'>殘</b><b class='flag-5'>差</b>模塊組成的多窗口<b class='flag-5'>殘</b><b class='flag-5'>差</b><b class='flag-5'>網(wǎng)絡(luò)</b>優(yōu)化模型

    一種改進(jìn)的網(wǎng)絡(luò)結(jié)構(gòu)以減少卷積層參數(shù)

    傳統(tǒng)的網(wǎng)絡(luò)在果實(shí)病害分類中存在層數(shù)較多,以及在實(shí)際應(yīng)用中有參數(shù)冗余的問(wèn)題,且原始損失函數(shù)對(duì)具有相似特征的病害容易造成錯(cuò)誤識(shí)別。為解決果害分類中參數(shù)過(guò)多及相似樣本區(qū)分度低的問(wèn)題,提出一種改進(jìn)的
    發(fā)表于 03-23 14:48 ?8次下載
    一種改進(jìn)的<b class='flag-5'>殘</b><b class='flag-5'>差</b><b class='flag-5'>網(wǎng)絡(luò)</b>結(jié)構(gòu)以減少卷積層參數(shù)

    基于雙超密集網(wǎng)絡(luò)的多模態(tài)醫(yī)學(xué)圖像融合方法

    針對(duì)基于網(wǎng)絡(luò)和密集網(wǎng)絡(luò)的圖像融合方法存在網(wǎng)絡(luò)中間層的部分有用信息丟失和融合圖像細(xì)節(jié)不清晰的問(wèn)題,提出了基于雙
    發(fā)表于 04-14 11:18 ?19次下載
    基于雙<b class='flag-5'>殘</b><b class='flag-5'>差</b>超密集<b class='flag-5'>網(wǎng)絡(luò)</b>的多模態(tài)醫(yī)學(xué)圖像融合方法

    基于連接的改進(jìn)端到端文本識(shí)別網(wǎng)絡(luò)結(jié)構(gòu)

    針對(duì)已有文本識(shí)別網(wǎng)絡(luò)由于深度不夠而識(shí)別準(zhǔn)確率較低的問(wèn)題,文中提岀一種改進(jìn)的端到端文本識(shí)別網(wǎng)絡(luò)結(jié)構(gòu)。首先,將文本作為序列,采用模塊將文本按列切分成特征向量輸入循環(huán)層。這種
    發(fā)表于 05-17 15:18 ?6次下載

    基于多尺度網(wǎng)絡(luò)的邊緣檢測(cè)技術(shù)

    面向?qū)ο蟮倪吘墮z測(cè)技術(shù)是智能視覺(jué)處理領(lǐng)堿的關(guān)鍵基礎(chǔ)技術(shù),然而目前基于卷積神經(jīng)網(wǎng)絡(luò)的邊緣檢測(cè)結(jié)果存在分辨率低、噪聲較多等問(wèn)題。因此,文中提出了一種基于多尺度網(wǎng)絡(luò)的對(duì)象級(jí)邊緣檢測(cè)算法。
    發(fā)表于 05-29 14:27 ?3次下載

    基于改進(jìn)網(wǎng)絡(luò)的水下圖像重建修復(fù)

    自然水體成像中湍流及懸浮顆粒等環(huán)境因素會(huì)造成水下采集的圖像存在扭曲失真、分辨率低、背景模糊等問(wèn)題,為了解決上述問(wèn)題并進(jìn)一步提高圖像重建和復(fù)原的質(zhì)量,提出了一種改進(jìn)的基于網(wǎng)絡(luò)的圖像超分辨率重建方法
    發(fā)表于 06-17 15:37 ?10次下載

    基于多尺度通道注意機(jī)制的人臉超分辨率網(wǎng)絡(luò)

    基于多尺度通道注意機(jī)制的人臉超分辨率網(wǎng)絡(luò)
    發(fā)表于 06-27 14:36 ?15次下載

    基于神經(jīng)網(wǎng)絡(luò)的微型電機(jī)轉(zhuǎn)子焊點(diǎn)圖像檢測(cè)

    基于神經(jīng)網(wǎng)絡(luò)的微型電機(jī)轉(zhuǎn)子焊點(diǎn)圖像檢測(cè)
    發(fā)表于 07-02 14:56 ?23次下載

    基于非對(duì)稱注意力機(jī)制網(wǎng)絡(luò)的圖像檢測(cè)

    基于非對(duì)稱注意力機(jī)制網(wǎng)絡(luò)的圖像檢測(cè)
    發(fā)表于 07-05 15:29 ?9次下載

    《圖學(xué)學(xué)報(bào)》—深度網(wǎng)絡(luò)的無(wú)人機(jī)多目標(biāo)識(shí)別

    級(jí)聯(lián)區(qū)域建議網(wǎng)絡(luò)(CRPN)的搜索模式對(duì)其進(jìn)行改善。此外,深層次的卷積神經(jīng)網(wǎng)絡(luò)訓(xùn)練中易產(chǎn)生退化現(xiàn)象,而引入學(xué)習(xí)的深度
    發(fā)表于 12-02 17:14 ?1216次閱讀
    《圖學(xué)學(xué)報(bào)》—深度<b class='flag-5'>殘</b><b class='flag-5'>差</b><b class='flag-5'>網(wǎng)絡(luò)</b>的無(wú)人機(jī)多目標(biāo)識(shí)別

    《圖學(xué)學(xué)報(bào)》—深度網(wǎng)絡(luò)的無(wú)人機(jī)多目標(biāo)識(shí)別

    一種級(jí)聯(lián)區(qū)域建議網(wǎng)絡(luò)(CRPN)的搜索模式對(duì)其進(jìn)行改善。此外,深層次的卷積神經(jīng)網(wǎng)絡(luò)訓(xùn)練中易產(chǎn)生退化現(xiàn)象,而引入學(xué)習(xí)的深度
    發(fā)表于 12-06 17:02 ?703次閱讀

    PyTorch教程8.6網(wǎng)絡(luò)(ResNet)和ResNeXt

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程8.6網(wǎng)絡(luò)(ResNet)和
    發(fā)表于 06-05 10:08 ?0次下載
    <b class='flag-5'>PyTorch</b>教程<b class='flag-5'>8.6</b>之<b class='flag-5'>殘</b><b class='flag-5'>差</b><b class='flag-5'>網(wǎng)絡(luò)</b>(<b class='flag-5'>ResNet</b>)和<b class='flag-5'>ResNeXt</b>

    網(wǎng)絡(luò)是深度神經(jīng)網(wǎng)絡(luò)

    網(wǎng)絡(luò)(Residual Network,通常簡(jiǎn)稱為ResNet) 是深度神經(jīng)網(wǎng)絡(luò)的一種 ,其獨(dú)特的結(jié)構(gòu)設(shè)計(jì)在解決深層
    的頭像 發(fā)表于 07-11 18:13 ?1424次閱讀
    主站蜘蛛池模板: 亚洲综合香蕉 | 欧美性猛交xxxx黑人喷水 | 俄罗斯欧美色黄激情 | 黄色三级录像 | 国产激情电影综合在线看 | 免费午夜视频 | 日本不卡在线播放 | a级精品九九九大片免费看 a级毛毛片看久久 | 天堂网在线www资源在线 | 国产综合视频在线观看 | 色婷婷久| 天天干天天要 | 天天爱天天干天天 | 亚洲福利秒拍一区二区 | 国产手机视频在线 | 岛国最新资源网站 | 日本一区免费在线观看 | 天天干天天干天天色 | 日本卡一卡2卡3卡4精品卡无人区 | 久草福利在线播放 | 久久99免费 | 狠狠一区| 国产乱码精品一区二区三区四川人 | 欧美性猛交xxxxbbbb | 轻点灬大ji巴太粗太大了小说 | 亚洲美女啪啪 | 午夜男人的天堂 | 我要看黄色一级毛片 | 夜夜骑日日射 | 欧美性69 | 久操视频在线观看免费 | 国产a三级三级三级 | 日本三级日本三级日本三级极 | 婷婷99视频精品全部在线观看 | 国产高清精品自在久久 | 五月婷婷六月丁香综合 | 日本拍拍拍 | 天天爱天天做天天爽 | 在线观看高清视频 | 国产在线免 | 日韩有色 |