メインコンテンツ

DDPG エージェントを使用したタンク内の水位の制御

この例では、rlwatertank Simulink® モデル内の PI コントローラーを、強化学習用の深層決定論的方策勾配 (DDPG) エージェントに変換する方法を説明します。MATLAB® で DDPG エージェントに学習させる例については、Compare DDPG Agent to LQR Controllerを参照してください。

再現性のための乱数ストリームの固定

サンプル コードには、いくつかの段階で乱数の計算が含まれる場合があります。サンプル コード内の複数のセクションの先頭にある乱数ストリームを固定すると、実行するたびにセクション内の乱数シーケンスが維持されるため、結果が再現される可能性が高くなります。詳細については、Results Reproducibilityを参照してください。

シード 0 で乱数ストリームを固定し、メルセンヌ・ツイスター乱数アルゴリズムを使用します。乱数生成に使用されるシード制御の詳細については、rngを参照してください。

previousRngState = rng(0,"twister");

出力 previousRngState は、ストリームの前の状態に関する情報が格納された構造体です。例の最後で、状態を復元します。

貯水タンク モデル

この例の元のモデルは貯水タンク モデルです。目的はタンク内の水位を制御することです。貯水タンク モデルの詳細については、watertank Simulink モデル (Simulink Control Design)を参照してください。

元のモデルに対して次の変更を加えます。

  1. PID Controller を削除します。

  2. RL Agent ブロックを挿入します。

  3. 観測値ベクトル [edteh]T を接続します。ここで、h はタンク内の水位、e=r-h、および r は基準の高さです。

  4. 報酬 reward=10(|e|<0.1)-1(|e|0.1)-100(h0||h20) を設定します。

  5. 終端信号を設定して、h0 または h20 の時点でシミュレーションが停止するようにします。

結果のモデルは rlwatertank.slx です。このモデルおよび変更内容の詳細については、強化学習のための貯水タンク カスタム Simulink 環境を参照してください。

open_system("rlwatertank")

カスタム Simulink 環境の詳細については、Create Custom Simulink Environmentsを参照してください。

環境の作成

次の定義を含む環境モデルを作成します。

観測値の仕様 obsInfo とアクションの仕様 actInfo を定義します。

% Observation info
obsInfo = rlNumericSpec([3 1], ...
    LowerLimit=[-inf -inf 0  ]', ...
    UpperLimit=[ inf  inf inf]');

% Name and description are optional and not used 
% by the software
obsInfo.Name = "observations";
obsInfo.Description = "integrated error, error," + ... 
    " and measured height";

% Action info
actInfo = rlNumericSpec([1 1], LowerLimit=0, UpperLimit=1);
actInfo.Name = "flow rate control signal";

環境オブジェクトを作成します。

env = rlSimulinkEnv("rlwatertank","rlwatertank/RL Agent", ...
    obsInfo,actInfo);

モデルの基準値をランダム化するカスタム リセット関数を設定します。localResetFcn 関数は、この例の最後に記載しています。

env.ResetFcn = @(in)localResetFcn(in);

エージェントのサンプル時間 Ts とシミュレーション時間 Tf を秒単位で指定します。

Ts = 1.0;
Tf = 100;

クリティックの作成

DDPG エージェントは、パラメーター化された Q 値関数近似器を使用して方策の価値を推定します。Q 値関数クリティックは、現在の観測値とアクションを入力として取り、単一のスカラーを出力として返します。この出力は、方策の推定値、つまりエージェントが現在の観測値に対応する状態から動作を開始し、アクションを実行して、その後の方策に従う場合の割引累積長期報酬の推定値です。

パラメーター化された Q 値関数をクリティック内でモデル化するには、2 つの入力層 (そのうち 1 つは obsInfo で指定された観測チャネル用で、もう 1 つは actInfo で指定されたアクション チャネル用) と 1 つの出力層 (これはスカラー値を返します) をもつニューラル ネットワークを使用します。

各ネットワーク パスを layer オブジェクトの配列として定義します。各パスの入力層と出力層に名前を割り当てます。これらの名前を使用すると、パスを接続してから、ネットワークの入力層と出力層に適切な環境チャネルを明示的に関連付けることができます。obsInfo および actInfo の仕様から、観測空間と行動空間の次元を取得します。

% Observation path
obsPath = featureInputLayer(obsInfo.Dimension(1), ...
    Name="obsInLyr");

% Action path
actPath = featureInputLayer(actInfo.Dimension(1), ...
    Name="actInLyr");

% Common path
commonPath = [
    concatenationLayer(1,2,Name="concat")
    fullyConnectedLayer(25)
    reluLayer()
    fullyConnectedLayer(25)
    reluLayer()
    fullyConnectedLayer(1,Name="QValue")
    ];

% Create the network object and add the layers
criticNet = dlnetwork();
criticNet = addLayers(criticNet,obsPath);
criticNet = addLayers(criticNet,actPath);
criticNet = addLayers(criticNet,commonPath);

% Connect the layers
criticNet = connectLayers(criticNet, ...
    "obsInLyr","concat/in1");
criticNet = connectLayers(criticNet, ...
    "actInLyr","concat/in2");

クリティック ネットワークの構成を表示します。

plot(criticNet)

Figure contains an axes object. The axes object contains an object of type graphplot.

dlnetwork オブジェクトを初期化し、そのプロパティを要約します。クリティック ネットワークの初期パラメーターはランダムな値で初期化されます。ネットワークが常に同じパラメーター値で初期化されるように、乱数ストリームを固定します。

rng(0,"twister");
criticNet = initialize(criticNet);
summary(criticNet)
   Initialized: true

   Number of learnables: 801

   Inputs:
      1   'obsInLyr'   3 features
      2   'actInLyr'   1 features

指定した深層ニューラル ネットワークと環境仕様オブジェクトを使用して、クリティック近似器オブジェクトを作成します。ネットワークの入力を観測値とアクションのチャネルに関連付ける場合は、それらの入力に名前を付けます。

critic = rlQValueFunction(criticNet, ...
    obsInfo,actInfo, ...
    ObservationInputNames="obsInLyr", ...
    ActionInputNames="actInLyr");

Q 値関数オブジェクトの詳細については、rlQValueFunctionを参照してください。

観測値とアクションのランダムな入力を使用して、クリティックをチェックします。

getValue(critic, ...
    {rand(obsInfo.Dimension)}, ...
    {rand(actInfo.Dimension)})
ans = single

-0.4320

クリティックの作成の詳細については、Create Actors, Critics, and Policy Objectsを参照してください。

アクターの作成

DDPG エージェントは、連続行動空間において、パラメーター化された決定論的方策を使用します。この方策は、連続決定論的アクターによって学習されます。

連続決定論的アクターは、連続行動空間に関するパラメーター化された決定論的方策を実装します。このアクターは、現在の観測値を入力として取り、観測値の決定論的関数であるアクションを出力として返します。

パラメーター化された方策をアクター内でモデル化するには、1 つの入力層 (これは、obsInfo で指定された、環境観測チャネルのコンテンツを受け取ります) と 1 つの出力層 (これは、actInfo で指定された、環境アクション チャネルへのアクションを返します) をもつニューラル ネットワークを使用します。

ネットワークを layer オブジェクトの配列として定義します。

actorNet = [
    featureInputLayer(obsInfo.Dimension(1))
    fullyConnectedLayer(25)
    reluLayer()
    fullyConnectedLayer(25)
    reluLayer()
    fullyConnectedLayer(actInfo.Dimension(1))
    tanhLayer()
    scalingLayer("Scale",0.5,"Offset",0.5)
    ];

ネットワークを dlnetwork オブジェクトに変換し、そのプロパティを要約します。アクター ネットワークの初期パラメーターはランダムな値で初期化されます。ネットワークが常に同じパラメーター値で初期化されるように、乱数ストリームを固定します。

rng(0,"twister");
actorNet = dlnetwork(actorNet);
summary(actorNet)
   Initialized: true

   Number of learnables: 776

   Inputs:
      1   'input'   3 features

アクターのネットワーク構成を表示します。

plot(actorNet)

Figure contains an axes object. The axes object contains an object of type graphplot.

指定した深層ニューラル ネットワーク、環境仕様オブジェクト、およびネットワークの入力を観測値のチャネルに関連付ける名前を使用して、アクター近似器オブジェクトを作成します。

actor = rlContinuousDeterministicActor(actorNet,obsInfo,actInfo);

詳細については、rlContinuousDeterministicActorを参照してください。

観測値のランダムな入力を使用して、アクターをチェックします。

getAction(actor,{rand(obsInfo.Dimension)})
ans = 1×1 cell array
    {[0.4500]}

クリティックの作成の詳細については、Create Actors, Critics, and Policy Objectsを参照してください。

DDPG エージェントの作成

指定したアクター近似器オブジェクトとクリティック近似器オブジェクトを使用して、DDPG エージェントを作成します。

agent = rlDDPGAgent(actor,critic);

詳細については、rlDDPGAgentを参照してください。

ドット表記を使用して、エージェント、アクター、クリティックのオプションを指定します。この学習では、次のようにします。

  • 経験を保存するために、容量 1e6 の経験バッファーを使用します。経験バッファーが大きいほど、多様な経験のセットを保存できます。

  • アクターとクリティックの学習率をそれぞれ 1e-41e-3 に指定します。学習率が大きいと、大幅な更新が発生し、動作の逸脱につながる可能性がありますが、値が低いと、最適な点に到達するまでに多くの更新が必要になる可能性があります。

  • 勾配をクリップするために、勾配しきい値 1 を使用します。勾配をクリップすると、学習の安定性が向上します。

  • 経験数 400 のミニバッチを使用します。ミニバッチが小さいほど計算効率は高くなりますが、学習にばらつきが生じる可能性があります。対照的に、バッチ サイズが大きいほど学習は安定しますが、より多くのメモリが必要になります。

  • 長期的な報酬の重要性を高めるために、割引率 0.99 を使用します。

  • エージェントに 1 秒のサンプル時間を割り当てます。

agent.AgentOptions.SampleTime = Ts;
agent.AgentOptions.DiscountFactor = 0.99;
agent.AgentOptions.MiniBatchSize = 400;
agent.AgentOptions.ExperienceBufferLength = 1e6;

actorOpts = rlOptimizerOptions( ...
    LearnRate=1e-4, ...
    GradientThreshold=1);
criticOpts = rlOptimizerOptions( ...
    LearnRate=1e-3, ...
    GradientThreshold=1);
agent.AgentOptions.ActorOptimizerOptions = actorOpts;
agent.AgentOptions.CriticOptimizerOptions = criticOpts;

この例の DDPG エージェントは、探索にオルンシュタイン・ウーレンベック (OU) ノイズ モデルを使用します。ノイズ モデルについては、次のようにします。

  • 学習中の探索を改善するために、標準偏差値 0.03 を指定します。

  • 標準偏差の減衰率を 5e-5 に指定します。減衰により、学習の進行に応じて、探索は徐々に減少します。

agent.AgentOptions.NoiseOptions.StandardDeviation = 0.03;
agent.AgentOptions.NoiseOptions.StandardDeviationMin = 0.001;
agent.AgentOptions.NoiseOptions.StandardDeviationDecayRate = 5e-5;

あるいは、rlDDPGAgentOptionsオブジェクトを使用してエージェント オプションを指定することもできます。

観測値のランダムな入力を使用して、エージェントをチェックします。

getAction(agent,{rand(obsInfo.Dimension)})
ans = 1×1 cell array
    {[0.5506]}

エージェントの学習

エージェントに学習させるには、まず、学習オプションを指定します。この例では、次のオプションを使用します。

  • 最大 5000 個のエピソードについて、各学習を実行。各エピソードが最大 ceil(Tf/Ts) (つまり 100) タイム ステップ持続するように指定します。

  • [強化学習の学習モニター] ダイアログ ボックスに学習の進行状況を表示し (Plots オプションを設定)、コマンド ラインの表示を無効化 (Verbose オプションを false に設定)。

  • 10 回の学習エピソードごとに貪欲方策のパフォーマンスを評価し、5 回のシミュレーションの累積報酬を平均化。

  • 評価スコアが 900 に達したら学習を停止。この時点で、エージェントはタンクの水位を制御できます。

学習オプションの詳細については、rlTrainingOptionsを参照してください。

% training options
trainOpts = rlTrainingOptions( ...
    MaxEpisodes=5000, ...
    MaxStepsPerEpisode=ceil(Tf/Ts), ...
    Plots="training-progress", ...
    Verbose=false, ...
    StopTrainingCriteria="EvaluationStatistic", ...
    StopTrainingValue=900);

% agent evaluator
evl = rlEvaluator(EvaluationFrequency=50,NumEpisodes=5,RandomSeeds=1:5);

再現性のために乱数ストリームを固定します。

rng(0,"twister");

関数 train を使用して、エージェントに学習させます。学習は計算量が多いプロセスのため、完了するのに数分かかります。この例の実行時間を節約するために、doTrainingfalse に設定して事前学習済みのエージェントを読み込みます。エージェントに学習させるには、doTrainingtrue に設定します。

doTraining = false;
if doTraining
    % Train the agent.
    trainingStats = train(agent,env,trainOpts,Evaluator=evl);
else
    % Load the pretrained agent for the example.
    load("WaterTankDDPG.mat","agent")
end

学習済みエージェントの検証

再現性のために乱数ストリームを固定します。

rng(0,"twister");

既定では、エージェントはシミュレーション時に貪欲 (したがって決定的) 方策を使用します。代わりに探索方策を使用するには、UseExplorationPolicy エージェントのプロパティを true に設定します。

特定の初期条件を使用するには、カスタム リセット関数 localValidationResetFcn を設定します。この関数は、この例の最後に記載しています。

env.ResetFcn = @(in)localValidationResetFcn(in);

環境内でこのエージェントをシミュレーションし、経験を出力として返します。特定の初期条件を使用するには、環境のリセット関数を設定します。

simOpts = rlSimulationOptions( ...
    MaxSteps=ceil(Tf/Ts), ...
    StopOnError="on");
experience = sim(env,agent,simOpts);

シミュレーションにおいてエージェントが収集する報酬の合計を表示します。

sum(experience.Reward)
ans = 
978

Simulink モデルの Scope ブロックを使用して、閉ループ システムのパフォーマンスを表示します。エージェントは 5 秒以内に基準信号を追跡します。

previousRngState に保存されている情報を使用して、乱数ストリームを復元します。

rng(previousRngState);

ローカル リセット関数

sim 関数は、各シミュレーション エピソードの開始時にリセット関数を呼び出し、train 関数は、各学習エピソードの開始時にリセット関数を呼び出します。リセット関数は入力として Simulink.SimulationInput (Simulink) オブジェクトを受け取り、出力として同じ型のオブジェクトを返します。出力オブジェクトには、モデルに一時的に適用される変更が指定され、これらの変更はシミュレーションまたは学習の完了時に破棄されます。この例のリセット関数は、setBlockParameter (Simulink)を使用して基準信号と初期水位のランダムな値を指定します。詳細については、Reset Function for Simulink Environmentsを参照してください。

function in = localResetFcn(in)
    
    % Randomize reference signal
    blk = sprintf("rlwatertank/Desired \nWater Level");
    h = 3*randn + 10;
    while h <= 0 || h >= 20
        h = 3*randn + 10;
    end
    in = setBlockParameter(in,blk,Value=num2str(h));
    
    % Randomize initial water height   
    h = 3*randn + 10;
    while h <= 0 || h >= 20
        h = 3*randn + 10;
    end
    blk = "rlwatertank/Water-Tank System/H";
    in = setBlockParameter(in,blk,InitialCondition=num2str(h));
end

function in = localValidationResetFcn(in)
    % Set reference signal
    blk = sprintf("rlwatertank/Desired \nWater Level");
    h = 10;    
    in = setBlockParameter(in,blk,Value=num2str(h));
    
    % Set initial height
    h = 1;
    blk = "rlwatertank/Water-Tank System/H";
    in = setBlockParameter(in,blk,InitialCondition=num2str(h));
end

参考

関数

オブジェクト

トピック