sequence-to-sequence LSTM ネットワーク向けのコード生成
この例では、長短期記憶 (LSTM) ネットワーク用の CUDA® コードを生成する方法を説明します。この例では、入力 timeseries の各ステップでの予測を行う MEX アプリケーションを生成します。次の 2 つの方法について説明します。1 つは標準の LSTM ネットワークを使用する方法、もう 1 つは同じ LSTM ネットワークのステートフルな動作を利用する方法です。この例では、身体に装着したスマートフォンから得られた加速度計のセンサー データを使用して、装着者のアクティビティについての予測を行います。ユーザーの動きは 5 つのカテゴリのうちの 1 つに分類されます。カテゴリは、ダンス中、ランニング中、着席中、起立中、歩行中です。この例では事前学習済みの LSTM ネットワークを使用します。学習の詳細については、Deep Learning Toolbox™ からの深層学習を使用したシーケンスの分類の例を参照してください。
サードパーティの必要条件
必須
この例では、CUDA MEX を生成します。以下のサードパーティ要件が適用されます。
CUDA 対応 NVIDIA® GPU および互換性のあるドライバー。
オプション
スタティック ライブラリ、ダイナミック ライブラリ、または実行可能ファイルなどの MEX 以外のビルドについて、この例では以下の要件も適用されます。
NVIDIA ツールキット。
NVIDIA cuDNN ライブラリ。
コンパイラおよびライブラリの環境変数。詳細は、サードパーティ ハードウェア (GPU Coder)と前提条件となる製品の設定 (GPU Coder)を参照してください。
GPU 環境の検証
関数coder.checkGpuInstall
(GPU Coder)を使用して、この例を実行するのに必要なコンパイラおよびライブラリが正しく設定されていることを検証します。
envCfg = coder.gpuEnvConfig('host'); envCfg.DeepLibTarget = 'cudnn'; envCfg.DeepCodegen = 1; envCfg.Quiet = 1; coder.checkGpuInstall(envCfg);
エントリポイント関数 lstmnet_predict
sequence-to-sequence LSTM ネットワークでは、データ シーケンスの個々のタイム ステップで異なる予測を行うことができます。エントリポイント関数 lstmnet_predict.m
は、入力シーケンスを受け取り、予測用の学習済み LSTM ネットワークに渡します。特に、この関数は "深層学習を使用した sequence-to-sequence 分類" の例で学習させた LSTM ネットワークを使用します。この関数は、ネットワーク オブジェクトを lstmnet_predict.mat
ファイルから永続変数に読み込み、以降の予測呼び出しではその永続オブジェクトを再利用します。
ネットワーク アーキテクチャの対話的な可視化とネットワーク層についての詳細情報を表示するには、関数analyzeNetwork
を使用します。
type('lstmnet_predict.m')
function out = lstmnet_predict(in) %#codegen % Copyright 2019-2021 The MathWorks, Inc. persistent mynet; if isempty(mynet) mynet = coder.loadDeepLearningNetwork('lstmnet.mat'); end % pass in input out = predict(mynet,in);
CUDA MEX の生成
エントリポイント関数 lstmnet_predict.m
用の CUDA MEX を生成するには、GPU の構成オブジェクトを作成し、ターゲットを MEX にするよう指定します。ターゲット言語を C++ に設定します。ターゲット ライブラリを cuDNN として指定する深層学習の構成オブジェクトを作成します。この深層学習構成オブジェクトを GPU 構成オブジェクトに追加します。
cfg = coder.gpuConfig('mex'); cfg.TargetLang = 'C++'; cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn');
コンパイル時に、GPU Coder™ はエントリポイント関数のすべての入力についてデータ型を把握していなければなりません。関数coder.typeof
(MATLAB Coder)を使用して、入力引数の型とサイズをcodegen
(MATLAB Coder)コマンドに指定します。この例では、入力は特徴次元値 3 と可変のシーケンス長をもつ double のデータ型です。シーケンス長を可変サイズとして指定することで、任意の長さの入力シーケンスについての予測を実行できるようになります。
matrixInput = coder.typeof(double(0),[3 Inf],[false true]);
codegen コマンドを実行します。
codegen -config cfg lstmnet_predict -args {matrixInput} -report
Code generation successful: View report
生成された MEX のテスト データについての実行
HumanActivityValidate
MAT ファイルを読み込みます。この MAT ファイルには、生成コードをテストできるセンサーの読み取り値のサンプル timeseries を含む変数 XValidate
が格納されます。最初の観測値について lstmnet_predict_mex
を呼び出します。
load HumanActivityValidate
YPred1 = lstmnet_predict_mex(XValidate{1});
YPred1
は、53888 のタイム ステップのそれぞれについての 5 つのクラスの確率を含む 5 行 53888 列の数値行列です。各タイム ステップについて、最大確率のインデックスを計算して、予測されたクラスを見つけます。
[~, maxIndex] = max(YPred1, [], 1);
最大確率のインデックスを対応するラベルに関連付けます。最初の 10 個のラベルを表示します。その結果から、ネットワークはその人が最初の 10 のタイム ステップの間、座っていると予測したことがわかります。
labels = categorical({'Dancing', 'Running', 'Sitting', 'Standing', 'Walking'}); predictedLabels1 = labels(maxIndex); disp(predictedLabels1(1:10)')
Sitting Sitting Sitting Sitting Sitting Sitting Sitting Sitting Sitting Sitting
予測とテスト データとの比較
プロットを使用して MEX の出力データをテスト データと比較します。
figure plot(predictedLabels1,'.-'); hold on plot(YValidate{1}); hold off xlabel("Time Step") ylabel("Activity") title("Predicted Activities") legend(["Predicted" "Test Data"])
異なるシーケンス長をもつ観測値についての生成された MEX の呼び出し
異なるシーケンス長をもつ 2 番目の観測値について lstmnet_predict_mex
を呼び出します。この例では、XValidate{2}
はシーケンス長が 64480 である一方、XValidate{1}
はシーケンス長が 53888 でした。シーケンス長の次元を可変サイズに指定したため、生成コードは予測を正しく処理します。
YPred2 = lstmnet_predict_mex(XValidate{2}); [~, maxIndex] = max(YPred2, [], 1); predictedLabels2 = labels(maxIndex); disp(predictedLabels2(1:10)')
Sitting Sitting Sitting Sitting Sitting Sitting Sitting Sitting Sitting Sitting
複数の観測値を受け入れる MEX の生成
多数の観測値について一度に予測を行う場合は、観測値をまとめて cell 配列にグループ化し、予測のためその cell 配列を渡すことができます。この cell 配列は列セル配列でなければならず、各 cell には 1 つの観測値が含まれていなければなりません。それぞれの観測値は同じ特徴次元をもたなければなりませんが、シーケンス長は変動してもかまいません。この例では、XValidate
には 5 つの観測値が含まれています。XValidate
を入力として受け入れる MEX を生成するには、入力の型を 5 行 1 列の cell 配列になるよう指定します。さらに、各 cell は matrixInput
と同じ型として指定します。これは、前の codegen
コマンドで単一の観測値について指定した型です。
matrixInput = coder.typeof(double(0),[3 Inf],[false true]); cellInput = coder.typeof({matrixInput}, [5 1]); codegen -config cfg lstmnet_predict -args {cellInput} -report
Code generation successful: View report
YPred3 = lstmnet_predict_mex(XValidate);
出力は、渡された 5 つの観測値に対する予測の 5 行 1 列の cell 配列です。
disp(YPred3)
{5×53888 single} {5×64480 single} {5×53696 single} {5×56416 single} {5×50688 single}
ステートフルな LSTM を使用した MEX の生成
時系列全体を渡して 1 ステップで予測する代わりに、関数predictAndUpdateState
を使用して 1 タイム ステップで一度にストリーミングすることにより、入力についての予測を実行できます。この関数は、入力を受け取り、出力予測を生成し、ネットワークの内部状態を更新して、将来の予測でこの初期入力が考慮されるようにします。
エントリポイント関数 lstmnet_predict_and_update.m
は、単一のタイム ステップ入力を受け取り、その入力を関数predictAndUpdateState
を使用して処理します。predictAndUpdateState
は、入力タイム ステップについての予測を出力し、ネットワークを更新して、以降の入力が同じサンプルに続くタイム ステップとして扱われるようにします。すべてのタイム ステップを一度に渡した場合、結果の出力は、すべてのタイム ステップが単一の入力として渡された場合と同じになります。
type('lstmnet_predict_and_update.m')
function out = lstmnet_predict_and_update(in) %#codegen % Copyright 2019-2021 The MathWorks, Inc. persistent mynet; if isempty(mynet) mynet = coder.loadDeepLearningNetwork('lstmnet.mat'); end % pass in input [mynet, out] = predictAndUpdateState(mynet,in);
この新しい設計ファイルで codegen を実行します。呼び出しごとに単一のタイム ステップを受け取るため、matrixInput
を指定して、可変シーケンス長の代わりに、シーケンス次元を 1 に固定します。
matrixInput = coder.typeof(double(0),[3 1]); codegen -config cfg lstmnet_predict_and_update -args {matrixInput} -report
Code generation successful: View report
最初の検証サンプルの最初のタイム ステップについて生成された MEX を実行します。
firstSample = XValidate{1}; firstTimestep = firstSample(:,1); YPredStateful = lstmnet_predict_and_update_mex(firstTimestep); [~, maxIndex] = max(YPredStateful, [], 1); predictedLabelsStateful1 = labels(maxIndex)
predictedLabelsStateful1 = categorical
Sitting
出力ラベルをグラウンド トゥルースと比較します。
YValidate{1}(1)
ans = categorical
Sitting