【画像分割】【ディープラーニング】PFNet公式Pytorchコード - PFNetネットワークFMフォーカスモジュール解析
記事ディレクトリ
序文
PFNet コードを詳細に分析する前に、最初のタスクは PFNet コード [win10 でのリファレンス チュートリアル] を正常に実行することです。その後の学習には意味があります。このブログでは、PFNet ニューラル ネットワーク モジュールの FM フォーカス モジュール コードについて説明し、他の機能モジュール コードについては説明しません。
各機能モジュールのコードはブロガーが別のブログ記事で詳細に分析しており、[Win10 でのリファレンス チュートリアル] をクリックすると、そのブログ記事のディレクトリ リンクが序文に掲載されています。
フォーカスモジュール フォーカスモジュール
元の論文のフォーカス モジュール (FM) の構造は次のとおりです。
迷彩オブジェクトは通常、背景と同様の外観をしているため、誤った警報が発生します。陽性 (FP) 予測と偽陰性 (FN) 予測の設計では、FM フォーカス モジュールを使用して、これらの誤った予測を見つけて削除します。
- 高レベルの予測をアップサンプリングし、シグモイド層を使用して正規化して生成します。 F h p F_{ {\rm{hp}}} Fhp ,并将 F h p F_{ {\rm{hp}}} Fhp 与 ( 1 − F h p ) (1 -{F_{ {\rm{hp}}}}) (1−Fhp ) と現在のレベルの機能 (現在のレベルの機能) F c F_c Fc 乗算して前景アテンション特徴をそれぞれ生成します F f a F_{ {\rm{fa}}} Fファ和背景注意特征 F b a F_{ {\rm{ba}}} Fば ;
- 将两种类型的特征 F f a F_{ {\rm{fa}}} Fファ和 F b a F_{ {\rm{ba}}} Fば は 2 つの並列コンテキスト探索ブロック (CE ブロック) に入力され、それぞれコンテキスト推論を実行し、誤検知干渉を検出します。 F f p d F_{ {\rm{fpd}}} FFPD 偽陰性干渉 F f n d F_{ {\rm{fnd}}} F見つける ;
- 高レベルの機能の場合 F h F_h Fh CBR 上実行 F u p = U p ( C B R ( F h ) ) {F_{up}} = {\rm{ }}Up\left( {CBR \左( { {F_h}} \right)} \right) Fup =うp(CBR(Fh ))、個別に実行 F r = B R ( F u p − α F f p d ) { F_r}{\rm{ }} = {\rm{ }}BR\left( { {F_{up}}{\rm{ }} - {\rm{ }}\alpha {F_{fpd}}} \right) Fr =BR(Fup −αFfpd )背景のぼやけ (つまり、偽陽性干渉) と F r ' = B R ( F r + β F f n d ) F_r^\prime {\rm{ }} = {\rm{ }}BR\left( { {F_r}{\rm{ }} + {\rm{ }}\beta {F_{fnd}}} \right) Fr' =BR(Fr +βFfnd ) 欠落した前景 (偽陰性干渉など) を追加するための特徴ピクセルごとの追加操作。
- 最後にペアを渡します F r ' F_r^\prime {\rm{ }} Fr' 畳み込みを実行して、より正確な予測マップを取得します。
CBR は畳み込み層、BN 層、ReLU 活性化層の略称です。BR は BN 層と ReLU 活性化層の略称です。 α \alpha α和 β \beta β はトレーニング可能なパラメータです。
コードの場所: PFNet.py
class Focus(nn.Module):
def __init__(self, channel1, channel2):
super(Focus, self).__init__()
self.channel1 = channel1
self.channel2 = channel2
# 对higher-level features上采样保持与current-level features通道一致
self.up = nn.Sequential(nn.Conv2d(self.channel2, self.channel1, 7, 1, 3),
nn.BatchNorm2d(self.channel1), nn.ReLU(), nn.UpsamplingBilinear2d(scale_factor=2))
# 对higher-level prediction上采样,nn.Sigmoid()将所以小于0的值都当作背景
self.input_map = nn.Sequential(nn.UpsamplingBilinear2d(scale_factor=2), nn.Sigmoid())
self.output_map = nn.Conv2d(self.channel1, 1, 7, 1, 3)
# CE block
self.fp = Context_Exploration_Block(self.channel1)
self.fn = Context_Exploration_Block(self.channel1)
# 可训练参数
self.alpha = nn.Parameter(torch.ones(1))
self.beta = nn.Parameter(torch.ones(1))
self.bn1 = nn.BatchNorm2d(self.channel1)
self.relu1 = nn.ReLU()
self.bn2 = nn.BatchNorm2d(self.channel1)
self.relu2 = nn.ReLU()
def forward(self, x, y, in_map):
# x; current-level features
# y: higher-level features
# in_map: higher-level prediction
# 对higher-level features上采样
up = self.up(y)
# 对higher-level prediction上采样
input_map = self.input_map(in_map)
# 获得current-level features上的前景
f_feature = x * input_map
# 获得current-level features上的背景
b_feature = x * (1 - input_map)
# 前景
fp = self.fp(f_feature)
# 背景
fn = self.fn(b_feature)
# 消除假阳性干扰
refine1 = up - (self.alpha * fp)
refine1 = self.bn1(refine1)
refine1 = self.relu1(refine1)
# 消除假阴性干扰
refine2 = refine1 + (self.beta * fn)
refine2 = self.bn2(refine2)
refine2 = self.relu2(refine2)
# 卷积精化
output_map = self.output_map(refine2)
return refine2, output_map
コンテキスト探索ブロック CE ブロック
元の論文のコンテキスト探索 (CE) ブロックの構造を次の図に示します。
CE ブロックは 4 つのコンテキスト探索ブランチで構成され、各ブランチには次のチャネルが含まれています。縮小 (次元削減) 1×1 畳み込み、局所特徴抽出に使用 k i k_i ki × k i k_i ki コンテキスト認識のための畳み込みと膨張率 r i r_i ri 3×3 拡張コンボリューション (または拡張コンボリューション)。このような設計により、CE Block は広範囲でリッチなコンテキストを認識する能力を獲得するため、コンテキスト推論に使用し、偽陽性干渉と偽陰性干渉を個別に検出することができます。
元の論文の説明では次元削減に 3×3 畳み込みを使用していますが、コードでは 1×1 畳み込みを使用しています。ブロガーはコードを参照する必要があります。 k i k_i ki 4 つのブランチのコンボリューション カーネル サイズは 1、3、5、7、 r i r_i ri それから、1、2、4、8です。
ブロガーは論文とコード フローを組み合わせて、コンテキスト探索のブロック構造図を再描画しました。
- 4 つのブランチの順序は、局所特徴抽出層のコンボリューション カーネルのサイズに応じて並べ替えられます。前者のブランチは、より大きな受容野でさらに処理するために出力特徴を後者のブランチにフィードバックし、後者のブランチは、前のブランチの出力特徴を後のブランチにフィードし、出力特徴と自身の次元削減プロセスの出力特徴を融合して追加して、自身の特徴を出力します。
- 4 つのブランチすべての出力が接続され、3×3 畳み込みによって融合され、広範囲で豊富なコンテキストを認識する能力が得られます。
コードの場所: PFNet.py
class Context_Exploration_Block(nn.Module):
def __init__(self, input_channels):
super(Context_Exploration_Block, self).__init__()
self.input_channels = input_channels
self.channels_single = int(input_channels / 4)
# 1×1卷积用减少通道数降维
self.p1_channel_reduction = nn.Sequential(
nn.Conv2d(self.input_channels, self.channels_single, 1, 1, 0),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p2_channel_reduction = nn.Sequential(
nn.Conv2d(self.input_channels, self.channels_single, 1, 1, 0),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p3_channel_reduction = nn.Sequential(
nn.Conv2d(self.input_channels, self.channels_single, 1, 1, 0),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p4_channel_reduction = nn.Sequential(
nn.Conv2d(self.input_channels, self.channels_single, 1, 1, 0),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
# k×k用于局部特征提取
self.p1 = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, 1, 1, 0),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
# 使用空洞卷积用于上下文感知
self.p1_dc = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, kernel_size=3, stride=1, padding=1, dilation=1),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p2 = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, 3, 1, 1),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p2_dc = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, kernel_size=3, stride=1, padding=2, dilation=2),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p3 = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, 5, 1, 2),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p3_dc = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, kernel_size=3, stride=1, padding=4, dilation=4),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p4 = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, 7, 1, 3),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
self.p4_dc = nn.Sequential(
nn.Conv2d(self.channels_single, self.channels_single, kernel_size=3, stride=1, padding=8, dilation=8),
nn.BatchNorm2d(self.channels_single), nn.ReLU())
# 综合考虑所有的特征
self.fusion = nn.Sequential(nn.Conv2d(self.input_channels, self.input_channels, 1, 1, 0),
nn.BatchNorm2d(self.input_channels), nn.ReLU())
def forward(self, x):
p1_input = self.p1_channel_reduction(x)
p1 = self.p1(p1_input)
p1_dc = self.p1_dc(p1)
# 融合前一个分支输出和自身降维输出
p2_input = self.p2_channel_reduction(x) + p1_dc
p2 = self.p2(p2_input)
p2_dc = self.p2_dc(p2)
# 融合前一个分支输出和自身降维输出
p3_input = self.p3_channel_reduction(x) + p2_dc
p3 = self.p3(p3_input)
p3_dc = self.p3_dc(p3)
# 融合前一个分支输出和自身降维输出
p4_input = self.p4_channel_reduction(x) + p3_dc
p4 = self.p4(p4_input)
p4_dc = self.p4_dc(p4)
# 四个分支融合
ce = self.fusion(torch.cat((p1_dc, p2_dc, p3_dc, p4_dc), 1))
return ce
要約する
PFNet ネットワークの FM 集束モジュールの構造とコードをできるだけ簡単かつ詳細に紹介します。