Pytorch | 从零构建ParNet/Non-Deep Networks对CIFAR10进行分类

Pytorch | 从零构建ParNet/Non-Deep Networks对CIFAR10进行分类

  • CIFAR10数据集
  • ParNet
    • 架构特点
    • 优势
    • 应用
  • ParNet结构代码详解
    • 结构代码
    • 代码详解
      • SSE
      • ParNetBlock 类
      • DownsamplingBlock 类
      • FusionBlock 类
      • ParNet 类
  • 训练过程和测试结果
  • 代码汇总
    • parnet.py
    • train.py
    • test.py

前面文章我们构建了AlexNet、Vgg、GoogleNet、ResNet、MobileNet、EfficientNet对CIFAR10进行分类:
Pytorch | 从零构建AlexNet对CIFAR10进行分类
Pytorch | 从零构建Vgg对CIFAR10进行分类
Pytorch | 从零构建GoogleNet对CIFAR10进行分类
Pytorch | 从零构建ResNet对CIFAR10进行分类
Pytorch | 从零构建MobileNet对CIFAR10进行分类
Pytorch | 从零构建EfficientNet对CIFAR10进行分类
这篇文章我们来构建ParNet(Non-Deep Networks).

CIFAR10数据集

CIFAR-10数据集是由加拿大高级研究所(CIFAR)收集整理的用于图像识别研究的常用数据集,基本信息如下:

  • 数据规模:该数据集包含60,000张彩色图像,分为10个不同的类别,每个类别有6,000张图像。通常将其中50,000张作为训练集,用于模型的训练;10,000张作为测试集,用于评估模型的性能。
  • 图像尺寸:所有图像的尺寸均为32×32像素,这相对较小的尺寸使得模型在处理该数据集时能够相对快速地进行训练和推理,但也增加了图像分类的难度。
  • 类别内容:涵盖了飞机(plane)、汽车(car)、鸟(bird)、猫(cat)、鹿(deer)、狗(dog)、青蛙(frog)、马(horse)、船(ship)、卡车(truck)这10个不同的类别,这些类别都是现实世界中常见的物体,具有一定的代表性。

下面是一些示例样本:
在这里插入图片描述

ParNet

ParNet是一种高效的深度学习网络架构由谷歌研究人员于2021年提出,以下从其架构特点、优势及应用等方面进行详细介绍:

架构特点

  • 并行子结构:ParNet的核心在于其并行的子结构设计。它由多个并行的分支组成,每个分支都包含一系列的卷积层和池化层等操作。这些分支在网络中同时进行计算,就像多条并行的道路同时运输信息一样,大大提高了信息处理的效率。
  • 多尺度特征融合:不同分支在不同的尺度上对输入图像进行处理,然后将这些多尺度的特征进行融合。例如,一个分支可能专注于提取图像中的局部细节特征,而另一个分支则更擅长捕捉图像的全局上下文信息。通过融合这些不同尺度的特征,ParNet能够更全面、更准确地理解图像内容。
  • 深度可分离卷积:在网络的卷积操作中,大量使用了深度可分离卷积。这种卷积方式将传统的卷积操作分解为深度卷积和逐点卷积两个步骤,大大减少了计算量,同时提高了模型的运行速度,使其更适合在移动设备等资源受限的环境中应用。
    在这里插入图片描述

优势

  • 高效性:由于其并行结构和深度可分离卷积的使用,ParNet在计算效率上具有很大的优势。它可以在保证模型性能的前提下,大大减少模型的参数量和计算量,从而实现快速的推理和训练。
  • 灵活性:ParNet的并行子结构和多尺度特征融合方式使其具有很强的灵活性。它可以根据不同的任务和数据集进行调整和优化,轻松适应各种图像识别和处理任务。
  • 可扩展性:该网络架构具有良好的可扩展性,可以方便地增加或减少分支的数量和深度,以满足不同的性能需求。

应用

  • 图像分类:在图像分类任务中,ParNet能够快速准确地对图像中的物体进行分类。例如,在CIFAR-10和ImageNet等标准图像分类数据集上,ParNet取得了与现有先进模型相当的准确率,同时具有更快的推理速度。
  • 目标检测:在目标检测任务中,ParNet可以有效地检测出图像中的目标物体,并确定其位置和类别。通过对多尺度特征的融合和利用,ParNet能够更好地处理不同大小和形状的目标物体,提高检测的准确率和召回率。
  • 语义分割:在语义分割任务中,ParNet能够对图像中的每个像素进行分类,将图像分割成不同的语义区域。其多尺度特征融合的特点使得它在处理复杂的场景和物体边界时具有更好的效果,能够生成更准确的分割结果。

ParNet结构代码详解

结构代码

import torch
import torch.nn as nn
import torch.nn.functional as F

class SSE(nn.Module):
    def __init__(self, in_channels):
        super(SSE, self).__init__()
        self.global_avgpool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Linear(in_channels, in_channels)

    def forward(self, x):
        out = self.global_avgpool(x)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        out = torch.sigmoid(out)
        out = out.view(out.size(0), out.size(1), 1, 1)
        
        return x * out
    

class ParNetBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(ParNetBlock, self).__init__()
        self.branch1x1 = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        self.branch3x3 = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        self.sse = SSE(out_channels)

    def forward(self, x):
        branch1x1 = self.branch1x1(x)
        branch3x3 = self.branch3x3(x)
        out = branch1x1 + branch3x3
        out = self.sse(out)
        out = F.silu(out)

        return out
    

class DownsamplingBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(DownsamplingBlock, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.se = SSE(out_channels)

    def forward(self, x):
        out = self.conv(x)
        out = self.bn(out)
        out = self.relu(out)
        out = self.se(out)

        return out
    

class FusionBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(FusionBlock, self).__init__()
        self.conv1x1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=2, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.se = SSE(out_channels)
        self.concat = nn.Conv2d(out_channels * 2, out_channels, kernel_size=1, bias=False)
    
    def forward(self, x1, x2):
        x1, x2 = self.conv1x1(x1), self.conv1x1(x2)
        x1, x2 = self.bn(x1), self.bn(x2)
        x1, x2 = self.relu(x1), self.relu(x2)
        x1, x2 = self.se(x1), self.se(x2)
        out = torch.cat([x1, x2], dim=1)
        out = self.concat(out)

        return out
    
class ParNet(nn.Module):
    def __init__(self, num_classes):
        super(ParNet, self).__init__()
        self.downsampling_blocks = nn.ModuleList([
            DownsamplingBlock(3, 64),
            DownsamplingBlock(64, 128),
            DownsamplingBlock(128, 256),
        ])

        self.streams = nn.ModuleList([
            nn.Sequential(
                ParNetBlock(64, 64),
                ParNetBlock(64, 64),
                ParNetBlock(64, 64),
                DownsamplingBlock(64, 128)
            ),
            nn.Sequential(
                ParNetBlock(128, 128),
                ParNetBlock(128, 128),
                ParNetBlock(128, 128),
                ParNetBlock(128, 128)
            ),
            nn.Sequential(
                ParNetBlock(256, 256),
                ParNetBlock(256, 256),
                ParNetBlock(256, 256),
                ParNetBlock(256, 256)
            )
        ])

        self.fusion_blocks = nn.ModuleList([
            FusionBlock(128, 256),
            FusionBlock(256, 256)
        ])

        self.final_downsampling = DownsamplingBlock(256, 1024)
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        downsampled_features = []
        for i, downsampling_block in enumerate(self.downsampling_blocks):
            x = downsampling_block(x)
            downsampled_features.append(x)

        stream_features = []
        for i, stream in enumerate(self.streams):
            stream_feature = stream(downsampled_features[i])
            stream_features.append(stream_feature)

        fused_features = stream_features[0]
        for i in range(1, len(stream_features)):
            fused_features = self.fusion_blocks[i - 1](fused_features, stream_features[i])

        x = self.final_downsampling(fused_features)
        x = F.adaptive_avg_pool2d(x, (1, 1))
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x

代码详解

以下是对上述提供的ParNet代码的详细解释,这段代码使用PyTorch框架构建了一个名为ParNet的神经网络模型,整体结构符合ParNet网络架构的特点,下面从不同模块依次进行分析:

SSE

class SSE(nn.Module):
    def __init__(self, in_channels):
        super(SSE, self).__init__()
        self.global_avgpool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Linear(in_channels, in_channels)

    def forward(self, x):
        out = self.global_avgpool(x)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        out = torch.sigmoid(out)
        out = out.view(out.size(0), out.size(1), 1, 1)
        
        return x * out
  • 功能概述
    这个类实现了类似Squeeze-and-Excitation(SE)模块的功能,旨在对输入特征进行通道维度的重加权,突出重要的通道特征,抑制相对不重要的通道特征。

  • __init__方法

    • 首先通过nn.AdaptiveAvgPool2d(1)创建了一个自适应平均池化层,它可以将输入特征图在空间维度上压缩为大小为(1, 1)的特征图,也就是将每个通道的特征进行全局平均池化,得到通道维度上的统计信息,无论输入特征图的尺寸是多少都可以自适应处理。
    • 接着创建了一个全连接层nn.Linear(in_channels, in_channels),其输入和输出维度都是in_channels,目的是学习通道维度上的变换权重。
  • forward方法

    • 先将输入x经过全局平均池化层得到压缩后的特征表示out,然后通过view操作将其维度调整为二维形式(批次大小,通道数),方便后续全连接层处理。
    • 接着将这个特征送入全连接层进行线性变换,再经过sigmoid激活函数,将输出值映射到(0, 1)区间,得到每个通道对应的权重。
    • 最后将权重的维度调整回四维(批次大小,通道数,1,1),并与原始输入x进行逐元素相乘,实现对不同通道特征的重加权。

ParNetBlock 类

class ParNetBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(ParNetBlock, self).__init__()
        self.branch1x1 = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        self.branch3x3 = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        self.sse = SSE(out_channels)

    def forward(self, x):
        branch1x1 = self.branch1x1(x)
        branch3x3 = self.branch3x3(x)
        out = branch1x1 + branch3x3
        out = self.sse(out)
        out = F.silu(out)

        return out
  • 功能概述
    该类定义了ParNet中的一个基础并行块结构,包含两个并行分支(1x1卷积分支和3x3卷积分支)以及一个SSE模块,用于提取和融合特征,并进行通道重加权和非线性激活。

  • __init__方法

    • 构建了两个并行分支,branch1x1是一个由1x1卷积层、批归一化层和ReLU激活函数组成的序列,1x1卷积主要用于调整通道维度,同时可以融合不同通道间的信息,且计算量相对较小。
    • branch3x3同样是由3x3卷积层(带有合适的填充保证特征图尺寸不变)、批归一化层和ReLU激活函数组成,3x3卷积能够捕捉局部空间特征信息。
    • 最后实例化了一个SSE模块,用于后续对融合后的特征进行通道维度的重加权。
  • forward方法

    • 首先将输入x分别送入两个并行分支进行处理,得到两个分支的输出branch1x1branch3x3,然后将它们对应元素相加进行特征融合。
    • 接着把融合后的特征送入SSE模块进行通道重加权,最后使用F.silu(也就是swish函数)激活函数对结果进行非线性激活,并返回处理后的特征。

DownsamplingBlock 类

class DownsamplingBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(DownsamplingBlock, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.se = SSE(out_channels)

    def forward(self, x):
        out = self.conv(x)
        out = self.bn(out)
        out = self.relu(out)
        out = self.se(out)

        return out
  • 功能概述
    用于对输入特征图进行下采样操作,同时融合了批归一化、非线性激活以及类似SE的通道重加权功能,以减少特征图的空间尺寸并提取更抽象的特征。

  • __init__方法
    创建了一个3x3卷积层,其步长设置为2,配合合适的填充,在进行卷积操作时可以实现特征图在空间维度上长宽各减半的下采样效果,同时调整通道维度到out_channels。还定义了批归一化层、ReLU激活函数以及一个SSE模块。

  • forward方法
    按照顺序依次将输入x经过卷积层、批归一化层、ReLU激活函数进行处理,然后再通过SSE模块进行通道重加权,最终返回下采样并处理后的特征图。

FusionBlock 类

class FusionBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(FusionBlock, self).__init__()
        self.conv1x1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=2, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.se = SSE(out_channels)
        self.concat = nn.Conv2d(out_channels * 2, out_channels, kernel_size=1, bias=False)
    
    def forward(self, x1, x2):
        x1, x2 = self.conv1x1(x1), self.conv1x1(x2)
        x1, x2 = self.bn(x1), self.bn(x2)
        x1, x2 = self.relu(x1), self.relu(x2)
        x1, x2 = self.se(x1), self.se(x2)
        out = torch.cat([x1, x2], dim=1)
        out = self.concat(out)

        return out
  • 功能概述
    该类用于融合不同分支或不同阶段的特征,通过一系列操作包括调整通道维度、批归一化、激活以及通道重加权,然后将两个特征在通道维度上进行拼接并进一步融合。

  • __init__方法

    • 首先创建了1x1卷积层,步长设置为2,用于对输入的两个特征分别进行通道维度调整以及下采样操作(特征图空间尺寸减半)。
    • 接着定义了批归一化层、ReLU激活函数以及SSE模块,用于对下采样后的特征进行处理。还创建了一个1x1卷积层concat,用于将拼接后的特征进一步融合为指定的通道维度。
  • forward方法
    分别对输入的两个特征x1x2依次进行1x1卷积、批归一化、ReLU激活以及SSE模块的处理,然后将它们在通道维度上进行拼接(torch.cat操作,维度dim=1表示按通道维度拼接),最后通过concat卷积层将拼接后的特征融合为指定的通道维度,并返回融合后的特征。

ParNet 类

class ParNet(nn.Module):
    def __init__(self, num_classes):
        super(ParNet, self).__init__()
        self.downsampling_blocks = nn.ModuleList([
            DownsamplingBlock(3, 64),
            DownsamplingBlock(64, 128),
            DownsamplingBlock(128, 256),
        ])

        self.streams = nn.ModuleList([
            nn.Sequential(
                ParNetBlock(64, 64),
                ParNetBlock(64, 64),
                ParNetBlock(64, 64),
                DownsamplingBlock(64, 128)
            ),
            nn.Sequential(
                ParNetBlock(128, 128),
                ParNetBlock(128, 128),
                ParNetBlock(128, 128),
                ParNetBlock(128, 128)
            ),
            nn.Sequential(
                ParNetBlock(256, 256),
                ParNetBlock(256, 256),
                ParNetBlock(256, 256),
                ParNetBlock(256, 256)
            )
        ])

        self.fusion_blocks = nn.ModuleList([
            FusionBlock(128, 256),
            FusionBlock(256, 256)
        ])

        self.final_downsampling = DownsamplingBlock(256, 1024)
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        downsampled_features = []
        for i, downsampling_block in enumerate(self.downsampling_blocks):
            x = downsampling_block(x)
            downsampled_features.append(x)

        stream_features = []
        for i, stream in enumerate(self.streams):
            stream_feature = stream(downsampled_features[i])
            stream_features.append(stream_feature)

        fused_features = stream_features[0]
        for i in range(1, len(stream_features)):
            fused_features = self.fusion_blocks[i - 1](fused_features, stream_features[i])

        x = self.final_downsampling(fused_features)
        x = F.adaptive_avg_pool2d(x, (1, 1))
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x
  • 功能概述
    这是整个ParNet网络的定义类,整合了前面定义的各个模块,构建出完整的网络结构,包括下采样、并行分支处理、特征融合以及最后的分类全连接层等部分,能够接收输入图像数据并输出对应的分类预测结果。

  • __init__方法

    • downsampling_blocks:通过nn.ModuleList创建了一个包含三个下采样块的列表,用于对输入图像依次进行下采样,将图像的空间尺寸逐步缩小,同时增加通道数,从最初的3通道(对应RGB图像)逐步变为64128256通道。
    • streams:同样是nn.ModuleList,定义了三个并行的流(stream),每个流由多个ParNetBlock和一个DownsamplingBlock组成,不同流在不同的特征图尺度和通道维度上进行特征提取和处理,每个流内部的ParNetBlock用于提取和融合局部特征,最后的DownsamplingBlock用于进一步下采样。
    • fusion_blocks:也是nn.ModuleList,包含两个特征融合块,用于融合不同流的特征,将各个流提取到的不同层次的特征进行融合,以综合利用多尺度信息。
    • final_downsampling:定义了一个下采样块,用于对融合后的特征再进行一次下采样,将通道数提升到1024,进一步提取更抽象的全局特征。
    • fc:创建了一个全连接层,用于将最终提取到的特征映射到指定的类别数量num_classes,实现图像分类任务的输出。
  • forward方法

    • 首先,通过循环将输入x依次经过各个下采样块进行下采样,并将每次下采样后的特征保存到downsampled_features列表中,得到不同阶段下采样后的特征图。
    • 接着,针对每个流,将对应的下采样后的特征图送入流中进行处理,每个流内部的模块会进一步提取和融合特征,得到每个流输出的特征,并保存在stream_features列表中。
    • 然后,先取第一个流的特征作为初始的融合特征,再通过循环依次使用特征融合块将其他流的特征与已有的融合特征进行融合,不断更新融合特征。
    • 之后,将融合后的特征送入最后的下采样块进行进一步下采样处理。
    • 再通过自适应平均池化F.adaptive_avg_pool2d将特征图在空间维度上压缩为(1, 1)大小,然后使用view操作将其展平为二维向量。
    • 最后将展平后的特征送入全连接层进行分类预测,返回最终的分类结果。

总体而言,这段代码构建了一个符合ParNet架构特点的神经网络模型,通过多个模块的组合实现了高效的特征提取、融合以及分类功能,可应用于图像分类等相关任务。

训练过程和测试结果

训练过程损失函数变化曲线:
在这里插入图片描述

训练过程准确率变化曲线:
在这里插入图片描述

测试结果:
在这里插入图片描述

代码汇总

项目github地址
项目结构:

|--data
|--models
	|--__init__.py
	|-parnet.py
	|--...
|--results
|--weights
|--train.py
|--test.py

parnet.py

import torch
import torch.nn as nn
import torch.nn.functional as F

class SSE(nn.Module):
    def __init__(self, in_channels):
        super(SSE, self).__init__()
        self.global_avgpool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Linear(in_channels, in_channels)

    def forward(self, x):
        out = self.global_avgpool(x)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        out = torch.sigmoid(out)
        out = out.view(out.size(0), out.size(1), 1, 1)
        
        return x * out
    

class ParNetBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(ParNetBlock, self).__init__()
        self.branch1x1 = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        self.branch3x3 = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        self.sse = SSE(out_channels)

    def forward(self, x):
        branch1x1 = self.branch1x1(x)
        branch3x3 = self.branch3x3(x)
        out = branch1x1 + branch3x3
        out = self.sse(out)
        out = F.silu(out)

        return out
    

class DownsamplingBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(DownsamplingBlock, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.se = SSE(out_channels)

    def forward(self, x):
        out = self.conv(x)
        out = self.bn(out)
        out = self.relu(out)
        out = self.se(out)

        return out
    

class FusionBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(FusionBlock, self).__init__()
        self.conv1x1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=2, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.se = SSE(out_channels)
        self.concat = nn.Conv2d(out_channels * 2, out_channels, kernel_size=1, bias=False)
    
    def forward(self, x1, x2):
        x1, x2 = self.conv1x1(x1), self.conv1x1(x2)
        x1, x2 = self.bn(x1), self.bn(x2)
        x1, x2 = self.relu(x1), self.relu(x2)
        x1, x2 = self.se(x1), self.se(x2)
        out = torch.cat([x1, x2], dim=1)
        out = self.concat(out)

        return out
    
class ParNet(nn.Module):
    def __init__(self, num_classes):
        super(ParNet, self).__init__()
        self.downsampling_blocks = nn.ModuleList([
            DownsamplingBlock(3, 64),
            DownsamplingBlock(64, 128),
            DownsamplingBlock(128, 256),
        ])

        self.streams = nn.ModuleList([
            nn.Sequential(
                ParNetBlock(64, 64),
                ParNetBlock(64, 64),
                ParNetBlock(64, 64),
                DownsamplingBlock(64, 128)
            ),
            nn.Sequential(
                ParNetBlock(128, 128),
                ParNetBlock(128, 128),
                ParNetBlock(128, 128),
                ParNetBlock(128, 128)
            ),
            nn.Sequential(
                ParNetBlock(256, 256),
                ParNetBlock(256, 256),
                ParNetBlock(256, 256),
                ParNetBlock(256, 256)
            )
        ])

        self.fusion_blocks = nn.ModuleList([
            FusionBlock(128, 256),
            FusionBlock(256, 256)
        ])

        self.final_downsampling = DownsamplingBlock(256, 1024)
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        downsampled_features = []
        for i, downsampling_block in enumerate(self.downsampling_blocks):
            x = downsampling_block(x)
            downsampled_features.append(x)

        stream_features = []
        for i, stream in enumerate(self.streams):
            stream_feature = stream(downsampled_features[i])
            stream_features.append(stream_feature)

        fused_features = stream_features[0]
        for i in range(1, len(stream_features)):
            fused_features = self.fusion_blocks[i - 1](fused_features, stream_features[i])

        x = self.final_downsampling(fused_features)
        x = F.adaptive_avg_pool2d(x, (1, 1))
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x

train.py


test.py


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

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

相关文章

Docker核心技术和实现原理

目录 1. Docker镜像原理介绍1.1 操作系统基础1.2 Union FS(联合文件系统)1.3 再看 Docker 镜像是什么 2. 镜像的实现原理2.1 Docker 分层存储实现原理2.2 docker 镜像加载原理 3. 镜像分层存储实战3.1 基础知识3.2 实战过程 4. overlay 文件系统工作实战5. Docker卷原理介绍5.1…

AI的进阶之路:从机器学习到深度学习的演变(二)

AI的进阶之路:从机器学习到深度学习的演变(一) 三、机器学习(ML):AI的核心驱动力 3.1 机器学习的核心原理 机器学习(Machine Learning, ML)突破了传统编程的局限,它不再…

34.正则表达式

python正则表达式,使用re模块,模块中三个基础方法来做正则匹配。 match re.match(匹配规则, 被匹配的字符串) 从字符串开头进行匹配,匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空。…

xpath插件安装与使用

1.背景 在使用python爬取页面数据时,经常会遇到解析页面数据,有一个非常好用的插件工具 是:xpath插件 2.安装与使用步骤 步骤1:准备xpath插件,并解压 步骤2:添加扩展程序 点击扩展程序后: 点击:加载已解压的扩展程序 安装成功后: 关闭浏览器,重新打开浏览器就可以使用了 步…

安徽医科大学卫生管理学院与和鲸科技签署“101 数智领航计划”,共拓“医学+AI”学科建设与人才培养

为进一步强化卫生健康人才培养关键方向,着力加强“医学AI”的复合型交叉人才培养,2024 年 12 月 13 日,安徽医科大学卫生管理学院与上海和今信息科技有限公司(以下简称“和鲸科技”)召开校企合作洽谈会,并正…

spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))

目录 一、单个字符串或对象引用的注入。 (1)简单案例演示。 1、项目的基本结构和类介绍。 2、接口"UserDao"代码。 3、实现类"UserDaoImpl"代码。 4、spring配置文件。 5、测试类(MainApp)。 6、查看执行结果。(对应成员变量成功注入…

三格电子——新品IE103转ModbusTCP网关

型号:SG-TCP-IEC103 产品概述 IE103转ModbusTCP网关型号SG-TCP-IEC103,是三格电子推出的工业级网关(以下简称网关),主要用于IEC103数据采集、DLT645-1997/2007数据采集,IEC103支持遥测和遥信,可…

leetcode-283.移动零-day13

方法一:双指针遇 0 交换 1. 基本思路回顾 该方法使用了两个指针m和i,m用于标记当前已经处理好的非零元素应该放置的位置,i用于遍历整个数组。当遇到nums[m]为0时,会通过内层while循环找到下一个非零元素(如果存在的话…

基于LabVIEW的USRP信道测量开发

随着无线通信技术的不断发展,基于软件无线电的设备(如USRP)在信道测量、无线通信测试等领域扮演着重要角色。通过LabVIEW与USRP的结合,开发者可以实现信号生成、接收及信道估计等功能。尽管LabVIEW提供了丰富的信号处理工具和图形…

Go怎么做性能优化工具篇之基准测试

一、什么是基准测试(Benchmark) 在 Go 中,基准测试是通过创建以 Benchmark 开头的函数,并接收一个 *testing.B 类型的参数来实现的。testing.B 提供了控制基准测试执行的接口,比如设置测试执行的次数、记录每次执行的…

Windows下使用git配置gitee远程仓库

目录 使用git配置(传统方法) 1、在桌面新建一个文件夹 2、git clone [ur1] 3、git branch查看分支 4、git branch新建分支(重要) 5、git push推送新分支 简单版(使用git小乌龟) 官网下载&#xff1…

DotNetBrowser 3.0.0 正式发布!

🛠️ 重要消息:DotNetBrowser 3.0.0 正式发布! 我们很高兴向您介绍全新的 DotNetBrowser 3.0.0 版本。此次更新带来了多项重要功能与优化,进一步提升了 Web 开发的效率和体验。 📢 DotNetBrowser 3.0.0 包含哪些新功…

【潜意识Java】深度解析黑马项目《苍穹外卖》与蓝桥杯算法的结合问题

目录 为什么要结合项目与算法? 1. 蓝桥杯与《苍穹外卖》项目的结合 实例:基于蓝桥杯算法思想的订单配送路径规划 问题描述: 代码实现:使用动态规划解决旅行商问题 代码解析: 为什么这个题目与蓝桥杯相关&#x…

严格推导质点曲线运动的运动学方程

前言 相当一部分物理学书籍在推导质点曲线运动的运动学方程时,采用的都是先建立位移的微元 Δ r ⃗ \Delta \vec{r} Δr ,然后几何近似求极限的方法。这种方法虽然能得到正确的结论,但数学上的严格性略有欠缺,且过程繁琐。考虑到…

【gym】理解gym并测试gym小游戏CartPole (一)

一、gym与文件位置的联合理解 import gym import inspect# 加载 CliffWalking 环境 env gym.make(CliffWalking-v0)# 获取环境的类 env_class type(env)# 获取环境类所在的文件路径 file_path inspect.getfile(env_class)print(f"The source code for CliffWalking-v0…

Mac升级macOS 15 Sequoia后,无法ssh连接本地虚拟机

现象 macOS 15后,无法ssh连接本地启动的虚拟机,提示错误: No route to host,也ping不通。包括UTM、Parallels Desktop这两个虚拟机软件。之前都是没问题的,通过一些简单排查,目前没发现什么问题。 在虚拟…

vue3 setup模式使用事件总线Event bus用mitt,app.config.globalProperties.$bus

环境介绍package.json中的内容如下 需要 npm install mitt: {"name": "event_bus_test","version": "0.0.0","private": true,"type": "module","scripts": {"dev": &…

【java基础系列】实现一个简单的猜数字小游戏

主要是用的java中的键盘录入和随机数两个api,实现这种人机交互的小游戏,可以用来锻炼基础算法思维 实现效果 实现代码 package com.gaofeng.day10;import java.util.Random; import java.util.Scanner;/*** author gaofeng* date 2024-12-22 - 9:21*/ …

Halcon例程代码解读:安全环检测(附源码|图像下载链接)

安全环检测核心思路与代码详解 项目目标 本项目的目标是检测图像中的安全环位置和方向。通过形状匹配技术,从一张模型图像中提取安全环的特征,并在后续图像中识别多个实例,完成检测和方向标定。 实现思路 安全环检测分为以下核心步骤&…

四、使用langchain搭建RAG:金融问答机器人--构建web应用,问答链,带记忆功能

经过前面3节完成金融问答机器人基本流程,这章将使用Gradio构建web应用,同时加入memory令提示模板带有记忆的,使用LCEL构建问答链。 加载向量数据库 from langchain.vectorstores import Chroma from langchain_huggingface import HuggingF…