メインコンテンツ

Raspberry Pi での LSTM ネットワークのコード生成

この例では、事前学習済み長短期記憶 (LSTM) ネットワーク用に、どの深層学習ライブラリにも依存しないコードを生成する方法を示します。コード生成後、コードを Raspberry Pi™ ターゲットに展開できます。この例では、LSTM ネットワークがマシンの残存耐用期間 (RUL) を予測します。ネットワークは、エンジン内のさまざまなセンサーを表す時系列データ セットを入力として受け取ります。ネットワークは、サイクルで測定されたエンジンの残存耐用期間を出力として返します。この例で使用する事前学習済み LSTM ネットワークの詳細については、深層学習を使用したシーケンスの分類の例を参照してください。

この例では、PIL ベースのワークフローを使用して、MEX 関数を生成します。この関数は、MATLAB からターゲット ハードウェアで生成された実行可能ファイルを呼び出します。この例は、LSTM ネットワークを使用して、予測を実行するための 2 つの異なる方法を示しています。

  • 1 番目の方法は、標準の LSTM ネットワークを使用し、時系列データのセットに対して推論を実行します。

  • 2 番目の方法は、同じ LSTM ネットワークのステートフルな動作を利用します。この方法では、一度にデータの単一のタイムステップを渡し、ネットワークにはタイム ステップごとにその状態を更新させます。

サードパーティの前提条件

  • Raspberry Pi ハードウェア

この例は、MATLAB Online ではサポートされていません。

テスト データのダウンロードと準備

このセクションでは、この例で使用するテスト データのダウンロードと準備の手順に関する概要を説明します。この例では、[1] に記載のある Turbofan Engine Degradation Simulation Data Set を使用します。データ セットには 100 個の学習観測値と 100 個のテスト観測値が含まれています。学習データには、100 個のエンジンのシミュレートされた時系列データが含まれています。各シーケンスには 17 個の特徴があり、長さはさまざまで、故障するまで使用 (RTF) した事例全体に対応します。テスト データには 100 個の部分シーケンスと、各シーケンスの最後における、対応する残存耐用期間の値が含まれています。Turbofan Engine Degradation Simulation データ セットと前処理手順の詳細については、深層学習を使用した sequence-to-sequence 回帰の例を参照してください。

Turbofan Engine Degradation Simulation データ セットを格納するディレクトリを作成します。

dataFolder = fullfile(tempdir,"turbofan");
if ~exist(dataFolder,'dir')
    mkdir(dataFolder);
end

Turbofan Engine Degradation Simulation データ セットをダウンロードして解凍します。

filename = matlab.internal.examples.downloadSupportFile("nnet","data/TurbofanEngineDegradationSimulationData.zip");
unzip(filename,dataFolder)

学習データの平均と標準偏差の計算

以下の手順では、学習データの平均と標準偏差を使用してテスト予測子を正規化します。そのため、はじめに学習データを使用してその正規化パラメーターを計算しなければなりません。

学習データを読み込みます。各列は 1 つの観測値に対応し、各行は 1 つの特徴に対応します。定数値が含まれる特徴を削除します。

filenamePredictors = fullfile(dataFolder,"train_FD001.txt");
[XTrain] = processTurboFanDataTrain(filenamePredictors);

m = min([XTrain{:}],[],2);
M = max([XTrain{:}],[],2);
idxConstant = M == m;

for i = 1:numel(XTrain)
    XTrain{i}(idxConstant,:) = [];
end

すべての観測値について、平均と標準偏差を計算します。

mu = mean([XTrain{:}],2);
sig = std([XTrain{:}],0,2);

テスト データの準備

この例に添付されている関数 processTurboFanDataTest を使用してテスト データを準備します。関数 processTurboFanDataTestfilenamePredictors および filenameResponses からデータを抽出し、cell 配列 XValidate および YValidate を返します。これらの cell 配列には、テスト予測子と応答シーケンスがそれぞれ含まれています。

filenamePredictors = fullfile(dataFolder,"test_FD001.txt");
filenameResponses = fullfile(dataFolder,"RUL_FD001.txt");
[XValidate,YValidate] = processTurboFanDataTest(filenamePredictors,filenameResponses);

学習データから計算された idxConstant を使用して定数値を持つ特徴を削除します。学習データから計算されたパラメーター mu および sig を使用して、テスト予測子を正規化します。しきい値 150 でテストの応答をクリップします。このクリッピングしきい値は、ネットワークの学習時に学習データに対して使用された値と同じです。

thr = 150;
for i = 1:numel(XValidate)
    XValidate{i}(idxConstant,:) = [];
    XValidate{i} = (XValidate{i} -  mu) ./ sig;
    YValidate{i}(YValidate{i} > thr) = thr;
end

展開のためにデータを single にキャストします。

XValidate = cellfun(@single, XValidate,  'UniformOutput',  false);
YValidate = cellfun(@single, YValidate,  'UniformOutput',  false);

スタティック ライブラリ用のコード生成構成オブジェクトの設定

指定されたエントリポイント関数に対して PIL MEX 関数を生成するには、スタティック ライブラリのコード構成オブジェクトを作成し、検証モードを 'PIL' に設定します。ターゲット言語を C に設定します。生成されたコードに Neon v7 内部パラメーターを活用するには、cfgInstructionSetExtensions プロパティを Neon v7 に設定します。

cfg = coder.config('lib', 'ecoder', true);
cfg.VerificationMode = 'PIL';
cfg.TargetLang = 'C';
cfg.InstructionSetExtensions = 'Neon v7';
cfg.InstructionSetExtensionsConfig.FMA = true;

深層学習コード生成用の構成オブジェクトの設定

深層学習構成オブジェクトを作成し、ターゲット ライブラリを none に設定します。コード生成構成オブジェクトに深層学習構成オブジェクトを追加します。

dlcfg = coder.DeepLearningConfig(TargetLibrary = 'none');
cfg.DeepLearningConfig = dlcfg;

Raspberry Pi への接続の作成

Raspberry Pi® Blockset の関数 raspi を使用して、Raspberry Pi への接続を作成します。以下のコードで、次を置き換えます。

  • raspiname: 自分の Raspberry Pi の名前

  • username: 自分のユーザー名

  • password: 自分のパスワード

r = raspi;

Raspberry Pi 用のコード生成ハードウェア パラメーターの構成

Raspberry Pi 用の coder.Hardware オブジェクトを作成してコード生成構成オブジェクトに追加します。

hw = coder.hardware('Raspberry Pi');
cfg.Hardware = hw;

LSTM ネットワークの PIL MEX 関数の生成

この方法では、エントリポイント関数 rulPredict のコードを生成します。

エントリポイント関数 rulPredict は、時系列データ セット全体を入力として受け取り、予測のためにネットワークに渡します。具体的には、関数は深層学習を使用したシーケンスの分類の例で学習させる LSTM ネットワークを使用します。関数は、ネットワーク オブジェクトを rulDlnetwork.mat ファイルから永続変数に読み込み、以降の予測呼び出しではこの永続オブジェクトを再利用します。sequence-to-sequence LSTM ネットワークでは、データ シーケンスの個々のタイム ステップで異なる予測を行うことができます。dlarray オブジェクトはエントリポイント関数内で作成され、関数への入力と出力のデータ型はプリミティブ型になります。詳細については、Code Generation for dlarray (MATLAB Coder)を参照してください。

type('rulPredict.m')
function out = rulPredict(in, dataFormat)
%#codegen

% Copyright 2020-2024 The MathWorks, Inc.

persistent mynet;

if isempty(mynet)
    mynet = coder.loadDeepLearningNetwork('rulDlnetwork.mat');
end

% Construct formatted dlarray input
dlIn = dlarray(in, dataFormat);

dlOut = predict(mynet, dlIn);

out = extractdata(dlOut);

end

エントリポイント関数 rulPredict には、dlnetwork への dlarray 入力を構築するためのデータ形式が必要です。特徴に対応する dlarray の最初の次元はチャネル 'C' に設定され、2 番目の次元はシーケンス長 'T' に設定されます。

dataFormat = 'CT';

codegen (MATLAB Coder)コマンドを使用してコードを生成し、関数coder.typeof (MATLAB Coder)を使用してエントリポイント関数への入力引数の型とサイズを指定します。この例では、入力は、特徴次元値 17 と可変のシーケンス長をもつ single のデータ型です。シーケンス長を可変サイズとして指定し、任意の長さの入力シーケンスについての予測を実行します。

matrixInput = coder.typeof(single(0),[17 Inf],[false true]);

codegen コマンドを実行し、ホスト プラットフォームで PIL ベースの MEX 関数 rulPredict_pil を生成します。

codegen -config cfg rulPredict -args  {matrixInput, coder.Constant(dataFormat)} -report

テスト データでの生成された PIL MEX 関数の実行

変数 XValidate には、100 個の入力観測値が含まれています。各観測には、シーケンス長が異なる 17 の特徴があります。

XValidate(1:5)
ans=5×1 cell array
     17×31 single
     17×49 single
    17×126 single
    17×106 single
     17×98 single

変数 YValidate には、入力変数 XValidate に対応する 100 個の出力観測値が含まれています。各出力観測値は、シーケンス全体の各タイム ステップ データに対してサイクルで測定される残存耐用年数 (RUI) の値です。

YValidate(1:5)
ans=5×1 cell array
     1×31 single
     1×49 single
    1×126 single
    1×106 single
     1×98 single

生成された MEX 関数 rul_lstmnet_predict_pil をランダムなテスト データ セットで実行します。

idx = randperm(numel(XValidate), 1);
inputData = XValidate{idx};

YPred1 = rulPredict_pil(inputData,dataFormat);
### Starting application: 'codegen\lib\rulPredict\pil\rulPredict.elf'
    To terminate execution: clear rulPredict_pil
### Launching application rulPredict.elf...

予測とテスト データとの比較

プロットを使用して MEX の出力データをテスト データと比較します。

figure('Name', 'Standard LSTM', 'NumberTitle', 'off');

plot(YValidate{idx},'--')
hold on
plot(YPred1,'.-')
hold off

ylim([0 175])
title("Test Observation " + idx)
xlabel("Time Step")
ylabel("RUL measured in cycles")

Figure Standard LSTM contains an axes object. The axes object with title Test Observation 53, xlabel Time Step, ylabel RUL measured in cycles contains 2 objects of type line.

PIL のクリア

PIL 実行プロセスを終了します。

clear rulPredict_pil;
### Host application produced the following standard output (stdout) and standard error (stderr) messages:

ステートフルな LSTM ネットワークの PIL MEX 関数の生成

timeseries 全体を渡して 1 ステップで予測する代わりに、dlnetwork の状態を更新して 1 タイムステップずつストリーミングすることにより、入力についての予測を実行できます。関数predictを使用することにより、更新されたネットワーク状態とともに出力予測を生成することができます。この関数 rulPredictAndUpdate は、単一のタイムステップ入力を受け取り、以降の入力が同じサンプルに続くタイムステップとして扱われるようにネットワークの状態を更新します。すべてのタイム ステップを一度に渡した場合、結果の出力は、すべてのタイム ステップが単一の入力として渡された場合と同じになります。

type('rulPredictAndUpdate.m')
function out = rulPredictAndUpdate(in, dataFormat)
%#codegen

% Copyright 2020-2024 The MathWorks, Inc.

persistent mynet;

if isempty(mynet)
    mynet = coder.loadDeepLearningNetwork('rulDlnetwork.mat');
end

% Construct formatted dlarray input
dlIn = dlarray(in, dataFormat);

% Predict and update the state of the network
[dlOut, updatedState] = predict(mynet, dlIn);
mynet.State = updatedState;

out = extractdata(dlOut);

end

codegen コマンドの入力タイプを作成します。rulPredictAndUpdate は、呼び出しごとに単一のタイムステップ データを受け入れ、入力タイプ matrixInput を、可変のシーケンス長ではなく固定のシーケンス長になるように 1 に指定します。

matrixInput = coder.typeof(single(0),[17 1]);

codegen コマンドを実行して、ホスト プラットフォームで PIL ベースの MEX 関数 rulPredictAndUpdate_pil を生成します。

codegen -config cfg rulPredictAndUpdate -args {matrixInput, coder.Constant(dataFormat)} -report
### Connectivity configuration for function 'rulPredictAndUpdate': 'Raspberry Pi'

テスト データでの生成された PIL MEX 関数の実行

inputData シーケンス内の各タイム ステップ データについて、生成された MEX 関数 rulPredictAndUpdate_pi を実行します。すべてのタイムステップを 1 つずつ関数 rulPredictAndUpdate に渡した後、結果の出力は、すべての入力を一度に渡した 1 番目の方法の出力と同じになります。

sequenceLength = size(inputData,2);
YPred2 = zeros(1, sequenceLength);
for i=1:sequenceLength
    inTimeStep = inputData(:,i);
    YPred2(:, i) = rulPredictAndUpdate_pil(inTimeStep,dataFormat);
end
### Starting application: 'codegen\lib\rulPredictAndUpdate\pil\rulPredictAndUpdate.elf'
    To terminate execution: clear rulPredictAndUpdate_pil
### Launching application rulPredictAndUpdate.elf...

予測とテスト データとの比較

プロットを使用して MEX の出力データをテスト データと比較します。

figure('Name', 'Stateful LSTM', 'NumberTitle', 'off');

plot(YValidate{idx},'--')
hold on
plot(YPred2,'.-')
hold off

ylim([0 175])
title("Test Observation " + idx)
xlabel("Time Step")
ylabel("RUL measured in cycles")

Figure Stateful LSTM contains an axes object. The axes object with title Test Observation 53, xlabel Time Step, ylabel RUL measured in cycles contains 2 objects of type line.

PIL のクリア

PIL 実行プロセスを終了します。

clear rulPredictAndUpdate_pil;
### Host application produced the following standard output (stdout) and standard error (stderr) messages:

参考文献

[1] Saxena, Abhinav, Kai Goebel, Don Simon, and Neil Eklund. "Damage propagation modeling for aircraft engine run-to-failure simulation." In Prognostics and Health Management, 2008. PHM 2008. International Conference on, pp. 1-9. IEEE, 2008.

参考

| | (MATLAB Coder) | (MATLAB Coder) | (MATLAB Coder)

トピック