目次
ミッション概要
エンコーダ-デコーダ
接続をスキップする
実装の詳細
損失関数
アップサンプリング方式
埋めるか埋めないか?
U-Netの仕組み
ミッション概要
U-Net は、セマンティック セグメンテーション タスク用に開発されました。ニューラル ネットワークが画像を入力として受け入れる場合、オブジェクトを一般的に分類するか、インスタンスごとに分類するかを選択できます。画像に含まれるオブジェクト (画像分類)、すべてのオブジェクトの位置 (画像ローカリゼーション/セマンティック セグメンテーション)、または個々のオブジェクトの位置 (オブジェクト検出/インスタンス セグメンテーション) を予測できます。
以下の図は、これらのコンピューター ビジョン タスクの違いを示しています。問題を単純化するために、1 つのカテゴリと 1 つのラベルによる分類のみを考慮します。
分類タスクでは、サイズ k のベクトルを出力します。ここで、k はカテゴリの数です。検出タスクでは、境界ボックスを定義するベクトル x、y、高さ、幅、カテゴリを出力する必要があります。
ただし、セグメンテーション タスクでは、元の入力と同じサイズの画像を出力する必要があります。これはエンジニアリング上の大きな課題です。ニューラル ネットワークはどのようにして入力画像から関連する特徴を抽出し、それらをセグメンテーション マスクに投影するのでしょうか?
エンコーダ-デコーダ
エンコーダとデコーダに詳しくない場合は、この記事を読むことをお勧めします。
https://towardsdatascience.com/ Understanding-latent-space-in-machine-learning-de5a7c687d8d
エンコーダーとデコーダーは、必要なものと同様の出力、つまり入力と同じ次元の出力を生成するため、関連しています。エンコーダ/デコーダの概念を画像セグメンテーションに適用できますか? 1D バイナリ マスクを生成し、クロスエントロピー損失を使用してネットワークをトレーニングできます。
私たちのネットワークは 2 つの部分で構成されています。エンコーダー部分は画像から関連する特徴を抽出し、デコーダー部分は抽出された特徴を取得してセグメンテーション マスクを再構築します。
エンコーダ部分では、畳み込み層が使用され、続いて特徴抽出器として ReLU と最大プーリングが使用されます。デコーダ部分では、転置畳み込みを使用して特徴マップのサイズを増やし、チャネル数を減らします。パディングは、畳み込み演算後に特徴マップのサイズを同じに保つために使用されます。
お気づきの点の 1 つは、分類ネットワークとは異なり、このネットワークには完全に接続された線形層がないことです。これは完全畳み込みネットワーク (FCN) の例です。FCN は、Shelhamer らによる論文「セマンティック セグメンテーションのための完全畳み込みネットワーク」を皮切りに、セグメンテーション タスクで優れたパフォーマンスを発揮することが示されています [1]。
ただし、このネットワークには問題があります。エンコーダ層とデコーダ層を追加すると、実際には特徴マップがどんどん「縮小」します。したがって、エンコーダは、より一般的な特徴を取得するために、より詳細な特徴を破棄する場合があります。医療画像のセグメンテーションを扱う場合、各ピクセルが病気か正常かを分類することが重要になる可能性があります。このエンコーダ/デコーダ ネットワークが一般的な機能と詳細な機能の両方を確実に受け入れるにはどうすればよいでしょうか?
接続をスキップする
https://towardsdatascience.com/introduction-to-resnets-c0a830a288a4
ディープ ニューラル ネットワークは、連続した層に情報を渡すときに特定の機能を「忘れる」可能性があるため、スキップ接続によりこれらの機能を再導入し、学習をより強力にすることができます。スキップ接続は残差ネットワーク (ResNet) に導入され、分類の改善とよりスムーズな学習勾配を示しました。このメカニズムを参考にして、U-Net にスキップ接続を追加して、各デコーダーに対応するエンコーダーの機能マップを含めることができます。これは U-Net の特徴的な機能です。
U-Net は、スキップ接続を備えたエンコーダ/デコーダ セグメンテーション ネットワークです。画像は作者提供。U-Net には 2 つの定義プロパティがあります。
エンコーダ/デコーダ ネットワークは、深くなるほど、より一般的な特徴を抽出します。
接続をスキップし、デコーダーの詳細な機能を再導入します。これら 2 つのプロパティは、U-Net がセグメンテーションに詳細機能と一般的な機能の両方を使用できることを意味します。U-Net は元々、セグメンテーションの精度が非常に重要な生物医学画像処理のために導入されました [2]。
実装の詳細
前のセクションでは、U-Net の非常に一般的な概要と、それが機能する理由を説明しました。ただし、一般的な理解と実際の実装の間では、詳細が重要な役割を果たします。ここでは、U-Net 実装の選択肢のいくつかを概説します。
損失関数
ターゲットはバイナリ マスクであるため (ピクセル値 1 は、ピクセルにオブジェクトが含まれていることを意味します)、出力をグラウンド トゥルースと比較するために使用される一般的な損失関数は、カテゴリカル クロス エントロピー損失 (または、次の場合はバイナリ クロス エントロピー損失) です。単一ラベルの場合)。
元の U-Net 論文では、損失関数に追加の重みが追加されました。この重みパラメータは 2 つのことを行います。クラスの不均衡を補正し、セグメンテーション境界により高い重要性を与えます。私が見つけた多くの U-Net 実装では、通常、この追加の重み係数は使用されません。
もう 1 つの一般的な損失関数は Dice 損失です。ダイス損失は、交差領域と合計領域を比較することによって、2 セットの画像の類似性を測定します。Dice の損失は Intersection over Union (IOU) と同じではないことに注意してください。それらは似たものを測定しますが、分母が異なります。Dice 係数が高いほど、Dice 損失は低くなります。
ここでは、0 による除算を避けるためにイプシロン項が追加されています (イプシロンは通常 1)。Milletari らの実装など、一部の実装では、合計する前に分母のピクセル値を 2 乗します [3]。クロスエントロピー損失と比較して、Dice 損失は、生物医学画像セグメンテーション タスクで一般的な不均衡なセグメンテーション マスクに対して非常に堅牢です。
アップサンプリング方式
もう 1 つの詳細は、デコーダのアップサンプリング方法の選択です。一般的な方法をいくつか示します。
双一次補間。この方法では、線形補間を使用して出力ピクセルを予測します。通常、この方法によるアップサンプリングの後に畳み込み層が続きます。
最大限のアンチプーリング。この方法は、最大プーリングの逆の操作です。これは、最大プーリング操作のインデックスを使用し、これらのインデックスを最大値まで埋めます。他のすべての値は 0 に設定されます。通常、最大アンプーリングの後には、欠損値を「平滑化」するための畳み込み層が続きます。
デコンボリューション/転置コンボリューション。デコンボリューションに関するブログ投稿は数多くあります。この記事を視覚的なガイドとして読むことをお勧めします。
https://towardsdatascience.com/types-of-convolutions-in-deep-learning-717013397f4d
デコンボリューションには 2 つのステップがあります。最初に元の画像の各ピクセルの周囲にパディングを追加し、次にコンボリューションを適用します。オリジナルの U-Net では、空間解像度とチャネル深度を変更するために、ストライド 2 の 2x2 転置畳み込みが使用されていました。
ピクセルの再配置。この手法は SRGAN などの超解像度ネットワークで使用されます。まず、畳み込みを使用して C x H x W 特徴マップを (Cr^2) x H x W に変換します。次に、ピクセル再配置によってこれらのピクセルがモザイク内に「再配置」され、サイズ C x (Hr) x (Wr) の出力が生成されます。
満たされていませんか、それとも満たされていますか?
畳み込み層は、カーネルが 1x1 より大きく、パディングがない場合、入力よりも小さい出力を生成します。これは U-Net にとって問題です。前のセクションの U-Net 図で、イメージの一部をそのデコードされた部分に接続したことを思い出してください。パディングを使用しない場合、デコードされたイメージの空間次元は、エンコードされたイメージと比較して小さくなります。
ただし、元の U-Net 論文ではパディングが使用されていませんでした。理由は示されていませんが、おそらく作者が画像の端にセグメンテーションエラーを導入したくなかったからだと思います。代わりに、連結前にエンコードされた画像の中央のトリミングを実行しました。入力サイズが 572 x 572 の画像の場合、出力は 388 x 388 になり、約 50% の損失が生じます。パディングなしで U-Net を実行する場合は、完全なセグメンテーション イメージを取得するために、重複するタイルで U-Net を複数回実行する必要があります。
U-Netの仕組み
ここでは、楕円をセグメント化するためだけに、非常に単純な U-Net のようなネットワークを実装します。この U-Net の深さはわずか 3 層で、同じパディングとバイナリのクロスエントロピー損失を使用します。より複雑なネットワークでは、解像度ごとにより多くの畳み込み層を使用したり、必要に応じて深さを拡張したりできます。
import torch
import numpy as np
import torch.nn as nn
class EncoderBlock(nn.Module):
# Consists of Conv -> ReLU -> MaxPool
def __init__(self, in_chans, out_chans, layers=2, sampling_factor=2, padding="same"):
super().__init__()
self.encoder = nn.ModuleList()
self.encoder.append(nn.Conv2d(in_chans, out_chans, 3, 1, padding=padding))
self.encoder.append(nn.ReLU())
for _ in range(layers-1):
self.encoder.append(nn.Conv2d(out_chans, out_chans, 3, 1, padding=padding))
self.encoder.append(nn.ReLU())
self.mp = nn.MaxPool2d(sampling_factor)
def forward(self, x):
for enc in self.encoder:
x = enc(x)
mp_out = self.mp(x)
return mp_out, x
class DecoderBlock(nn.Module):
# Consists of 2x2 transposed convolution -> Conv -> relu
def __init__(self, in_chans, out_chans, layers=2, skip_connection=True, sampling_factor=2, padding="same"):
super().__init__()
skip_factor = 1 if skip_connection else 2
self.decoder = nn.ModuleList()
self.tconv = nn.ConvTranspose2d(in_chans, in_chans//2, sampling_factor, sampling_factor)
self.decoder.append(nn.Conv2d(in_chans//skip_factor, out_chans, 3, 1, padding=padding))
self.decoder.append(nn.ReLU())
for _ in range(layers-1):
self.decoder.append(nn.Conv2d(out_chans, out_chans, 3, 1, padding=padding))
self.decoder.append(nn.ReLU())
self.skip_connection = skip_connection
self.padding = padding
def forward(self, x, enc_features=None):
x = self.tconv(x)
if self.skip_connection:
if self.padding != "same":
# Crop the enc_features to the same size as input
w = x.size(-1)
c = (enc_features.size(-1) - w) // 2
enc_features = enc_features[:,:,c:c+w,c:c+w]
x = torch.cat((enc_features, x), dim=1)
for dec in self.decoder:
x = dec(x)
return x
class UNet(nn.Module):
def __init__(self, nclass=1, in_chans=1, depth=5, layers=2, sampling_factor=2, skip_connection=True, padding="same"):
super().__init__()
self.encoder = nn.ModuleList()
self.decoder = nn.ModuleList()
out_chans = 64
for _ in range(depth):
self.encoder.append(EncoderBlock(in_chans, out_chans, layers, sampling_factor, padding))
in_chans, out_chans = out_chans, out_chans*2
out_chans = in_chans // 2
for _ in range(depth-1):
self.decoder.append(DecoderBlock(in_chans, out_chans, layers, skip_connection, sampling_factor, padding))
in_chans, out_chans = out_chans, out_chans//2
# Add a 1x1 convolution to produce final classes
self.logits = nn.Conv2d(in_chans, nclass, 1, 1)
def forward(self, x):
encoded = []
for enc in self.encoder:
x, enc_output = enc(x)
encoded.append(enc_output)
x = encoded.pop()
for dec in self.decoder:
enc_output = encoded.pop()
x = dec(x, enc_output)
# Return the logits
return self.logits(x)
ご覧のとおり、U-Net はスキップ接続がなくても許容できるセグメンテーション結果を生成できますが、スキップ接続を追加すると、より詳細な詳細が導入される可能性があります (右側の 2 つの楕円間の接続を参照)。
結論は
U-Net を一言で説明すると、U-Net は画像のエンコーダ/デコーダのようなものですが、詳細が失われないようにスキップ接続を備えているということになります。U-Net は多くのセグメンテーション タスクで頻繁に使用され、近年では画像生成タスクでも成功を収めています。
参考文献:
[1] ロング、ジョナサン、エヴァン・シェルハマー、トレバー・ダレル。「セマンティックセグメンテーションのための完全畳み込みネットワーク」コンピュータ ビジョンとパターン認識に関する IEEE 会議の議事録。2015年。
[2] ロンネバーガー、オラフ、フィリップ・フィッシャー、トーマス・ブロックス。「U-net: 生物医学画像セグメンテーションのための畳み込みネットワーク」医療画像コンピューティングとコンピュータ支援介入に関する国際会議。スプリンガー、チャム、2015 年。
[3] ミレタリ、ファウスト、ナシル・ナヴァブ、シード・アフマド・アフマディ。「V-net: 体積医療画像セグメンテーション用の完全畳み込みニューラル ネットワーク」2016 年 3D ビジョン (3DV) に関する第 4 回国際会議。IEEE、2016 年。
☆終わり☆
これが表示された場合は、この記事が気に入っていることを意味します。転送して「いいね!」してください。WeChat で "uncle_pn" を検索してください。編集者の WeChat "woshicver" を追加することを歓迎します。高品質のブログ投稿が友達の輪に毎日更新されます。
↓ QRコードを読み取ってエディタを追加↓