经典神经网络(7)DenseNet及其在Fashion-MNIST数据集上的应用

news2024/11/26 14:41:50

经典神经网络(7)DenseNet及其在Fashion-MNIST数据集上的应用

1 DenseNet的简述

  1. DenseNet不是通过更深或者更宽的结构,而是通过特征重用来提升网络的学习能力。

  2. ResNet 的思想是:创建从“靠近输入的层” 到 “靠近输出的层” 的直连。而DenseNet 做得更为彻底:将所有层以前馈的形式相连,这种网络因此称作DenseNet

  3. DenseNet 具有以下的优点:

    • 缓解梯度消失的问题。因为每层都可以直接从损失函数中获取梯度、从原始输入中获取信息,从而易于训练。
    • 密集连接还具有正则化的效应,缓解了小训练集任务的过拟合。
    • 鼓励特征重用。网络将不同层学到的 feature map 进行组合。
    • 大幅度减少参数数量。因为每层的卷积核尺寸都比较小,输出通道数较少 (由增长率决定)。
  4. DenseNet 具有比传统卷积网络更少的参数,因为它不需要重新学习多余的feature map

    • 传统的前馈神经网络可以视作在层与层之间传递状态的算法,每一层接收前一层的状态,然后将新的状态传递给下一层。

      这会改变状态,但是也传递了需要保留的信息。

    • ResNet 通过恒等映射来直接传递需要保留的信息,因此层之间只需要传递状态的变化

    • DenseNet 会将所有层的状态 全部保存到集体知识中,同时每一层增加很少数量的feture map 到网络的集体知识中

  5. DenseNet 的层很窄(即:feature map 的通道数很小),如:每一层的输出只有 12 个通道。

  6. 在跨层连接上,不同于ResNet中将输⼊与输出相加,稠密连接网络(DenseNet)在通道维上连结输⼊与输出。DenseNet的主要构建模块是稠密块和过渡层。在构建DenseNet时,我们需要通过添加过渡层来控制网络的维数,从⽽再次减少通道的数量。

  7. 虽然 DenseNet 的计算效率较高、参数相对较少,但是DenseNet 对内存不友好。可以考虑通过共享内存,解决这个问题。

  8. 论文下载地址: https://arxiv.org/pdf/1608.06993.pdf

1.1 稠密块(dense block)

在这里插入图片描述

在这里插入图片描述

ResNet和DenseNet的关键区别在于,DenseNet输出是连接(下图中的[ , ] 表示),而不是如ResNet的简单相加。

在这里插入图片描述

DenseNet这个名字由变量之间的“稠密连接”⽽得来,最后⼀层与之前的所有层紧密相连。

在这里插入图片描述

注意:当 feature map 的尺寸改变时,无法沿着通道方向进行拼接。此时将网络划分为多个DenseNet 块,每块内部的 feature map尺寸相同,块之间的feature map 尺寸不同。

1.1.1 增长率

  1. DenseNet 块中,每层的 H(即BN-ReLU-Conv) 输出的feature map 通道数都相同,都是k个。 k是一个重要的超参数,称作网络的增长率。

    第 l 层的输入【特征图】的通道数为: k 0 + k ( l − 1 ) 。其中 k 0 为输入层的通道数。 第l层的输入【特征图】 的通道数为:k_0 + k(l-1) 。其中k_0为输入层的通道数。 l层的输入【特征图】的通道数为:k0+k(l1)。其中k0为输入层的通道数。

  2. DenseNet 不同于现有网络的一个重要地方是:DenseNet 的网络很窄,即输出的 feature map 通道数较小,如:k = 12 。

    • 一个很小的增长率就能够获得不错的效果。一种解释是:DenseNet 块的每层都可以访问块内的所有早前层输出的feature map,这些feature map 可以视作DenseNet 块的全局状态。每层输出的feature map 都将被添加到块的这个全局状态中,该全局状态可以理解为网络块的【集体知识】,由块内所有层共享。增长率决定了新增特征占全局状态的比例。

    • 因此feature map 无需逐层复制(因为它是全局共享),这也是DenseNet 与传统网络结构不同的地方。这有助于整个网络的特征重用,并产生更紧凑的模型。

1.1.2 非线性变换

  • H可以是包含了 Batch Normalization(BN) 、ReLU 单元、池化或者卷积等操作的复合函数。

  • 论文中的结构为:先执行BN ,再执行ReLU,最后接一个3 x 3 的卷积,即:BN-ReLU-Conv(3x3)

  • pytorch实现如下

import torch.nn as nn
import torch


'''
DenseNet使⽤了ResNet改良版的“批量规范化、激活和卷积”架构

    卷积块:BN-ReLU-Conv
'''
def conv_block(input_channels, num_channels):

    return nn.Sequential(
                  nn.BatchNorm2d(input_channels),
                  nn.ReLU(),
                  nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1)
         )

1.1.3 bottleneck

在这里插入图片描述

1.1.4 pytorch实现稠密块

import torch.nn as nn
import torch


'''
DenseNet使⽤了ResNet改良版的“批量规范化、激活和卷积”架构

    卷积块:BN-ReLU-Conv
'''
def conv_block(input_channels, num_channels):

    return nn.Sequential(
                  nn.BatchNorm2d(input_channels),
                  nn.ReLU(),
                  nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1)
         )



'''
⼀个稠密块由多个卷积块组成,每个卷积块使⽤相同数量的输出通道。

然⽽,在前向传播中,我们将每个卷积块的输⼊和输出在通道维上连结。
'''
class DenseBlock(nn.Module):
    def __init__(self, num_convs, input_channels, num_channels):
        super(DenseBlock, self).__init__()

        layer = []
        for i in range(num_convs):
            layer.append(
                conv_block(num_channels * i + input_channels, num_channels)     # 一个稠密块由多个卷积块组成
            )
        self.net = nn.Sequential(*layer)


    def forward(self, X):
        for blk in self.net:
            Y = blk(X)
            # 连接通道维度上每个块的输⼊和输出
            X = torch.cat((X, Y), dim=1)
        return X

if __name__ == '__main__':
    '''
    1、稠密块 dense block
    我们定义⼀个有2个输出通道数为10的DenseBlock。
    使⽤通道数为3的输⼊时,我们会得到通道数为3 + 2 × 10 = 23的输出。
    卷积块的通道数控制了输出通道数相对于输⼊通道数的增⻓,因此也被称为增⻓率(growth rate)。
    '''
    blk = DenseBlock(2, 3, 10)
    # X经过第一个卷积块后变为(4, 10, 8, 8),然后和原始X(4, 3, 8, 8)进行在维度1进行拼接,X变成(4, 13, 8, 8)
    # 然后输入到第二个卷积块,第二个卷积块将channels由(10+3)变为10,因此输出Y(4, 10, 8, 8)
    # 然后X和Y在维度1进行拼接,得到最终输出(4, 23, 8, 8)
    X = torch.randn(4, 3, 8, 8)
    Y = blk(X)
    print(Y.shape)  # (4, 23, 8, 8)

1.2 过渡层(transition layer)

1.2.1 过渡层的介绍

  • 一个DenseNet 网络具有多个DenseNet块,DenseNet 块之间由过渡层连接。DenseNet 块之间的层称为过渡层,其主要作用是连接不同的DenseNet块。

  • 过渡层可以包含卷积或池化操作,从而改变前一个DenseNet 块的输出feature map 的大小(包括尺寸大小、通道数量)。

    • 论文中的过渡层由一个BN层、一个1x1 卷积层、一个2x2 平均池化层组成。其中 1x1 卷积层用于减少DenseNet 块的输出通道数,提高模型的紧凑性。
    • 如果不减少DenseNet 块的输出通道数,则经过了 个DenseNet 块之后,网络的feature map 的通道数会变得很大(通道数计算如下图公式)

在这里插入图片描述

  • 如果Dense 块输出feature map的通道数为 m,则可以使得过渡层输出feature map 的通道数为 theta ✖ m ,其中0< theta <=1 为压缩因子。
    • 当theta = 1时,经过过渡层的feature map 通道数不变。
    • 当theta < 1时,经过过渡层的feature map 通道数减小。此时的DenseNet 称做 DenseNet-C
    • 结合了DenseNet-CDenseNet-B 的改进的网络称作 DenseNet-BC

1.2.2 过渡层的实现

'''
由于每个稠密块都会带来通道数的增加,使⽤过多则会过于复杂化模型。
⽽过渡层可以⽤来控制模型复杂度。它通过1 × 1卷积层来减⼩通道数,并使⽤步幅为2的平均汇聚层减半⾼和宽,从⽽进⼀步降低模型复杂度。
'''
def transition_block(input_channels, num_channels):
    return nn.Sequential(
            nn.BatchNorm2d(input_channels),
            nn.ReLU(),
            nn.Conv2d(input_channels, num_channels, kernel_size=1), # 1×1卷积层来减⼩通道数
            nn.AvgPool2d(kernel_size=2, stride=2)                   # 步幅为2的平均汇聚层减半⾼和宽
    )




if __name__ == '__main__':
    '''
    1、稠密块 dense block
    我们定义⼀个有2个输出通道数为10的DenseBlock。
    使⽤通道数为3的输⼊时,我们会得到通道数为3 + 2 × 10 = 23的输出。
    卷积块的通道数控制了输出通道数相对于输⼊通道数的增⻓,因此也被称为增⻓率(growth rate)。
    '''
    blk = DenseBlock(2, 3, 10)
    # X经过第一个卷积块后变为(4, 10, 8, 8),然后和原始X(4, 3, 8, 8)进行在维度1进行拼接,X变成(4, 13, 8, 8)
    # 然后输入到第二个卷积块,第二个卷积块将channels由(10+3)变为10,因此输出Y(4, 10, 8, 8)
    # 然后X和Y在维度1进行拼接,得到最终输出(4, 23, 8, 8)
    X = torch.randn(4, 3, 8, 8)
    Y = blk(X)
    print(Y.shape)  # (4, 23, 8, 8)


    '''
    2、过渡层 transition layer
    '''
    blk = transition_block(23, 10)
    print(blk(Y).shape)  # torch.Size([4, 10, 4, 4])

1.3 DenseNet网络性能

1.3.1 网络结构

网络结构:ImageNet 训练的DenseNet 网络结构,其中增长率k = 32 。

  • 表中的 conv 代表的是 BN-ReLU-Conv 的组合。如 1x1 conv 表示:先执行BN,再执行ReLU,最后执行1x1 的卷积。
  • DenseNet-xx 表示DenseNet 块有xx 层。如:DenseNet-169 表示 DenseNet 块有L=169 层 。
  • 所有的 DenseNet 使用的是 DenseNet-BC 结构,输入图片尺寸为224x224,初始卷积尺寸为7x7、输出通道 2k、步长为2 ,压缩因子 theta=0.5。
  • 在所有DenseNet 块的最后接一个全局平均池化层,该池化层的结果作为softmax 输出层的输入。

在这里插入图片描述

1.3.2 在ImageNet 验证集的错误率

下图是DenseNetResNetImageNet 验证集的错误率的比较(single-crop)。左图为参数数量,右图为计算量。

在这里插入图片描述

从实验可见:DenseNet 的参数数量和计算量相对ResNet 明显减少。

  • 具有 20M 个参数的DenseNet-201 与具有 40M 个参数的ResNet-101 验证误差接近。
  • ResNet-101 验证误差接近的DenseNet-201 的计算量接近于ResNet-50,几乎是ResNet-101 的一半。

1.3.3 一个简单版本DenseNet的实现

我们实现一个简单的版本的DenseNet,使用DenseNet,而非DenseNet-BC,以应用在Fashion-MNIST数据集上。

稠密块和过度层

import torch.nn as nn
import torch


'''
DenseNet使⽤了ResNet改良版的“批量规范化、激活和卷积”架构

    卷积块:BN-ReLU-Conv
'''
def conv_block(input_channels, num_channels):

    return nn.Sequential(
                  nn.BatchNorm2d(input_channels),
                  nn.ReLU(),
                  nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1)
         )



'''
⼀个稠密块由多个卷积块组成,每个卷积块使⽤相同数量的输出通道。

然⽽,在前向传播中,我们将每个卷积块的输⼊和输出在通道维上连结。
'''
class DenseBlock(nn.Module):
    def __init__(self, num_convs, input_channels, num_channels):
        super(DenseBlock, self).__init__()

        layer = []
        for i in range(num_convs):
            layer.append(
                conv_block(num_channels * i + input_channels, num_channels)     # 一个稠密块由多个卷积块组成
            )
        self.net = nn.Sequential(*layer)


    def forward(self, X):
        for blk in self.net:
            Y = blk(X)
            # 连接通道维度上每个块的输⼊和输出
            X = torch.cat((X, Y), dim=1)
        return X

'''
由于每个稠密块都会带来通道数的增加,使⽤过多则会过于复杂化模型。
⽽过渡层可以⽤来控制模型复杂度。它通过1 × 1卷积层来减⼩通道数,并使⽤步幅为2的平均汇聚层减半⾼和宽,从⽽进⼀步降低模型复杂度。
'''
def transition_block(input_channels, num_channels):
    return nn.Sequential(
            nn.BatchNorm2d(input_channels),
            nn.ReLU(),
            nn.Conv2d(input_channels, num_channels, kernel_size=1), # 1×1卷积层来减⼩通道数
            nn.AvgPool2d(kernel_size=2, stride=2)                   # 步幅为2的平均汇聚层减半⾼和宽
    )


if __name__ == '__main__':
    '''
    1、稠密块 dense block
    我们定义⼀个有2个输出通道数为10的DenseBlock。
    使⽤通道数为3的输⼊时,我们会得到通道数为3 + 2 × 10 = 23的输出。
    卷积块的通道数控制了输出通道数相对于输⼊通道数的增⻓,因此也被称为增⻓率(growth rate)。
    '''
    blk = DenseBlock(2, 3, 10)
    # X经过第一个卷积块后变为(4, 10, 8, 8),然后和原始X(4, 3, 8, 8)进行在维度1进行拼接,X变成(4, 13, 8, 8)
    # 然后输入到第二个卷积块,第二个卷积块将channels由(10+3)变为10,因此输出Y(4, 10, 8, 8)
    # 然后X和Y在维度1进行拼接,得到最终输出(4, 23, 8, 8)
    X = torch.randn(4, 3, 8, 8)
    Y = blk(X)
    print(Y.shape)  # (4, 23, 8, 8)


    '''
    2、过渡层 transition layer
    '''
    blk = transition_block(23, 10)
    print(blk(Y).shape)  # torch.Size([4, 10, 4, 4])

DenseNet

import torch.nn as nn
import torch
from _08_dense_block import DenseBlock,transition_block

class DenseNet(nn.Module):


    def __init__(self):
        super(DenseNet, self).__init__()
        '''
            1、DenseNet⾸先使⽤同ResNet⼀样的单卷积层和最⼤汇聚层。
        '''
        b1 = nn.Sequential(
                nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                nn.BatchNorm2d(64),
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )
        '''
            2、接下来,类似于ResNet使⽤的4个残差块,DenseNet使⽤的是4个稠密块。
        与ResNet类似,我们可以设置每个稠密块使⽤多少个卷积层。这⾥我们设成4,从⽽之前的ResNet-18保持⼀致。
        稠密块⾥的卷积层通道数(即增⻓率)设为32,所以每个稠密块将增加128个通道。
        
        
            3、在每个模块之间,ResNet通过步幅为2的残差块减⼩⾼和宽,DenseNet则使⽤过渡层来减半⾼和宽,并减半通道数。
        '''
        # num_channels为当前的通道数
        num_channels, growth_rate = 64, 32
        num_convs_in_dense_blocks = [4, 4, 4, 4]
        blks = []
        for i, num_convs in enumerate(num_convs_in_dense_blocks):
            # 添加稠密块
            blks.append(DenseBlock(num_convs, num_channels, growth_rate))
            # 上⼀个稠密块的输出通道数
            num_channels += num_convs * growth_rate

            # 在稠密块之间添加⼀个转换层,使通道数量减半
            if i != len(num_convs_in_dense_blocks) - 1:
                blks.append(transition_block(num_channels, num_channels // 2))
                num_channels = num_channels // 2
        '''
        4、与ResNet类似,最后接上全局汇聚层和全连接层来输出结果。
        '''
        self.model = nn.Sequential(
            b1,
            *blks,
            nn.BatchNorm2d(num_channels),
            nn.ReLU(),
            nn.AdaptiveAvgPool2d((1, 1)),
            nn.Flatten(),
            nn.Linear(num_channels, 10)
        )




    def forward(self, X):
        return self.model(X)


if __name__ == '__main__':
    net = DenseNet()
    X = torch.rand(size=(1, 1, 224, 224), dtype=torch.float32)
    for layer in net.model:
        X = layer(X)
        print(layer.__class__.__name__, 'output shape:', X.shape)
Sequential output shape: torch.Size([1, 64, 56, 56])

DenseBlock output shape: torch.Size([1, 192, 56, 56])
Sequential output shape: torch.Size([1, 96, 28, 28])
DenseBlock output shape: torch.Size([1, 224, 28, 28])
Sequential output shape: torch.Size([1, 112, 14, 14])
DenseBlock output shape: torch.Size([1, 240, 14, 14])
Sequential output shape: torch.Size([1, 120, 7, 7])
DenseBlock output shape: torch.Size([1, 248, 7, 7])

BatchNorm2d output shape: torch.Size([1, 248, 7, 7])
ReLU output shape: torch.Size([1, 248, 7, 7])
AdaptiveAvgPool2d output shape: torch.Size([1, 248, 1, 1])
Flatten output shape: torch.Size([1, 248])
Linear output shape: torch.Size([1, 10])

1.4 DenseNet的内存或显存消耗过多问题

虽然 DenseNet 的计算效率较高、参数相对较少,但是DenseNet 对内存不友好。考虑到GPU 显存大小的限制,因此无法训练较深的 DenseNet

1.4.1 内存的计算

假设DenseNet块包含L层,则:
对于第 l 层,有 x l = H l ( [ x 0 , x 1 , . . . , x l − 1 ] ) 对于第l层,有x_l = H_l([x_0,x_1,...,x_{l-1}]) 对于第l层,有xl=Hl([x0,x1,...,xl1])
假设每层的输出feature map 尺寸均为W×H、通道数为k, 由BN-ReLU-Conv(3x3) 组成,则:

  • 拼接Concat操作 :需要生成临时feature map 作为第l层的输入,内存消耗为 W×H×k×l 。
  • BN 操作:需要生成临时feature map 作为ReLU 的输入,内存消耗为 W×H×k×l 。
  • ReLU 操作:可以执行原地修改,因此不需要额外的feature map 存放ReLU 的输出。
  • Conv 操作:需要生成输出feature map 作为第l层的输出,它是必须的开销。

因此除了第1,2,…,L层的输出feature map 需要内存开销之外,第l层还需要2W×H×k×l 的内存开销来存放中间生成的临时feature map
整个 D e n s e N e t 块需要 W × H × k × ( L + 1 ) L 的内存开销来存放中间生成的临时特征图。 即 D e n s e N e t 块的内存消耗为 O ( L 2 ) ,是网络深度的平方关系。 整个 DenseNet 块 需要W×H×k×(L+1)L 的内存开销来存放中间生成的临时特征图。\\ 即DenseNet 块 的内存消耗为O(L^2),是网络深度的平方关系。 整个DenseNet块需要W×H×k×(L+1)L的内存开销来存放中间生成的临时特征图。DenseNet块的内存消耗为O(L2),是网络深度的平方关系。

1.4.2 拼接的必要性及内存消耗的原因

  • 拼接Concat操作是必须的,因为当卷积的输入存放在连续的内存区域时,卷积操作的计算效率较高。而DenseNet Block 中,第l层的输入feature map 由前面各层的输出feature map 沿通道方向拼接而成。而这些输出feature map 并不在连续的内存区域。

  • DenseNet Block 的这种内存消耗并不是DenseNet Block 的结构引起的,而是由深度学习库引起的。因为Tensorflow/PyTorch 等库在实现神经网络时,会存放中间生成的临时节点(如BN 的输出节点),这是为了在反向传播阶段可以直接获取临时节点的值。

  • 这是在时间代价和空间代价之间的折中:通过开辟更多的空间来存储临时值,从而在反向传播阶段节省计算。

1.4.3 网络的参数也会消耗内存

除了临时feature map 的内存消耗之外,网络的参数也会消耗内存。设 H由BN-ReLU-Conv(3x3) 组成,则第l层的网络参数数量为: 9×l×k^2(不考虑 BN )。
整个 D e n s e N e t 块的参数数量为 9 k 2 ( L + 1 ) L 2 , 即 O ( L 2 ) 整个 DenseNet块的参数数量为\frac{9k^2(L+1)L}{2},即O(L^2) 整个DenseNet块的参数数量为29k2(L+1)L,O(L2)

  • 由于DenseNet 参数数量与网络的深度呈平方关系,因此DenseNet 网络的参数更多、网络容量更大。这也是DenseNet 优于其它网络的一个重要因素。
  • 通常情况下都有WH > (9×k/2) ,其中W,H为网络feature map 的宽、高,k为网络的增长率。所以网络参数消耗的内存要远小于临时feature map 消耗的内存。

1.5 DenseNet内存优化_共享内存

其思想是利用时间代价和空间代价之间的折中,但是侧重于牺牲时间代价来换取空间代价。

其背后支撑的因素是:Concat操作和BN 操作的计算代价很低,但是空间代价很高。因此这种做法在DenseNet 中非常有效。

1.5.1 传统做法

传统的DenseNet Block 的第 l 层。首先将 feature map 拷贝到连续的内存块,拷贝时完成拼接的操作。然后依次执行BNReLUConv 操作。

该层的临时feature map 需要消耗内存 2W×H×k×l,该层的输出feature map 需要消耗内存W×H×k 。

  • 另外某些实现(如LuaTorch)还需要为反向传播过程的梯度分配内存,如左图下半部分所示。如:计算 BN 层输出的梯度时,需要用到第 l 层输出层的梯度和BN 层的输出。存储这些梯度需要额外的 O(lk)的内存。
  • 另外一些实现(如PyTorch,MxNet)会对梯度使用共享的内存区域来存放这些梯度,因此只需要O(k)的内存。

在这里插入图片描述

1.5.2 共享内存做法

右图为内存优化的DenseNet Block 的第 l 层。采用两组预分配的共享内存区Shared memory Storage location 来存concate 操作和BN 操作输出的临时feature map

对于第一组预分配的共享内存区:

第一组预分配的共享内存区:concat 操作共享区。第1,2,…,L 层的 concat 操作的输出都写入到该共享区,第(l+1) 层的写入会覆盖第 (l)层的结果。

  • 对于整个Dense Block,这个共享区只需要分配 W×H×k×L(最大的feature map )的内存,即内存消耗为O(kL) (对比传统DenseNet的O(kL^2) )。

  • 后续的BN 操作直接从这个共享区读取数据。

  • 由于第 (l+1)层的写入会覆盖第(l) 层的结果,因此这里存放的数据是临时的、易丢失的。因此在反向传播阶段还需要重新计算第 (l)层的Concat 操作的结果。

    因为concat 操作的计算效率非常高,因此这种额外的计算代价很低。

对于第二组预分配的共享内存区

第二组预分配的共享内存区:BN 操作共享区。第1,2,…,L 层的 concat 操作的输出都写入到该共享区,第(l+1) 层的写入会覆盖第 (l)层的结果。

  • 对于整个Dense Block,这个共享区也只需要分配W×H×k×L (最大的feature map )的内存,即内存消耗为O(kL) (对比传统DenseNet的O(kL^2) )。

  • 后续的卷积操作直接从这个共享区读取数据。

  • concat 操作共享区同样的原因,在反向传播阶段还需要重新计算第(l)层的BN 操作的结果。

    BN 的计算效率也很高,只需要额外付出大约 5% 的计算代价。

由于BN 操作和concat 操作在神经网络中大量使用,因此这种预分配共享内存区的方法可以广泛应用。它们可以在增加少量的计算时间的情况下节省大量的内存消耗。

2 DenseNet在Fashion-MNIST数据集上的应用示例

2.1 创建DenseNet网络模型

如1.3.3所示。

2.2 读取Fashion-MNIST数据集

batch_size = 256

# 为了使Fashion-MNIST上的训练短⼩精悍,将输⼊的⾼和宽从224降到96,简化计算
train_iter,test_iter = get_mnist_data(batch_size,resize=96)

2.3 在GPU上进行模型训练

from _08_DenseNet import DenseNet

# 初始化模型
net = DenseNet()

lr, num_epochs = 0.1, 10
train_ch(net, train_iter, test_iter, num_epochs, lr, try_gpu())

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/722472.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea取消右侧边缘提示信息;IDEA使用心得:工作区右边框提示信息

idea的提示是很人性的&#xff0c;合理使用一定程度上也会提升个人的编码水平 最终效果&#xff1a;能看到weak warning个数&#xff0c;但侧边栏不会显示直接清爽 但是idea在检查代码时有一种警告叫做weak warning 什么是weak warning 图中这种只是其中一种&#xff0c;这种…

导出本地文件(模板)

/*** 导出模板*/GetMapping("export/template")ApiOperationSupport(order 16)ApiOperation(value "导出模板")public void exportTemplate(HttpServletResponse response) {List<JointExcel> list new ArrayList<>();ClassPathResource cl…

Fortran 中的 goto 语句

注意项 避免滥用&#xff1a;Fortran 90引入了结构化编程的概念&#xff0c;切记不要滥用goto语句明确标签&#xff1a;在使用goto语句时&#xff0c;标签要明确避免跳转过多&#xff1a;过多的跳转会增加代码的复杂性和可读性避免跳转到循环内部&#xff1a;在循环内部使用go…

力扣 93. 复原 IP 地址

题目来源&#xff1a;https://leetcode.cn/problems/restore-ip-addresses/description/ C题解&#xff1a;递归回溯法。 递归参数&#xff1a;因为不能重复分割&#xff0c;需要ind记录下一层递归分割的起始位置&#xff1b;还需要一个变量num&#xff0c;记录ip段的数量。递…

【设计模式】第二十章:解释器模式详解及应用案例

系列文章 【设计模式】七大设计原则 【设计模式】第一章&#xff1a;单例模式 【设计模式】第二章&#xff1a;工厂模式 【设计模式】第三章&#xff1a;建造者模式 【设计模式】第四章&#xff1a;原型模式 【设计模式】第五章&#xff1a;适配器模式 【设计模式】第六章&…

中国移动九天毕昇部署fastchat Ubuntu18.04+torch1.13.1+cuda11.6+python3.9

8核CPU,RAM32G 时间&#xff1a;2023年7月 项目地址:GitHub - lm-sys/FastChat: An open platform for training, serving, and evaluating large language models. Release repo for Vicuna and FastChat-T5. 体验地址:https://chat.lmsys.org/ 时间20230703&#xff0c;fas…

【Web3】Web3连接到以太坊网络(测试网、主网)

目录 什么是Web3 Web3项目连接以太坊网络 1.下载Web3 2.实例化Web3对象 3.infura 获取连接以太坊网络节点 4.添加网络节点 什么是Web3 web3.js开发文档&#xff1a;web3.js - Ethereum JavaScript API — web3.js 1.0.0 documentation web3.js 中文文档 : web3.js - 以…

【观察】技术创新+以行践言双管齐下,戴尔科技加速边缘计算落地

众所周知&#xff0c;随着混合多云的快速发展&#xff0c;来自百行千业的用户都在通过混合多云的策略推进加速企业的数字化转型&#xff0c;其中边缘计算由于能够在靠近数据生成的地点和位置就近提供计算、网络、智能等关键能力&#xff0c;其不仅正成为构建“云边端”一体化的…

实验:验证TCP套接字传输的数据不存在数据边界

来源&#xff1a;《TCP/IP网络编程》 学习ing 自己动手&#xff0c;把坑踩一遍&#xff0c;也可以学习到很多。 Linux环境下: 客户端&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <…

23西安电子科技大学人工智能学院821考研录取情况

23西安电子科技大学人工智能学院821考研录取情况 01、人工智能学院各个方向 02、23人工智能学院一志愿考研录取情况总览、均分 PS&#xff1a;智能院23年院线相对于22年院线下降很多分&#xff0c;对于广大考生来说是一个好事 PS&#xff1a;1、总成绩均分在330-345之间&#…

封装一个类似微信通讯录带有字母检索功能的vue组件

这里我们直接使用scrollIntoView方法 该方法将调用它的元素滚动到浏览器窗口的可见区域 语法 element.scrollIntoView&#xff08;&#xff09;; // 等同于element.scrollIntoView(true) element.scrollIntoView&#xff08;alignToTop&#xff09;; //布尔参数 element.scro…

新颖的文档、视频交互方式:以《GPT API Unofficial Docs》和《渐构》为例

一、背景 无意中看到一份 《GPT API 非官方文档》&#xff1a;https://gpt.pomb.us/ 被网站的交互方式所吸引&#xff0c;颇为新颖&#xff0c;值得借鉴。 左侧是对应的 API 代码调用示例&#xff0c;右侧是文档的每个部分&#xff0c;滑动到对应部分&#xff0c;左侧相关的代…

前台-倒计时hooks

import { useIntervalFn } from @vueuse/core import { ref, onUnmounted } from vue// 全部显示 天、时、分、秒 const setAllCountdownStr = (value: number) => {let second = parseInt(value.toString())const day = second / (3600 * 24) //3600秒等于60分钟等于1小时s…

已上架的App在AppStore上无法搜索到的问题

已上架的App在AppStore上无法搜索到的问题 在AppStore上搜不到已经上架的应用程序可以采取以下解决办法&#xff1a; 拨打iTunes提供的支持电话&#xff1a;4006-701-855&#xff08;中国时间9:00-17:00&#xff09;。发送邮件给Review团队&#xff0c;在iTunes Connect登录后…

ElasticSearch8.x和Java API Client

建议 建议直接阅读我的博客原文 10.4 Java API Client操作-索引库/文档 客户端更新 ES为不同语言提供了用于操作ES的客户端&#xff0c;截至2023年7月4日&#xff0c;根据官网Java REST Client已经被弃用&#xff0c;取而代之的是Java API Client&#xff0c;黑马的教程里都…

Docker(一)之 应用(Application)部署容器化的演进之路

前言 容器化技术可以帮助企业更大化资源利用&#xff0c;同时帮助项目相关人员&#xff08;开发、运维、测试、安全等&#xff09;以最小的成本实现项目快速上云部署&#xff0c;掌握容器化管理工具Docker&#xff0c;就掌握了云原生最基础、最核心技术之一 应用&#xff08;…

springboot整合hibernate,gradle,达梦8数据库,实现增删改查的功能

1.新建一个springboot项目&#xff0c;选择gradle管理 2.gradle添加以下依赖&#xff0c;gradle版本7.4 dependencies {// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-webimplementation org.springframework.boot:spring-boot-starter…

Pytorch深度强化学习1-3:策略评估与贝尔曼期望方程详细推导

目录 0 专栏介绍1 从一个例子出发2 回报与奖赏3 策略评估函数4 贝尔曼期望方程5 收敛性证明 0 专栏介绍 本专栏重点介绍强化学习技术的数学原理&#xff0c;并且采用Pytorch框架对常见的强化学习算法、案例进行实现&#xff0c;帮助读者理解并快速上手开发。同时&#xff0c;辅…

ZUH-ACDM-网络-网闸-根据论坛的案例进行分析

H3C技术论坛&#xff1a;https://zhiliao.h3c.com/Theme/details/64343 文章目录 需求&#xff1a;源地址&#xff1a;172.16.1.33访问目的地址&#xff1a;192.168.4.233确认确定是主机确认外端机连接地址172.16.1.254确认内端机到源地址172.16.1.33是通的确认流量走向/路由第…

基于matlab使用高斯混合模型检测和计数视频序列中的汽车(附源码)

一、前言 此示例演示如何使用基于高斯混合模型 &#xff08;GMM&#xff09; 的前景检测器检测和计数视频序列中的汽车。 检测和计数汽车可用于分析交通模式。检测也是执行更复杂的任务&#xff08;例如按类型跟踪或分类车辆&#xff09;之前的第一步。 此示例演示如何使用前…