「UNETアーキテクチャの包括的なガイド | 画像セグメンテーションのマスタリング」
Comprehensive Guide to UNET Architecture | Mastering Image Segmentation
イントロダクション
コンピュータビジョンという興奮する分野では、画像には多くの秘密と情報が含まれており、アイテムを区別し強調することが重要です。画像セグメンテーションは、画像を意味のある領域やオブジェクトに分割するプロセスであり、医療画像から自動運転や物体認識までさまざまなアプリケーションで必要です。正確で自動的なセグメンテーションは長い間課題であり、従来の手法では精度と効率が不足することがよくありました。そこで登場するのがUNETアーキテクチャです。UNETは画像セグメンテーションを革新した知能的な手法であり、そのシンプルな設計と独創的な技術により、より正確で堅牢なセグメンテーション結果を実現しました。コンピュータビジョンのエキサイティングな分野に初めて足を踏み入れる方でも、セグメンテーションの能力を向上させたい経験豊富なプラクティショナーでも、この詳細なブログ記事はUNETの複雑さを解き明かし、そのアーキテクチャ、コンポーネント、有用性を完全に理解することができます。
この記事はData Science Blogathonの一部として公開されました。
畳み込みニューラルネットワークの理解
CNNはコンピュータビジョンのタスクで頻繁に使用されるディープラーニングモデルであり、画像分類、物体認識、画像セグメンテーションなどに非常に役立ちます。CNNは主に画像から関連する情報を学習し抽出するため、視覚データ分析に非常に有用です。
CNNの重要なコンポーネント
- 畳み込み層: CNNは学習可能なフィルタ(カーネル)の集合で構成されており、入力画像または特徴マップに畳み込まれます。各フィルタは要素ごとの乗算と合計を適用し、特定のパターンやローカルな特徴を強調した特徴マップを生成します。これらのフィルタはエッジ、コーナー、テクスチャなど、多くの視覚要素を捉えることができます。
- ChatGPT コードインタプリターを使用できない 4 つの方法、それらがアナリティクスを乱す
- Apache BeamにおけるParDoとDoFnの実装の詳細
- 『革命的なロボティクス:電子機器なしで機能する3Dプリントグリッパー』
- プーリング層: 畳み込み層によって生成された特徴マップをプーリング層を使用してダウンサンプリングします。プーリングは特徴マップの空間的な次元を削減しながら、最も重要な情報を保持し、後続の層の計算量を減らし、モデルを入力の変動に対してより抵抗力のあるものにします。最も一般的なプーリング操作は、与えられた近傍内の最大値を取るマックスプーリングです。
- 活性化関数: 活性化関数を使用して、CNNモデルに非線形性を導入します。畳み込み層やプーリング層の出力に要素ごとに適用し、ネットワークが複雑な関連性を理解し非線形の決定を行うことができるようにします。勾配消失問題を解決するためのシンプルさと効率性から、ReLU(Rectified Linear Unit)活性化関数がCNNでよく使用されます。
- 全結合層: 全結合層、または密結合層とも呼ばれるものは、取得した特徴を使用して最終的な分類または回帰操作を行います。これにより、1つの層のすべてのニューロンが次の層のすべてのニューロンに接続され、ネットワークは前の層の組み合わせ入力に基づいてグローバルな表現を学習し、高レベルの判断を行うことができます。
ネットワークは、低レベルの特徴を捉えるために畳み込み層のスタックから始まり、その後プーリング層が続きます。より深い畳み込み層はネットワークが進化するにつれてより高レベルの特徴を学習します。最後に、1つまたは複数の全結合層を使用して分類または回帰操作を行います。
全結合ネットワークの必要性
従来のCNNは通常、単一のラベルが入力画像全体に割り当てられる画像分類のジョブに適しています。一方、従来のCNNアーキテクチャは、各ピクセルをさまざまなクラスや領域に分類するセマンティックセグメンテーションのようなより詳細なタスクには問題があります。ここでFully Convolutional Networks(FCN)が活躍します。
セグメンテーションタスクにおける従来のCNNアーキテクチャの制約
空間情報の喪失: 従来のCNNはプーリング層を使用して特徴マップの空間的な次元を徐々に減少させます。このダウンサンプリングは高レベルの特徴を捉えるのに役立ちますが、ピクセルレベルでのオブジェクトの正確な検出と分割を困難にします。
固定された入力サイズ: CNNアーキテクチャは通常、特定のサイズの画像を受け入れるように構築されています。しかし、セグメンテーションタスクでは入力画像のサイズがさまざまであり、典型的なCNNで扱うのが難しい可変サイズの入力に対応することがあります。
限られた位置情報の正確さ: 伝統的なCNNは通常、分類のために最後に完全に接続された層を使用します。彼らは空間情報を保持しないため、イメージ内のオブジェクトや領域を正確に特定することはできません。
セマンティックセグメンテーションの解決策としての完全畳み込みネットワーク(FCN)
畳み込み層のみで作業し、ネットワーク全体で空間情報を保持することにより、完全畳み込みネットワーク(FCN)はセグメンテーションタスクにおけるクラシックなCNNアーキテクチャの制約に対処します。 FCNはピクセルごとの予測を行い、入力イメージの各ピクセルにラベルやクラスを割り当てます。 FCNは特徴マップをアップサンプリングすることで、ピクセルレベルの予測を提供する密なセグメンテーションマップの構築を可能にします。トランスポーズ畳み込み(デコンボリューションまたはアップサンプリング層とも呼ばれます)は、CNN設計後の完全接続層を置き換えるために使用されます。トランスポーズ畳み込みにより、特徴マップの空間解像度が増加し、入力イメージと同じサイズになります。
アップサンプリング中、FCNは一般的にスキップ接続を使用し、特定のレイヤーをバイパスして下位レベルの特徴マップを直接上位レベルのものにリンクします。これらのスキップ関係は、細かい詳細と文脈情報を保持し、セグメンテーションされた領域の位置情報の正確さを向上させるのに役立ちます。 FCNは、医療画像セグメンテーション、シーン解析、インスタンスセグメンテーションを含むさまざまなセグメンテーションアプリケーションで非常に効果的です。 FCNを利用してセマンティックセグメンテーションを実現することで、さまざまなサイズの入力イメージを処理し、ピクセルレベルの予測を提供し、ネットワーク全体で空間情報を保持することができます。
画像セグメンテーション
画像セグメンテーションは、画像を多くの意味のある別々の部分またはセグメントに分割するコンピュータビジョンの基本プロセスです。画像分類とは異なり、セグメンテーションは各ピクセルまたはピクセルグループにラベルを付け加え、実質的に画像を意味のある部分に分割します。画像セグメンテーションは重要です。それにより、画像の内容についてより詳細な理解が可能になります。画像を複数のパーツに分割することで、オブジェクトの境界、形状、サイズ、空間関係について多くの情報を抽出することができます。この細かい解析は、さまざまなコンピュータビジョンのタスクで重要であり、改良されたアプリケーションを可能にし、より高いレベルの視覚データの解釈をサポートします。
UNETアーキテクチャの理解
手動注釈やピクセルごとの分類など、従来の画像セグメンテーション技術にはさまざまな欠点があり、正確で効果的なセグメンテーション作業には無駄が多く困難です。これらの制約により、UNETアーキテクチャなど、より高度な解決策が開発されています。従来の方法の欠点と、なぜUNETがこれらの問題を克服するために作成されたかを見てみましょう。
- 手動注釈: 手動注釈は、イメージの境界や関心領域をスケッチやマーキングすることを意味します。この方法は信頼性のあるセグメンテーション結果を生み出しますが、時間がかかり、労働集約的であり、人間のミスに対しても影響を受けやすいです。手動注釈は大規模なデータセットにはスケーラブルではなく、一貫性とアノテータ間の合意を維持することは困難です、特に複雑なセグメンテーションタスクでは。
- ピクセルごとの分類: もう一つの一般的なアプローチは、ピクセルごとの分類です。画像内の各ピクセルを独立して分類し、通常は決定木、サポートベクターマシン(SVM)、ランダムフォレストなどのアルゴリズムを使用します。ピクセルごとの分類は、周囲のピクセル間のグローバルなコンテキストと依存関係を捉えるのが難しく、過剰または過少セグメンテーションの問題を引き起こします。空間的な関係を考慮することができず、正確なオブジェクトの境界を提供することができません。
課題の克服
UNETアーキテクチャは、これらの制約に対処し、従来の画像セグメンテーション手法が直面する課題を克服するために開発されました。UNETがこれらの問題に取り組む方法は次のとおりです:
- エンドツーエンドの学習: UNETはエンドツーエンドの学習手法を採用しており、ユーザーの注釈なしで入出力ペアから直接画像をセグメント化する方法を学習します。UNETは大規模なラベル付きデータセットでトレーニングすることにより、キーとなる特徴を自動的に抽出し、正確なセグメンテーションを実行することができます。労働集約的な手動注釈の必要性を排除します。
- 完全畳み込みアーキテクチャ: UNETは完全畳み込みアーキテクチャを基にしており、完全に接続された層を含まないことを意味します。このアーキテクチャにより、UNETは任意のサイズの入力イメージで機能することができ、さまざまなセグメンテーションタスクと入力のバリエーションに対して柔軟性と適応性を高めます。
- U字型のアーキテクチャとスキップ接続: ネットワークの特徴的なアーキテクチャには、エンコーディングパス(縮小パス)とデコーディングパス(拡張パス)が含まれており、ローカルな情報とグローバルなコンテキストを収集することができます。スキップ接続は、エンコーディングパスとデコーディングパスの間のギャップを埋め、前のレイヤーからの重要な情報を維持し、より正確なセグメンテーションを可能にします。
- 文脈情報と位置情報: UNETはスキップ接続を使用して、複数のレイヤーからのマルチスケールの特徴マップを集約し、ネットワークが文脈情報を吸収し、異なる抽象度のレベルで詳細をキャプチャすることができます。この情報の統合により、位置情報の正確さが向上し、正確なオブジェクトの境界と正確なセグメンテーション結果が得られます。
- データ拡張と正則化: UNETはデータ拡張と正則化の技術を使用して、トレーニング中の耐性と一般化能力を向上させます。データ拡張には、回
UNETアーキテクチャの概要
UNETは、画像セグメンテーションアプリケーション向けに構築された完全畳み込みニューラルネットワーク(FCN)アーキテクチャです。Olaf Ronneberger、Philipp Fischer、Thomas Broxによって2015年に最初に提案されました。UNETは、画像セグメンテーションの正確さで頻繁に使用され、さまざまな医療画像アプリケーションで人気の選択肢となっています。UNETは、エンコーディングパス(収縮パスとも呼ばれる)とデコーディングパス(拡張パスとも呼ばれる)を組み合わせています。このアーキテクチャは、図で描かれたときにU字型の外観を持つことから名付けられました。このU字型のアーキテクチャにより、ネットワークはローカルな特徴とグローバルなコンテキストの両方を記録し、正確なセグメンテーション結果を得ることができます。
UNETアーキテクチャの重要な要素
- 収縮パス(エンコーディングパス):UNETの収縮パスは、畳み込み層に続いて最大プーリング操作が行われる構成です。この方法により、入力画像の空間次元を徐々に低下させることで、高解像度の低レベルの特徴を捉えることができます。
- 拡張パス(デコーディングパス):UNETの拡張パスでは、エンコーディングパスからの特徴マップをアップサンプリングするために、転置畳み込み(デコンボリューションまたはアップサンプリング層とも呼ばれる)が使用されます。アップサンプリングのフェーズで特徴マップの空間解像度が増加し、ネットワークは密なセグメンテーションマップを再構成することができます。
- スキップ接続:UNETでは、エンコーディングからデコーディングパスへの対応する層を接続するためにスキップ接続が使用されます。これらの接続により、ネットワークはローカルとグローバルの両方のデータを収集することができます。ネットワークは、以前の層の特徴マップとデコーディングルートの特徴マップを統合することで、重要な空間情報を保持し、セグメンテーションの精度を向上させることができます。
- 連結:UNETでは、スキップ接続を実装するために一般的に連結が使用されます。エンコーディングパスの特徴マップは、アップサンプリング手順中にデコーディングパスのアップサンプリングされた特徴マップと連結されます。この連結により、ネットワークは適切なセグメンテーションのためにマルチスケールの情報を組み込むことができ、高レベルのコンテキストと低レベルの特徴を利用することができます。
- 完全畳み込み層:UNETは、完全に畳み込み層から構成されています。完全畳み込みアーキテクチャにより、UNETは無制限のサイズの画像を扱うことができ、ネットワーク全体で空間情報を保持しながら柔軟かつ適応的なさまざまなセグメンテーションタスクを処理することができます。
エンコーディングパスまたは収縮パスは、UNETアーキテクチャの重要な要素です。このパスは、入力画像から高レベルの情報を抽出し、空間次元を徐々に縮小させる役割を担っています。
畳み込み層
エンコーディングプロセスは、一連の畳み込み層で開始されます。畳み込み層は、学習可能なフィルタのセットを入力画像に適用することで、複数のスケールで情報を抽出します。これらのフィルタは、局所受容野で動作し、ネットワークが空間的なパターンや細かい特徴を捉えることができます。各畳み込み層ごとに、特徴マップの深さが増加し、ネットワークはより複雑な表現を学習することができます。
活性化関数
各畳み込み層の後には、Rectified Linear Unit(ReLU)などの活性化関数が要素ごとに適用され、ネットワークに非線形性をもたらします。活性化関数は、入力画像と取得された特徴間の非線形の相関関係を学習するのに役立ちます。
プーリング層
プーリング層は、畳み込み層の後に使用され、特徴マップの空間次元を削減します。最大プーリングなどの操作により、特徴マップは重ならない領域に分割され、各領域内の最大値のみが保持されます。これにより、特徴マップの空間解像度がダウンサンプリングされ、ネットワークはより抽象的で高レベルなデータを捉えることができます。
エンコーディングパスの役割は、階層的な方法でさまざまなスケールと抽象度のレベルで特徴を捉えることです。エンコーディングプロセスでは、空間次元が低下するにつれて、グローバルなコンテキストと高レベルの情報を抽出することに重点が置かれます。
スキップ接続
エンコーディングパスからデコーディングパスへの適切なレベルを接続するスキップ接続の利用可能性は、UNETアーキテクチャの特徴的な特徴の一つです。これらのスキップリンクは、エンコーディングプロセス中に重要なデータを維持するために重要です。
以前の層からの特徴マップは、エンコーディングパス中にローカルの詳細と細かい情報を収集します。これらの特徴マップは、スキップ接続を利用してデコーディングパイプラインのアップサンプリングされた特徴マップと連結されます。これにより、ネットワークはマルチスケールのデータ、低レベルの特徴、高レベルのコンテキストをセグメンテーションプロセスに組み込むことができます。
以前の層からの空間情報を保存することにより、UNETはオブジェクトを確実にローカライズし、セグメンテーション結果に細部を保持することができます。UNETのスキップ接続は、ダウンサンプリングによる情報の損失の問題に対処するのに役立ちます。スキップリンクにより、より優れたローカルおよびグローバルな情報の統合が可能となり、セグメンテーションのパフォーマンスが向上します。
要約すると、UNETのエンコーディング手法は、高レベルの特徴を捉え、入力画像の空間次元を低減するために重要です。エンコーディングパスは、畳み込み層、活性化関数、プーリング層を介して徐々に抽象的な表現を抽出します。スキップリンクを導入することで、ローカルな特徴とグローバルなコンテキストを統合することができ、重要な空間情報を保存し、信頼性のあるセグメンテーション結果を容易にします。
UNETのデコーディングパス
UNETアーキテクチャの重要な要素であるデコーディングパス、または拡張パスは、エンコーディングパスの特徴マップをアップサンプリングし、最終的なセグメンテーションマスクを構築する責任があります。
アップサンプリング層(転置畳み込み)
特徴マップの空間解像度を向上させるために、UNETのデコーディング手法にはアップサンプリング層が含まれており、これは頻繁に転置畳み込みまたは逆畳み込みを使用して行われます。転置畳み込みは通常の畳み込みの逆の効果を持ちます。空間次元を増加させるために空間次元を減少させるのではなく、アップサンプリングを可能にします。疎なカーネルを構築し、それを入力特徴マップに適用することで、転置畳み込みは特徴マップをアップサンプリングする方法を学習します。ネットワークはこのプロセス中に現在の空間位置の間のギャップを埋める方法を学習し、特徴マップの解像度を向上させることができます。
連結
前の層からの特徴マップは、デコーディングフェーズ中にアップサンプリングされた特徴マップと連結されます。この連結により、ネットワークは正確なセグメンテーションのためにマルチスケールの情報を集約し、高レベルのコンテキストと低レベルの特徴を活用することができます。アップサンプリングに加えて、UNETのデコーディングパスには、エンコーディングパスの同等のレベルからのスキップ接続も含まれています。
スキップ接続からの特徴マップの連結により、エンコーディング中に失われた細かい特徴を回復して統合することができます。これにより、セグメンテーションマスク内でのより正確なオブジェクトの位置特定と区別が可能になります。
UNETのデコーディングプロセスは、特徴マップを徐々にアップサンプリングし、スキップリンクを含めて入力画像の空間解像度に合わせた密なセグメンテーションマップを再構築します。
デコーディングパスの役割は、エンコーディングパスで失われた空間情報を回復し、セグメンテーションの結果を洗練させることです。アップサンプリング層からの低レベルのエンコーディングの詳細を、エンコーディングパスの同等のマップと統合することで、高いレベルのコンテキストと細かい空間情報を組み合わせ、正確かつ詳細なセグメンテーションマスクを提供します。
UNETは、デコーディングプロセスで転置畳み込みを使用することで、特徴マップの空間解像度を向上させ、元の画像サイズにアップサンプリングすることができます。転置畳み込みは、ギャップを埋め、空間次元を拡大する方法を学習することで、密な細かいセグメンテーションマスクを生成するのにネットワークを支援します。
要約すると、UNETのデコーディングプロセスは、アップサンプリング層とスキップ接続を介して特徴マップの空間解像度を向上させ、セグメンテーションマスクを再構築します。転置畳み込みはこのフェーズで重要であり、特徴マップをアップサンプリングし、元の入力画像に合う詳細なセグメンテーションマスクを構築することができます。
UNETの収縮パスと拡張パス
UNETアーキテクチャは、「エンコーダ-デコーダ」構造を採用しており、収縮パスがエンコーダを表し、拡張パスがデコーダを表します。このデザインは、情報を圧縮形式にエンコードし、元のデータを再構築することに似ています。
収縮パス(エンコーダ)
UNETのエンコーダは収縮パスです。エンコーダは、畳み込み層に続くプーリングなどの手法を使用して、入力画像のコンテキストを抽出し、空間次元を徐々に減少させることで、入力画像を圧縮します。この手法では、高レベルの特性を取得し、グローバルなコンテキストを学習し、空間解像度を低下させることが目的です。セグメンテーションのために関連する情報を効率的にキャプチャするために、入力画像を圧縮して抽象化することに重点が置かれます。
拡張パス(デコーダ)
UNETのデコーダは拡張パスです。収縮パスからの特徴マップをアップサンプリングすることで、空間情報を回復し、最終的なセグメンテーションマップを生成します。拡張パスには、アップサンプリング層が含まれており、これは通常、転置畳み込みまたは逆畳み込みを使用して行われます。拡張パスは、アップサンプリングされた特徴マップを収縮パスの同等のマップと統合することで、元の空間次元を再構築します。この方法により、ネットワークは細かい特徴を回復し、アイテムを適切にローカライズすることができます。
UNETの設計は、収縮パスと拡張パスを組み合わせて、グローバルなコンテキストとローカルな詳細をキャプチャします。収縮パスは、入力画像をコンパクトな表現に圧縮し、拡張パスによって詳細なセグメンテーションマップを構築するために設計されています。拡張パスは、圧縮された表現を密で正確なセグメンテーションマップに復元することに焦点を当てています。欠落している空間情報を再構築し、セグメンテーション結果を洗練させます。このエンコーダ-デコーダ構造により、高レベルのコンテキストと細かい空間情報を使用した正確なセグメンテーションが可能になります。
UNETの収縮経路と拡張経路は「エンコーダ-デコーダ」構造に似ています。拡張経路はデコーダであり、空間情報を回復し、最終的なセグメンテーションマップを生成します。対照的に、収縮経路はエンコーダとして機能し、コンテキストをキャプチャし、入力画像を圧縮します。このアーキテクチャにより、UNETは情報を効果的にエンコードおよびデコードし、正確かつ完全な画像セグメンテーションを可能にします。
UNETのスキップ接続
スキップ接続は、収縮(エンコーディング)経路と拡張(デコーディング)経路の間で情報をやりとりするために、UNETの設計において重要です。これらは、空間情報を維持し、セグメンテーションの精度を向上させるために重要です。
空間情報の保持
フィーチャーマップが最大プーリングなどのダウンサンプリング手法を経る過程で、エンコーディング経路で一部の空間情報が失われる可能性があります。この情報の損失は、位置特定の精度の低下やセグメンテーションマスクの詳細な情報の喪失を引き起こす可能性があります。
エンコーディングとデコーディングのプロセス間の対応するレイヤー間に直接の接続を確立することで、スキップ接続はこの問題に対処するのに役立ちます。スキップ接続は、ダウンサンプリング中に失われるはずの重要な空間情報を保護します。これらの接続により、エンコーディングストリームの情報はダウンサンプリングを回避してデコーディング経路に直接伝達されることが可能となります。
マルチスケール情報の統合
スキップ接続により、多くのネットワーク層からのマルチスケール情報の統合が可能となります。エンコーディングプロセスの後のレベルでは、高レベルのコンテキストと意味情報が捉えられますが、より早いレイヤーではローカルな詳細や細かい情報が捉えられます。UNETは、エンコーディング経路のフィーチャーマップをデコーディング経路の対応するレイヤーに接続することで、ローカルとグローバルな情報を組み合わせることができます。このマルチスケール情報の統合により、セグメンテーションの精度が全体的に向上します。ネットワークはエンコーディング経路からの低レベルのデータを使用して、デコーディング経路でのセグメンテーションの結果を洗練させることができます。これにより、より正確な位置特定とオブジェクトの境界定義が可能となります。
高レベルのコンテキストと低レベルの詳細の組み合わせ
スキップ接続により、デコーディング経路は高レベルのコンテキストと低レベルの詳細を組み合わせることができます。スキップ接続からの連結されたフィーチャーマップには、デコーディング経路のアップサンプルされたフィーチャーマップとエンコーディング経路のフィーチャーマップが含まれます。
この組み合わせにより、ネットワークはデコーディング経路で記録された高レベルのコンテキストとエンコーディング経路で捉えられた細かい特徴を利用することができます。ネットワークはさまざまなサイズの情報を組み込むことができ、より正確で詳細なセグメンテーションが可能となります。
スキップ接続を追加することにより、UNETはマルチスケール情報を利用し、空間的な詳細を保持し、高レベルのコンテキストと低レベルの詳細を統合することができます。その結果、セグメンテーションの精度が向上し、オブジェクトの位置特定が改善され、セグメンテーションマスクの細かい情報が保持されます。
UNETのスキップ接続は、空間情報の維持、マルチスケール情報の統合、セグメンテーションの精度向上において重要です。エンコーディング経路とデコーディング経路を横断する直接的な情報フローを提供し、ネットワークがローカルとグローバルの詳細を収集することで、より正確で詳細な画像セグメンテーションが可能となります。
UNETにおける損失関数
UNETをトレーニングし、そのパラメータを画像セグメンテーションのタスクに最適化する際には、適切な損失関数を選択することが重要です。UNETでは、Dice係数やクロスエントロピー損失などのセグメンテーションに適した損失関数がよく使用されます。
Dice係数損失
Dice係数は、予測されたセグメンテーションマスクと真のセグメンテーションマスクの重なりを計算する類似性統計量です。Dice係数損失、またはソフトDice損失は、Dice係数から1を減算して計算されます。予測されたマスクと正しいマスクがよく一致する場合、損失は最小化され、Dice係数は高くなります。
Dice係数損失は、背景クラスのピクセルが多いようなバランスのとれていないデータセットに特に効果的です。偽陽性と偽陰性をペナルティとして課すことで、ネットワークは正確に前景と背景領域を分割するよう促されます。
クロスエントロピー損失
画像セグメンテーションのタスクでは、クロスエントロピー損失関数を使用します。これは、予測されたクラスの確率と真のクラスラベルとの不一致を測定します。画像セグメンテーションでは、各ピクセルを独立した分類問題として扱い、クロスエントロピー損失はピクセルごとに計算されます。
クロスエントロピー損失は、ネットワークが各ピクセルに対して正しいクラスラベルに高い確率を割り当てるよう促すことです。真の値からの逸脱にペナルティを与え、正確なセグメンテーション結果を促進します。この損失関数は、前景と背景クラスがバランスしている場合や、セグメンテーションタスクに複数のクラスが関与している場合に効果的です。
Dice係数の損失とクロスエントロピー損失の選択は、セグメンテーションタスクの具体的な要件とデータセットの特性に依存します。両方の損失関数には利点があり、特定のニーズに応じて組み合わせたりカスタマイズしたりすることができます。
1: ライブラリのインポート
import tensorflow as tf import os import numpy as np from tqdm import tqdm from skimage.io import imread, imshow from skimage.transform import resize import matplotlib.pyplot as plt import random
2: 画像の寸法 – 設定
IMG_WIDTH = 128 IMG_HEIGHT = 128 IMG_CHANNELS = 3
3: ランダム性の設定
seed = 42 np.random.seed = seed
4: データセットのインポート
# データはここからダウンロードする - https://www.kaggle.com/competitions/data-science-bowl-2018/data # データセットのインポート TRAIN_PATH = 'stage1_train/' TEST_PATH = 'stage1_test/'
5: サブフォルダ内のすべての画像の読み込み
train_ids = next(os.walk(TRAIN_PATH))[1] test_ids = next(os.walk(TEST_PATH))[1]
6: トレーニング
X_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8) Y_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)
7: 画像のリサイズ
print('トレーニング用の画像とマスクのリサイズ') for n, id_ in tqdm(enumerate(train_ids), total=len(train_ids)): path = TRAIN_PATH + id_ img = imread(path + '/images/' + id_ + '.png')[:,:,:IMG_CHANNELS] img = resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True) X_train[n] = img #imgの値で空のX_trainを埋める mask = np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool) for mask_file in next(os.walk(path + '/masks/'))[2]: mask_ = imread(path + '/masks/' + mask_file) mask_ = np.expand_dims(resize(mask_, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True), axis=-1) mask = np.maximum(mask, mask_) Y_train[n] = mask
8: 画像のテスト
# テスト用の画像 X_test = np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8) sizes_test = [] print('テスト用の画像のリサイズ') for n, id_ in tqdm(enumerate(test_ids), total=len(test_ids)): path = TEST_PATH + id_ img = imread(path + '/images/' + id_ + '.png')[:,:,:IMG_CHANNELS] sizes_test.append([img.shape[0], img.shape[1]]) img = resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True) X_test[n] = img print('完了!')
9: 画像のランダムチェック
image_x = random.randint(0, len(train_ids)) imshow(X_train[image_x]) plt.show() imshow(np.squeeze(Y_train[image_x])) plt.show()
10: モデルの構築
inputs = tf.keras.layers.Input((IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS)) s = tf.keras.layers.Lambda(lambda x: x / 255)(inputs)
11: パス
# 収縮パス c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(s) c1 = tf.keras.layers.Dropout(0.1)(c1) c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1) p1 = tf.keras.layers.MaxPooling2D((2, 2))(c1) c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1) c2 = tf.keras.layers.Dropout(0.1)(c2) c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2) p2 = tf.keras.layers.MaxPooling2D((2, 2))(c2) c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p2) c3 = tf.keras.layers.Dropout(0.2)(c3) c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3) p3 = tf.keras.layers.MaxPooling2D((2, 2))(c3) c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p3) c4 = tf.keras.layers.Dropout(0.2)(c4) c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4) p4 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(c4) c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p4) c5 = tf.keras.layers.Dropout(0.3)(c5) c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5)
12: エクスパンションパス
u6 = tf.keras.layers.Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5) u6 = tf.keras.layers.concatenate([u6, c4]) c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u6) c6 = tf.keras.layers.Dropout(0.2)(c6) c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6) u7 = tf.keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6) u7 = tf.keras.layers.concatenate([u7, c3]) c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u7) c7 = tf.keras.layers.Dropout(0.2)(c7) c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7) u8 = tf.keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7) u8 = tf.keras.layers.concatenate([u8, c2]) c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u8) c8 = tf.keras.layers.Dropout(0.1)(c8) c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8) u9 = tf.keras.layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8) u9 = tf.keras.layers.concatenate([u9, c1], axis=3) c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u9) c9 = tf.keras.layers.Dropout(0.1)(c9) c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)
13: 出力
outputs = tf.keras.layers.Conv2D(1, (1, 1), activation='sigmoid')(c9)
14: サマリー
model = tf.keras.Model(inputs=[inputs], outputs=[outputs]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) model.summary()
15: モデルチェックポイント
checkpointer = tf.keras.callbacks.ModelCheckpoint('model_for_nuclei.h5', verbose=1, save_best_only=True) callbacks = [ tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'), tf.keras.callbacks.TensorBoard(log_dir='logs')] results = model.fit(X_train, Y_train, validation_split=0.1, batch_size=16, epochs=25, callbacks=callbacks)
16: 最終ステージ – 予測
idx = random.randint(0, len(X_train)) preds_train = model.predict(X_train[:int(X_train.shape[0]*0.9)], verbose=1) preds_val = model.predict(X_train[int(X_train.shape[0]*0.9):], verbose=1) preds_test = model.predict(X_test, verbose=1) preds_train_t = (preds_train > 0.5).astype(np.uint8) preds_val_t = (preds_val > 0.5).astype(np.uint8) preds_test_t = (preds_test > 0.5).astype(np.uint8) # ランダムなトレーニングサンプルでのサニティチェックを実行する ix = random.randint(0, len(preds_train_t)) imshow(X_train[ix]) plt.show() imshow(np.squeeze(Y_train[ix])) plt.show() imshow(np.squeeze(preds_train_t[ix])) plt.show() # ランダムなバリデーションサンプルでのサニティチェックを実行する ix = random.randint(0, len(preds_val_t)) imshow(X_train[int(X_train.shape[0]*0.9):][ix]) plt.show() imshow(np.squeeze(Y_train[int(Y_train.shape[0]*0.9):][ix])) plt.show() imshow(np.squeeze(preds_val_t[ix])) plt.show()
結論
この包括的なブログ記事では、画像セグメンテーションのためのUNETアーキテクチャについて説明しました。先行手法の制約に対処することで、UNETアーキテクチャは画像セグメンテーションを革新しました。エンコーディングとデコーディングの経路、スキップ接続、U-Net++、Attention U-Net、Dense U-Netなどの他の改良点は、コンテキストのキャプチャ、空間情報の維持、セグメンテーションの精度向上に非常に効果的であることが証明されています。UNETによる正確で自動的なセグメンテーションの可能性は、コンピュータビジョンの向上やその先に新たな展開の道を切り開いています。読者にはUNETについてさらに学び、その実装を試して、画像セグメンテーションプロジェクトでの有用性を最大限に引き出すことをお勧めします。
キーポイント
1. 画像セグメンテーションは、画像を意味のある領域やオブジェクトに分割することで、コンピュータビジョンのタスクにおいて重要です。
2. 手動注釈やピクセル単位の分類など、従来の画像セグメンテーションのアプローチは、効率と精度の面で制約があります。
3. これらの制約に対処し、正確なセグメンテーション結果を達成するためにUNETアーキテクチャを開発しました。
4. UNETは、高レベルの特徴を捉えるためのエンコーディングパスと、セグメンテーションマスクを生成するためのデコーディング手法を組み合わせた完全畳み込みニューラルネットワーク(FCN)です。
5. UNETのスキップ接続は、空間情報を保持し、特徴の伝播を強化し、セグメンテーションの精度を向上させます。
6. 医療画像、衛星画像解析、産業品質管理などで成功した応用があり、競技会での注目すべき成果と認識を達成しています。
よくある質問
この記事で表示されるメディアはAnalytics Vidhyaの所有ではなく、著者の裁量で使用されています。
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles