深度学习--CNN应用--VGG16网络和ResNet18网络

前言

我们在学习这两个网络时,应先了解CNN网络的相关知识

深度学习--CNN卷积神经网络(附图)-CSDN博客

这篇博客能够帮我们更好的理解VGG16和RetNet18

1.VGG16

1.1 VGG简介

VGG论文网址:VGG论文

大家有兴趣的可以去研读一下。

首先呢,我们先简单了解一下VGG网络的来源

自2012年AlexNet在lmageNet图片分类比赛中大获成功以来,关于深度神经网络的研究又一次如火如茶般进行;
VGGNet 是由牛津大学视觉几何小组 (Visual Geometry Group,VGG)提出的一种深层卷积网络结构,他们以 7.32% 的错误率赢得了 2014年 ILSVRC 分类任务的亚军(冠军由 GoogLeNet 以 6.65% 的错误率夺得)
AlexNet的网络结构来提高自己的准确率,主要有两个方向:小卷积核和多尺度。而VGG的作者们则选择了另外一个方向,即加深网络深度

VGG网络有一个特点:在每一次池化之后,经过卷积通道数都会翻倍,这样呢,就可以保留更多的特征。

1.2 VGG模型

最上面我们可以看出VGG共有6中配置,分别为A,A-LRN,B,C,D,E,其中D和E两种最为常用,即我们所说的VGG16和VGG19。 

而我们今天要学习的主要是D,即VGG16

下面是模型参数

这样看可能有点抽象,让我们结合下面的图片进行理解

我们结合图片对表进行理解:

“Weight layers” 在深度学习和神经网络的上下文中,通常指的是神经网络中的权重层。在神经网络中,每一层(无论是卷积层、全连接层还是其他类型的层)都有与之关联的权重矩阵(或权重张量,对于卷积层而言)。16Weight layers是指卷积层加上连接层共有16层,也就是对应着16个权重层。

con3-aaa,卷积层全部都是3*3的卷积核,用上图中conv3-aaa表示,xxx表示通道数。其步长为1,用padding=same填充。池化层的池化核为2*2

maxpool :是指最大池化,在这里,pooling采用的是2*2的最大池化方法

FC-aaaa:指的是全连接层中有aaaa个节点

通俗地说,就是两组:两个卷积层接上一个池化层,

三组:三个卷积层接一个池化层,

最后是三个全连接层接上一个归一化处理,

这里的每一个卷积层和连接层后都要加上ReLu激活函数。

这样,我们对VGG16论文中的两个表格以及VGG16的框架就大体了解完了,接下来我们就具体分析它们,然后用代码实现它。

1.2.1 通道数

我们可以看出,初始的图片经过卷积层和池化层的处理,通道数在不断增加,有最开始输入的3个,变为了512个,再到4096个,最终的1000是指分类的类别数为1000个。

我们先要理解通道数指的是什么:通道数指的是同时处理的并发任务数量。

也就是说,通道数越多,同时处理的并发任务数量也就越多,

这样的好处也有很多,比如:

  1. 提高并发性:通过增加通道数,可以同时处理更多的任务,从而提高程序的并发性。这对于需要同时处理多个请求或任务的应用程序非常有用,例如网络服务器、数据处理等。

  2. 提高性能:增加通道数可以充分利用计算资源,提高程序的处理能力和响应速度。当程序中存在大量的IO操作时,通过增加通道数可以减少IO等待时间,提高整体性能。

  3. 提高资源利用率:通过增加通道数,可以更好地利用系统资源,充分发挥多核处理器的并行计算能力。这对于需要处理大量数据或计算密集型任务的应用程序非常有益。

  4. 支持高并发场景:在一些需要处理大量并发请求的场景下,增加通道数可以有效地提高系统的吞吐量和响应能力。例如,在Web服务器中,通过增加通道数可以同时处理更多的客户端请求,提供更好的用户体验。

1.2.3 最大池化层

通过最大池化层时,我们将池化层的大小和步长都设为2,这样可以将特征图的长和宽减半

不用多说,这样也有很多好处:

1. 降维与减少冗余信息:池化层通过下采样操作,有效地减少了特征图的维度,从而降低了数据的冗余性。这对于后续的卷积层或全连接层来说,可以显著减少计算量和内存消耗。
2. 扩大感知野:池化操作使得后续的卷积层能够看到更大范围的输入信息,即提高了网络的感知野。这有助于捕捉全局特征和上下文信息,从而增强网络的特征提取能力。
3. 防止过拟合:通过减少特征图的尺寸和参数数量,池化层有助于防止模型在训练过程中出现过拟合现象。过拟合是指模型在训练数据上表现良好,但在未见过的数据上性能较差的情况。
4. 提高鲁棒性:池化操作,尤其是最大池化,能够提取出输入中最显著的特征,对于输入中的微小变化(如平移、旋转或尺度变化)具有一定的不变性。这使得模型对于不同的输入变化更为鲁棒。
5. 加速计算:由于池化层减少了特征图的尺寸,后续的卷积或全连接层的计算量也会相应减少,从而提高了整体的计算速度。 

1.2.4 ReLu函数

我们通过ReLu函数进行非线性激活,并且inplace选择了True,这样可以大大减少VGG16模型所需要的内存

下面,我们就尝试用代码实现CNN网络 

1.3 核心代码

import torch.nn
class VGG16(torch.nn.Module):
    def __init__(self):
        super(VGG16, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1)
        self.relu1 = torch.nn.ReLU(inplace=True)
        self.conv2 = torch.nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
        self.relu2 = torch.nn.ReLU(inplace=True)
        self.maxpl1 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv3 = torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
        self.relu3 = torch.nn.ReLU(inplace=True)
        self.conv4 = torch.nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)
        self.relu4 = torch.nn.ReLU(inplace=True)
        self.maxpl2 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv5 = torch.nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
        self.relu5 = torch.nn.ReLU(inplace=True)
        self.conv6 = torch.nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.relu6 = torch.nn.ReLU(inplace=True)
        self.conv7 = torch.nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.relu7 = torch.nn.ReLU(inplace=True)
        self.maxpl3 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv8 = torch.nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)
        self.relu8 = torch.nn.ReLU(inplace=True)
        self.conv9 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu9 = torch.nn.ReLU(inplace=True)
        self.conv10 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu10 = torch.nn.ReLU(inplace=True)
        self.maxpl4 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv11 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu11 = torch.nn.ReLU(inplace=True)
        self.conv12 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu12 = torch.nn.ReLU(inplace=True)
        self.conv13 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu13 = torch.nn.ReLU(inplace=True)
        self.maxpl5 = torch.nn.MaxPool2d(kernel_size=2, stride=2)
        self.fc1 = torch.nn.Linear(7*7*512, 4096)
        self.relu14 = torch.nn.ReLU(inplace=True)
        self.fc2 = torch.nn.Linear(4096, 4096)
        self.relu15 = torch.nn.ReLU(inplace=True)
        self.dropout = torch.nn.Dropout()
        self.fc3 = torch.nn.Linear(4096, 1000)

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.maxpl1(x)

        x = self.conv3(x)
        x = self.relu3(x)
        x = self.conv4(x)
        x = self.relu4(x)
        x = self.maxpl2(x)

        x = self.conv5(x)
        x = self.relu5(x)
        x = self.conv6(x)
        x = self.relu6(x)
        x = self.conv7(x)
        x = self.relu7(x)
        x = self.maxpl3(x)

        x = self.conv8(x)
        x = self.relu8(x)
        x = self.conv9(x)
        x = self.relu9(x)
        x = self.conv10(x)
        x = self.relu10(x)
        x = self.maxpl4(x)

        x = self.conv11(x)
        x = self.relu11(x)
        x = self.conv12(x)
        x = self.relu12(x)
        x = self.conv13(x)
        x = self.relu13(x)
        x = self.maxpl5(x)

        x = x.view(-1,7*7*512)
        x = self.fc1(x)
        x = self.relu14(x)
        x = self.fc2(x)
        x = self.relu15(x)
        x = self.fc3(x)

        return x

 注意:

在实现全连接层之前,我们需要用view将x展平 原理如下:

1.4 实战实现猫狗数据集分类

1.4.1代码

import torch.nn
from PIL import Image                   # 这行代码从Pillow库中导入了Image模块,它提供了许多用于打开、操作和保存图像的函数。
import numpy as np
from torch.utils.data import Dataset    # Dataset类是torch.utils.data模块中的一个抽象类,用于表示一个数据集
from torchvision import transforms
import os                               # os模块提供了与操作系统交互的函数,例如读取目录内容、检查文件是否存在等。
import torch
import matplotlib.pyplot as plt
import matplotlib
#设置字体为楷体
matplotlib.rcParams['font.sans-serif'] = ['KaiTi']

# 自定义数据集
class mydataset(Dataset):
    def __init__(self,root_dir,lable_dir):
        self.root_dir=root_dir      # 文件主路径dataset/train
        self.label_dir=lable_dir    # 分路径 cat 和 dog
        self.path=os.path.join(self.root_dir,self.label_dir)  # 将文件路径的两部分连接起来
        self.img_path=os.listdir(self.path)     # 查看
        self.transform = transforms.Compose([   # 包含:
            transforms.Resize((224,224)),       # 统一大小为224*224
            transforms.ToTensor()               # 转化为Tensor类型
        ])
    def __len__(self):
        ilen=len(self.img_path)
        return ilen                            # 返回self.img_path列表的长度,即该数据集包含的图像数量。
    def __getitem__(self,item):
        img_name=self.img_path[item]                     # path路径中的第item个图像
        img_item_path=os.path.join(self.path,img_name)   # 该图像的路径
        img=Image.open(img_item_path)                    # 打开并读取图像
        img=self.transform(img)                          # 转化为(224*224)并转化为Tensor类型
        if self.label_dir=="cat":           # 如果是cat下的图片
            label=1                         # label为猫狗的二分类值,因为二分类不能用文字“猫,狗”表示,所以这里我们用1来表示猫,0来表示狗
        else:
            label=0
        return img,label
root_dir="dataset/train"
lable_cat_dir="cat"
lable_dog_dir="dog"
cat_dataset=mydataset(root_dir,lable_cat_dir)
dog_dataset=mydataset(root_dir,lable_dog_dir)
train_dataset=cat_dataset+dog_dataset
class VGG16(torch.nn.Module):
    def __init__(self):
        super(VGG16, self).__init__()
        self.conv1 = torch.nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
        self.relu1 = torch.nn.ReLU(inplace=True)
        self.conv2 = torch.nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
        self.relu2 = torch.nn.ReLU(inplace=True)
        self.maxpl1 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv3 = torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
        self.relu3 = torch.nn.ReLU(inplace=True)
        self.conv4 = torch.nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)
        self.relu4 = torch.nn.ReLU(inplace=True)
        self.maxpl2 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv5 = torch.nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
        self.relu5 = torch.nn.ReLU(inplace=True)
        self.conv6 = torch.nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.relu6 = torch.nn.ReLU(inplace=True)
        self.conv7 = torch.nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.relu7 = torch.nn.ReLU(inplace=True)
        self.maxpl3 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv8 = torch.nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)
        self.relu8 = torch.nn.ReLU(inplace=True)
        self.conv9 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu9 = torch.nn.ReLU(inplace=True)
        self.conv10 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu10 = torch.nn.ReLU(inplace=True)
        self.maxpl4 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv11 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu11 = torch.nn.ReLU(inplace=True)
        self.conv12 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu12 = torch.nn.ReLU(inplace=True)
        self.conv13 = torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1)
        self.relu13 = torch.nn.ReLU(inplace=True)
        self.maxpl5 = torch.nn.MaxPool2d(kernel_size=2, stride=2)
        self.fc1 = torch.nn.Linear(7*7*512, 4096)
        self.relu14 = torch.nn.ReLU(inplace=True)
        self.fc2 = torch.nn.Linear(4096, 4096)
        self.relu15 = torch.nn.ReLU(inplace=True)
        self.dropout = torch.nn.Dropout()
        self.fc3 = torch.nn.Linear(4096, 2)

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.maxpl1(x)
        x = self.conv3(x)
        x = self.relu3(x)
        x = self.conv4(x)
        x = self.relu4(x)
        x = self.maxpl2(x)
        x = self.conv5(x)
        x = self.relu5(x)
        x = self.conv6(x)
        x = self.relu6(x)
        x = self.conv7(x)
        x = self.relu7(x)
        x = self.maxpl3(x)
        x = self.conv8(x)
        x = self.relu8(x)
        x = self.conv9(x)
        x = self.relu9(x)
        x = self.conv10(x)
        x = self.relu10(x)
        x = self.maxpl4(x)
        x = self.conv11(x)
        x = self.relu11(x)
        x = self.conv12(x)
        x = self.relu12(x)
        x = self.conv13(x)
        x = self.relu13(x)
        x = self.maxpl5(x)
        x = x.view(-1,7*7*512)
        x = self.fc1(x)
        x = self.relu14(x)
        x = self.fc2(x)
        x = self.relu15(x)
        x = self.fc3(x)
        return x
trainloader=torch.utils.data.DataLoader(train_dataset,batch_size=4, shuffle=True)

model=VGG16()
criterion = torch.nn.CrossEntropyLoss()
optimizer= torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

eopchs=6
for i in range(eopchs):
    sumloss=0
    for images, lables in trainloader:
        ypre=model(images)
        loss=criterion(ypre,lables)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        sumloss+=loss.item()
    print("Epoch {}, Loss: {}".format(i+1, sumloss/len(trainloader)))

examples=enumerate(trainloader)
batch,(images,lables)=next(examples)

fig=plt.figure()
for i in range(4):
    t=torch.unsqueeze(images[i],dim=0)       # 增加一个维度,使t的形状与模型期望的形状相匹配
    logps=model(t)
    probab=list(logps.detach().numpy()[0])
    # logps.detach()从计算图中分离出logps,确保后续的操作不会影响到模型的梯度。
    # 接着,.numpy()将张量转换为NumPy数组。[0]取出第一个元素(因为t是一个批次大小为1的数据),
    # 最后list()将这个元素转换为一个列表。此时,probab是一个包含所有类别概率的列表。
    pred_label=probab.index(max(probab))   # 找出probab列表中概率最大的元素的索引,这个索引即代表模型预测的类别标签。
    if pred_label==0:
        pre="狗"
    else:
        pre="猫"
    img=torch.squeeze(images[i])           # 移除大小为1的维度,让它回到原来的形状
    img1=img.permute(1, 2, 0)              # 将图像的维度从(channels, height, width)调整为(height, width, channels)
    img1=img1.numpy()

    plt.subplot(2,2,i+1)    # 创建一个2*2的子图网格,并选择第i+1个子图作为当前绘图区域
    plt.tight_layout()            # 自动调整子图参数,使之填充整个图像区域并尽量减少重叠
    plt.imshow(img1,cmap='gray',interpolation='none')
    plt.title(f"预测值:{pre}")
    plt.xticks([])                # 设置x轴和y轴的刻度标签为空
    plt.yticks([])
plt.show()

在第一层卷积层,我们需要根据图片原本的通道数进行修改,

最后的全连接层也要根据要分类的类的个数进行修改

注意:因为我们只是简单的进行示例,我们的循环次数就只循环了6次

大家可以根据自己的需要,来增加卷积层的层数来更好地获取图片特征,

增加循环次数,来减少损失值

1.4.2 结果

Epoch 1, Loss: 0.6931928634643555
Epoch 2, Loss: 0.6932265162467957
Epoch 3, Loss: 0.6932326912879944
Epoch 4, Loss: 0.6933580279350281
Epoch 5, Loss: 0.6932826161384582
Epoch 6, Loss: 0.6932745337486267

因为我们的循环次数和卷积层层数过少,损失值仍然较大,所以即使我们训练集和测试集用的同一数据集 ,我们地预测仍然存在误差。

2. ResNet18

2.1 ResNet18简介

论文网址:

ResNet网络

让我们先来简单了解一下ResNet18

ResNet18是深度学习中的一个模型,它由残差网络(ResNet)和18层深度组成。

具体来说,ResNet18包含了4个卷积层,每个卷积层使用3x3的卷积核和ReLU激活函数来提取图像的局部特征。

同时,它还有8个残差块,每个残差块由两个卷积层和一条跳跃连接(恒等连接)构成,这有助于解决深度卷积神经网络中可能出现的梯度消失和梯度爆炸问题。

此外,ResNet18还包括全局平均池化层、一个大小为1000的全连接层以及输出层,后者使用softmax激活函数生成1000个类别的概率分布。

这这里,我们主要学习ResNet18,即18-layer

我们的代码主要围绕这部分展开

实线代表卷积直接进行相加,虚线代表要先将长和宽转化一下(使前一个和下一个能够相加),在求和

2.2 结构图 

这个结构图是引入了一个快捷连接来解决深度神经网络训练中的梯度消失和梯度爆炸等问题,从而构建深层的神经网络 。

这部分也是残差函数的相关内容。

那么,什么是残差函数呢

2.3 代码 

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import font_manager
from sklearn.metrics import accuracy_score
import torch
from torch.utils.data import DataLoader, Dataset
import torchvision
from torchvision import transforms
from PIL import Image
import torch.nn.functional as F
import os


class CommonBlock(torch.nn.Module):
    def __init__(self, in_channel, out_channel, stride):
        super(CommonBlock, self).__init__()
        self.conv1 = torch.nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride, bias=False)
        self.bn1 = torch.nn.BatchNorm2d(out_channel)  # 添加BatchNorm2d进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定
        self.conv2 = torch.nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride, bias=False)
        self.bn2 = torch.nn.BatchNorm2d(out_channel)

    def forward(self, x):
        identity = x

        x = F.relu(self.bn1(self.conv1(x)), inplace=True)
        x = self.bn2(self.conv2(x))

        x += identity
        return F.relu(x, inplace=True)


class SpecialBlock(torch.nn.Module):
    def __init__(self, in_channel, out_channel, stride):
        super(SpecialBlock, self).__init__()
        self.change_channel = torch.nn.Sequential(
            torch.nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride[0], padding=0, bias=False),
            torch.nn.BatchNorm2d(out_channel)
        )

        self.conv1 = torch.nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride[0], padding=1, bias=False)
        self.bn1 = torch.nn.BatchNorm2d(out_channel)  # 添加BatchNorm2d进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定
        self.conv2 = torch.nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride[1], padding=1, bias=False)
        self.bn2 = torch.nn.BatchNorm2d(out_channel)

    def forward(self, x):
        identity = self.change_channel(x)

        x = F.relu(self.bn1(self.conv1(x)), inplace=True)
        x = self.bn2(self.conv2(x))

        x += identity
        return F.relu(x, inplace=True)


class ResNet18(torch.nn.Module):
    def __init__(self, classes_num):
        super(ResNet18).__init__()
        # 定义网络模块
        self.prepare = torch.nn.Sequential(
            torch.nn.Conv2d(3, 64, 7, 2, 3),
            torch.nn.BatchNorm2d(64),  # Batch Normanlization简称 BN,也就是数据归一化
            torch.nn.ReLU(inplace=True),
            torch.nn.MaxPool2d(3, 2, 1)
        )
        self.layer1 = torch.nn.Sequential(
            CommonBlock(64, 64, 1),
            CommonBlock(64, 64, 1)
        )
        self.layer2 = torch.nn.Sequential(
            SpecialBlock(64, 128, [2, 1]),
            CommonBlock(128, 128, 1)
        )
        self.layer3 = torch.nn.Sequential(
            SpecialBlock(128, 256, [2, 1]),
            CommonBlock(256, 256, 1)
        )
        self.layer4 = torch.nn.Sequential(
            SpecialBlock(256, 512, [2, 1]),
            CommonBlock(512, 512, 1)
        )
        self.pool = torch.nn.AdaptiveAvgPool2d(output_size=(1, 1))
        self.fc = torch.nn.Sequential(
            torch.nn.Linear(256, classes_num)
        )

    def forward(self, x):
        x = self.prepare(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = self.pool(x)
        x = x.reshape(x.shape[0], -1)

        x = self.fc(x)
        return x

有几个我们需要注意的点:

class SpecialBlock(torch.nn.Module):
    def __init__(self, in_channel, out_channel, stride):
        super(SpecialBlock, self).__init__()
        self.change_channel = torch.nn.Sequential(
            torch.nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride[0], padding=0, bias=False),
            torch.nn.BatchNorm2d(out_channel)
        )

        self.conv1 = torch.nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride[0], padding=1, bias=False)
        self.bn1 = torch.nn.BatchNorm2d(out_channel)  # 添加BatchNorm2d进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定
        self.conv2 = torch.nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride[1], padding=1, bias=False)
        self.bn2 = torch.nn.BatchNorm2d(out_channel)

    def forward(self, x

在下面,stride传入的是一个列表,分别表示横向滑动和纵向滑动的步长,当 stride=stride[0]时表示的是横向滑动,另一个则是纵向滑动。

self.change_channel = torch.nn.Sequential

它允许用户将多个计算层按照顺序组合成一个模型。在深度学习中,模型可以是由各种不同类型的层组成的,例如卷积层、池化层、全连接层等。nn.Sequential()方法可以将这些层组合在一起,形成一个整体模型。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++设计模式|创建型 4.建造者模式

1.什么是建造者模式? 建造者模式(也被成为生成器模式),是一种创建型设计模式,软件开发过程中有的时候需要创建很复杂的对象,而建造者模式的主要思想是将对象的构建过程分为多个步骤,并为每个步骤定义一个…

共享WiFi贴推广有哪些好处?一文说清

随着科技发展,更加智能高效便捷的共享wifi贴开始越来越多的取代了传统的wifi连接方法。 因为传统的连接WiFi方式确实存在一些不便之处。客人需要手动搜索店铺的WiFi,然后输入复杂的密码进行连接。这一过程不仅繁琐,而且容易出错。有时&#…

稳了!麒麟信安云应用助力国产化生态平稳过渡

2016年国家提出了安全可控体系并大力推进基础硬件、基础软件及应用软件的国产化建设进程。各行业及企事业单位加大国产设备及软硬件的采购力度,逐步完成国产化转型。而操作系统作为上层应用的载体,在信息建设国产化中有着举足轻重的地位。 过去几十年&a…

虚函数求圆形、矩形面积

目录 题目 源码 结果示例 题目 写一个程序,定义抽象基类Shape,由它派生出2个派生类:Circle(圆形)、Rectangle(矩形),用一个普通函数printarea分别输出以上二者的面积,2个图形的数据在定义对象时给定。 源码 #inc…

软考-系统集成项目管理中级--项目质量管理(输入输出很重要!!!本章占分较高,着重复习)

本章历年考题分值统计 本章重点常考知识点汇总清单 5、成本效益分析法:对每个质量活动进行成本效益分析,就是要比较其可能的成本与预期的效益。达到质量要求的主要效益包括减少返工、提高生产率、降低成本、提升干系人满意度及提升赢利能力。(掌握)17下64考题 本章…

【Flutter】GetX状态管理及路由管理用法

目录 一、安装二、使用1.安装GetX插件,快捷生成模版代码2.主入口MaterialApp改成GetMaterialApp3.定义路由常量RoutePath类、别名映射页面RoutePages类4. 初始initialRoute,getPages。5.调用 总结 一、安装 dependencies: get: ^4.6.6二、使用 1.安装G…

Java八股文4

Linux篇 1.free命令-查看内存状态 free命令用于显示内存状态,它可以提供关于系统内存使用情况的详细信息。这个命令会显示出内存的使用情况,包括实体内存、虚拟的交换文件内存、共享内存区段,以及系统核心使用的缓冲区等。 其中,参…

2024年国内USB Type-C厂商的机遇与挑战分析

USB Type-C接口作为一种全新的连接标准,已经在各种电子设备中得到了广泛的应用。2024年,国内USB Type-C厂商将面临着诸多机遇和挑战,需要全面分析和应对,以确保在竞争激烈的市场中保持竞争力和持续增长。 USB TYPE-C厂商在2024年…

09-ARM开发板的HelloWorld

在ARM开发板上运行x86_64平台程序 前面在Ubuntu系统编译生成了X86_64平台的HelloWorld程序,通过NFS服务器,尝试在开发板上直接运行。 如图所示,程序无法正常运行,终端提示ARM开发板在执行x86架构(Intel或AMD&#xff…

【Linux】地址空间虚拟地址

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 虚拟地址1.1 虚拟地址引入1.2 虚拟地址理解1.3 虚拟地址细节问题 2. 地址空间2.1 理解地址空间2.2 页表和写时拷贝 3. 进程调度 1. 虚拟地址 1.1 虚拟地址引入 先先来一个测试代码&#xff1a; 1 #include<st…

Grass注册不了、按钮灰色的解决方案

近期相信grass挂机项目不少人有所有接触。还有不了解这个项目的可以看看博客&#xff1a; http://t.csdnimg.cn/bI4UO 但是不少人注册时遇到无法注册的问题&#xff0c;或者是注册按钮显示灰色&#xff0c;放上鼠标时显示禁止。这也是博主在尝试时遇到的问题。 经过探索&…

如何解决python安装mysqlclient失败问题

在使用Django等框架来操作MySQL时&#xff0c;实际上底层还是通过Python来操作的&#xff0c;首先需要安装一个驱动程序&#xff0c;在Python3中&#xff0c;驱动程序有多种选择&#xff0c;比如有pymysql以及mysqlclient等。使用pip命令安装mysqlclient失败应如何解决&#xf…

【Linux实践室】Linux高级用户管理实战指南:Linux用户与用户组编辑操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;Linux查看用户属性命令2.1.1 &#x1f47b;…

测试用例的编写评审

1、什么叫软件测试用例 什么是测试用例 测试用例(TestCase) 是为项目需求而编制的一组测试输入、执行条件 以及预期结果&#xff0c;以便测试某个程序是否满足客户需求。–测试依据 可以总结为:每一个测试点的数据设计和步骤设计。–测试用例 2、测试用例的重要性(了解) 2.1…

社媒矩阵运营解决方案:海外云手机

在全球化的浪潮下&#xff0c;企业愈发认识到通过海外社交媒体平台扩大影响力、树立品牌形象及抢占国际市场的巨大机遇。因此&#xff0c;运营海外社交媒体账户已逐渐成为企业战略部署的重要组成部分。为了全面捕捉多渠道的流量&#xff0c;众多企业选择同时运营多个平台的多个…

基于Spring Boot的校园招聘系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

基于SkyEye运行Qt:著名应用程序开发框架

Qt是一个著名的跨平台的C图形用户界面应用程序开发框架&#xff0c;目前包括Qt Creator、Qt Designer等等快速开发工具&#xff0c;还支持2D/3D图形渲染、OpenGL&#xff0c;允许真正的组件编程&#xff0c;是与GTK、MFC、OWL、ATL一样的图形界面库。使用Qt开发的软件可以做到一…

抖音直播间没流量怎么办?如何快速提升直播间人气?

抖音直播间人气低迷&#xff0c;是否因为投入的资金不足或是数据表现不够抢眼而让你感到困惑&#xff1f;要提升抖音直播间的人气&#xff0c;首先需要深入了解抖音的推荐逻辑&#xff0c;探究直播间人气的真正来源。 抖音直播间的人气来源有哪些&#xff1f; 抖音直播间人气…

SpringMVC核心流程解析

SpringMVC核心流程解析 DispatcherServlet的继承关系请求流程分析获取HandlerChain(ControllrtMethod拦截器)获取HandlerAdapter handlerMappings的初始化过程 DispatcherServlet的继承关系 DispatcherServlet本质是一个servlet&#xff0c;既然是servlet&#xff0c;一个请求…

Pytorch-自动微分模块

&#x1f947;接下来我们进入到Pytorch的自动微分模块torch.autograd~ 自动微分模块是PyTorch中用于实现张量自动求导的模块。PyTorch通过torch.autograd模块提供了自动微分的功能&#xff0c;这对于深度学习和优化问题至关重要&#xff0c;因为它可以自动计算梯度&#xff0c…