image-to-image 回帰用のデータストアの準備
この例では、ImageDatastore
の関数 transform
および combine
を使用して image-to-image 回帰ネットワークの学習用のデータストアを準備する方法を説明します。
この例では、ノイズ除去ネットワークの学習に適したパイプラインを使用してデータの前処理を行う方法を説明します。その後、前処理したノイズ データを使用して、シンプルな畳み込み自己符号化器ネットワークにイメージ ノイズを削除することを学習させます。
前処理パイプラインを使用したデータの準備
この例では、入力イメージの各ピクセルが 0
または 1
(それぞれ黒および白) に設定されているごま塩ノイズ モデルを使用します。ノイズを含むイメージはネットワーク入力となります。初期状態のイメージは予測されるネットワーク応答となります。ネットワークはごま塩ノイズを検出して削除することを学習します。
データの読み込み
数字のデータ セット内の初期状態のイメージを imageDatastore
として読み込みます。データストアには、0 ~ 9 の数字から成る 10,000 個の合成イメージが格納されています。イメージは、さまざまなフォントを使用して作成された数字のイメージにランダム変換を適用して生成されたものです。数字のイメージはそれぞれ 28 x 28 ピクセルです。データストアには、カテゴリごとに同じ数のイメージが含まれます。
digitDatasetPath = fullfile(matlabroot,"toolbox","nnet", ... "nndemos","nndatasets","DigitDataset"); imds = imageDatastore(digitDatasetPath, ... IncludeSubfolders=true,LabelSource="foldernames");
大きい読み込みサイズを指定して、ファイル I/O のコストを最小化します。
imds.ReadSize = 500;
学習、検証、テストに向けたデータ分割
学習前に関数 shuffle
を使用して数字データをシャッフルします。
rng("default")
imds = shuffle(imds);
関数 splitEachLabel
を使用して、imds
を学習、検証、およびテスト用の初期状態のイメージが含まれる 3 つのイメージ データストアに分割します。
[imdsTrain,imdsVal,imdsTest] = splitEachLabel(imds,0.95,0.025);
入力イメージへの合成ノイズの付加
この例では、ネットワーク入力として機能する各入力イメージに合成ノイズを付加します。関数imnoise
(Image Processing Toolbox)を使用してイメージにごま塩ノイズを追加する addNoise
という補助関数を定義します。関数 addNoise
では、入力データの形式がイメージ データの cell 配列であることが必要です。これは、ImageDatastore
の関数 read
によって返されるデータの形式と一致します。
function dataOut = addNoise(data) dataOut = data; for idx = 1:size(data,1) dataOut{idx} = imnoise(data{idx},"salt & pepper"); end end
関数transform
を使用して、入力イメージにノイズを適用します。関数 transform
は、基になるデータストアからデータを読み取り、関数 addNoise
で定義されている演算を使用してデータを処理します。関数 transform
の出力は TransformedDatastore
です。
dsTrainNoisy = transform(imdsTrain,@addNoise); dsValNoisy = transform(imdsVal,@addNoise); dsTestNoisy = transform(imdsTest,@addNoise);
データの前処理
関数combine
を使用して、ノイズを含むイメージと初期状態のイメージを組み合わせて trainnet
にデータを供給する単一のデータストアにします。このデータストアの組み合わせは、データのバッチを trainnet
で期待される 2 列の cell 配列に読み取ります。関数 combine
の出力は CombinedDatastore
です。
dsTrain = combine(dsTrainNoisy,imdsTrain); dsVal = combine(dsValNoisy,imdsVal); dsTest = combine(dsTestNoisy,imdsTest);
入力イメージと応答イメージのペアを前処理する commonPreprocessing
という補助関数を定義します。この補助関数は以下の前処理手順を実行します。
この補助関数では、入力データの形式がイメージ データの 2 列の cell 配列であることが必要です。これは、CombinedDatastore
の関数 read
によって返されるデータの形式と一致します。
function dataOut = commonPreprocessing(data) dataOut = cell(size(data)); for col = 1:size(data,2) for idx = 1:size(data,1) temp = single(data{idx,col}); temp = imresize(temp,[32,32]); temp = rescale(temp); dataOut{idx,col} = temp; end end end
関数 transform
を使用して、入力データと応答データに前処理を適用します。学習データ セット、検証データ セット、およびテスト データ セットを前処理します。
dsTrain = transform(dsTrain,@commonPreprocessing); dsVal = transform(dsVal,@commonPreprocessing); dsTest = transform(dsTest,@commonPreprocessing);
学習データの拡張
拡張は過適合を抑え、学習済みネットワークに存在する回転のロバスト性を高めます。検証データ セットまたはテスト データ セットにはランダム化された拡張は不要です。
関数rot90
を使用して、ランダムな 90 度の回転をデータに追加する augmentImages
という補助関数を定義します。ネットワーク入力とそれに対応する予測される応答に同一の回転が適用されます。この関数では、入力データの形式がイメージ データの 2 列の cell 配列であることが必要です。これは、CombinedDatastore
の関数 read
によって返されるデータの形式と一致します。
function dataOut = augmentImages(data) dataOut = cell(size(data)); for idx = 1:size(data,1) rot90Val = randi(4,1,1)-1; dataOut(idx,:) = {rot90(data{idx,1},rot90Val), ... rot90(data{idx,2},rot90Val)}; end end
関数 transform
を使用して、ランダム化された拡張を学習データに適用します。
dsTrain = transform(dsTrain,@augmentImages);
前処理したデータのプレビュー
学習データの準備に必要な前処理演算はいくつかあるため、学習前に前処理したデータをプレビューして正しいことを確認します。関数 preview
を使用してデータをプレビューします。
関数 montage
(Image Processing Toolbox) を使用して、ノイズを含むイメージと初期状態のイメージのペアの例を可視化します。学習データは正しいようです。左列の入力イメージにはごま塩ノイズが見られます。ノイズが追加されている点を除き、入力イメージと応答イメージは同じです。入力イメージと応答イメージにはランダムな 90 度の回転が同じ方法で適用されています。
exampleData = preview(dsTrain);
inputs = exampleData(:,1);
responses = exampleData(:,2);
minibatch = cat(2,inputs,responses);
montage(minibatch',Size=[8 2])
title("Inputs (Left) and Responses (Right)")
畳み込み自己符号化器ネットワークの定義
畳み込み自己符号化器は、イメージのノイズ除去を行うための一般的なアーキテクチャです。畳み込み自己符号化器は、符号化器と復号化器という 2 つの段階で構成されます。符号化器は、元の入力イメージを圧縮して潜在表現にします。この潜在表現では、元の入力イメージより幅と高さが小さくなっている一方で、空間位置あたりの特徴マップ数が多いという点で深度が大きくなっています。圧縮された潜在表現は、元のイメージの高周波数の特徴を復元する際に空間分解能がいくらか低下しますが、元のイメージの符号化でノイズ アーティファクトを含めないように学習しています。復号化器は、符号化された信号を繰り返しアップサンプリングして、元の幅、高さ、チャネル数に戻します。符号化器はノイズを除去するため、復号化された最終的なイメージに含まれるノイズ アーティファクトは少なくなります。
この例では、以下を含む Deep Learning Toolbox™ の層を使用して畳み込み自己符号化器ネットワークを定義します。
imageInputLayer
- イメージ入力層convolution2dLayer
- 畳み込みニューラル ネットワーク用の畳み込み層reluLayer
- 正規化線形ユニット層maxPooling2dLayer
- 2 次元最大プーリング層transposedConv2dLayer
- 転置畳み込み層clippedReluLayer
- クリップされた正規化線形ユニット層
イメージ入力層を作成します。係数 2 によるダウンサンプリングとアップサンプリングに関連するパディングの問題を簡略化するには、入力サイズに 32 行 32 列を選択します。これは、32 が 2、4、および 8 できれいに割り切れるためです。
imageLayer = imageInputLayer([32,32,1]);
符号化層を作成します。符号化器でのダウンサンプリングは、プール サイズ 2 およびストライド 2 で最大プーリングを行うことによって実行できます。
encodingLayers = [ ... convolution2dLayer(3,8,Padding="same"), ... reluLayer, ... maxPooling2dLayer(2,Padding="same",Stride=2), ... convolution2dLayer(3,16,Padding="same"), ... reluLayer, ... maxPooling2dLayer(2,Padding="same",Stride=2), ... convolution2dLayer(3,32,Padding="same"), ... reluLayer, ... maxPooling2dLayer(2,Padding="same",Stride=2)];
復号化層を作成します。復号化器は、ストライドが 2 の転置畳み込み層を使用して、符号化された信号を係数 2 でアップサンプリングします。ネットワークは clippedReluLayer
を最終的な活性化層として使用して、出力の範囲を [0, 1] にします。
decodingLayers = [ ... transposedConv2dLayer(2,32,Stride=2), ... reluLayer, ... transposedConv2dLayer(2,16,Stride=2), ... reluLayer, ... transposedConv2dLayer(2,8,Stride=2), ... reluLayer, ... convolution2dLayer(1,1,Padding="same"), ... clippedReluLayer(1.0)];
イメージ入力層、符号化層、および復号化層を連結して、畳み込み自己符号化器ネットワーク アーキテクチャを形成します。
layers = [imageLayer,encodingLayers,decodingLayers];
学習オプションの定義
Adam 最適化を使用してネットワークに学習させます。関数trainingOptions
を使用してハイパーパラメーター設定を指定します。学習を 50 エポック行います。
options = trainingOptions("adam", ... MaxEpochs=50, ... MiniBatchSize=imds.ReadSize, ... ValidationData=dsVal, ... ValidationPatience=5, ... Plots="training-progress", ... OutputNetwork="best-validation", ... Verbose=false);
ネットワークの学習
関数trainnet
を使用してニューラル ネットワークに学習させます。回帰の場合は、平均二乗誤差損失を使用します。既定では、関数 trainnet
は利用可能な GPU がある場合にそれを使用します。GPU での学習には、Parallel Computing Toolbox™ ライセンスとサポートされている GPU デバイスが必要です。サポートされているデバイスについては、GPU 計算の要件 (Parallel Computing Toolbox)を参照してください。そうでない場合、関数 trainnet
は CPU を使用します。実行環境を指定するには、ExecutionEnvironment
学習オプションを使用します。学習には NVIDIA Titan XP で約 15 分を要します。
net = trainnet(dsTrain,layers,"mse",options);
modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss")); save("trainedImageToImageRegressionNet-"+modelDateTime+".mat","net");
ノイズ除去ネットワークの性能の評価
関数minibatchpredict
を使用して、テスト セットから出力イメージを取得します。
ypred = minibatchpredict(net,dsTest);
関数 preview
を使用して、ノイズを含むイメージと初期状態のイメージのペアをテスト セットから取得します。
testBatch = preview(dsTest);
サンプル入力イメージと、ネットワークからの関連する予測出力を可視化して、ノイズ除去がどれだけ適切に機能しているかを把握します。予測どおり、ネットワークからの出力イメージでは、入力イメージのノイズ アーティファクトの大部分が除去されています。符号化および復号化プロセスの結果、ノイズ除去後のイメージは少し不鮮明になっています。
idx = 1; y = ypred(:,:,:,idx); x = testBatch{idx,1}; ref = testBatch{idx,2}; montage({x,y})
ピーク S/N 比 (PSNR) を解析することによって、ネットワークの性能を評価します。予想どおり、出力イメージの PSNR は、ノイズを含む入力イメージより高くなっています。
psnrNoisy = psnr(x,ref)
psnrNoisy = single
20.8214
psnrDenoised = psnr(y,ref)
psnrDenoised = single
21.5986
参考
trainnet
| trainingOptions
| dlnetwork
| transform
| combine
| imageDatastore