Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

Sequence-to-Sequence 深層学習 Simulink モデルの汎用 C/C++ の生成

この例では、深層学習 Simulink® モデルからサードパーティの深層学習ライブラリに依存しない汎用 C/C++ 実行可能ファイルを生成する方法を示します。この例では、ターボファン エンジンの残存耐用期間 (RUL) を予測します。これは、エンジンの各種センサーからのデータを表す入力時系列の各ステップにおいてサイクル単位で測定されます。

この例のビデオによるデモについては、Generate Generic C/C++ Code for Deep Learning Networks in Simulink を参照してください。

この例は、MATLAB® Coder™ の "Generate Generic C/C++ Code for Sequence-to-Sequence Regression That Uses Deep Learning" の例に基づいています。詳細については、Generate Generic C Code for Sequence-to-Sequence Regression Using Deep Learningを参照してください。

前提条件

PIL 検証用に以下が必要になります。

  • STMicroelectronics STM32F407G-Discovery、STM32F746G-Discovery、または STM32F769I-Discovery ボード

  • USB type A Mini-B ケーブル

  • USB TTL-232 ケーブル - TTL-232R 3.3V (STM32F4-Discovery ボードのシリアル通信)

  • Embedded Coder® Support Package for STMicroelectronics® Discovery Boards。このサポート パッケージをインストールするには、MATLAB の [ホーム] タブの [環境] セクションにある [アドオン] メニューを使用します。

事前学習済みの RUL ネットワーク

この例では、事前学習済みの LSTM ネットワークを使用して、サイクル単位で測定されるエンジンの残存耐用期間を予測します。LSTM ネットワークは、200 の隠れユニットをもつ LSTM 層に続き、サイズ 50 の全結合層とドロップアウト確率 0.5 のドロップアウト層で構成されます。このネットワークに [1] に記載された "Turbofan Engine Degradation Simulation Data Set" を使用して学習させてあります。学習データには、100 個のエンジンのシミュレートされた時系列データが含まれています。各シーケンスの長さはさまざまで、故障するまで使用 (RTF) した事例全体に対応します。テスト データには、100 個の部分シーケンスと、各シーケンスの最後における対応する残存耐用期間の値が含まれています。ネットワークの学習の詳細については、深層学習を使用した sequence-to-sequence 回帰 (Deep Learning Toolbox)の例を参照してください。

net = coder.loadDeepLearningNetwork('rulNetwork.mat')
net = 
  SeriesNetwork with properties:

         Layers: [6×1 nnet.cnn.layer.Layer]
     InputNames: {'sequenceinput'}
    OutputNames: {'regressionoutput'}

コード生成でサポートされるネットワーク、層、およびクラスの詳細については、コード生成でサポートされているネットワークとレイヤーを参照してください。

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

このセクションには、例で使用されるテスト データをダウンロードして準備する手順をまとめています。Turbofan Engine Degradation Simulation データ セットと前処理手順の詳細については、例深層学習を使用した sequence-to-sequence 回帰 (Deep Learning Toolbox)を参照してください。

データ セットのダウンロード

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 を使用して、テスト データを準備します。関数 processTurboFanDataTestfilenamePredictorsfilenameResponses からデータを抽出し、cell 配列 XValidateYValidate を返します。これらの配列にはそれぞれ、テスト予測子と応答シーケンスが含まれます。

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

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

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

入力検証データが Simulink のコード生成と互換性をもつように、独立した 100 個の各観測値のシーケンス長がゼロでパディングされ、均一なサイズの 17 行 303 列の入力配列が作成されます。

sequenceLengths = cellfun(@length,XValidate,'UniformOutput',true);
maxSequenceLen = max(sequenceLengths);
padFcn = @(x) [x,zeros(size(x,1),maxSequenceLen-size(x,2))];             
XValidatePad = cellfun(padFcn,XValidate,'UniformOutput',false);

その後、パディングされた値が 17×303×100 の数値配列に変換されます。このデータを Simulink モデルにインポートするには、データ値と空の時間ベクトルを含む構造体変数を指定します。シミュレーション時、最初のタイム ステップの入力が配列の最初の 17 行 303 列の要素から読み取られます。次に 2 番目のタイム ステップの値が 2 番目の要素からというように、合計 100 ステップまで読み取られます。

simin.time = [];
simin.signals.values = cell2mat(reshape(XValidatePad,1,1,[]));
simin.signals.dimensions = size(XValidatePad{1});

予測用の Simulink モデル

ターボファン エンジンの残存耐用期間の予測に使用する Simulink モデルを示します。このモデルでは、rulNetwork MAT ファイルから学習済みネットワークをインポートする Deep Neural Networks ライブラリの Predict ブロックを使用しています。さらに、ブロックの Mini-batch size パラメーターは 1 に設定されています。

model = 'rulPredict';
open_system(model)

rulPredictModel.png

シミュレーションの実行

Simulink モデルを検証するには、シミュレーションを実行します。

set_param(model,'SimulationMode','Normal');
sim(model);

Simulink モデルの出力 YPred にネットワークによる残存耐用期間の予測値が格納されます。この出力は、ゼロでパディングされた値からの結果を削除するために最初に平衡化されてから cell 配列に変換されます。

YPred_cell = squeeze(mat2cell(YPred,1,maxSequenceLen,ones(1,100)));

for t = 1:length(sequenceLengths)   
    YPred_cell{t}(:,sequenceLengths(t) + 1:end) = [];
end 

ランダムに選択された 4 つの観測値について、残存耐用期間 (RUL) の予測値をプロットし、それらを検証データと比較します。

observationIdx = randperm(100,4);
rulExamplePlots(observationIdx,YValidate,YPred_cell);

コード生成のためのモデルの構成

サードパーティの深層学習ライブラリに依存しない C/C++ コードを生成して実行可能ファイルをビルドするには、Embedded Coder から汎用リアルタイム (ERT) ターゲットを選択します。

cs = getActiveConfigSet(model);
switchTarget(cs,'ert.tlc',[]);

コード生成の固有のパラメーターを構成します。MAT ファイルのログを設定すると、生成コードから MAT ファイルへのシミュレーション データのリダイレクトが有効になります。

set_param(cs,'TargetLang','C');
set_param(cs,'Toolchain','Automatically locate an installed toolchain');
set_param(cs,'ObjectivePriorities','Execution efficiency');
set_param(cs,'DLTargetLibrary','None');
set_param(cs,'GenerateReport','on');
set_param(cs,'CodeInterfacePackaging','Nonreusable function'); 
set_param(cs,'MatFileLogging', 'on');

Simulink モデルの生成とビルド

slbuild コマンドを使用して Simulink モデルを生成およびビルドします。コード ジェネレーターは、ファイルを "ビルド フォルダー" に配置します。これは、現在の作業フォルダーの配下にある rulPredict_ert_rtw という名前のサブフォルダーです。

evalc("slbuild('rulPredict')");

生成された実行可能ファイルの実行

生成された実行可能ファイルを実行します。rulPredict 実行可能ファイルが正常に実行されると、現在の作業フォルダーに rulPredict MAT ファイルが作成されます。

if ispc
    status = system('rulPredict.exe');
else
    status = system('./rulPredict');
end 
** created rulPredict.mat ** 
 
load('rulPredict.mat')

出力 rt_YPred にネットワークによる残存耐用期間の予測値が格納されます。この出力は、ゼロでパディングされた値からの結果を削除するために最初に平衡化されてから cell 配列に変換されます。

rt_YPred_cell = squeeze(mat2cell(rt_YPred,1,maxSequenceLen,ones(1,100)));

for t = 1:length(sequenceLengths)   
    rt_YPred_cell{t}(:,sequenceLengths(t) + 1:end) = [];
end 

ランダムに選択された 4 つの観測値について、残存耐用期間 (RUL) の予測値をプロットし、それらを検証データと比較します。

rulExamplePlots(observationIdx,YValidate,rt_YPred_cell);

プロセッサインザループ (PIL) シミュレーションによるコード検証

展開コードの動作が設計と一致することを確認するために、組み込みボードで PIL シミュレーションを実行するように Simulink モデルを構成できます。PIL シミュレーションでは、STMicroelectronics® Discovery などの組み込みボードで生成コードが実行されます。PIL シミュレーションの結果が Simulink に転送され、シミュレーションとコード生成の結果の数値的等価性が検証されます。

この例では、STMicroelectronics Discovery ボードでの "Embedded Coder® Support Package for STMicroelectronics Discovery Boards" を使用した PIL 検証を示します。この例は STM32F746G-Discovery ボードで実行するように事前に構成されています。[モデル コンフィギュレーション パラメーター] の [ハードウェア実行] ペインにある [ハードウェア ボード] で選択することにより、サポートされている他の組み込みボードをこのモデルで使用するように構成できます。

テスト データの準備

PIL シミュレーション用に、この例では観測値をシーケンス長の順に並べ替えて最初の 10 個を選択します。これらの観測値がゼロでパディングされ、均一なサイズの 17 行 54 列の入力配列が作成されます。その後、パディングされた値が 17×54×10 の数値配列に変換されます。このデータを Simulink モデルにインポートするには、データ値と空の時間ベクトルを含む構造体変数を指定します。シミュレーション時、最初のタイム ステップの入力が配列の最初の 17 行 54 列の要素から読み取られます。次に 2 番目のタイム ステップの値が 2 番目の要素からというように、合計 10 ステップまで読み取られます。

[~,idx] = sort(cellfun(@length,XValidate));
XValidatePIL = XValidate(idx(1:10));
YValidatePIL = YValidate(idx(1:10));
sequenceLengths = cellfun(@length,XValidatePIL,'UniformOutput',true);
maxSequenceLen = max(sequenceLengths);
padFcn = @(x) [x,zeros(size(x,1),maxSequenceLen-size(x,2))];             
XValidatePILPad = cellfun(padFcn,XValidatePIL,'UniformOutput',false);
simin.time = [];
simin.signals.values = cell2mat(reshape(XValidatePILPad,1,1,[]));
simin.signals.dimensions = size(XValidatePILPad{1});

最上位モデルの PIL

rulPredict_pil は、rulPredict モデルを PIL 検証用に変更したバージョンです。テスト ベクトルを読み込むために、rulPredict_pil モデルでは From Workspace ブロックを Inport ブロックに置き換えてあります。Inport ブロックは、17 行 54 列の double データ型の配列を受け入れるように構成されています。

pilModel = 'rulPredict_pil';
open_system(pilModel);

rulPredict_pil.png

rulPredict_pil モデルを "STM32F467G-Discovery" ターゲット用に構成します。

cs = getActiveConfigSet(pilModel);
set_param(cs,'HardwareBoard','STM32F746G-Discovery');
set_param(cs,'Toolchain','GNU Tools for ARM Embedded Processors');

STM32F4-Discovery ボードは、PIL 用に 2 種類の通信インターフェイスをサポートしています。ST-LINK とシリアルです。ST-LINK 通信インターフェイスでは、STM32F4-Discovery ボードをホスト コンピューターに接続するために使用する USB type A Mini-B ケーブルを除き、追加のケーブルやハードウェアを必要としません。シリアル通信インターフェイスでは、USB TTL-232 ケーブルが必要になります。シリアル通信インターフェイスを使用して PIL シミュレーションを実行すると、ST-LINK を使用して PIL シミュレーションを実行するよりもはるかに高速になります。この例はシリアル インターフェイスを使用するように構成されています。

さらに、[コンフィギュレーション パラメーター]、[ハードウェア実行]、[Target Hardware Resources]、[PIL] の [COM ポート] パラメーターを Windows コンピューターのシリアル インターフェイスのポート番号と一致するように設定する必要があります。

PIL シミュレーションの実行

PIL の結果をシミュレーションと比較するには、rulPredict_pil モデルをノーマル モードで実行してから PIL モードで実行します。

set_param(cs,'LoadExternalInput','on');
set_param(cs, 'ExternalInput','simin');
set_param(pilModel,'SimulationMode','Normal');

sim(pilModel);
YPred_ref = YPred_pil;

set_param(pilModel,'SimulationMode','Processor-in-the-Loop (PIL)')
sim(pilModel);

結果のプロット

出力 YPred_pilYPred_ref にネットワークによる残存耐用期間の予測値が格納されます。この出力は、ゼロでパディングされた値からの結果を削除するために最初に平衡化されてから cell 配列に変換されます。

PIL による残存耐用期間 (RUL) の予測値をプロットし、それらを検証データと比較します。

YPred_ref_cell = squeeze(mat2cell(YPred_ref,1,maxSequenceLen,ones(1,10)));
YPred_pil_cell = squeeze(mat2cell(YPred_pil,1,maxSequenceLen,ones(1,10)));

for t = 1:length(sequenceLengths)   
    YPred_ref_cell{t}(:,sequenceLengths(t) + 1:end) = [];
    YPred_pil_cell{t}(:,sequenceLengths(t) + 1:end) = [];
end 

rulExamplePlots([1:10],YValidatePIL,YPred_pil_cell);

pilOutput.png

PIL シミュレーションによる誤差を計算し、結果をプロットします。

YPred_error = YPred_ref-YPred_pil;

figure('Name', 'PIL Error')
for i = 1:10
    subplot(5,2,i)
    
    plot(YPred_error(:,:,i),'.-')
    maxerror = string(max(YPred_error(:,:,i)));
    ylim([-1e-4 1e-4])
    title("Test Observation " + i)
    xlabel("Time Step")
    ylabel("Difference")
end
legend(["Difference in Simulation Modes"],'Location','southeast')

pilError.png

サポート関数

function rulExamplePlots(observationIdx,YTest,YPred)
N = numel(observationIdx);

figure
for i = 1:N
    subplot(N/2,2,i)

    plot(YTest{observationIdx(i)},'--')
    hold on
    plot(YPred{observationIdx(i)},'.-')
    hold off

    ylim([0 175])
    title("Test Observation " + observationIdx(i))
    xlabel("Time Step")
    ylabel("RUL")
end
legend(["Test Data" "Predicted"],'Location','southeast')

end

参考文献

  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.