このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。
イメージ分類用の残差ネットワークの学習
この例では、残差結合のある深層学習ニューラル ネットワークを作成し、CIFAR-10 データで学習を行う方法を説明します。残差結合は畳み込みニューラル ネットワーク アーキテクチャでよく使用される要素です。残差結合を使用すると、ネットワークを通じた勾配フローが改善し、より深いネットワークの学習が可能になります。
多くの用途では、層のシンプルなシーケンスで構成されるネットワークを使用するだけで十分です。ただし、用途によっては、各層に複数の層からの入力と複数の層への出力がある、より複雑なグラフ構造のネットワークが必要です。多くの場合、これらのタイプのネットワークは有向非循環グラフ (DAG) ネットワークと呼ばれます。残差ネットワークは、メイン ネットワーク層をバイパスする残差 (またはショートカット) 結合のある DAG ネットワークの一種です。残差結合では、パラメーターの勾配がネットワークの出力層からより初期の層へとよりスムーズに伝播するため、更に深いネットワークに学習させることができます。このようにネットワークが深くなると、より難しいタスクで高い精度を実現できます。
グラフ構造を持つネットワークを作成して学習させるには、以下の手順に従います。
layerGraph
を使用してLayerGraph
オブジェクトを作成します。層グラフでネットワーク アーキテクチャが指定されます。空の層グラフを作成してから、この層グラフに層を追加できます。ネットワーク層の配列から直接、層グラフを作成することもできます。この場合、layerGraph
は配列内の層を 1 つずつ結合します。addLayers
を使用して層グラフに層を追加し、removeLayers
を使用してグラフから層を削除します。connectLayers
を使用して層を他の層に結合し、disconnectLayers
を使用して他の層から層を切り離します。plot
を使用してネットワーク アーキテクチャをプロットします。trainNetwork
を使用してネットワークに学習させます。学習済みネットワークはDAGNetwork
オブジェクトになります。classify
およびpredict
を使用して、新しいデータで分類と予測を実行します。
この例では、残差ネットワークをゼロから構築する方法を示します。関数resnetLayers
を使用して、残差ネットワークを作成することもできます。この関数を使用すると、イメージ分類タスク用の残差ネットワークを短時間で構築できます。
イメージ分類用の事前学習済みネットワークを読み込むこともできます。詳細については、事前学習済みの深層ニューラル ネットワークを参照してください。
データの準備
CIFAR-10 データセット [1] をダウンロードします。このデータセットには 60,000 個のイメージが格納されています。各イメージのサイズは 32 x 32 で 3 つのカラー チャネル (RGB) があります。データセットのサイズは 175 MB です。インターネット接続の速度によっては、ダウンロード プロセスに時間がかかることがあります。
datadir = tempdir; downloadCIFARData(datadir);
CIFAR-10 学習イメージとテスト イメージを 4 次元配列として読み込みます。学習セットには 50,000 個のイメージが格納されていて、テスト セットには 10,000 個のイメージが格納されています。CIFAR-10 テスト イメージをネットワークの検証用に使用します。
[XTrain,YTrain,XValidation,YValidation] = loadCIFARData(datadir);
次のコードを使用して、ランダムにサンプリングされた学習イメージを表示できます。
figure;
idx = randperm(size(XTrain,4),20);
im = imtile(XTrain(:,:,:,idx),'ThumbnailSize',[96,96]);
imshow(im)
ネットワーク学習に使用する augmentedImageDatastore
オブジェクトを作成します。学習中に、データストアは縦軸に沿って学習イメージをランダムに反転させ、水平方向および垂直方向に最大 4 ピクセルだけランダムに平行移動させます。データ拡張は、ネットワークで過適合が発生したり、学習イメージの正確な詳細が記憶されたりすることを防止するのに役立ちます。
imageSize = [32 32 3]; pixelRange = [-4 4]; imageAugmenter = imageDataAugmenter( ... 'RandXReflection',true, ... 'RandXTranslation',pixelRange, ... 'RandYTranslation',pixelRange); augimdsTrain = augmentedImageDatastore(imageSize,XTrain,YTrain, ... 'DataAugmentation',imageAugmenter, ... 'OutputSizeMode','randcrop');
ネットワーク アーキテクチャの定義
残差ネットワーク アーキテクチャは以下のコンポーネントで構成されます。
畳み込み層、バッチ正規化層、および ReLU 層が順に結合された主分岐。
主分岐の畳み込みユニットをバイパスする "残差結合"。残差結合と畳み込みユニットの出力は要素単位で追加されます。活性化のサイズが変化すると、残差結合も 1 x 1 畳み込み層を含まなければなりません。残差結合では、パラメーターの勾配がネットワークの出力層からより初期の層へとよりスムーズに流れるため、更に深いネットワークに学習させることができます。
主分岐の作成
まず、ネットワークの主分岐を作成します。主分岐には 5 つのセクションが含まれています。
イメージ入力層と、活性化のある初期畳み込みを含む初期セクション。
異なる特徴サイズ (32 x 32、16 x 16 および 8 x 8) を持つ畳み込み層の 3 つの段階。各段階には N 個の畳み込みユニットが含まれています。ここでは、N = 2 です。各畳み込みユニットには、活性化のある 3 x 3 畳み込み層が 2 つ含まれています。
netWidth
パラメーターはネットワークの幅であり、ネットワークの最初の段階における畳み込み層のフィルターの数として定義されます。2 番目と 3 番目の段階における最初の畳み込みユニットは、係数 2 で空間次元をダウンサンプリングします。ネットワーク全体で各畳み込み層に必要な計算量をほぼ同じに保つには、空間のダウンサンプリングを実行するたびに、フィルターの数を 2 倍ずつ増加させます。グローバル平均プーリング層、全結合層、ソフトマックス層、および分類層のある最後のセクション。
convolutionalUnit(numF,stride,tag)
を使用して畳み込みユニットを作成します。numF
は各層の畳み込みフィルターの数です。stride
はユニットの最初の畳み込み層のストライドで、tag
は層の名前の先頭に追加する文字配列です。関数 convolutionalUnit
の定義は、この例の終わりで行います。
すべての層に一意の名前を付けます。畳み込みユニットの層の名前は 'SjUk'
で始まります。ここで、j
は段階のインデックスで、k
はその段階内の畳み込みユニットのインデックスです。たとえば、'S2U1'
は段階 2、ユニット 1 を表します。
netWidth = 16; layers = [ imageInputLayer([32 32 3],'Name','input') convolution2dLayer(3,netWidth,'Padding','same','Name','convInp') batchNormalizationLayer('Name','BNInp') reluLayer('Name','reluInp') convolutionalUnit(netWidth,1,'S1U1') additionLayer(2,'Name','add11') reluLayer('Name','relu11') convolutionalUnit(netWidth,1,'S1U2') additionLayer(2,'Name','add12') reluLayer('Name','relu12') convolutionalUnit(2*netWidth,2,'S2U1') additionLayer(2,'Name','add21') reluLayer('Name','relu21') convolutionalUnit(2*netWidth,1,'S2U2') additionLayer(2,'Name','add22') reluLayer('Name','relu22') convolutionalUnit(4*netWidth,2,'S3U1') additionLayer(2,'Name','add31') reluLayer('Name','relu31') convolutionalUnit(4*netWidth,1,'S3U2') additionLayer(2,'Name','add32') reluLayer('Name','relu32') averagePooling2dLayer(8,'Name','globalPool') fullyConnectedLayer(10,'Name','fcFinal') softmaxLayer('Name','softmax') classificationLayer('Name','classoutput') ];
層配列から層グラフを作成します。layerGraph
は layers
のすべての層を順に結合します。層グラフをプロットします。
lgraph = layerGraph(layers); figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]); plot(lgraph);
残差結合の作成
畳み込みユニットの周りに残差結合を追加します。ほとんどの残差結合は、演算を実行せず、畳み込みユニットの出力に要素単位で単に追加されます。
'reluInp'
層から 'add11'
層への残差結合を作成します。層の作成時に加算層への入力数を 2 に指定しているため、層には 'in1'
および 'in2'
という名前の 2 つの入力があります。最初の畳み込みユニットの最後の層は、既に 'in1'
入力に結合されています。さらに、加算層は最初の畳み込みユニットと 'reluInp'
層の出力を合計します。
同様に、'relu11'
層を 'add12'
層の 2 番目の入力に結合します。層グラフをプロットして、層を正しく結合したことを確認します。
lgraph = connectLayers(lgraph,'reluInp','add11/in2'); lgraph = connectLayers(lgraph,'relu11','add12/in2'); figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]); plot(lgraph);
畳み込みユニットにおける層の活性化のサイズが変化した場合 (つまり、空間的にダウンサンプリングされ、チャネルの次元でアップサンプリングされた場合)、残差結合の活性化もサイズを変更しなければなりません。1 x 1 畳み込み層をそのバッチ正規化層と共に使用して、残差結合の活性化サイズを変更します。
skip1 = [ convolution2dLayer(1,2*netWidth,'Stride',2,'Name','skipConv1') batchNormalizationLayer('Name','skipBN1')]; lgraph = addLayers(lgraph,skip1); lgraph = connectLayers(lgraph,'relu12','skipConv1'); lgraph = connectLayers(lgraph,'skipBN1','add21/in2');
ネットワークの 2 番目の段階に恒等結合を追加します。
lgraph = connectLayers(lgraph,'relu21','add22/in2');
別の 1 x 1 畳み込み層をそのバッチ正規化層と共に使用して、2 番目と 3 番目の段階の間にある残差結合の活性化サイズを変更します。
skip2 = [ convolution2dLayer(1,4*netWidth,'Stride',2,'Name','skipConv2') batchNormalizationLayer('Name','skipBN2')]; lgraph = addLayers(lgraph,skip2); lgraph = connectLayers(lgraph,'relu22','skipConv2'); lgraph = connectLayers(lgraph,'skipBN2','add31/in2');
最後の恒等結合を追加し、最後の層グラフをプロットします。
lgraph = connectLayers(lgraph,'relu31','add32/in2'); figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]); plot(lgraph)
より深いネットワークの作成
任意の深さおよび幅の CIFAR-10 データの残差結合を持つ層グラフを作成するには、サポート関数 residualCIFARlgraph を使用します。
lgraph = residualCIFARlgraph(netWidth,numUnits,unitType)
は、残差結合のある CIFAR-10 データの層グラフを作成します。
netWidth
はネットワークの幅であり、ネットワークの最初にある 3 x 3 畳み込み層のフィルターの数として定義されます。numUnits
は、ネットワークの主分岐の畳み込みユニットの数です。ネットワークは 3 つの段階で構成されていて、各段階には同じ数の畳み込みユニットがあるため、numUnits
は 3 の整数倍でなければなりません。unitType
は畳み込みユニットのタイプで、"standard"
または"bottleneck"
として指定します。標準の畳み込みユニットは、2 つの 3 x 3 畳み込み層で構成されています。ボトルネック畳み込みユニットは、チャネルの次元でダウンサンプリングするための 1 x 1 の層、3 x 3 畳み込み層、およびチャネルの次元でアップサンプリングするための 1 x 1 の層の 3 つの畳み込み層で構成されています。そのため、ボトルネック畳み込みユニットにある畳み込み層の数は、標準のユニットより 50% 多くなりますが、空間的な 3 x 3 畳み込みの数の半分でしかありません。2 つのユニット タイプの計算量は同程度ですが、残差結合で伝播される特徴の合計数は、ボトルネック ユニットを使用する場合の方が 4 倍多くなります。合計の深さは、逐次畳み込み層と全結合層の最大数として定義され、標準ユニットを持つネットワークでは 2*numUnits
+ 2 となり、ボトルネック ユニットを持つネットワークでは 3*numUnits
+ 2 となります。
9 つの標準畳み込みユニット (1 つの段階あたり 3 ユニット) があり、幅が 16 の残差ネットワークを作成します。ネットワークの深さの合計は 2*9+2 = 20 です。
numUnits = 9; netWidth = 16; lgraph = residualCIFARlgraph(netWidth,numUnits,"standard"); figure('Units','normalized','Position',[0.1 0.1 0.8 0.8]); plot(lgraph)
ネットワークの学習
学習オプションの指定。ネットワークの学習を 80 エポック行います。ミニバッチ サイズに比例する学習率を選択し、60 エポック後に学習率を 10 分の 1 に下げます。検証データを使用してエポックごとに 1 回ネットワークを検証します。
miniBatchSize = 128; learnRate = 0.1*miniBatchSize/128; valFrequency = floor(size(XTrain,4)/miniBatchSize); options = trainingOptions('sgdm', ... 'InitialLearnRate',learnRate, ... 'MaxEpochs',80, ... 'MiniBatchSize',miniBatchSize, ... 'VerboseFrequency',valFrequency, ... 'Shuffle','every-epoch', ... 'Plots','training-progress', ... 'Verbose',false, ... 'ValidationData',{XValidation,YValidation}, ... 'ValidationFrequency',valFrequency, ... 'LearnRateSchedule','piecewise', ... 'LearnRateDropFactor',0.1, ... 'LearnRateDropPeriod',60);
trainNetwork
を使用してネットワークに学習させるには、doTraining
フラグを true
に設定します。そうでない場合は、事前学習済みのネットワークを読み込みます。このネットワークの学習を適切な GPU で行った場合、約 2 時間かかります。GPU がない場合、学習に長い時間がかかります。
doTraining = false; if doTraining trainedNet = trainNetwork(augimdsTrain,lgraph,options); else load('CIFARNet-20-16.mat','trainedNet'); end
学習済みネットワークの評価
学習セット (データ拡張なし) と検証セットに対するネットワークの最終精度を計算します。
[YValPred,probs] = classify(trainedNet,XValidation); validationError = mean(YValPred ~= YValidation); YTrainPred = classify(trainedNet,XTrain); trainError = mean(YTrainPred ~= YTrain); disp("Training error: " + trainError*100 + "%")
Training error: 2.862%
disp("Validation error: " + validationError*100 + "%")
Validation error: 9.76%
混同行列をプロットします。列と行の要約を使用して、各クラスの適合率と再現率を表示します。このネットワークは、猫と犬を混同することがよくあります。
figure('Units','normalized','Position',[0.2 0.2 0.4 0.4]); cm = confusionchart(YValidation,YValPred); cm.Title = 'Confusion Matrix for Validation Data'; cm.ColumnSummary = 'column-normalized'; cm.RowSummary = 'row-normalized';
次のコードを使用して、ランダムにサンプリングされた 9 つのテスト イメージを、予測されたクラスとそのクラスである確率と共に表示できます。
figure idx = randperm(size(XValidation,4),9); for i = 1:numel(idx) subplot(3,3,i) imshow(XValidation(:,:,:,idx(i))); prob = num2str(100*max(probs(idx(i),:)),3); predClass = char(YValPred(idx(i))); title([predClass,', ',prob,'%']) end
convolutionalUnit(numF,stride,tag)
は 2 つの畳み込み層と対応するバッチ正規化層および ReLU 層のある層の配列を作成します。numF
は畳み込みフィルターの数です。stride
は最初の畳み込み層のストライドで、tag
はすべての層の名前の先頭に追加されるタグです。
function layers = convolutionalUnit(numF,stride,tag) layers = [ convolution2dLayer(3,numF,'Padding','same','Stride',stride,'Name',[tag,'conv1']) batchNormalizationLayer('Name',[tag,'BN1']) reluLayer('Name',[tag,'relu1']) convolution2dLayer(3,numF,'Padding','same','Name',[tag,'conv2']) batchNormalizationLayer('Name',[tag,'BN2'])]; end
参照
[1] Krizhevsky, Alex. "Learning multiple layers of features from tiny images." (2009). https://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf
[2] He, Kaiming, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. "Deep residual learning for image recognition." In Proceedings of the IEEE conference on computer vision and pattern recognition, pp. 770-778. 2016.
参考
resnetLayers
| resnet3dLayers
| trainNetwork
| trainingOptions
| layerGraph
| analyzeNetwork