Обучите нейронную сеть ОДУ

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

Нейронные ОДУ [1] являются операциями глубокого обучения, заданными решением обыкновенного дифференциального уравнения (ODE). А именно, нейронное ОДУ является слоем, который может использоваться в любой архитектуре и, даваться вход, задает его выход как числовое решение следующего ОДУ:

y=f(t,y,θ)

в течение периода времени (t0,t1) и с начальным условием y(t0)=y0. Правый размер f(t,y,θ) из ОДУ зависит от набора обучаемых параметров θ, которые изучены во время учебного процесса. В этом примере f(t,y,θ) моделируется с dlnetwork объект, который встраивается на пользовательский слой. Как правило, начальное условие y0 или вход целой сети, как в случае этого примера, или выход предыдущего слоя.

В этом примере показано, как обучить нейронную сеть с нейронными обыкновенными дифференциальными уравнениями (ОДУ), чтобы изучить динамику x из данной физической системы, описанной следующим ОДУ:

x=Ax,

где A2 x 2 матрицы.

В этом примере ОДУ, который задает модель, решен численно с 4-м порядком явный метод Рунге-Кутта (RK) [2] в прямой передаче. Обратный проход использует автоматическое дифференцирование, чтобы изучить обучаемые параметры θ, backpropagating посредством каждой работы решателя RK.

Пример затем показывает, как использовать изученную функцию f(t,y,θ) как правая сторона модели в ode45 для вычисления решения той же модели от дополнительных начальных условий.

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

Задайте целевую динамику как линейную модель ODE.

x0 = [2; 0];
A = [-0.1 -1; 1 -0.1];

Найдите числовое решение проблемы выше. Создайте набор точек данных, чтобы использовать для обучения с помощью функции ode45.

numTimeSteps = 4000;
T = 15;
odeOptions = odeset('RelTol', 1.e-7, 'AbsTol', 1.e-9);
t = linspace(0, T, numTimeSteps);
[~, x] = ode45(@(t,y) A*y, t, x0, odeOptions);
x = x';

Сеть Define

Задайте dlnetwork объект, который используется в качестве Learnable свойство в пользовательском слое neuralOdeLayer.

Для каждого наблюдения, neuralOdeInternalDlnetwork берет вектор из длины inputSize, размер решения для ОДУ, как введено, это увеличивает его так, чтобы это имело длину hiddenSize и затем сжатия это снова, чтобы иметь длину outputSize. Объект neuralOdeInternalDlnetwork задает правую сторону f(t,y,θ)из ОДУ, которое будет решено и обучаемые параметры θ learnables neuralOdeInternalDlnetwork. Можно сделать нейронную архитектуру оды более выразительной, например, путем увеличения скрытого размера или количества слоев, которые задают neuralOdeInternalDlnetwork.

hiddenSize = 30;
inputSize = size(x,1);
outputSize = inputSize;

neuralOdeLayers = [
    fullyConnectedLayer(hiddenSize)
    tanhLayer
    fullyConnectedLayer(hiddenSize)
    tanhLayer
    fullyConnectedLayer(outputSize)
    ];

neuralOdeInternalDlnetwork = dlnetwork(neuralOdeLayers,'Initialize',false);
neuralOdeInternalDlnetwork.Learnables
ans=6×3 table
    Layer     Parameter       Value    
    ______    _________    ____________

    "fc_1"    "Weights"    {0×0 double}
    "fc_1"    "Bias"       {0×0 double}
    "fc_2"    "Weights"    {0×0 double}
    "fc_2"    "Bias"       {0×0 double}
    "fc_3"    "Weights"    {0×0 double}
    "fc_3"    "Bias"       {0×0 double}

Задайте нейронный слой ODE, который имеет dlnetwork возразите как настраиваемый параметр. Пользовательский слой neuralODELayer берет мини-пакет данных, которые представляют начальные условия и выводят предсказанную динамику путем числового решения задачи y=f(t,y,θ), где f(t,y,θ) моделируется neuralOdeInternalDlnetwork.

The value из neuralOdeInternalTimesteps определяет номер тактов тот из решателя ОДУ Рунге-Кутта, внутренне используемого в пользовательском слое neuralOdeLayer. Значение dt размер шага, используемый во внутреннем методе RK.

neuralOdeInternalTimesteps = 40;
dt = t(2);
neuralOdeLayerName = 'neuralOde';

customNeuralOdeLayer = neuralOdeLayer(neuralOdeInternalDlnetwork,neuralOdeInternalTimesteps,dt,neuralOdeLayerName);

Объявите внешний dlnetwork возразите и инициализируйте сеть. Это инициализирует также объект dlnetwork в пользовательском neuralOdeLayer.

dlnet=dlnetwork(customNeuralOdeLayer,'Initialize',false);
dlnet = initialize(dlnet, dlarray(ones(inputSize,1),'CB'));

Задайте опции обучения

Задайте опции для оптимизации ADAM.

gradDecay = 0.9;
sqGradDecay = 0.999;
learnRate = 0.001;

Обучайтесь для 400 итераций с "мини-пакетным размером" 50.

numIter = 1500;
miniBatchSize = 200;

Инициализируйте график процесса обучения.

plots = "training-progress";
lossHistory = [];

Каждые 50 итераций, решите с ode45 изученную динамику, описанную как внутренний объект dlnetwork в объекте neuralOdeLayer, и отобразите его с основной истиной в схеме фазы, это показывает учебный путь изученной динамики.

plotFrequency = 50;

Обучите сеть Используя пользовательский учебный цикл

Инициализируйте averageGrad и averageSqGrad параметры для решателя ADAM.

averageGrad = [];
averageSqGrad= [];

Для каждой итерации:

  • создайте мини-пакет данных из синтезируемых данных.

  • оцените градиенты модели и потерю с помощью dlfeval и функции modelGradients.

  • обновите сетевые параметры с помощью adamupdate функция.

if plots == "training-progress"
    figure(1)
    clf
    title('Training Loss');
    lossline = animatedline;
    xlabel('Iteration')
    ylabel("Loss")
    grid on
end
numTrainingTimesteps = numTimeSteps;
trainingTimesteps = 1:numTrainingTimesteps;

start = tic;

for iter=1:numIter
    
    % Create batch 
    [dlx0, targets] = createMiniBatch(numTrainingTimesteps, neuralOdeInternalTimesteps, miniBatchSize, x);
    
    % Evaluate network and compute gradients 
    [grads,loss] = dlfeval(@modelGradients,dlnet,dlx0,targets);
    
    % Update network 
    [dlnet,averageGrad,averageSqGrad] = adamupdate(dlnet,grads,averageGrad,averageSqGrad,iter,...
        learnRate,gradDecay,sqGradDecay);
    
    % Plot loss
    currentLoss = extractdata(loss);
    
    if plots == "training-progress"
        addpoints(lossline, iter, currentLoss);
        drawnow
    end
    
    % Plot predicted vs. real dynamics
    if mod(iter,plotFrequency) == 0
        figure(2)
        clf

        % Extract the learnt dynamics
        internalNeuralOdeLayer = dlnet.Layers(1);
        dlnetODEFcn = @(t,y) evaluateODE(internalNeuralOdeLayer, y);

        % Use ode45 to compute the solution 
        [~,y] = ode45(dlnetODEFcn, [t(1) t(end)], x0, odeOptions);
        y = y';
        
        plot(x(1,trainingTimesteps),x(2,trainingTimesteps),'r--')
        hold on
        plot(y(1,:),y(2,:),'b-')
        hold off
        D = duration(0,0,toc(start),'Format','hh:mm:ss');
        title("Iter = " + iter + ", loss = " + num2str(currentLoss) + ", Elapsed: " + string(D))
        legend('Training ground truth', 'Predicted')
    end
end

Оцените модель

Используйте изученную модель в качестве правой стороны для той же проблемы с различными начальными условиями, решите численно изученную динамику ОДУ с ode45 и сравните его с использованием истинной модели.

tPred = t;
x0Pred1 = sqrt([2;2]);
x0Pred2 = [-1;-1.5];
x0Pred3 = [0;2];
x0Pred4 = [-2;0];

[xPred1, xTrue1, err1] = predictWithOde45(dlnet,A,tPred,x0Pred1,odeOptions);
[xPred2, xTrue2, err2] = predictWithOde45(dlnet,A,tPred,x0Pred2,odeOptions);
[xPred3, xTrue3, err3] = predictWithOde45(dlnet,A,tPred,x0Pred3,odeOptions);
[xPred4, xTrue4, err4] = predictWithOde45(dlnet,A,tPred,x0Pred4,odeOptions);

Постройте предсказанные решения против решений для основной истины.

subplot(2,2,1)
plotTrueAndPredictedSolutions(xTrue1, xPred1, err1, "[sqrt(2) sqrt(2)]");

subplot(2,2,2)
plotTrueAndPredictedSolutions(xTrue2, xPred2, err2, "[-1 -1.5]");

subplot(2,2,3)
plotTrueAndPredictedSolutions(xTrue3, xPred3, err3, "[0 2]");

subplot(2,2,4)
plotTrueAndPredictedSolutions(xTrue4, xPred4, err4, "[-2 0]");

Функция градиентов модели

Эта функция берет набор начальных условий, dlx0, и целевые последовательности, targets, и вычисляет потерю и градиенты относительно параметров сети.

function [gradients,loss] = modelGradients(dlnet, dlX0, targets)

% Compute prediction of network
dlX = forward(dlnet,dlX0);

% Compute mean absolute error loss
loss = sum(abs(dlX - targets), 'all') / numel(dlX);

% Compute gradients
gradients = dlgradient(loss,dlnet.Learnables);

end

Эта функция создает пакет наблюдений за целевой динамикой.

function [dlX0, dlT] = createMiniBatch(numTimesteps, numTimesPerObs, miniBatchSize, X)
% Create batches of trajectories
s = randperm(numTimesteps - numTimesPerObs, miniBatchSize);

dlX0 = dlarray(X(:, s),'CB');
dlT = zeros([size(dlX0,1) miniBatchSize numTimesPerObs]);

for i = 1:miniBatchSize
    dlT(:, i, 1:numTimesPerObs) = X(:, s(i):(s(i) + numTimesPerObs - 1));
end
end

Функциональный predictWithOde45 вычисляет истинные и предсказанные решения и возвращает их вместе с соответствующей ошибкой.

function [xPred, xTrue, error] = predictWithOde45(dlnet,A,tPred,x0Pred,odeOptions)
% Use ode45 to compute the solution both with the true and the learnt
% models.

[~, xTrue] = ode45(@(t,y) A*y, tPred, x0Pred, odeOptions);

% Extract the learnt dynamics
internalNeuralOdeLayer = dlnet.Layers(1);
dlnetODEFcn = @(t,y) evaluateODE(internalNeuralOdeLayer, y);

[~,xPred] = ode45(dlnetODEFcn, tPred, x0Pred, odeOptions);
error = mean(abs(xTrue - xPred), 'all');
end

function plotTrueAndPredictedSolutions(xTrue, xPred, err, x0Str)
plot(xTrue(:,1),xTrue(:,2),'r--',xPred(:,1),xPred(:,2),'b-','LineWidth',1)
title("x_0 = " + x0Str + ", err = " + num2str(err) )
xlabel('x1')
ylabel('x2')
xlim([-2 2])
ylim([-2 2])
legend('Ground truth', 'Predicted')
end

[1] Нейронные Обыкновенные дифференциальные уравнения, Рики Т. К. Чен, Юлия Рубанова, Джесси Бетанкур, Давид Дювено, 2019, https://arxiv.org/abs/1806.07366

[2] Числовая математика, Quarteroni, Р Сакко, F Saleri - 2010

Смотрите также

| |

Похожие темы