Водное планирование системы распределения Используя обучение с подкреплением

В этом примере показано, как изучить оптимальную политику планирования насоса для водной системы распределения с помощью обучения с подкреплением (RL).

Водная система распределения

Следующий рисунок показывает водную систему распределения.

Здесь:

  • QSupply количество воды, предоставленной баку с водой от резервуара.

  • QDemand количество воды, вытекающей из бака, чтобы удовлетворить спросу на использование.

Цель агента обучения с подкреплением состоит в том, чтобы запланировать количество насосов, запускающихся, чтобы и минимизировать энергетическое использование системы и удовлетворить спросу на использование (h>0). Движущими силами системы бака управляет следующее уравнение.

Adhdt=QSupply(t)-QDemand(t)

Здесь, A=40m2 и hmax=7m. Спрос за 24-часовой период является функцией времени, данного как

QDemand(t)=μ(t)+η(t)

где μ(t)ожидаемый спрос и η(t) представляет неопределенность спроса, которая производится от универсального случайного распределения.

Предоставление определяется количеством выполнения насосов, a{0,1,2,3}согласно следующему отображению.

QSupply(t)=Q(a)={0a=0164a=1279a=2344a=3  cmh

Чтобы упростить проблему, потребление энергии задано как количество выполнения насосов, a.

Следующая функция является вознаграждением за эту среду. Чтобы постараться не переполнять или опорожнять бак, дополнительная стоимость добавляется, если уровень воды близко к максимальному или минимальному уровню воды, hmaxили hmin, соответственно.

r(h,a)=-10(h(hmax-0.1))-10(h0.1)-a

Сгенерируйте профиль спроса

Чтобы сгенерировать и профиль водопотребности на основе номера рассмотренных дней, используйте generateWaterDemand функция, определяемая в конце этого примера.

num_days = 4; % Number of days
[WaterDemand,T_max] = generateWaterDemand(num_days);

Просмотрите профиль спроса.

plot(WaterDemand)

Figure contains an axes object. The axes object with title Time Series Plot:Water Demand contains an object of type line.

Откройте и сконфигурируйте модель

Откройте модель Simulink системы распределения.

mdl = "watertankscheduling";
open_system(mdl)

В дополнение к агенту обучения с подкреплением простой базовый контроллер задан в блоке MATLAB function Закона о надзоре. Этот диспетчер активирует определенное число насосов в зависимости от уровня воды.

Задайте начальный уровень воды.

h0 = 3; % m

Задайте параметры модели.

SampleTime = 0.2;
H_max = 7; % Max tank height (m)
A_tank = 40; % Area of tank (m^2)

Создайте интерфейс среды для агента RL

Чтобы создать интерфейс среды для модели Simulink, сначала задайте действие и спецификации наблюдений, actInfo и obsInfo, соответственно. Действие агента является выбранным количеством насосов. Наблюдение агента является уровнем воды, который измеряется как сигнал непрерывного времени.

actInfo = rlFiniteSetSpec([0,1,2,3]);
obsInfo = rlNumericSpec([1,1]);

Создайте интерфейс среды.

env = rlSimulinkEnv(mdl,mdl+"/RL Agent",obsInfo,actInfo);

Задайте пользовательскую функцию сброса, которая задана в конце этого примера, который рандомизирует начальный уровень воды и водопотребность. Выполнение так позволяет агенту быть обученным на различном начальном уровне воды и функциях водопотребности для каждого эпизода.

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

Создайте агента DQN

Агент DQN аппроксимирует долгосрочное вознаграждение, заданные наблюдения и действия, с помощью представления Q-функции-ценности критика. Чтобы создать критика, сначала создайте глубокую нейронную сеть. Для получения дополнительной информации о создании представления функции ценности глубокой нейронной сети смотрите, Создают Представления Функции ценности и политика.

% Fix the random generator seed for reproducibility.
rng(0);

Создайте глубокую нейронную сеть для критика. В данном примере используйте единовременную нейронную сеть. Чтобы использовать рекуррентную нейронную сеть, установите useLSTM к true.

useLSTM = false;
if useLSTM
    layers = [
        sequenceInputLayer(obsInfo.Dimension(1),"Name","state","Normalization","none")
        fullyConnectedLayer(32,"Name","fc_1")
        reluLayer("Name","relu_body1")
        lstmLayer(32,"Name","lstm")
        fullyConnectedLayer(32,"Name","fc_3")
        reluLayer("Name","relu_body3")
        fullyConnectedLayer(numel(actInfo.Elements),"Name","output")];
else
    layers = [
        featureInputLayer(obsInfo.Dimension(1),"Name","state","Normalization","none")
        fullyConnectedLayer(32,"Name","fc_1")
        reluLayer("Name","relu_body1")
        fullyConnectedLayer(32,"Name","fc_2")
        reluLayer("Name","relu_body2")
        fullyConnectedLayer(32,"Name","fc_3")
        reluLayer("Name","relu_body3")
        fullyConnectedLayer(numel(actInfo.Elements),"Name","output")];
end

Задайте опции для создания представления критика.

criticOpts = rlRepresentationOptions('LearnRate',0.001,'GradientThreshold',1);

Создайте представление критика с помощью заданной глубокой нейронной сети и опций.

critic = rlQValueRepresentation(layerGraph(layers),obsInfo,actInfo,...
    'Observation',{'state'},criticOpts);

Создайте агента DQN

Чтобы создать агента, сначала задайте опции агента. Если вы используете сеть LSTM, установите длину последовательности на 20.

opt = rlDQNAgentOptions('SampleTime',SampleTime); 
if useLSTM
    opt.SequenceLength = 20;
else
    opt.SequenceLength = 1;
end
opt.DiscountFactor = 0.995;
opt.ExperienceBufferLength = 1e6;
opt.EpsilonGreedyExploration.EpsilonDecay = 1e-5;
opt.EpsilonGreedyExploration.EpsilonMin = .02;

Создайте агента с помощью заданных опций и представления критика.

agent = rlDQNAgent(critic,opt);    

Обучите агента

Чтобы обучить агента, сначала задайте опции обучения. В данном примере используйте следующие опции.

  • Запустите обучение 1 000 эпизодов с каждым эпизодом, длящимся в ceil(T_max/Ts) временные шаги.

  • Отобразите прогресс обучения в диалоговом окне Episode Manager (установите Plots опция)

Задайте опции для обучения с помощью rlTrainingOptions объект.

trainOpts = rlTrainingOptions(...
    'MaxEpisodes',1000, ...
    'MaxStepsPerEpisode',ceil(T_max/SampleTime), ...
    'Verbose',false, ...
    'Plots','training-progress',...
    'StopTrainingCriteria','EpisodeCount',...
    'StopTrainingValue',1000,...
    'ScoreAveragingWindowLength',100);

В то время как вы не делаете этого для этого примера, можно сохранить агентов во время учебного процесса. Например, следующие опции сохраняют каждого агента с премиальным значением, больше, чем или равный -42.

Save agents using SaveAgentCriteria if necessary
trainOpts.SaveAgentCriteria = 'EpisodeReward';
trainOpts.SaveAgentValue = -42;

Обучите агента с помощью train функция. Обучение этот агент является в вычислительном отношении интенсивным процессом, который занимает несколько часов, чтобы завершиться. Чтобы сэкономить время при выполнении этого примера, загрузите предварительно обученного агента установкой doTraining к false. Чтобы обучить агента самостоятельно, установите doTraining к true.

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

Следующий рисунок показывает процесс обучения.

Симулируйте агента DQN

Чтобы подтвердить производительность обученного агента, симулируйте его в среде бака с водой. Для получения дополнительной информации о симуляции агента смотрите rlSimulationOptions и sim.

Чтобы симулировать эффективность агента, соедините блок RL Agent путем переключения ручного блока switch.

set_param(mdl+"/Manual Switch",'sw','0');

Определите максимальный номер шагов для каждой симуляции и количества симуляций. В данном примере запустите 30 симуляций. Функция сброса среды устанавливает различный начальный уровень воды, и водопотребность отличаются в каждой симуляции.

NumSimulations = 30;
simOptions = rlSimulationOptions('MaxSteps',T_max/SampleTime,...
    'NumSimulations', NumSimulations);

Чтобы сравнить агента с базовым контроллером при тех же условиях, сбросьте начальный случайный seed, используемый в функции сброса среды.

env.ResetFcn("Reset seed");

Симулируйте агента против среды.

experienceDQN = sim(env,agent,simOptions);

Симулируйте базовый контроллер

Чтобы сравнить агента DQN с базовым контроллером, необходимо симулировать базовый контроллер, использующий те же опции симуляции и начальный случайный seed для функции сброса.

Включите базовому контроллеру.

set_param(mdl+"/Manual Switch",'sw','1');

Чтобы сравнить агента с базовым контроллером при тех же условиях, сбросьте случайный seed, используемый в функции сброса среды.

env.ResetFcn("Reset seed");

Симулируйте базовый контроллер против среды.

experienceBaseline = sim(env,agent,simOptions);

Сравните агента DQN с базовым контроллером

Инициализируйте совокупные премиальные итоговые векторы и для агента и для базового контроллера.

resultVectorDQN = zeros(NumSimulations, 1);
resultVectorBaseline = zeros(NumSimulations,1);

Вычислите совокупные вознаграждения и за агента и за базовый контроллер.

for ct = 1:NumSimulations
    resultVectorDQN(ct) = sum(experienceDQN(ct).Reward);
    resultVectorBaseline(ct) = sum(experienceBaseline(ct).Reward);
end

Постройте совокупные вознаграждения.

plot([resultVectorDQN resultVectorBaseline],'o')
set(gca,'xtick',1:NumSimulations)
xlabel("Simulation number")
ylabel('Cumulative Reward')
legend('DQN','Baseline','Location','NorthEastOutside')

Figure contains an axes object. The axes object contains 2 objects of type line. These objects represent DQN, Baseline.

Совокупное вознаграждение, полученное агентом, последовательно является приблизительно-40. Это значение очень больше среднего вознаграждения, полученного базовым контроллером. Поэтому агент DQN последовательно превосходит базовый контроллер по характеристикам в терминах энергосбережений.

Локальные функции

Функция водопотребности

function [WaterDemand,T_max] = generateWaterDemand(num_days)

    t = 0:(num_days*24)-1; % hr
    T_max = t(end);

    Demand_mean = [28, 28, 28, 45, 55, 110, 280, 450, 310, 170, 160, 145, 130, ...
        150, 165, 155, 170, 265, 360, 240, 120, 83, 45, 28]'; % m^3/hr

    Demand = repmat(Demand_mean,1,num_days);
    Demand = Demand(:);

    % Add noise to demand
    a = -25; % m^3/hr
    b = 25; % m^3/hr
    Demand_noise = a + (b-a).*rand(numel(Demand),1);

    WaterDemand = timeseries(Demand + Demand_noise,t);
    WaterDemand.Name = "Water Demand";
end

Функция сброса

function in = localResetFcn(in)

    % Use a persistent random seed value to evaluate the agent and the baseline
    % controller under the same conditions.
    persistent randomSeed
    if isempty(randomSeed)
        randomSeed = 0;
    end
    if strcmp(in,"Reset seed")
        randomSeed = 0;
        return
    end    
    randomSeed = randomSeed + 1;
    rng(randomSeed)
    
    % Randomize water demand.
    num_days = 4;
    H_max = 7;
    [WaterDemand,~] = generateWaterDemand(num_days);
    assignin('base','WaterDemand',WaterDemand)

    % Randomize initial height.
    h0 = 3*randn;
    while h0 <= 0 || h0 >= H_max
        h0 = 3*randn;
    end
    blk = 'watertankscheduling/Water Tank System/Initial Water Height';

    in = setBlockParameter(in,blk,'Value',num2str(h0));

end