ドキュメンテーション

最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

学習可能なパラメーターを含むカスタム深層学習層の定義

目的の分類または回帰問題に必要な層が Deep Learning Toolbox™ に用意されていない場合、この例を指針として使用して独自のカスタム層を定義できます。組み込まれている層の一覧については、深層学習層の一覧を参照してください。

カスタム深層学習層を定義するために、この例で提供するテンプレートを使用できます。この例では、次のステップで説明を進めます。

  1. 層の命名 – MATLAB® で使用できるように層に名前を付けます。

  2. 層のプロパティの宣言 – 層のプロパティと、学習中にどのパラメーターが学習されるかを指定します。

  3. コンストラクター関数の作成 (オプション) – 層の構築とそのプロパティ初期化の方法を指定します。コンストラクター関数を指定しない場合、作成時に NameDescription、および Type の各プロパティは [] で初期化され、層の入力および出力の数は 1 に設定されます。

  4. 順方向関数の作成 – 予測時および学習時に層経由でデータを順方向にパス (順伝播) する方法を指定します。

  5. 逆方向関数の作成 – 入力データおよび学習可能なパラメーターにおける損失の微分を指定します (逆伝播)。

この例では、学習可能なパラメーターを含む層である PReLU 層を作成し、畳み込みニューラル ネットワークで使用する方法を説明します。PReLU 層は、しきい値処理を実行し、各チャネルで、入力値がゼロよりも小さい場合は、学習時に得られたスカラーによって乗算されます。[1]0 より小さい値の場合、PReLU 層は入力の各チャネルにスケーリング係数 αi を適用します。これらの係数は学習可能なパラメーターとなり、学習時にその層によって学習されます。

[1] から抜粋した次の図は、ReLU 層の関数と PReLU 層の関数を比較したものです。

学習可能なパラメーターを含む層のテンプレート

学習可能なパラメーターを含む層のテンプレートを MATLAB の新しいファイルにコピーします。このテンプレートは、学習可能なパラメーターを含む層の構造の概要を示しており、層の動作を定義する関数を含んでいます。

classdef myLayer < nnet.layer.Layer

    properties
        % (Optional) Layer properties.

        % Layer properties go here.
    end

    properties (Learnable)
        % (Optional) Layer learnable parameters.

        % Layer learnable parameters go here.
    end
    
    methods
        function layer = myLayer()
            % (Optional) Create a myLayer.
            % This function must have the same name as the class.

            % Layer constructor function goes here.
        end
        
        function [Z1, …, Zm] = predict(layer, X1, …, Xn)
            % Forward input data through the layer at prediction time and
            % output the result.
            %
            % Inputs:
            %         layer       - Layer to forward propagate through
            %         X1, ..., Xn - Input data
            % Outputs:
            %         Z1, ..., Zm - Outputs of layer forward function
            
            % Layer forward function for prediction goes here.
        end

        function [Z1, …, Zm, memory] = forward(layer, X1, …, Xn)
            % (Optional) Forward input data through the layer at training
            % time and output the result and a memory value.
            %
            % Inputs:
            %         layer       - Layer to forward propagate through
            %         X1, ..., Xn - Input data
            % Outputs:
            %         Z1, ..., Zm - Outputs of layer forward function
            %         memory      - Memory value for backward propagation

            % Layer forward function for training goes here.
        end

        function [dLdX1, …, dLdXn, dLdW1, …, dLdWk] = ...
                backward(layer, X1, …, Xn, Z1, …, Zm, dLdZ1, …, dLdZm, memory)
            % Backward propagate the derivative of the loss function through 
            % the layer.
            %
            % Inputs:
            %         layer             - Layer to backward propagate through
            %         X1, ..., Xn       - Input data
            %         Z1, ..., Zm       - Outputs of layer forward function            
            %         dLdZ1, ..., dLdZm - Gradients propagated from the next layers
            %         memory            - Memory value from forward function
            % Outputs:
            %         dLdX1, ..., dLdXn - Derivatives of the loss with respect to the
            %                             inputs
            %         dLdW1, ..., dLdWk - Derivatives of the loss with respect to each
            %                             learnable parameter
            
            % Layer backward function goes here.
        end
    end
end

層の命名

まず、層に名前を付けます。クラス ファイルの最初の行で、既存の名前 myLayerpreluLayer に置き換えます。

classdef preluLayer < nnet.layer.Layer
    ...
end

次に、コンストラクター関数 myLayer (methods セクションの最初の関数) の名前を層と同じ名前に変更します。

    methods
        function layer = preluLayer()           
            ...
        end

        ...
     end

層の保存

層のクラス ファイルを preluLayer.m という名前の新しいファイルに保存します。このファイル名は層の名前に一致しなければなりません。この層を使用するには、このファイルを現在のフォルダーまたは MATLAB パス上のフォルダーに保存しなければなりません。

プロパティおよび学習可能なパラメーターの宣言

層のプロパティを properties セクションで宣言し、学習可能なパラメーターを properties (Learnable) セクションにリストすることによって宣言します。

既定では、カスタム中間層には次のプロパティがあります。

Property説明
Name 層の名前。文字ベクトルまたは string スカラーとして指定します。層グラフに層を含めるには、空ではない一意の層の名前を指定しなければなりません。この層が含まれる系列ネットワークに学習させて Name'' に設定すると、学習時に層に名前が自動的に割り当てられます。
Description

層についての 1 行の説明。文字ベクトルまたは string スカラーとして指定します。この説明は、層が Layer 配列に表示されるときに表示されます。層の説明を指定しない場合、層のクラス名が表示されます。

Type層のタイプ。文字ベクトルまたは string スカラーとして指定します。Type の値は、層が Layer 配列に表示されるときに表示されます。層のタイプを指定しない場合、層のクラス名が表示されます。
NumInputs層の入力の数。正の整数として指定します。この値を指定しない場合、NumInputsInputNames の名前の数に自動的に設定されます。既定値は 1 です。
InputNames層の入力の名前。文字ベクトルの cell 配列として指定します。この値を指定せず、NumInputs が 1 より大きい場合、InputNames{'in1',...,'inN'} に自動的に設定されます。ここで、N = NumInputs です。既定値は {'in'} です。
NumOutputs層の出力の数。正の整数として指定します。この値を指定しない場合、NumOutputsOutputNames の名前の数に自動的に設定されます。既定値は 1 です。
OutputNames層の出力の名前。文字ベクトルの cell 配列として指定します。この値を指定せず、NumOutputs が 1 より大きい場合、OutputNames{'out1',...,'outM'} に自動的に設定されます。ここで、M = NumOutputs です。既定値は {'out'} です。

層にその他のプロパティがない場合は、properties セクションを省略できます。

ヒント

複数の入力がある層を作成する場合、層のコンストラクターで NumInputs または InputNames のいずれかを設定しなければなりません。複数の出力がある層を作成する場合、層のコンストラクターで NumOutputs または OutputNames のいずれかを設定しなければなりません。たとえば、複数の入力があるカスタム深層学習層の定義を参照してください。

PReLU 層には追加のプロパティが必要ないため、properties セクションは削除できます。

PReLU 層には、唯一の学習可能なパラメーターとしてスケーリング係数 a があります。この学習可能なパラメーターを properties (Learnable) セクションで宣言し、パラメーター Alpha を呼び出します。

    properties (Learnable)
        % Layer learnable parameters
            
        % Scaling coefficient
        Alpha
    end

コンストラクター関数の作成

層を構築する関数を作成し、層のプロパティを初期化します。層を作成するために必要な変数をコンストラクター関数への入力として指定します。

PReLU 層のコンストラクター関数に必要な 2 つの入力引数は、想定される入力データのチャネル数と層の名前です。チャネルの数は学習可能なパラメーター Alpha のサイズを指定します。numChannels および name という名前の 2 つの入力引数を関数 preluLayer に指定します。関数の構文を説明するコメントを関数の上部に追加します。

        function layer = preluLayer(numChannels, name)
            % layer = preluLayer(numChannels) creates a PReLU layer with
            % numChannels channels and specifies the layer name.

            ...
        end

層のプロパティの初期化

コンストラクター関数に学習可能なパラメーターを含め、層のプロパティを初期化します。コメント % Layer constructor function goes here を、層のプロパティを初期化するコードに置き換えます。

Name プロパティを入力引数 name に設定します。

            % Set layer name.
            layer.Name = name;

層の Description プロパティを設定して、層に 1 行の説明を指定します。層のタイプとサイズの説明を設定します。

            % Set layer description.
            layer.Description = "PReLU with " + numChannels + " channels";

PReLU 層の場合、入力値が負であれば、層は入力の各チャネルに Alpha の対応するチャネルを乗算します。学習可能なパラメーター Alpha をサイズ 1 x 1 x numChannels のランダム ベクトルに初期化します。3 つ目の次元をサイズ numChannels に指定することで、層は順方向関数で入力の要素単位の乗算を使用できます。Alpha は層オブジェクトのプロパティであるため、ベクトルを layer.Alpha に割り当てなければなりません。

            % Initialize scaling coefficient.
            layer.Alpha = rand([1 1 numChannels]);

完成したコンストラクター関数を表示します。

        function layer = preluLayer(numChannels, name) 
            % layer = preluLayer(numChannels, name) creates a PReLU layer
            % with numChannels channels and specifies the layer name.

            % Set layer name.
            layer.Name = name;

            % Set layer description.
            layer.Description = "PReLU with " + numChannels + " channels";
        
            % Initialize scaling coefficient.
            layer.Alpha = rand([1 1 numChannels]); 
        end

このコンストラクター関数を使用すると、コマンド preluLayer(3,'prelu') により 3 つのチャネルを持ち、名前が 'prelu' の PReLU 層が作成されます。

順方向関数の作成

予測時と学習時に使用する層の順方向関数を作成します。

"予測時" に層経由でデータを順伝播させて結果を出力する、predict という名前の関数を作成します。

predict の構文は、以下のとおりです。

[Z1,…,Zm] = predict(layer,X1,…,Xn)
ここで、X1,…,Xnn 個の層入力、Z1,…,Zmm 個の層出力です。値 n および m は、層の NumInputs プロパティおよび NumOutputs プロパティに対応しなければなりません。

ヒント

predict への入力の数が変化する可能性がある場合、X1,…,Xn ではなく varargin を使用します。この場合、varargin は入力の cell 配列です。ここで、varargin{i}Xi に対応します。出力の数が変化する可能性がある場合、Z1,…,Zm ではなく varargout を使用します。この場合、varargout は出力の cell 配列です。ここで、varargout{j}Zj に対応します。

PReLU 層の入力および出力はそれぞれ 1 つのみであるため、PReLU 層の predict の構文は Z = predict(layer,X) になります。

既定では、層は学習時に predict を順方向関数として使用します。学習時に別の順方向関数を使用する、または逆方向関数に必要な値を保持するには、forward という名前の関数も作成しなければなりません。

入力の次元は、データのタイプと結合層の出力によって異なります。

層入力入力サイズ観察値の次元
2 次元イメージh x w x c x N。ここで、h、w、および c は、それぞれイメージの高さ、幅、およびチャネル数に対応します。N は観測値の数です。4
3 次元イメージh x w x D x c x N。ここで、h、w、D、および c は、それぞれ 3 次元イメージの高さ、幅、深さ、およびチャネル数に対応します。N は観測値の数です。5
ベクトル シーケンスc x N x S。ここで、c はシーケンスの特徴の数、N は観測値の数、S はシーケンス長です。2
2 次元イメージ シーケンスh x w x c x N x S。ここで、h、w、および c は、それぞれイメージの高さ、幅、およびチャネル数に対応します。N は観測値の数、S はシーケンス長です。4
3 次元イメージ シーケンスh x w x d x c x N x S。ここで、h、w、d、および c は、それぞれ 3 次元イメージの高さ、幅、深さ、およびチャネル数に対応します。N は観測値の数、S はシーケンス長です。5

関数 forward"学習時" に層経由でデータを順伝播させ、メモリ値も出力します。

forward の構文は、以下のとおりです。

[Z1,…,Zm,memory] = forward(layer,X1,…,Xn)
ここで、X1,…,Xnn 個の層入力、Z1,…,Zmm 個の層出力、memory は層のメモリです。

ヒント

forward への入力の数が変化する可能性がある場合、X1,…,Xn ではなく varargin を使用します。この場合、varargin は入力の cell 配列です。ここで、varargin{i}Xi に対応します。出力の数が変化する可能性がある場合、Z1,…,Zm ではなく varargout を使用します。この場合、varargout は出力の cell 配列です。ここで、j=1,…,NumOutputs について varargout{j}Zj に対応し、varargout{NumOutputs+1}memory に対応します。

PReLU 演算は次の式で表されます。

f(xi)={xiif xi>0αixiif xi0

ここで、xi はチャネル i の非線形活性化 f の入力、αi は負の部分の勾配を制御する係数です。αi の添字 i は、別のチャネルでは非線形活性化が変わる場合があることを示しています。

この演算を predict に実装します。predict では、入力 X がこの方程式の x に対応します。出力 Zf(xi) に対応します。PReLU 層では、学習にメモリまたは別の順方向関数が必要ないため、クラス ファイルから関数 forward を削除できます。関数の構文を説明するコメントを関数の上部に追加します。

        function Z = predict(layer, X)
            % Z = predict(layer, X) forwards the input data X through the
            % layer and outputs the result Z.
            
            Z = max(0, X) + layer.Alpha .* min(0, X);
        end

逆方向関数の作成

入力データおよび学習可能なパラメーターについて、損失の微分を関数 backward に実装します。

backward の構文は、以下のとおりです。

[dLdX1,…,dLdXn,dLdW1,…,dLdWk] = backward(layer,X1,…,Xn,Z1,…,Zm,dLdZ1,…,dLdZm,memory)
ここで、X1,…,Xnn 個の層入力、Z1,…,Zmforwardm 個の出力、dLdZ1,…,dLdZm は次の層から逆伝播された勾配、memoryforward のメモリ出力です。出力において、dLdX1,…,dLdXn は層入力についての損失の微分で、dLdW1,…,dLdWkk 個の学習可能なパラメーターについての損失の微分です。順方向パスと逆方向パスの間に使用されない変数が保存されることを防いでメモリ使用量を削減するには、対応する入力引数を ~ に置き換えます。

ヒント

backward への入力の数が変化する可能性がある場合、layer の後に入力引数ではなく varargin を使用します。この場合、varargin は入力の cell 配列です。ここで、i=1,…,NumInputs について varargin{i}Xi に対応し、j=1,…,NumOutputs について varargin{NumInputs+j} および varargin{NumInputs+NumOutputs+j} はそれぞれ Zj および dLdZj に対応し、varargin{end}memory に対応します。

出力の数が変化する可能性がある場合、出力引数ではなく varargout を使用します。この場合、varargout は出力の cell 配列です。ここで、i=1,…,NumInputs について varargout{i}dLdXit=1,…,k について varargout{NumInputs+t}dLdWt に対応します。k は学習可能なパラメーターの数です。

PReLU 層の入力、出力、および学習可能なパラメーターはそれぞれ 1 つのみであるため、PReLU 層の backward の構文は [dLdX,dLdAlpha] = backward(layer,X,Z,dLdZ,memory) になります。XZ の次元は順方向関数の場合と同じです。dLdZ の次元は Z の次元と同じです。dLdX の次元およびデータ型は X の次元およびデータ型と同じです。dLdAlpha の次元およびデータ型は、学習可能なパラメーター Alpha の次元およびデータ型と同じです。

逆方向パスの間に、層は対応する微分を使用して学習可能なパラメーターを自動的に更新します。

ネットワークにカスタム層を含めるには、層の順方向関数は、前の層の出力を受け取り、次の層で想定されるサイズの配列を順伝播しなければなりません。同様に、backward は、順方向関数の対応する出力と同じサイズの入力を受け取り、同じサイズの微分を逆伝播しなければなりません。

入力データについての損失の微分は次のとおりです。

Lxi=Lf(xi)f(xi)xi

ここで、L/f(xi) は次の層から伝播された勾配です。活性化の微分は次のとおりです。

f(xi)xi={1if xi0αiif xi<0.

学習可能なパラメーターについての損失の微分は、

Lαi=jLf(xij)f(xij)αi

です。ここで、i はチャネルのインデックス、j は高さ、幅、観測値に関する要素のインデックス、L/f(xi) は深い層から伝播された勾配で、活性化の勾配は次のとおりです。

f(xi)αi={0if xi0xiif xi<0.

層のテンプレートの backward では、出力 dLdW を出力 dLdAlpha に置き換えます。ここで、dLdAlphaL/αi に対応します。backward では、入力 X が x に対応します。入力 Zf(xi) に対応します。入力 dLdZL/f(xi) に対応します。出力 dLdXL/xi に対応します。

関数の構文を説明するコメントを関数の上部に追加します。順方向パスと逆方向パスの間に使用されない変数が保存されることを防いでメモリ使用量を削減するには、対応する入力引数を ~ に置き換えます。層関数には入力引数 Z および memory が必要ないため、これらの引数を ~ に置き換えます。

        function [dLdX, dLdAlpha] = backward(layer, X, ~, dLdZ, ~)
            % [dLdX, dLdAlpha] = backward(layer, X, ~, dLdZ, ~)
            % backward propagates the derivative of the loss function
            % through the layer.
            % Inputs:
            %         layer    - Layer to backward propagate through
            %         X        - Input data
            %         dLdZ     - Gradient propagated from the deeper layer
            % Outputs:
            %         dLdX     - Derivative of the loss with respect to the
            %                    input data
            %         dLdAlpha - Derivative of the loss with respect to the
            %                    learnable parameter Alpha
            
            dLdX = layer.Alpha .* dLdZ;
            dLdX(X>0) = dLdZ(X>0);
            dLdAlpha = min(0,X) .* dLdZ;
            dLdAlpha = sum(sum(dLdAlpha,1),2);
            
            % Sum over all observations in mini-batch.
            dLdAlpha = sum(dLdAlpha,4);
        end

完成した層

完成した層のクラス ファイルを表示します。

classdef preluLayer < nnet.layer.Layer
    % Example custom PReLU layer.

    properties (Learnable)
        % Layer learnable parameters
            
        % Scaling coefficient
        Alpha
    end
    
    methods
        function layer = preluLayer(numChannels, name) 
            % layer = preluLayer(numChannels, name) creates a PReLU layer
            % with numChannels channels and specifies the layer name.

            % Set layer name.
            layer.Name = name;

            % Set layer description.
            layer.Description = "PReLU with " + numChannels + " channels";
        
            % Initialize scaling coefficient.
            layer.Alpha = rand([1 1 numChannels]); 
        end
        
        function Z = predict(layer, X)
            % Z = predict(layer, X) forwards the input data X through the
            % layer and outputs the result Z.
            
            Z = max(0, X) + layer.Alpha .* min(0, X);
        end
        
        function [dLdX, dLdAlpha] = backward(layer, X, ~, dLdZ, ~)
            % [dLdX, dLdAlpha] = backward(layer, X, ~, dLdZ, ~)
            % backward propagates the derivative of the loss function
            % through the layer.
            % Inputs:
            %         layer    - Layer to backward propagate through
            %         X        - Input data
            %         dLdZ     - Gradient propagated from the deeper layer
            % Outputs:
            %         dLdX     - Derivative of the loss with respect to the
            %                    input data
            %         dLdAlpha - Derivative of the loss with respect to the
            %                    learnable parameter Alpha
            
            dLdX = layer.Alpha .* dLdZ;
            dLdX(X>0) = dLdZ(X>0);
            dLdAlpha = min(0,X) .* dLdZ;
            dLdAlpha = sum(sum(dLdAlpha,1),2);
            
            % Sum over all observations in mini-batch.
            dLdAlpha = sum(dLdAlpha,4);
        end
    end
end

GPU 互換性

GPU 互換性のために、層関数は gpuArray 型の入力をサポートし、この型の出力を返さなければなりません。層が使用する他の関数も同じ動作をしなければなりません。多くの MATLAB 組み込み関数が gpuArray 入力引数をサポートしています。これらの関数のいずれかを、1 つ以上の gpuArray 入力を指定して呼び出した場合、その関数は GPU で実行され、gpuArray 出力を返します。GPU で実行される関数の一覧については、GPU での MATLAB 関数の実行 (Parallel Computing Toolbox)を参照してください。深層学習に GPU を使用するには、Compute Capability 3.0 以上の CUDA® 対応 NVIDIA® GPU も必要です。MATLAB での GPU の使用の詳細は、MATLAB での GPU 計算 (Parallel Computing Toolbox)を参照してください。

predictforward、および backward で使用する MATLAB 関数はすべて、gpuArray 入力をサポートしているため、層は GPU 互換です。

checkLayer を使用した層の有効性のチェック

カスタム層 preluLayer について層の有効性をチェックします。

カスタム PReLU 層を定義します。この層を作成するには、ファイル preluLayer.m を現在のフォルダーに保存します。

層のインスタンスを作成し、checkLayer を使用してその有効性をチェックします。層への典型的な入力における 1 つの観測値のサイズになるように有効な入力サイズを指定します。層には 4 次元配列を入力する必要があり、最初の 3 つの次元は前の層の出力における高さ、幅、およびチャネル数に対応し、4 番目の次元は観測値に対応します。

観測値の入力の典型的なサイズを指定し、'ObservationDimension' を 4 に設定します。

layer = preluLayer(20,'prelu');
validInputSize = [24 24 20];
checkLayer(layer,validInputSize,'ObservationDimension',4)
Skipping GPU tests. No compatible GPU device found.
 
Running nnet.checklayer.TestCase
.......... ........
Done nnet.checklayer.TestCase
__________

Test Summary:
	 18 Passed, 0 Failed, 0 Incomplete, 6 Skipped.
	 Time elapsed: 171.4609 seconds.

ここでは、関数で層に関する問題はまったく検出されていません。

ネットワークにカスタム層を含める

Deep Learning Toolbox では、カスタム層を他の層と同じように使用できます。この節では、前に作成した PReLU 層を使用した数字分類用ネットワークを作成し、学習させる方法を説明します。

例の学習データを読み込みます。

[XTrain,YTrain] = digitTrain4DArrayData;

カスタム PReLU 層を定義します。この層を作成するには、ファイル preluLayer.m を現在のフォルダーに保存します。カスタム層 preluLayer を含む層配列を作成します。

layers = [ 
    imageInputLayer([28 28 1])
    convolution2dLayer(5,20)
    batchNormalizationLayer
    preluLayer(20,'prelu')
    fullyConnectedLayer(10)
    softmaxLayer
    classificationLayer];

学習オプションを設定し、ネットワークに学習させます。

options = trainingOptions('adam','MaxEpochs',10);
net = trainNetwork(XTrain,YTrain,layers,options);
Training on single CPU.
Initializing input data normalization.
|========================================================================================|
|  Epoch  |  Iteration  |  Time Elapsed  |  Mini-batch  |  Mini-batch  |  Base Learning  |
|         |             |   (hh:mm:ss)   |   Accuracy   |     Loss     |      Rate       |
|========================================================================================|
|       1 |           1 |       00:00:00 |        7.03% |       3.3828 |          0.0010 |
|       2 |          50 |       00:00:11 |       74.22% |       0.7206 |          0.0010 |
|       3 |         100 |       00:00:24 |       89.84% |       0.3583 |          0.0010 |
|       4 |         150 |       00:00:32 |       88.28% |       0.4037 |          0.0010 |
|       6 |         200 |       00:00:40 |       96.88% |       0.2034 |          0.0010 |
|       7 |         250 |       00:00:47 |       96.88% |       0.1370 |          0.0010 |
|       8 |         300 |       00:00:56 |      100.00% |       0.0609 |          0.0010 |
|       9 |         350 |       00:01:13 |      100.00% |       0.0534 |          0.0010 |
|      10 |         390 |       00:01:25 |       99.22% |       0.0527 |          0.0010 |
|========================================================================================|

新しいデータについて予測を行い、精度を計算することによって、ネットワーク性能を評価します。

[XTest,YTest] = digitTest4DArrayData;
YPred = classify(net,XTest);
accuracy = sum(YTest==YPred)/numel(YTest)
accuracy = 0.9194

参照

[1] He, Kaiming, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. "Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification." In Proceedings of the IEEE international conference on computer vision, pp. 1026-1034. 2015.

参考

|

関連するトピック