当前位置: 首页 > news >正文

#4【CVPR2024】SHIP:图像融合的一种协同高阶交互范式


📜 Probing Synergistic High-Order Interaction in Infrared and Visible Image Fusion


🍕 源码: https://github.com/zheng980629/SHIP

在这里插入图片描述
先前融合规则与我们提出的范式之间的比较。之前的工作要么(a)缺乏明确的交互,要么(b)仅实现了 2 n d 2^{n d} 2nd阶空间交互;而(c)我们的SHIP融合了高阶空间和通道交互,以探索模态之间在空间细粒度细节和全局统计中的协同相关性,逐步整合并区分互补信息

如何将二阶交互扩展到任意高阶而又不会显著增加计算量呢?论文并不仅仅只是将低阶计算平庸地升阶,而是考虑了计算量的问题,这就为以后潜在的实际应用增添了可能性

🍟 贡献:
  • 本研究中的新型协同高阶交互范式(SHIP)探索了红外与可见光图像融合中复杂的高阶交互。通过在空间和通道维度上融入高阶交互,SHIP作为一种开创性方法,研究了模态之间的协同相关性
  • 该范式研究了涉及空间细粒度细节和全局统计的高阶交互,协同整合互补信息,并从源模态中区分相互依赖性
  • 展示了其在全色锐化任务中的有效性

🍭 理解空间和通道上的阶数
  • 普通卷积(vanilla convolution):传统的卷积操作通过对输入图像的局部区域进行加权求和来提取特征。然而,这种操作本质上无法捕捉特定位置与其邻近区域之间的空间交互。换句话说,卷积核的权重是固定的,无法根据输入内容动态调整,因此难以建模复杂的空间依赖关系。

  • 动态卷积:动态卷积通过根据输入生成动态权重来改进普通卷积。这种改进引入了一阶空间交互,即卷积核的权重会根据输入内容自适应调整。

  • 自注意力机制:Transformer 通过自注意力机制实现了二阶空间交互。其核心是通过矩阵乘法计算查询(queries)、键(keys)和值(values)之间的关系。

  • SE 模块:SE 模块通过一阶统计量来重新校准通道响应。具体来说,它首先对每个通道进行全局平均池化(squeeze),然后通过全连接层学习通道权重(excitation),最后用这些权重对特征图进行加权。


在这里插入图片描述
所提出的协同高阶交互范式(SHIP)的详细框架包括在L次迭代中执行的交替空间和通道高阶交互。具体来说,空间高阶交互充分挖掘了两种模态之间的协作,并通过高阶建模整合了空间细粒度的互补信息。随后,基于全局一阶统计量(均值)的通道高阶相互作用进一步研究了全局统计量,区分了可见光和红外模态之间的相互依赖性


🍥 高阶空间交互

I F = SHIP ⁡ L ( ψ ( I R ) , ϕ ( I V ) ) I_{\mathcal{F}}=\operatorname{SHIP}_L\left(\psi\left(I_{\mathcal{R}}\right), \phi\left(I_{\mathcal{V}}\right)\right) IF=SHIPL(ψ(IR),ϕ(IV))整体结构也是比较简单的,就是一个相同的模块重复处理了好多次得到结果,效果好不好全靠模块设计

传统的注意力机制
O S ( ( F V ) 2 ) = F V S 1 = softmax ⁡ ( Q ⊗ K T d k ) ⊗ V = A ⊗ V \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^2\right)=F_{\mathcal{V}_S}^1=\operatorname{softmax}\left(\frac{\mathbf{Q} \otimes \mathbf{K}^T}{\sqrt{d_k}}\right) \otimes \mathbf{V}=\mathbf{A} \otimes \mathbf{V} OS((FV)2)=FVS1=softmax(dk QKT)V=AV文中提出了一种近似形式
A i j = ⟨ q i , k j ⟩ \mathbf{A}_{i j}=\left\langle\mathbf{q}_i, \mathbf{k}_j\right\rangle Aij=qi,kj A = F − 1 ( F ( F V W Q ) ⊙ F ( F R W K ) ‾ ) \mathbf{A}=\mathcal{F}^{-1}\left(\mathcal{F}\left(F_{\mathcal{V}} \mathbf{W}^Q\right) \odot \overline{\mathcal{F}\left(F_{\mathcal{R}} \mathbf{W}^K\right)}\right) A=F1(F(FVWQ)F(FRWK)) O S ( ( F V ) 2 ) = F V S 1 = Norm ⁡ ( A ) ⊙ ( F R W V ) \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^2\right)=F_{\mathcal{V}_S}^1=\operatorname{Norm}(\mathbf{A}) \odot\left(\mathrm{F}_{\mathcal{R}} \mathbf{W}^{\mathrm{V}}\right) OS((FV)2)=FVS1=Norm(A)(FRWV)本质上就是把矩阵乘法转到频域中变为点乘,再转回去空间域,其实说不上创新,而且在计算上面是不是变得高效也难说呀 O S ( ( F V S i − 1 ) 2 ) = F V S i = Attention ⁡ ( Q i , K i , V i ) , Q i = F V S i − 1 W Q i , K i = F R W K i , V i = F R W v i \begin{aligned} & \mathcal{O}_S\left(\left(F_{\mathcal{V}_S}^{i-1}\right)^2\right)=F_{\mathcal{V}_S}^i=\operatorname{Attention}\left(\mathbf{Q}_i, \mathbf{K}_i, \mathbf{V}_i\right), \\ & \mathbf{Q}_i=F_{\mathcal{V}_S}^{i-1} \mathbf{W}^{\mathbf{Q}_i}, \mathbf{K}_i=F_{\mathcal{R}} \mathbf{W}^{\mathbf{K}_i}, \mathbf{V}_i=F_{\mathcal{R}} \mathbf{W}^{\mathbf{v}_i} \end{aligned} OS((FVSi1)2)=FVSi=Attention(Qi,Ki,Vi),Qi=FVSi1WQi,Ki=FRWKi,Vi=FRWvi F V → O S ( ( F V ) 2 ) → F V S 1 → O S ( ( F V S 1 ) 2 ) → F V S 2 ⋯ → O S ( ( F V S i ) 2 ) → F V S i + 1 … O S ( ( F V S L − 1 ) 2 ) → F V S L \begin{aligned} & F_{\mathcal{V}} \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^1 \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^1\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^2 \cdots \\ & \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^i\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^{i+1} \ldots \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^{L-1}\right)^2\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^L \end{aligned} FVOS((FV)2)FVS1OS((FVS1)2)FVS2OS((FVSi)2)FVSi+1OS((FVSL1)2)FVSL之前的多个自注意力模块堆叠集中在索引上,集中在一个模态,另一个模态的键值对并没有因为模块的堆叠而更新,而本文可以提高到任意阶:
O S ( ( F V i − 1 ) j ) = F V s j i = Norm ⁡ ( F V s j − 1 i W Q j ) ⊙ ( F R s j − 1 i W V ) F V s j − 1 i = Norm ⁡ ( F V s i − 2 i W Q j − 1 ) , F R s j − 1 i = F R s j − 2 i W V j − 1 i \begin{aligned} & \mathcal{O}_S\left(\left(F_{\mathcal{V}}^{i-1}\right)^j\right)=F_{\mathcal{V}_s^j}^i=\operatorname{Norm}\left(F_{\mathcal{V}_s^{j-1}}^i \mathbf{W}^{\mathbf{Q} \mathbf{j}}\right) \odot\left(F_{\mathcal{R}_s^{j-1}}^i \mathbf{W}^{\mathbf{V}}\right)\\ & F_{\mathcal{V}_s^{j-1}}^i=\operatorname{Norm}\left(F_{\mathcal{V}_s^{i-2}}^i \mathbf{W}^{\mathbf{Q} \mathbf{j}-1}\right), F_{\mathcal{R}_s^{j-1}}^i=F_{\mathcal{R}_s^{j-2}}^i \mathbf{W}^{\mathbf{V}_{\mathbf{j}-1}^{\mathbf{i}}} \end{aligned} OS((FVi1)j)=FVsji=Norm(FVsj1iWQj)(FRsj1iWV)FVsj1i=Norm(FVsi2iWQj1),FRsj1i=FRsj2iWVj1i F V → O S ( ( F V ) N ) → F V S 1 → O S ( ( F V s 1 ) N ) → F V S 2 … → O S ( ( F V S i ) N ) → F V S i + 1 … O S ( ( F V S L − 1 ) N ) → F V S L \begin{aligned} & F_{\mathcal{V}} \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^N\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^1 \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_s}^1\right)^N\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^2 \ldots \\ & \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_S}^i\right)^N\right) \rightarrow F_{\mathcal{V}_{\mathcal{S}}}^{i+1} \ldots \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^{L-1}\right)^N\right) \rightarrow F_{\mathcal{V}_S}^L \end{aligned} FVOS((FV)N)FVS1OS((FVs1)N)FVS2OS((FVSi)N)FVSi+1OS((FVSL1)N)FVSL

在这里插入图片描述
在不同的空间高阶交互步骤中,每次交互后的特征可视化。例如, F V s 3 2 F_{\mathcal{V}_s^3} ^2 FVs32表示 2 n d 2^{nd} 2nd空间高阶相互作用中三阶相互作用后的特征。这些可视化从两个角度说明了高阶空间相互作用的有效性:(1)在每个高阶相互作用中,特征响应随着顺序的增加而升级,突出了突出的对象;(2) 不同的高阶相互作用产生独特的响应,展示了特征表示的多样性

class spatialInteraction(nn.Module):def __init__(self, channelin, channelout):super(spatialInteraction, self).__init__()# 定义三个卷积序列用于处理融合特征self.reflashFused1 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),  # 3x3卷积,输入通道为channelin,输出通道为channelout,padding为1nn.ReLU(),  # ReLU激活函数nn.Conv2d(channelout, channelout, 3, 1, 1)  # 再次3x3卷积)self.reflashFused2 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))self.reflashFused3 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))# 定义三个卷积序列用于处理红外特征self.reflashInfrared1 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))self.reflashInfrared2 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))self.reflashInfrared3 = nn.Sequential(nn.Conv2d(channelin, channelout, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout, channelout, 3, 1, 1))# 定义四个LayerNorm层,用于归一化self.norm1 = LayerNorm(channelout, LayerNorm_type='WithBias')self.norm2 = LayerNorm(channelout, LayerNorm_type='WithBias')self.norm3 = LayerNorm(channelout, LayerNorm_type='WithBias')self.norm4 = LayerNorm(channelout, LayerNorm_type='WithBias')def forward(self, vis, inf, i, j):# 获取可见光图像的尺寸_, C, H, W = vis.size()# 对可见光和红外图像进行快速傅里叶变换(FFT)vis_fft = torch.fft.rfft2(vis.float())inf_fft = torch.fft.rfft2(inf.float())# 计算可见光和红外图像的频域注意力图atten = vis_fft * inf_fftatten = torch.fft.irfft2(atten, s=(H, W))  # 逆FFT变换回空间域atten = self.norm1(atten)  # 归一化fused_OneOrderSpa = atten * inf  # 一阶空间融合特征# 通过第一个融合卷积序列处理一阶融合特征fused_OneOrderSpa = self.reflashFused1(fused_OneOrderSpa)fused_OneOrderSpa = self.norm2(fused_OneOrderSpa)  # 归一化infraredReflash1 = self.reflashInfrared1(inf)  # 处理红外特征fused_twoOrderSpa = fused_OneOrderSpa * infraredReflash1  # 二阶空间融合特征# 通过第二个融合卷积序列处理二阶融合特征fused_twoOrderSpa = self.reflashFused2(fused_twoOrderSpa)fused_twoOrderSpa = self.norm3(fused_twoOrderSpa)  # 归一化infraredReflash2 = self.reflashInfrared2(infraredReflash1)  # 处理红外特征fused_threeOrderSpa = fused_twoOrderSpa * infraredReflash2  # 三阶空间融合特征# 通过第三个融合卷积序列处理三阶融合特征fused_threeOrderSpa = self.reflashFused3(fused_threeOrderSpa)fused_threeOrderSpa = self.norm4(fused_threeOrderSpa)  # 归一化infraredReflash3 = self.reflashInfrared3(infraredReflash2)  # 处理红外特征fused_fourOrderSpa = fused_threeOrderSpa * infraredReflash3  # 四阶空间融合特征# 将最终融合特征与原始可见光图像相加fused = fused_fourOrderSpa + vis# 返回融合后的特征和处理后的红外特征return fused, infraredReflash3

🍉 高阶通道交互

传统的SE(squeeze and excitation)模块
Z i = 1 H × W ∑ x = 1 H ∑ y = 1 W F i ( x , y ) O C ( ( F i ) 1 ) = F C i = σ ( W Z 1 i Z i ) ⋅ F i \begin{gathered} Z^i=\frac{1}{H \times W} \sum_{x=1}^H \sum_{y=1}^W F^i(x, y) \\ \mathcal{O}_C\left(\left(F^i\right)^1\right)=F_C^i=\sigma\left(\mathbf{W}^{\mathbf{Z}_1^i} Z^i\right) \cdot F^i \end{gathered} Zi=H×W1x=1Hy=1WFi(x,y)OC((Fi)1)=FCi=σ(WZ1iZi)Fi其中 F i = concat ⁡ [ F V S i , F R S i ] F^i = \operatorname{concat}\left[F_{\mathcal{V}_S}^i, F_{\mathcal{R}_S}^i\right] Fi=concat[FVSi,FRSi] Z c Z_c Zc 表示一阶统计量, σ \sigma σ 表示 Sigmoid 函数。 W Z \mathbf{W}^{\mathbf{Z}} WZ 包括两个线性变换和一个 ReLU 函数。作者将其扩展到了高阶形式: O C ( ( F i ) j ) = F C j i = σ ( W Z j i Z j − 1 i ) ⋅ ( W F j i F j − 1 i ) , Z j − 1 i = σ ( W F j − 1 i Z j − 2 i ) , F j − 1 i = W F j − 1 i F j − 2 i F V → O S ( ( F V ) N ) → O C ( ( F V S 1 ) N ) → O S ( ( F V C 1 ) N ) → O C ( ( F V S 2 ) N ) → … O S ( ( F V C L − 1 ) N ) → O C ( ( F V S L ) N ) \begin{aligned} & \mathcal{O}_C\left(\left(F^i\right)^j\right)=F_{C j}^i=\sigma\left(\mathbf{W}^{\mathbf{Z}_j^i} Z_{j-1}^i\right) \cdot\left(\mathbf{W}^{\mathbf{F}_j^i} F_{j-1}^i\right), \\ & Z_{j-1}^i=\sigma\left(\mathbf{W}^{\mathbf{F}_{j-1}^i} Z_{j-2}^i\right), F_{j-1}^i=\mathbf{W}^{\mathbf{F}_{j-1}^i} F_{j-2}^i \\ & F_{\mathcal{V}} \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}}\right)^N\right) \rightarrow \mathcal{O}_C\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^1\right)^N\right) \rightarrow \mathcal{O}_S\left(\left(F_{\mathcal{V}_{\mathcal{C}}}^1\right)^N\right) \\ & \rightarrow \mathcal{O}_C\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^2\right)^N\right) \rightarrow \ldots \mathcal{O}_S\left(\left(F_{\mathcal{V}_C}^{L-1}\right)^N\right) \rightarrow \mathcal{O}_C\left(\left(F_{\mathcal{V}_{\mathcal{S}}}^L\right)^N\right) \end{aligned} OC((Fi)j)=FCji=σ(WZjiZj1i)(WFjiFj1i),Zj1i=σ(WFj1iZj2i),Fj1i=WFj1iFj2iFVOS((FV)N)OC((FVS1)N)OS((FVC1)N)OC((FVS2)N)OS((FVCL1)N)OC((FVSL)N)具体原理不是很懂,但好像是做一次变换就可以升一次阶数,具体可能需要结合代码来看
在这里插入图片描述
不同阶次在通道索引上的通道交互。这个观察结果为有力证据,表明不同阶次的交互探索了红外和可见模态之间的多样化相互依赖关系

class channelInteraction(nn.Module):def __init__(self, channelin, channelout):super(channelInteraction, self).__init__()# 初始化各个卷积层,包括通道注意力模块和融合模块self.chaAtten = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),  # 1x1卷积nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True)  # 1x1卷积)# 重构通道注意力模块1、2、3,用于逐步改进注意力特征self.reflashChaAtten1 = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True))self.reflashChaAtten2 = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True))self.reflashChaAtten3 = nn.Sequential(nn.Conv2d(channelin * 2, channelout, kernel_size=1, padding=0, bias=True),nn.ReLU(),nn.Conv2d(channelout, channelin * 2, kernel_size=1, padding=0, bias=True))# 融合模块,用于融合不同通道的特征self.reflashFused1 = nn.Sequential(nn.Conv2d(channelin * 2, channelout * 2, 3, 1, 1),  # 3x3卷积nn.ReLU(),nn.Conv2d(channelout * 2, channelout * 2, 3, 1, 1)  # 3x3卷积)self.reflashFused2 = nn.Sequential(nn.Conv2d(channelin * 2, channelout * 2, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout * 2, channelout * 2, 3, 1, 1))self.reflashFused3 = nn.Sequential(nn.Conv2d(channelin * 2, channelout * 2, 3, 1, 1),nn.ReLU(),nn.Conv2d(channelout * 2, channelout * 2, 3, 1, 1))# 自适应平均池化层,将输入大小调整为 (batch_size, channels, 1, 1)self.avgpool = nn.AdaptiveAvgPool2d(1)# 后处理模块,用于处理融合后的特征self.postprocess = nn.Sequential(InvBlock(DenseBlock, 2 * channelin, channelout),nn.Conv2d(2 * channelout, channelout, 1, 1, 0)  # 1x1卷积,减少通道数)def forward(self, vis, inf, i, j):# 输入:vis和inf是两个不同的特征图(例如,视觉和信息特征图)# 首先将这两个特征图按通道维度拼接vis_cat = torch.cat([vis, inf], 1)# 使用通道注意力机制对拼接后的特征图进行处理chanAtten = self.chaAtten(self.avgpool(vis_cat)).softmax(1)channel_response = self.chaAtten(self.avgpool(vis_cat))# 使用通道注意力对特征图进行加权fused_OneOrderCha = vis_cat * chanAtten# 通过第一个重构模块(reflashFused1)进一步处理fused_OneOrderCha = self.reflashFused1(fused_OneOrderCha)chanAttenReflash1 = self.reflashChaAtten1(chanAtten).softmax(1)fused_twoOrderCha = fused_OneOrderCha * chanAttenReflash1# 通过第二个重构模块(reflashFused2)进一步处理fused_twoOrderCha = self.reflashFused2(fused_twoOrderCha)chanAttenReflash2 = self.reflashChaAtten2(chanAttenReflash1).softmax(1)fused_threeOrderCha = fused_twoOrderCha * chanAttenReflash2# 通过第三个重构模块(reflashFused3)进一步处理fused_threeOrderCha = self.reflashFused3(fused_threeOrderCha)chanAttenReflash3 = self.reflashChaAtten3(chanAttenReflash2).softmax(1)fused_fourOrderCha = fused_threeOrderCha * chanAttenReflash3# 最终的后处理模块,生成最终的输出output = self.postprocess(fused_fourOrderCha)return output

🍫 损失函数

L = L int  + λ L gra  \mathcal{L}=\mathcal{L}_{\text {int }}+\lambda \mathcal{L}_{\text {gra }} L=Lint +λLgra  L i n t = ∥ ( ω V ∘ I V + ω R ∘ I R ) − I F ∥ 1 \mathcal{L}_{\mathrm{int}}=\left\|\left(\omega_{\mathcal{V}} \circ I_{\mathcal{V}}+\omega_{\mathcal{R}} \circ I_{\mathcal{R}}\right)-I_{\mathcal{F}}\right\|_1 Lint=(ωVIV+ωRIR)IF1 ω V = S V / ( S V − S R ) and  S R = 1 − S V \omega_{\mathcal{V}}=S_{\mathcal{V}} /\left(S_{\mathcal{V}}-S_{\mathcal{R}}\right) \text { and } S_{\mathcal{R}}=1-S_{\mathcal{V}} ωV=SV/(SVSR) and SR=1SV L gra  = 1 H W ∥ ∇ I F − max ⁡ ( ∇ I R , ∇ I V ) ∥ 1 \mathcal{L}_{\text {gra }}=\frac{1}{H W}\left\|\nabla I_{\mathcal{F}}-\max \left(\nabla I_{\mathcal{R}}, \nabla I_{\mathcal{V}}\right)\right\|_1 Lgra =HW1IFmax(IR,IV)1损失函数中规中矩,因为重点不在这边,重点在于前面的模块设计。实验部分有兴趣的可以自行去看,我觉得没有什么亮点,就不说了


本文的源代码中,作者引用了Facebook研究的一个注册器,可以用来组织代码结构,使得整个代码部分更加容易管理:

# 修改自: https://github.com/facebookresearch/fvcore/blob/master/fvcore/common/registry.py  # noqa: E501class Registry():"""提供名称 -> 对象映射的注册表,用于支持第三方用户的自定义模块。创建一个注册表(例如,创建一个骨干网络的注册表):.. code-block:: pythonBACKBONE_REGISTRY = Registry('BACKBONE')注册一个对象:.. code-block:: python@BACKBONE_REGISTRY.register()class MyBackbone():...或者:.. code-block:: pythonBACKBONE_REGISTRY.register(MyBackbone)"""def __init__(self, name):"""构造函数参数:name (str): 注册表的名称"""self._name = name# key: 数据集/模型等的类名,value: 对应的类对象self._obj_map = {}def _do_register(self, name, obj):# 注册一个对象assert (name not in self._obj_map), (f"名为 '{name}' 的对象已经在 '{self._name}' 注册表中注册过了!")self._obj_map[name] = objdef register(self, obj=None):"""使用给定对象的名称 `obj.__name__` 注册该对象。可以作为装饰器使用,也可以不使用装饰器。参见本类的文档字符串了解使用方法。"""if obj is None:# 用作装饰器def deco(func_or_class):name = func_or_class.__name__# print(name)self._do_register(name, func_or_class)return func_or_classreturn deco# 作为函数调用时name = obj.__name__self._do_register(name, obj)def get(self, name):# 获取对应类的对象ret = self._obj_map.get(name)if ret is None:raise KeyError(f"在 '{self._name}' 注册表中没有找到名为 '{name}' 的对象!")return retdef __contains__(self, name):# 判断某个对象是否已经注册return name in self._obj_mapdef __iter__(self):# 迭代器方法return iter(self._obj_map.items())def keys(self):# 获取所有注册的对象的名称return self._obj_map.keys()# 以下是不同模块的注册表实例DATASET_REGISTRY = Registry('dataset')  # 数据集注册表
ARCH_REGISTRY = Registry('arch')        # 网络架构注册表
MODEL_REGISTRY = Registry('model')      # 模型注册表
LOSS_REGISTRY = Registry('loss')        # 损失函数注册表
METRIC_REGISTRY = Registry('metric')    # 指标注册表
http://www.lryc.cn/news/541136.html

相关文章:

  • 虚拟机从零实现机器人控制
  • 趣味数学300题1981版-八个等式、五个5等于24
  • Microsoft Office 2024 软件安装教程(免费)
  • Linux 常见指令
  • HTML Application(hta)入门教程
  • pytest运行用例的常见方式及参数
  • XML Schema 元素替换
  • OpenBMC:BmcWeb app.run
  • hot100_74. 搜索二维矩阵
  • 光明谷推出AT指令版本的蓝牙音箱SOC 开启便捷智能音频开发新体验
  • 基于windows的docker-desktop安装kubenetes以及dashboard
  • MT7628基于原厂的SDK包, 修改ra1网卡的MAC方法。
  • 网络安全第三次练习
  • BFS 和 DFS(深度优先搜索、广度优先搜索)
  • Casbin 权限管理介绍及在 Go 语言中的使用入门
  • Two Sum
  • 3.3.2 交易体系构建——缠论操作思路
  • [SQL] 事务的四大特性(ACID)
  • 使用 Three.js 实现流光特效
  • Error [ERR_REQUIRE_ESM]: require() of ES Module
  • 沉浸式翻译插件深度评测:打破语言壁垒的黑科技利器
  • Java 中 HTTP 协议版本使用情况剖析
  • 蓝桥杯学习大纲
  • VSCode ssh远程连接内网服务器(不能上网的内网环境的Linux服务器)的终极解决方案
  • 【多模态处理篇五】【DeepSeek文档解析:PDF/Word智能处理引擎】
  • STM32-心知天气项目
  • cs106x-lecture14(Autumn 2017)-SPL实现
  • 基于STM32的智能家居语音系统(单片机毕设)
  • ASP.NET Core 简单文件上传
  • 2502C++,C++继承的多态性