Main Content

コード生成用のカスタム深層学習層の定義

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

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

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

  2. 層のプロパティの宣言 — 層のプロパティ (学習可能なパラメーターや状態パラメーターなど) を指定します。

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

  4. 初期化関数の作成 (オプション) — ネットワークの初期化時に学習可能なパラメーターと状態パラメーターを初期化する方法を指定します。初期化関数を指定しない場合、ネットワークの初期化時にパラメーターは初期化されません。

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

  6. リセット ステート関数の作成 (オプション) — 状態パラメーターをリセットする方法を指定します。

  7. 逆方向関数の作成 (オプション) — 入力データおよび学習可能なパラメーターにおける損失の微分を指定します (逆伝播)。逆方向関数を指定しない場合、順方向関数は dlarray オブジェクトをサポートしなければなりません。

コード生成をサポートするカスタム層を作成するには、次のことが必要です。

  • 層は層の定義でプラグマ %#codegen を指定しなければなりません。

  • predict の入力は次のようになっていなければなりません。

    • 次元が一致している。各入力の次元の数が同じでなければなりません。

    • バッチ サイズが一致している。各入力のバッチ サイズが同じでなければなりません。

  • predict の出力の次元とバッチ サイズが層の入力と一致していなければなりません。

  • 非スカラーのプロパティは、single 配列、double 配列、または文字配列でなければなりません。

  • スカラーのプロパティは、数値型、logical 型、または string 型でなければなりません。

コード生成では、2 次元イメージ入力または特徴入力のみで中間層がサポートされます。コード生成は、状態プロパティ (属性が State であるプロパティ) をもつ層をサポートしません。

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

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

Side by side plots of the ReLU and PReLU activation functions. For values of y greater than zero, both functions scale linearly. For values of y less than zero, the ReLU function returns zero and the PReLU function scales linearly according to the scaling coefficient.

中間層テンプレート

中間層のテンプレートを MATLAB の新しいファイルにコピーします。このテンプレートは、中間層のクラス定義の構造を示しています。概要は次のとおりです。

  • 層のプロパティ、学習可能なパラメーター、状態パラメーターに関する、オプションの properties ブロック。

  • 層のコンストラクター関数。

  • オプションの関数 initialize

  • 関数 predict とオプションの関数 forward

  • 状態プロパティをもつ層におけるオプションの関数 resetState

  • オプションの関数 backward

classdef myLayer < nnet.layer.Layer % ...
        % & nnet.layer.Formattable ... % (Optional) 
        % & nnet.layer.Acceleratable % (Optional)

    properties
        % (Optional) Layer properties.

        % Declare layer properties here.
    end

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

        % Declare learnable parameters here.
    end

    properties (State)
        % (Optional) Layer state parameters.

        % Declare state parameters here.
    end

    properties (Learnable, State)
        % (Optional) Nested dlnetwork objects with both learnable
        % parameters and state parameters.

        % Declare nested networks with learnable and state parameters here.
    end

    methods
        function layer = myLayer()
            % (Optional) Create a myLayer.
            % This function must have the same name as the class.

            % Define layer constructor function here.
        end

        function layer = initialize(layer,layout)
            % (Optional) Initialize layer learnable and state parameters.
            %
            % Inputs:
            %         layer  - Layer to initialize
            %         layout - Data layout, specified as a networkDataLayout
            %                  object
            %
            % Outputs:
            %         layer - Initialized layer
            %
            %  - For layers with multiple inputs, replace layout with 
            %    layout1,...,layoutN, where N is the number of inputs.
            
            % Define layer initialization function here.
        end
        

        function [Z,state] = predict(layer,X)
            % Forward input data through the layer at prediction time and
            % output the result and updated state.
            %
            % Inputs:
            %         layer - Layer to forward propagate through 
            %         X     - Input data
            % Outputs:
            %         Z     - Output of layer forward function
            %         state - (Optional) Updated layer state
            %
            %  - For layers with multiple inputs, replace X with X1,...,XN, 
            %    where N is the number of inputs.
            %  - For layers with multiple outputs, replace Z with 
            %    Z1,...,ZM, where M is the number of outputs.
            %  - For layers with multiple state parameters, replace state 
            %    with state1,...,stateK, where K is the number of state 
            %    parameters.

            % Define layer predict function here.
        end

        function [Z,state,memory] = forward(layer,X)
            % (Optional) Forward input data through the layer at training
            % time and output the result, the updated state, and a memory
            % value.
            %
            % Inputs:
            %         layer - Layer to forward propagate through 
            %         X     - Layer input data
            % Outputs:
            %         Z      - Output of layer forward function 
            %         state  - (Optional) Updated layer state 
            %         memory - (Optional) Memory value for custom backward
            %                  function
            %
            %  - For layers with multiple inputs, replace X with X1,...,XN, 
            %    where N is the number of inputs.
            %  - For layers with multiple outputs, replace Z with 
            %    Z1,...,ZM, where M is the number of outputs.
            %  - For layers with multiple state parameters, replace state 
            %    with state1,...,stateK, where K is the number of state 
            %    parameters.

            % Define layer forward function here.
        end

        function layer = resetState(layer)
            % (Optional) Reset layer state.

            % Define reset state function here.
        end

        function [dLdX,dLdW,dLdSin] = backward(layer,X,Z,dLdZ,dLdSout,memory)
            % (Optional) Backward propagate the derivative of the loss
            % function through the layer.
            %
            % Inputs:
            %         layer   - Layer to backward propagate through 
            %         X       - Layer input data 
            %         Z       - Layer output data 
            %         dLdZ    - Derivative of loss with respect to layer 
            %                   output
            %         dLdSout - (Optional) Derivative of loss with respect 
            %                   to state output
            %         memory  - Memory value from forward function
            % Outputs:
            %         dLdX   - Derivative of loss with respect to layer input
            %         dLdW   - (Optional) Derivative of loss with respect to
            %                  learnable parameter 
            %         dLdSin - (Optional) Derivative of loss with respect to 
            %                  state input
            %
            %  - For layers with state parameters, the backward syntax must
            %    include both dLdSout and dLdSin, or neither.
            %  - For layers with multiple inputs, replace X and dLdX with
            %    X1,...,XN and dLdX1,...,dLdXN, respectively, where N is
            %    the number of inputs.
            %  - For layers with multiple outputs, replace Z and dlZ with
            %    Z1,...,ZM and dLdZ,...,dLdZM, respectively, where M is the
            %    number of outputs.
            %  - For layers with multiple learnable parameters, replace 
            %    dLdW with dLdW1,...,dLdWP, where P is the number of 
            %    learnable parameters.
            %  - For layers with multiple state parameters, replace dLdSin
            %    and dLdSout with dLdSin1,...,dLdSinK and 
            %    dLdSout1,...,dldSoutK, respectively, where K is the number
            %    of state parameters.

            % Define layer backward function here.
        end
    end
end

層の命名とスーパークラスの指定

まず、層に名前を付けます。クラス ファイルの最初の行で、既存の名前 myLayercodegenPreluLayer に置き換え、層を説明するコメントを追加します。

classdef codegenPreluLayer < nnet.layer.Layer & nnet.layer.Formattable
    % Example custom PReLU layer with codegen support.

    ...
end

逆方向関数を指定しない場合、既定では、層関数は "書式化されていない" dlarray オブジェクトを入力として受け取ります。層が、"書式化された" dlarray オブジェクトを入力として受け取り、さらに書式化された dlarray オブジェクトを出力するように指定するには、カスタム層の定義時に nnet.layer.Formattable クラスから継承します。

この層には書式設定可能な入力が必要ないため、オプションの nnet.layer.Formattable スーパークラスを削除します。

classdef codegenPreluLayer < nnet.layer.Layer
    % Example custom PReLU layer with codegen support.

    ...
end

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

    methods
        function layer = codegenPreluLayer()           
            ...
        end

        ...
     end

層の保存

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

コード生成プラグマの指定

この層のコードを生成することを示すため、層の定義に %#codegen 命令 (プラグマ) を追加します。この命令は、MATLAB コード アナライザーに対し、コード生成時のエラーの原因となる違反を診断して修正するのに役立つ情報を提供するよう指示します。

classdef codegenPreluLayer < nnet.layer.Layer
    % Example custom PReLU layer with codegen support.

    %#codegen

    ...
end

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

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

既定では、カスタム中間層に次のプロパティがあります。これらのプロパティを properties セクションで宣言しないでください。

Property説明
Name層の名前。文字ベクトルまたは string スカラーとして指定します。Layer 配列入力の場合、関数 trainnettrainNetworkassembleNetworklayerGraph、および dlnetwork は、名前が "" の層に自動的に名前を割り当てます。
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 セクションを省略できます。

ヒント

複数の入力がある層を作成する場合、層のコンストラクターで NumInputsInputNames のいずれかのプロパティを設定しなければなりません。複数の出力がある層を作成している場合、層のコンストラクターで NumOutputsOutputNames のいずれかのプロパティを設定しなければなりません。例については、複数の入力があるカスタム深層学習層の定義を参照してください。

コード生成をサポートするには、次のことが必要です。

  • 非スカラーのプロパティは、single 配列、double 配列、または文字配列でなければなりません。

  • スカラーのプロパティは、数値型、logical 型、または string 型でなければなりません。

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

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

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

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

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

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

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

            ...
        end

コード生成では arguments ブロックがサポートされていません。

層のプロパティの初期化

コンストラクター関数に学習可能なパラメーターを含め、層のプロパティを初期化します。コメント % 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 = codegenPreluLayer(numChannels, name) 
            % layer = codegenPreluLayer(numChannels, name) creates a PReLU
            % layer for 2-D image input 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

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

順方向関数の作成

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

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

関数 predict の構文は、層のタイプによって異なります。

  • Z = predict(layer,X) は、層を介して入力データ X を転送し、結果 Z を出力します。この場合、layer は、1 つの入力と 1 つの出力をもちます。

  • [Z,state] = predict(layer,X) は、更新された状態パラメーター state も出力します。この場合、layer は 1 つの状態パラメーターをもちます。

複数の入力、複数の出力、または複数の状態パラメーターを使用して、層の構文を調整できます。

  • 複数の入力をもつ層の場合、XX1,...,XN に置き換えます。ここで、N は入力の数です。NumInputs プロパティは N と一致しなければなりません。

  • 複数の出力をもつ層の場合、ZZ1,...,ZM に置き換えます。ここで、M は出力の数です。NumOutputs プロパティは M と一致しなければなりません。

  • 複数の状態パラメーターをもつ層の場合、statestate1,...,stateK に置き換えます。ここで、K は状態パラメーターの数です。

ヒント

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

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

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

コード生成では、2 次元イメージ入力のみでカスタム中間層がサポートされます。入力は、h x w x c x N の配列です。ここで、h、w、および c は、それぞれイメージの高さ、幅、およびチャネル数に対応します。N は観測値の数です。観測値の次元は 4 です。

コード生成のサポートでは、層入力すべての次元数およびバッチ サイズは同じでなければなりません。

既定では、層は学習時に predict を順方向関数として使用します。学習時に別の順方向関数を使用する、またはカスタム逆方向関数に必要な値を保持するには、forward という名前の関数も作成しなければなりません。ソフトウェアは関数 forward のコードを生成しませんが、コード生成の互換性がなければなりません。

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

関数 forward の構文は、層のタイプによって異なります。

  • Z = forward(layer,X) は、層を介して入力データ X を転送し、結果 Z を出力します。この場合、layer は、1 つの入力と 1 つの出力をもちます。

  • [Z,state] = forward(layer,X) は、更新された状態パラメーター state も出力します。この場合、layer は 1 つの状態パラメーターをもちます。

  • [__,memory] = forward(layer,X) は、前述の構文のいずれかを使用して、カスタム関数 backward のメモリ値も返します。カスタム関数 forward とカスタム関数 backward の両方が層に存在する場合、順方向関数によってメモリ値が返されなければなりません。

複数の入力、複数の出力、または複数の状態パラメーターを使用して、層の構文を調整できます。

  • 複数の入力をもつ層の場合、XX1,...,XN に置き換えます。ここで、N は入力の数です。NumInputs プロパティは N と一致しなければなりません。

  • 複数の出力をもつ層の場合、ZZ1,...,ZM に置き換えます。ここで、M は出力の数です。NumOutputs プロパティは M と一致しなければなりません。

  • 複数の状態パラメーターをもつ層の場合、statestate1,...,stateK に置き換えます。ここで、K は状態パラメーターの数です。

ヒント

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

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

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

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

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

この演算を predict に実装します。predict では、入力 X がこの方程式の x に対応します。出力 Zf(xi) に対応します。

関数の構文を説明するコメントを関数の上部に追加します。

ヒント

zeros のような関数を使用して配列を事前に割り当てる場合、これらの配列のデータ型が層関数の入力と一致していることを確認しなければなりません。別の配列と同じデータ型のゼロの配列を作成するには、zeros"like" オプションを使用します。たとえば、配列 X と同じデータ型でサイズが sz のゼロの配列を初期化するには、Z = zeros(sz,"like",X) を使用します。

順方向関数が dlarray 入力を完全にサポートしている場合、関数 backward の実装はオプションです。コード生成のサポートでは、関数 predict が数値入力もサポートしなければなりません。

PReLU 演算の出力を計算する 1 つの方法は、次のコードを使用することです。

Z = max(X,0) + layer.Alpha .* min(0,X);
コード生成では、.* 演算を使用した暗黙的な拡張がサポートされないため、代わりに関数 bsxfun を使用できます。
Z = max(X,0) + bsxfun(@times, layer.Alpha, min(0,X));
ただし、bsxfun では dlarray 入力がサポートされません。コード生成と dlarray 入力の両方をサポートする関数 predict を実装するには、if ステートメントと関数 isdlarray を使用して、入力タイプに合う適切なコードを選択してください。

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

関数 predictdlarray オブジェクトを完全にサポートするため、関数 backward の定義はオプションです。dlarray オブジェクトをサポートしている関数の一覧については、dlarray をサポートする関数の一覧を参照してください。

完成した層

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

classdef codegenPreluLayer < nnet.layer.Layer
    % Example custom PReLU layer with codegen support.

    %#codegen

    properties (Learnable)
        % Layer learnable parameters
            
        % Scaling coefficient
        Alpha
    end
    
    methods
        function layer = codegenPreluLayer(numChannels, name) 
            % layer = codegenPreluLayer(numChannels, name) creates a PReLU
            % layer for 2-D image input 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.
            
            if isdlarray(X)
                Z = max(X,0) + layer.Alpha .* min(0,X);
            else
                Z = max(X,0) + bsxfun(@times, layer.Alpha, min(0,X));
            end
        end
    end
end

コード生成の互換性に関するカスタム層のチェック

カスタム層 codegenPreluLayer について、コード生成の互換性をチェックします。

この例にサポート ファイルとして添付されているカスタム層 codegenPreluLayer は、入力データに PReLU 演算を適用します。この層にアクセスするには、この例をライブ スクリプトとして開きます。

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

観測値の入力の典型的なサイズを指定し、'ObservationDimension' オプションを 4 に設定します。有効な入力サイズの代わりに networkDataLayout オブジェクトを指定する場合は、観測次元を指定する必要はありません。コード生成の互換性をチェックするには、CheckCodegenCompatibility オプションを true に設定します。関数 checkLayer は、関数がコード生成と互換性があるかどうかをチェックしません。カスタム層定義がコード生成でサポートされているかどうかをチェックするには、まず "コード生成の準備状態" アプリを使用します。詳細については、コード生成の準備状態ツールを使ったコードのチェック (MATLAB Coder)を参照してください。

layer = codegenPreluLayer(20,"prelu");
validInputSize = [24 24 20];
checkLayer(layer,validInputSize,ObservationDimension=4,CheckCodegenCompatibility=true)
Skipping initialization tests. The layer does not have an initialize function.
 
Skipping GPU tests. No compatible GPU device found.
 
Running nnet.checklayer.TestLayerWithoutBackward
.......... .......... ...
Done nnet.checklayer.TestLayerWithoutBackward
__________

Test Summary:
	 23 Passed, 0 Failed, 0 Incomplete, 11 Skipped.
	 Time elapsed: 0.90077 seconds.

この関数によって層に関する問題は検出されていません。

参照

[1] "Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification." In 2015 IEEE International Conference on Computer Vision (ICCV), 1026–34. Santiago, Chile: IEEE, 2015. https://doi.org/10.1109/ICCV.2015.123.

参考

| | | | | | | | |

関連するトピック