Main Content

インクリメンタル学習用のコード生成

この例では、バイナリ線形分類用のインクリメンタル学習を実装するコードの生成方法を示します。この目的を達するために、ウェアラブル デバイスの学習を検討します。着用者がアイドルであるか移動しているかを判別するタスクは、デバイスが読み取る感覚の特徴に基づきます。

生成されたコードで実行するタスクは次のとおりです。これらをエントリポイント関数で定義します。

  1. コマンド ラインで作成した構成済みのインクリメンタル学習モデル テンプレートを読み込みます。

  2. データ ストリームからのデータの入力バッチでパフォーマンス メトリクスを追跡します。この例では、誤分類率とヒンジ損失を追跡します。

  3. インクリメンタル モデルをデータのバッチに当てはめてモデルを更新します。

  4. データのバッチのラベルを予測します。

この例では MATLAB® コマンド ラインからコードを生成しますが、代わりに MATLAB Coder™ アプリを使用してコードを生成することもできます。詳細については、機械学習モデルの予測を MATLAB Coder アプリを使用して行うコードの生成を参照してください。

バイナリ線形分類 (および線形回帰) 用のインクリメンタル学習のオブジェクト関数は、いずれもコード生成をサポートしています。インクリメンタル学習用にコードを生成する準備として、適切に構成されたインクリメンタル学習モデル オブジェクトがオブジェクト関数に必要ですが、codegen (MATLAB Coder)-args オプションはこれらのオブジェクトを受け入れません。この制限に対処するために、関数saveLearnerForCoderloadLearnerForCoderを使用します。

次のフロー チャートは、線形モデル用のインクリメンタル学習のオブジェクト関数に対するコード生成ワークフローを示しています。

codegen_flowchart_incrementallearning.png

フロー チャートでは、2 つの異なるワークフローがマージされる形で示されています。

  • Train Model > Convert Model で始まるワークフローでは、データが必要になります。この場合、インクリメンタル学習用のコードを生成する前に、必要に応じて特徴選択を実行したり、交差検証を実行してモデルを最適化したりできます。

  • Configure Model で始まるワークフローでは、データは必要ありません。代わりに、インクリメンタル学習モデル オブジェクトを手動で構成しなければなりません。

これらのワークフローの違いとどちらを使用すべきかの詳細については、インクリメンタル学習モデルの構成を参照してください。

選択するワークフローに関係なく、結果のインクリメンタル学習モデルは次のすべての品質を満たしていなければなりません。

  • インクリメンタル学習中に予測子データに含まれる予測子の数を NumPredictors プロパティに反映します。

  • 分類の場合、インクリメンタル学習中に予測されるすべてのクラス名を ClassNames プロパティに含めなければなりません。

Train Model > Convert Model のワークフローを選択した場合、すべての既知のクラスを含むデータにモデルを当てはめると、コード生成用にモデルが構成されます。

インクリメンタル学習モデルの準備ができたら、saveLearnerForCoder を使用してモデル オブジェクトを保存します。その後、loadLearnerForCoder を使用して保存済みモデルを読み込み、オブジェクト関数を呼び出してインクリメンタル学習を実行するエントリポイント関数を定義します。代わりに、インクリメンタル学習の段階をそれぞれ実行する複数のエントリポイント関数を定義することもできます (この例ではこのワークフローを使用します)。ただし、このワークフローでは、更新されたモデル オブジェクトが別のエントリポイント関数の入力である場合に特別な扱いが必要になります。たとえば、次の 3 つのエントリポイント関数を記述します。

  • 現在のモデルとデータのバッチを受け入れ、updateMetricsを呼び出して、パフォーマンス メトリクスが更新されたモデルを返す関数。

  • 更新されたモデルとデータのバッチを受け入れ、fitを呼び出して、係数が更新されたモデルを返す関数。

  • さらに更新されたモデルと予測子データのバッチを受け入れ、predictを呼び出して、予測されたラベルを返す関数。

最後に、codegen を使用してエントリポイント関数のコードを生成し、生成されたコードを確認します。

データの読み込みと前処理

人の行動のデータ セットを読み込みます。データをランダムにシャッフルします。

load humanactivity
rng(1); % For reproducibility
n = numel(actid);
p = size(feat,2);
idx = randsample(n,n);
X = feat(idx,:);
actid = actid(idx);

データ セットの詳細については、コマンド ラインで Description を入力してください。

応答は、次の 5 つのクラスのいずれかになります。座る、立つ、歩く、走る、または踊る。被験者がアイドルであるかどうか (actid <= 2) を基準に、応答を二分します。一意のクラス名を格納します。categorical 配列を作成します。

classnames = categorical(["Idle" "NotIdle"]);
Y = repmat(classnames(1),n,1);
Y(actid > 2) = classnames(2);

インクリメンタル学習モデルの構成

インクリメンタル分類用のコードを生成するには、インクリメンタル学習用のバイナリ分類線形モデル incrementalClassificationLinear を適切に構成しなければなりません。

インクリメンタル学習用のバイナリ分類 (SVM) モデルを作成します。予測されるすべてのクラス名と予測子変数の数を指定して、モデルをコード生成用に完全に構成します。さらに、誤分類率とヒンジ損失を追跡するように指定します。再現性を得るために、この例ではスケール不変ソルバー用の観測値のシャッフルはオフにします。

metrics = ["classiferror" "hinge"];
IncrementalMdl = incrementalClassificationLinear('ClassNames',classnames,'NumPredictors',p,...
    'Shuffle',false,'Metrics',metrics)
IncrementalMdl = 
  incrementalClassificationLinear

            IsWarm: 0
           Metrics: [2x2 table]
        ClassNames: [Idle    NotIdle]
    ScoreTransform: 'none'
              Beta: [60x1 double]
              Bias: 0
           Learner: 'svm'


Mdl はコード生成用に構成された incremenalClassificationLinear モデル オブジェクトです。Mdl はデータを処理していないため "コールド" (Mdl.IsWarm0) であり、係数は 0 になります。

代わりに、利用できるデータがあるため、fitcsvm または fitclinear を使用してデータに SVM モデルを当てはめてから、結果のモデルを incrementalLearner に渡してインクリメンタル学習モデルに変換できます。結果のモデルはデータを処理してあるため "ウォーム" であり、係数は非ゼロになる可能性が高くなります。

saveLearnerForCoder の使用によるモデルの保存

saveLearnerForCoderを使用して、インクリメンタル学習モデルを InitialMdl.mat というファイルに保存します。

saveLearnerForCoder(IncrementalMdl,'InitialMdl');

saveLearnerForCoder は、現在のフォルダーの MATLAB バイナリ ファイル SVMClassIncrLearner.mat に構造体配列としてインクリメンタル学習モデルを保存します。

エントリポイント関数の定義

"エントリポイント" 関数は、コード生成用に定義する関数で、"最上位" 関数または "プライマリ" 関数とも呼ばれます。codegen を使用して最上位レベルの関数を呼び出すことはできないので、コード生成に対応する関数を呼び出すエントリポイント関数を定義し、codegen を使用してエントリポイント関数の C/C++ コードを生成しなければなりません。エントリポイント関数内のすべての関数がコード生成をサポートしなければなりません。

次のアクションを実行する 4 つの別々のエントリポイント関数を現在のフォルダーで定義します。

  • myInitialModelIncrLearn.mloadLearnerForCoderを使用して保存済みモデルを読み込み、コード生成用に同じ形式のモデルを返します。このエントリポイント関数により、エントリポイント関数から返されるモデルを別のエントリポイント関数の入力として簡単に使用できるようになります。

  • myUpdateMetricsIncrLearn.m — 現在のモデルの性能をデータの入力バッチで測定し、パフォーマンス メトリクスをモデルに格納します。この関数は、現在のモデルと予測子および応答のデータを受け入れ、更新されたモデルを返します。

  • myFitIncrLearn.m — 現在のモデルをデータの入力バッチに当てはめ、更新された係数をモデルに格納します。この関数は、現在のモデルと予測子および応答のデータを受け入れ、更新されたモデルを返します。

  • myPredictIncrLearn.m — 現在のモデルを使用して、データの入力バッチのラベルを予測します。この関数は、現在のモデルと予測子のデータを受け入れ、ラベルとクラス スコアを返します。

複数のエントリポイント関数に対するコード生成の詳細については、複数のエントリポイント関数のためのコード生成 (MATLAB Coder)を参照してください。

MATLAB のアルゴリズムについてのコードを生成しようとしていることを指示するため、コンパイラ命令 %#codegen (またはプラグマ) をエントリポイント関数のシグネチャの後に追加します。この命令を追加すると、MATLAB Code Analyzer はコード生成時にエラーになる違反の診断と修正を支援します。コード アナライザーによるコードのチェック (MATLAB Coder)を参照してください。

代わりに、mlr/examples/stats/main の関数にアクセスすることもできます。mlrmatlabroot の値です。

各関数の本体を表示します。

type myInitialModelIncrLearn.m
function incrementalModel = myInitialModelIncrLearn() %#codegen
% MYINITIALMODELINCRLEARN Load and return configured linear model for
% binary classification InitialMdl
    incrementalModel = loadLearnerForCoder('InitialMdl');
end
type myUpdateMetricsIncrLearn.m
function incrementalModel = myUpdateMetricsIncrLearn(incrementalModel,X,Y) %#codegen
% MYUPDATEMETRICSINCRLEARN Measure model performance metrics on new data
      incrementalModel = updateMetrics(incrementalModel,X,Y); 
end
type myFitIncrLearn.m
function incrementalModel = myFitIncrLearn(incrementalModel,X,Y) %#codegen
% MYFITINCRLEARN Fit model to new data
      incrementalModel = fit(incrementalModel,X,Y); 
end
type myPredictIncrLearn.m
function [labels,scores] = myPredictIncrLearn(incrementalModel,X) %#codegen
% MYPREDICTINCRLEARN Predict labels and classification scores on new data
      [labels,scores] = predict(incrementalModel,X); 
end

コードの生成

コンパイラの設定

C/C++ コードを生成するには、適切に設定されている C/C++ コンパイラにアクセスできなければなりません。MATLAB Coder は、サポートされているインストール済みのコンパイラを探して使用します。mex -setup を使用すると、既定のコンパイラを表示および変更できます。詳細は、既定のコンパイラの変更を参照してください。

ビルド タイプ

MATLAB Coder は、以下のビルド タイプについてコードを生成できます。

  • MEX (MATLAB 実行可能ファイル) 関数

  • スタンドアロン C/C++ コード

  • スタティック ライブラリにコンパイルされるスタンドアロン C/C++ コード

  • ダイナミック リンク ライブラリにコンパイルされるスタンドアロン C/C++ コード

  • 実行可能ファイルにコンパイルされるスタンドアロン C/C++ コード

ビルド タイプは、codegen (MATLAB Coder)-config オプションを使用して指定できます。コード生成オプションの設定の詳細については、codegen (MATLAB Coder)-config オプションとビルド設定の構成 (MATLAB Coder)を参照してください。

既定では、codegen は MEX 関数を生成します。MEX 関数は、MATLAB から実行できる C/C++ プログラムです。MEX 関数を使用すると、MATLAB のアルゴリズムを高速化し、生成されたコードの機能と実行時の問題をテストすることができます。詳細は、MATLAB アルゴリズムの高速化 (MATLAB Coder)MATLAB で MEX 関数をテストする理由 (MATLAB Coder) を参照してください。

codegen の使用によるコードの生成

C および C++ は静的な型の言語なので、エントリポイント関数内のすべての変数のプロパティをコンパイル時に指定しなければなりません。次を指定します。

predictorData = coder.typeof(X,[],[true false]); 
responseData = coder.typeof(Y,[],true);
IncrMdlOutputType = coder.OutputType('myInitialModelIncrLearn');

codegen (MATLAB Coder)を使用して、エントリポイント関数のコードを生成します。エントリポイント関数の各引数について、コード生成における変数の表現を -args フラグを使用して指定します。出力の MEX 関数の名前 myIncrLearn_mex を指定します。

 codegen -o myIncrLearn_mex ...
 myInitialModelIncrLearn ... 
 myUpdateMetricsIncrLearn -args {IncrMdlOutputType,predictorData,responseData} ...
 myFitIncrLearn -args {IncrMdlOutputType,predictorData,responseData} ...
 myPredictIncrLearn –args {IncrMdlOutputType,predictorData} -report
Warning: C Compiler produced warnings. See the build log for further details.

Code generation successful (with warnings): To view the report, open('codegen/mex/myIncrLearn_mex/html/report.mldatx')

コード生成の問題についてデバッグするときは、[View report] をクリックして生成された C/C++ コードを確認すると便利です (コード生成レポート (MATLAB Coder)を参照)。

生成されたコードの確認

MEX 関数をテストして、生成されたコードが元の MATLAB コードと同じ機能を提供するかどうかを確認します。このテストを実行するため、元の MATLAB コードの実行に使用したものと同じ入力を使用して MEX 関数を実行してから、結果を比較します。スタンドアロン コードを生成する前に MATLAB で MEX 関数を実行すると、実行時エラーを検出して修正することもできます。生成されたスタンドアロン コードでは、このようなエラーの診断がはるかに困難になります。詳細は、MATLAB で MEX 関数をテストする理由 (MATLAB Coder)を参照してください。

生成された MEX 関数を使用し、オブジェクト関数を直接使用して、インクリメンタル学習を実行します。バッチを指定します。

% Preallocation
numObsPerChunk = 50;
nchunk = floor(n/numObsPerChunk);
ce = array2table(zeros(nchunk,2),'VariableNames',["Cumulative" "Window"]);
hinge = ce;
ceCG = ce;
hingeCG = ce;
IncrementalMdlCG = myIncrLearn_mex('myInitialModelIncrLearn');
scores = zeros(n,2);
scoresCG = zeros(n,2);

% Incremental fitting
for j = 1:nchunk
    ibegin = min(n,numObsPerChunk*(j-1) + 1);
    iend   = min(n,numObsPerChunk*j);
    idx = ibegin:iend;

    IncrementalMdl = updateMetrics(IncrementalMdl,X(idx,:),Y(idx));
    ce{j,:} = IncrementalMdl.Metrics{"ClassificationError",:};
    hinge{j,:} = IncrementalMdl.Metrics{"HingeLoss",:};
    IncrementalMdlCG = myIncrLearn_mex('myUpdateMetricsIncrLearn',IncrementalMdlCG,...
        X(idx,:),Y(idx));
    ceCG{j,:} = IncrementalMdlCG.Metrics{"ClassificationError",:};
    hingeCG{j,:} = IncrementalMdlCG.Metrics{"HingeLoss",:};

    IncrementalMdl = fit(IncrementalMdl,X(idx,:),Y(idx));
    IncrementalMdlCG = myIncrLearn_mex('myFitIncrLearn',IncrementalMdlCG,X(idx,:),Y(idx));
    
    [~,scores(idx,:)] = predict(IncrementalMdl,X(idx,:));
    [~,scoresCG(idx,:)] = myIncrLearn_mex('myPredictIncrLearn',IncrementalMdlCG,X(idx,:));
end

オブジェクト関数および MEX 関数で返される累積メトリクスと Idle を分類するスコアを比較します。

idx = all(~isnan(ce.Variables),2);
areCEsEqual = norm(ce.Cumulative(idx) - ceCG.Cumulative(idx))
areCEsEqual = 8.9904e-18
idx = all(~isnan(hinge.Variables),2);
areHingeLossesEqual = norm(hinge.Cumulative(idx) - hingeCG.Cumulative(idx))
areHingeLossesEqual = 8.4704e-17
areScoresEqual = norm(scores(:,1) - scoresCG(:,1))
areScoresEqual = 8.8356e-13

返される量には無視できるほどの差しかありません。