01 動的スネークコnvolutionの概要
主に管状構造のセグメンテーションに使用されるコnvolutionで、血管や道路などの細長い連続的な構造を対象としています。スネークコnvolutionは高精度なセグメンテーションを可能にしますが、その課題は細かい局所的な構造特徴と複雑で変動する全体的な形状特徴からの学習です。したがって、モデルは特徴を学ぶ際にコnvolutionカーネルの形状を変化させることで、管状構造の核となる特徴に注目します。管状構造が全体の中での割合が少ないため、モデルはそれらの構造を認識することを避けがちです。コnvolutionカーネルが目標から完全に逸脱することもあります。そこで、管状構造の特性に基づいて特定のネットワーク構造を設計し、モデルが重要な特徴に注目するようにすることが目的となります。
この記事では、管状構造の細長い連続的なトポロジカル特性を利用して、以下の3つの段階で感知を強化します:特徴抽出、特徴融合、損失制約。それぞれ動的スネークコnvolution、マルチビューアプローチの特徴融合戦略、連続性トポロジカル損失を提案します。さらに2Dおよび3Dの手法も提案し、実験結果によると、この論文で提案したDSCNetは管状構造のセグメンテーションタスクにおいて高い精度と連続性を提供しています。
論文へのリンク: https://arxiv.org/abs/2307.08388
02 DySnakeConv - 動的スネークコnvolutionのコード
以下のコードには以下のものが含まれています:
DySnakeConv - 単一のスネークコnvolution
DSConv - スネークコnvolutionで使用されるオフセットコnvolution
DSC - オフセットコnvolutionで使用されるモジュール
class FlexibleSnakeConv(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=3, activation=True) -> None:
super().__init__()
self.conv_initial = BasicConv(in_channels, out_channels, kernel_size, activation=activation)
self.conv_x = OffsetConv(in_channels, out_channels, direction=0, kernel_size=kernel_size)
self.conv_y = OffsetConv(in_channels, out_channels, direction=1, kernel_size=kernel_size)
self.conv_final = BasicConv(out_channels * 3, out_channels, 1, activation=activation)
def forward(self, x):
combined_features = torch.cat([self.conv_initial(x), self.conv_x(x), self.conv_y(x)], dim=1)
return self.conv_final(combined_features)
class OffsetConv(nn.Module):
def __init__(self, in_ch, out_ch, direction, kernel_size=3, apply_deformation=True, deformation_range=1):
"""
動的スネークコnvolution
:param in_ch: 入力チャネル数
:param out_ch: 出力チャネル数
:param kernel_size: カーネルサイズ
:param deformation_range: 变形範囲(このメソッドではデフォルトで1)
:param direction: コnvolutionカーネルの形態は主にx軸(0)とy軸(1)の2つに分類される
:param apply_deformation: 変形が必要かどうか、Falseの場合標準的なコnvolutionカーネルとなる
"""
super(OffsetConv, self).__init__()
# <offset_conv>を使用して可変形オフセットを学習
self.offset_conv = nn.Conv2d(in_ch, 2 * kernel_size, 3, padding=1)
self.bn = nn.BatchNorm2d(2 * kernel_size)
self.kernel_size = kernel_size
# 2種類のOffsetConv(x軸方向とy軸方向)
self.snake_conv_x = nn.Conv2d(
in_ch,
out_ch,
kernel_size=(kernel_size, 1),
stride=(kernel_size, 1),
padding=0,
)
self.snake_conv_y = nn.Conv2d(
in_ch,
out_ch,
kernel_size=(1, kernel_size),
stride=(1, kernel_size),
padding=0,
)
self.gn = nn.GroupNorm(out_ch // 4, out_ch)
self.act = BasicConv.default_activation
self.deformation_range = deformation_range
self.direction = direction
self.apply_deformation = apply_deformation
def forward(self, feature_map):
offset = self.offset_conv(feature_map)
offset = self.bn(offset)
# スネークの振れ幅を模倣するために-1から1までの範囲が必要
offset = torch.tanh(offset)
input_shape = feature_map.shape
snake_module = SnakeModule(input_shape, self.kernel_size, self.deformation_range, self.direction)
deformed_feature = snake_module.deformable_conv(feature_map, offset, self.apply_deformation)
if self.direction == 0:
result = self.snake_conv_x(deformed_feature.type(feature_map.dtype))
result = self.gn(result)
result = self.act(result)
return result
else:
result = self.snake_conv_y(deformed_feature.type(feature_map.dtype))
result = self.gn(result)
result = self.act(result)
return result
# 核心コード、理解しやすくするために各コードの入力と出力の次元を示す
class SnakeModule(object):
def __init__(self, input_shape, kernel_size, deformation_range, direction):
self.num_points = kernel_size
self.width = input_shape[2]
self.height = input_shape[3]
self.direction = direction
self.deformation_range = deformation_range # offset (-1 ~ 1) * deformation_range
# 特徴マップの形状を定義
"""
B: バッチサイズ C: チャネル W: 幅 H: 高さ
"""
self.batch_size = input_shape[0]
self.channels = input_shape[1]
"""
input: offset [B,2*K,W,H] K: カーネルサイズ (2*K: 2D画像、変形には<x_offset>と<y_offset>が含まれる)
output_x: [B,1,W,K*H] 座標マップ
output_y: [B,1,K*W,H] 座標マップ
"""
def _create_coordinate_map(self, offset, apply_deformation):
device = offset.device
# offset
y_offset, x_offset = torch.split(offset, self.num_points, dim=1)
y_center = torch.arange(0, self.width).repeat([self.height])
y_center = y_center.reshape(self.height, self.width)
y_center = y_center.permute(1, 0)
y_center = y_center.reshape([-1, self.width, self.height])
y_center = y_center.repeat([self.num_points, 1, 1]).float()
y_center = y_center.unsqueeze(0)
x_center = torch.arange(0, self.height).repeat([self.width])
x_center = x_center.reshape(self.width, self.height)
x_center = x_center.permute(0, 1)
x_center = x_center.reshape([-1, self.width, self.height])
x_center = x_center.repeat([self.num_points, 1, 1]).float()
x_center = x_center.unsqueeze(0)
if self.direction == 0:
"""
カーネルを初期化し、カーネルをフラット化する
y: 0のみ必要
x: -num_points//2 ~ num_points//2 (カーネルサイズによって決定)
!!! 関連のPPTは後日提出予定で、PPTには各ステップの変化が含まれます
"""
y = torch.linspace(0, 0, 1)
x = torch.linspace(
-int(self.num_points // 2),
int(self.num_points // 2),
int(self.num_points),
)
y, x = torch.meshgrid(y, x)
y_spread = y.reshape(-1, 1)
x_spread = x.reshape(-1, 1)
y_grid = y_spread.repeat([1, self.width * self.height])
y_grid = y_grid.reshape([self.num_points, self.width, self.height])
y_grid = y_grid.unsqueeze(0) # [B*K*K, W,H]
x_grid = x_spread.repeat([1, self.width * self.height])
x_grid = x_grid.reshape([self.num_points, self.width, self.height])
x_grid = x_grid.unsqueeze(0) # [B*K*K, W,H]
y_new = y_center + y_grid
x_new = x_center + x_grid
y_new = y_new.repeat(self.batch_size, 1, 1, 1).to(device)
x_new = x_new.repeat(self.batch_size, 1, 1, 1).to(device)
y_offset_new = y_offset.detach().clone()
if apply_deformation:
y_offset = y_offset.permute(1, 0, 2, 3)
y_offset_new = y_offset_new.permute(1, 0, 2, 3)
center = int(self.num_points // 2)
# 中央位置は変化せず、他の位置から揺れ始めます
# この部分は非常にシンプルで、主なアイデアは「オフセットは反復過程である」ことです
y_offset_new[center] = 0
for index in range(1, center):
y_offset_new[center + index] = (y_offset_new[center + index - 1] + y_offset[center + index])
y_offset_new[center - index] = (y_offset_new[center - index + 1] + y_offset[center - index])
y_offset_new = y_offset_new.permute(1, 0, 2, 3).to(device)
y_new = y_new.add(y_offset_new.mul(self.deformation_range))
y_new = y_new.reshape(
[self.batch_size, self.num_points, 1, self.width, self.height])
y_new = y_new.permute(0, 3, 1, 4, 2)
y_new = y_new.reshape([
self.batch_size, self.num_points * self.width, 1 * self.height
])
x_new = x_new.reshape(
[self.batch_size, self.num_points, 1, self.width, self.height])
x_new = x_new.permute(0, 3, 1, 4, 2)
x_new = x_new.reshape([
self.batch_size, self.num_points * self.width, 1 * self.height
])
return y_new, x_new
else:
"""
カーネルを初期化し、カーネルをフラット化する
y: -num_points//2 ~ num_points//2 (カーネルサイズによって決定)
x: 0のみ必要
"""
y = torch.linspace(
-int(self.num_points // 2),
int(self.num_points // 2),
int(self.num_points),
)
x = torch.linspace(0, 0, 1)
y, x = torch.meshgrid(y, x)
y_spread = y.reshape(-1, 1)
x_spread = x.reshape(-1, 1)
y_grid = y_spread.repeat([1, self.width * self.height])
y_grid = y_grid.reshape([self.num_points, self.width, self.height])
y_grid = y_grid.unsqueeze(0)
x_grid = x_spread.repeat([1, self.width * self.height])
x_grid = x_grid.reshape([self.num_points, self.width, self.height])
x_grid = x_grid.unsqueeze(0)
y_new = y_center + y_grid
x_new = x_center + x_grid
y_new = y_new.repeat(self.batch_size, 1, 1, 1)
x_new = x_new.repeat(self.batch_size, 1, 1, 1)
y_new = y_new.to(device)
x_new = x_new.to(device)
x_offset_new = x_offset.detach().clone()
if apply_deformation:
x_offset = x_offset.permute(1, 0, 2, 3)
x_offset_new = x_offset_new.permute(1, 0, 2, 3)
center = int(self.num_points // 2)
x_offset_new[center] = 0
for index in range(1, center):
x_offset_new[center + index] = (x_offset_new[center + index - 1] + x_offset[center + index])
x_offset_new[center - index] = (x_offset_new[center - index + 1] + x_offset[center - index])
x_offset_new = x_offset_new.permute(1, 0, 2, 3).to(device)
x_new = x_new.add(x_offset_new.mul(self.deformation_range))
y_new = y_new.reshape(
[self.batch_size, 1, self.num_points, self.width, self.height])
y_new = y_new.permute(0, 3, 1, 4, 2)
y_new = y_new.reshape([
self.batch_size, 1 * self.width, self.num_points * self.height
])
x_new = x_new.reshape(
[self.batch_size, 1, self.num_points, self.width, self.height])
x_new = x_new.permute(0, 3, 1, 4, 2)
x_new = x_new.reshape([
self.batch_size, 1 * self.width, self.num_points * self.height
])
return y_new, x_new
"""
input: 入力特徴マップ [N,C,D,W,H]; 座標マップ [N,K*D,K*W,K*H]
output: [N,1,K*D,K*W,K*H] 変形された特徴マップ
"""
def _bilinear_interpolation(self, input_feature, y, x):
device = input_feature.device
y = y.reshape([-1]).float()
x = x.reshape([-1]).float()
zero = torch.zeros([]).int()
max_y = self.width - 1
max_x = self.height - 1
# 8つのグリッド位置を見つける
y0 = torch.floor(y).int()
y1 = y0 + 1
x0 = torch.floor(x).int()
x1 = x0 + 1
# 特徴マップのボリュームを超える座標をクリッピング
y0 = torch.clamp(y0, zero, max_y)
y1 = torch.clamp(y1, zero, max_y)
x0 = torch.clamp(x0, zero, max_x)
x1 = torch.clamp(x1, zero, max_x)
input_feature_flat = input_feature.flatten()
input_feature_flat = input_feature_flat.reshape(
self.batch_size, self.channels, self.width, self.height)
input_feature_flat = input_feature_flat.permute(0, 2, 3, 1)
input_feature_flat = input_feature_flat.reshape(-1, self.channels)
dimension = self.height * self.width
base = torch.arange(self.batch_size) * dimension
base = base.reshape([-1, 1]).float()
repeat = torch.ones([self.num_points * self.width * self.height
]).unsqueeze(0)
repeat = repeat.float()
base = torch.matmul(base, repeat)
base = base.reshape([-1])
base = base.to(device)
base_y0 = base + y0 * self.height
base_y1 = base + y1 * self.height
# 近傍ボリュームの上部の長方形
index_a0 = base_y0 - base + x0
index_c0 = base_y0 - base + x1
# 近傍ボリュームの下部の長方形
index_a1 = base_y1 - base + x0
index_c1 = base_y1 - base + x1
# 8つのグリッド値を取得
value_a0 = input_feature_flat[index_a0.type(torch.int64)].to(device)
value_c0 = input_feature_flat[index_c0.type(torch.int64)].to(device)
value_a1 = input_feature_flat[index_a1.type(torch.int64)].to(device)
value_c1 = input_feature_flat[index_c1.type(torch.int64)].to(device)
# 8つのグリッド位置を見つける
y0 = torch.floor(y).int()
y1 = y0 + 1
x0 = torch.floor(x).int()
x1 = x0 + 1
# 特徴マップのボリュームを超える座標をクリッピング
y0 = torch.clamp(y0, zero, max_y + 1)
y1 = torch.clamp(y1, zero, max_y + 1)
x0 = torch.clamp(x0, zero, max_x + 1)
x1 = torch.clamp(x1, zero, max_x + 1)
x0_float = x0.float()
x1_float = x1.float()
y0_float = y0.float()
y1_float = y1.float()
vol_a0 = ((y1_float - y) * (x1_float - x)).unsqueeze(-1).to(device)
vol_c0 = ((y1_float - y) * (x - x0_float)).unsqueeze(-1).to(device)
vol_a1 = ((y - y0_float) * (x1_float - x)).unsqueeze(-1).to(device)
vol_c1 = ((y - y0_float) * (x - x0_float)).unsqueeze(-1).to(device)
outputs = (value_a0 * vol_a0 + value_c0 * vol_c0 + value_a1 * vol_a1 +
value_c1 * vol_c1)
if self.direction == 0:
outputs = outputs.reshape([
self.batch_size,
self.num_points * self.width,
1 * self.height,
self.channels,
])
outputs = outputs.permute(0, 3, 1, 2)
else:
outputs = outputs.reshape([
self.batch_size,
1 * self.width,
self.num_points * self.height,
self.channels,
])
outputs = outputs.permute(0, 3, 1, 2)
return outputs
def deformable_conv(self, input, offset, apply_deformation):
y, x = self._create_coordinate_map(offset, apply_deformation)
deformed_feature = self._bilinear_interpolation(input, y, x)
return deformed_feature
03 コードの使用方法
FlexibleSnakeConvはコnvolutionの一種であり、完全なコードをコnvolutionのパスに配置します。私のパスはultralytics/nn/modules/conv.pyです。
01 __all__ にエクスポート名を追加
Pythonコードにおいて、__all__ は特別な変数で、モジュールからどのオブジェクト(関数、クラス、変数など)をエクスポートするか(つまり、from モジュール名 import * を通じてインポートできるか)を指定します。
__all__ = [
'Conv', 'LightConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv', 'ChannelAttention',
'SpatialAttention', 'CBAM', 'Concat', 'RepConv',
'DCNv2','DiverseBranchBlock','FlexibleSnakeConv']
01 FlexibleSnakeConvを直接使用する
01 convモジュールをインポートする
パスultralytics/nn/modules/__init__.pyにFlexibleSnakeConvを追加します。
from .conv import (CBAM, ChannelAttention, Concat, Conv, ConvTranspose, DWConv, DWConvTranspose2d, Focus, GhostConv,
LightConv, RepConv, SpatialAttention,
DCNv2,FlexibleSnakeConv,
)
下方の__all__にもFlexibleSnakeConvを追加します。
__all__ = [
'Conv', 'LightConv', 'RepConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv','FlexibleSnakeConv',
'ChannelAttention', 'SpatialAttention', 'CBAM', 'Concat', 'TransformerLayer', 'TransformerBlock', 'MLPBlock',
'LayerNorm2d', 'DFL', 'HGBlock', 'HGStem', 'SPP', 'SPPF', 'C1', 'C2', 'C3', 'C2f', 'C3x', 'C3TR', 'C3Ghost',
'GhostBottleneck', 'Bottleneck', 'BottleneckCSP', 'Proto', 'Detect', 'Segment', 'Pose', 'Classify',
'TransformerEncoderLayer', 'RepC3', 'RTDETRDecoder', 'AIFI', 'DeformableTransformerDecoder',
'DeformableTransformerDecoderLayer', 'MSDeformAttn', 'MLP','deattn',
'DCNv2','ODConv_3rd',
'C2f_DCN','C2f_DSConv','C2f_Biformer','DiverseBranchBlock','Bottleneck_DBB','C2f_DBB','EVCBlock','C2f_EMA','C2f_ODConv']
02 タスクスクリプトに追加する
パスはultralytics/nn/tasks.pyで、FlexibleSnakeConvのインポート名を追加します。
from ultralytics.nn.modules import (AIFI, C1, C2, C3, C3TR, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x,
Classify, Concat, Conv, ConvTranspose, Detect, DWConv, DWConvTranspose2d, Focus,
GhostBottleneck, GhostConv, HGBlock, HGStem, Pose, RepC3, RepConv, RTDETRDecoder,
DCNv2,
FlexibleSnakeConv,
Segment,CBAM,C2f_DCN,C2f_DSConv,C2f_Biformer,C2f_DBB, C2f_EMA,EVCBlock,
ODConv_3rd)
03 パラメータのパースに追加する
def parse_model(d, ch, verbose=True): 内でFlexibleSnakeConvを探して追加します。
if m in (Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus,DCNv2,FlexibleSnakeConv,
BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x, RepC3,BasicRFB_a,BasicRFB,
C2f_DCN,C2f_DSConv,C2f_DBB,EVCBlock,BoT3, C2f_EMA,ODConv_3rd,C2f_Biformer):
04 yamlに組み込む
FlexibleSnakeConvは画像サイズを変更せずに動作するため、ストライド2のプロセスはありません。したがって、レイヤーを追加し、チャンネル数を入力と同じに設定し、カーネルサイズは3にします。YOLOv8のyamlには画像サイズを変更しないconvが存在しないため、置き換えることはできません。
- [-1, 1, FlexibleSnakeConv, [512, 3]]
head:
- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 40*40*512 w*r
- [[-1, 6], 1, Concat, [1]] # cat backbone P4 11 40*40*512 *(w+wr)
- [-1, 3, C2f, [512]] # 12 # 40*40*512 *w
- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 80*80*256 *w
- [[-1, 4], 1, Concat, [1]] # cat backbone P3 80*80*512 *w
- [-1, 3, C2f, [256]] # 15 (P3/8-small) 80*80*256 *w --Detect
- [-1, 1, Conv, [256, 3, 2]] # 40*40*256 *w
- [-1, 1, FlexibleSnakeConv, [256, 3]]
- [[-1, 12], 1, Concat, [1]] # cat head P4 # 40*40*512 *w
- [-1, 3, C2f, [512]] # 19 (P4/16-medium) # 40*40*512 *w --Detect
- [-1, 1, Conv, [512, 3, 2]] # 20*20*512 *w
- [[-1, 9], 1, Concat, [1]] # cat head P5 # 20*20*512 *(w+wr)
- [-1, 3, C2f, [1024]] # 22 (P5/32-large) # 20*20*512 *w --Detect
- [[15, 19, 22], 1, Detect, [nc]] # Detect(P3=80*80*256 *w, P4=40*40*512 *w, P5=20*20*512)
02 FlexibleSnakeConvをモジュールに組み込む
FlexibleSnakeConvをBottleneckに統合してBottleneck_FlexibleSnakeConvを作成し、それをC2FのモデルとしてC2F_FlexibleSnakeConvを作成します。
01 FlexibleSnakeConvをインポートする
他のコnvolutionと同様に、モデルのサブモジュールとしてインポートします。
from .conv import Conv, DWConv, GhostConv, LightConv, RepConv,DCNv2,FlexibleSnakeConv
02 FlexibleSnakeConvをBottleneckに追加する
class Bottleneck_FlexibleSnakeConv(nn.Module):
# 標準ボトルネックにDCNを含む
def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5): # 入力チャネル, 出力チャネル, ショートカット, グループ, カーネル, 拡張
super().__init__()
c_ = int(c2 * e) # 隠れチャネル
if k[0] == 3:
self.cv1 = FlexibleSnakeConv(c1, c_, k[0], 1)
else:
self.cv1 = Conv(c1, c_, k[0], 1) #self.cv2 = FlexibleSnakeConv(c_, c2, 3)
if k[1] == 3:
self.cv2 = FlexibleSnakeConv(c_, c2, k[1])
else:
self.cv2 = Conv(c_, c2, k[1], 1, g=g)
self.add = shortcut and c1 == c2 #もしショートカットかつチャネル数が同じ場合
def forward(self, x):
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
03 FlexibleSnakeConvをC2Fに追加する
class C2f_FlexibleSnakeConv(nn.Module):
# CSPボトルネックに2つのコnvolutionを使用する
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # 入力チャネル, 出力チャネル, 数, ショートカット, グループ, 拡張
super().__init__()
self.c = int(c2 * e) # 隠れチャネル
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # オプション act=FReLU(c2)
self.m = nn.ModuleList(Bottleneck_FlexibleSnakeConv(self.c, self.c, shortcut, g, k=(3, 3), e=1.0) for _ in range(n)) #Bottleneck
def forward(self, x):
y = list(self.cv1(x).split((self.c, self.c), 1)) #まずコnvolutionを行い、その後分割する
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
04 C2f_FlexibleSnakeConvをallに書き込む
allのパスは同じスクリプト内
__all__ = [
'DFL', 'HGBlock', 'HGStem', 'SPP', 'SPPF', 'C1', 'C2', 'C3', 'C2f', 'C3x', 'C3TR', 'C3Ghost', 'GhostBottleneck',
'Bottleneck', 'BottleneckCSP', 'Proto', 'RepC3',
'C2f_DCN','C2f_DSConv','C2f_Biformer','Bottleneck_DBB','C2f_DBB','EVCBlock','C2f_EMA','ODConv_3rd','C2f_FlexibleSnakeConv']
05 C2f_FlexibleSnakeConvをinitに書き込む
C2f_FlexibleSnakeConvはモジュールであり、block内に存在する
from .block import (C1, C2, C3, C3TR, DFL, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x, GhostBottleneck,
HGBlock, HGStem, Proto, RepC3,
C2f_DCN,C2f_DSConv,C2f_Biformer,C2f_DBB,Bottleneck_DBB,EVCBlock, C2f_EMA,ODConv_3rd,C2f_FlexibleSnakeConv)#追加したもの
下方のallにも追加
__all__ = [
'Conv', 'LightConv', 'RepConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv','FlexibleSnakeConv',
'ChannelAttention', 'SpatialAttention', 'CBAM', 'Concat', 'TransformerLayer', 'TransformerBlock', 'MLPBlock',
'LayerNorm2d', 'DFL', 'HGBlock', 'HGStem', 'SPP', 'SPPF', 'C1', 'C2', 'C3', 'C2f', 'C3x', 'C3TR', 'C3Ghost',
'GhostBottleneck', 'Bottleneck', 'BottleneckCSP', 'Proto', 'Detect', 'Segment', 'Pose', 'Classify',
'TransformerEncoderLayer', 'RepC3', 'RTDETRDecoder', 'AIFI', 'DeformableTransformerDecoder',
'DeformableTransformerDecoderLayer', 'MSDeformAttn', 'MLP','deattn',
'DCNv2','ODConv_3rd',
'C2f_DCN','C2f_DSConv','C2f_Biformer','DiverseBranchBlock','Bottleneck_DBB','C2f_DBB','EVCBlock','C2f_EMA','C2f_ODConv','C2f_FlexibleSnakeConv']
06 task.pyに追加する
パスはultralytics/nn/tasks.py。
モジュールからC2f_FlexibleSnakeConvをインポートする
from ultralytics.nn.modules import (AIFI, C1, C2, C3, C3TR, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x,
Classify, Concat, Conv, ConvTranspose, Detect, DWConv, DWConvTranspose2d, Focus,
GhostBottleneck, GhostConv, HGBlock, HGStem, Pose, RepC3, RepConv, RTDETRDecoder,
DCNv2,
FlexibleSnakeConv,C2f_FlexibleSnakeConv,
Segment,CBAM,C2f_DCN,C2f_DSConv,C2f_Biformer,C2f_DBB, C2f_EMA,EVCBlock,
ODConv_3rd)
パラメータを解析する-def parse_model(d, ch, verbose=True):で追加する
if m in (Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus,DCNv2,FlexibleSnakeConv,
BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x, RepC3,BasicRFB_a,BasicRFB,
C2f_DCN,C2f_DSConv,C2f_DBB,EVCBlock,BoT3, C2f_EMA,ODConv_3rd,C2f_Biformer,C2f_FlexibleSnakeConv):
C2f_FlexibleSnakeConvが複数回使用されるため、以下にも追加
if m in (BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, C3x, RepC3,
C2f_DCN,C2f_DSConv,C2f_DBB,BoT3,C2f_EMA,C2f_Biformer,C2f_FlexibleSnakeConv):#ブロック追加