Используя LSTM для идентификации линейной системы

В этом примере показано, как использовать нейронные сети Долгой краткосрочной памяти (LSTM), чтобы оценить линейную систему и сравнивает это с оценкой передаточной функции. Пример исследует способность LTSM получить базовую динамику смоделированной системы. Для этого LTSM обучен на входных и выходных данных от линейной передаточной функции. Гауссов белый шум используется, чтобы создать входной сигнал, вход применяется к системе, чтобы сгенерировать соответствующий выходной сигнал. Вход и соответствующие выходные сигналы затем используются, чтобы обучить сеть. Если LSTM обучен, для него определяют задачу с оценкой ответа системы на ступенчатое изменение. Способность LSTM получить системную динамику измеряется тем, как точно система может оценить ответ системы на ступенчатое изменение.

LSTM's является определенным типом сети от общего класса Рекуррентных нейронных сетей (RNN), которые способны к изучению временных функций и долгосрочных зависимостей от последовательного и данных временных рядов. Из-за взрывающегося явления градиента, с которым сталкиваются с RNNs, определенная архитектура, LSTM, появилась, чтобы решить эту проблему. А именно, модули LSTM делают вывод хорошо для получения временных зависимостей, не страдая от взрывающегося явления градиента. Поэтому они были успешно реализованы в широком спектре проблем raning от распознавания рукописного текста до белка вторичное предсказание структуры.

Первая версия модульной архитектуры LSTM была введена в 1 997 [4]. С тех пор различные улучшения архитектуры были сделаны: расширение LSTM с забывает логические элементы [1], разрабатывая двунаправленный LSTMs [2] и глубокий LSTM [3].

Передаточная функция

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

fourthOrderMdl = zpk(-4,[-9+5i;-9-5i;-2+50i;-2-50i],5e5);
[stepRespone,stepTime] = step(fourthOrderMdl);

Постройте переходной процесс передаточных функций

plot(stepTime,stepRespone)
grid on
axis tight
title('Fourth order mixed step response')

Пропускная способность системы (который измеряется как первая частота, где усиление опускается ниже 70,79% (~3dB) его значения DC)

bodeplot(fourthOrderMdl)

fb = bandwidth(fourthOrderMdl)
fb = 62.8858

Сгенерируйте обучающие данные

Для того, чтобы исследовать, получаются ли базовые движущие силы, LSTM обучен на случайном гауссовом шуме. После этого обучающего сеть определен задачу с предсказанием ответа системы на вход шага. Способность LSTM получить базовую динамику поэтому измеряется ее успехом в предсказании переходного процесса. Для этого процесса Гауссов шумовой сигнал сгенерирован, как введено к передаточным функциям, и LSTM обучен на случайном сигнале и соответствующем ответе от передаточной функции.

Гауссовы шумовые данные

Случайный Гауссов шумовой сигнал сгенерирован как учебный сигнал. Сигнал производится 100 раз в секунду и имеет длительность 50 секунд

signalType = 'rgs'; % Gaussian 
signalLength = 5000; % Number of points in the signal
fs = 100; % Sample frequency.
signalAmplitude = 1; % Maximum signal amplitude.

Гауссов шумовой сигнал сгенерирован с помощью встроенного idinput Функция MATLAB. Функция требует, чтобы длина сигнала и тип сигнала были заданы. Здесь, rgs выбран, чтобы сгенерировать случайный Гауссов шум.

Сгенерируйте и масштабируйте учебный сигнал

urgs = idinput(signalLength,signalType);
urgs = (signalAmplitude/max(urgs))*urgs';

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

trgs = 0:1/fs:length(urgs)/fs-1/fs;

lsim функция используется, чтобы сгенерировать ответ системы. Соответствующий ответ от системы сгенерирован и сохранен в yrgs. Симулированный выход транспонирован, чтобы соответствовать структуре входа LSTM, которая требует векторов-строк и не вектор-столбцов.

yrgs = lsim(fourthOrderMdl,urgs,trgs);
yrgs = yrgs';

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

xval = idinput(100,signalType);
yval = lsim(fourthOrderMdl,xval,trgs(1:100));

Обучение сеть

Сетевая архитектура была определена при помощи Байесовой стандартной программы оптимизации, где Байесова функция стоимости оптимизации использует независимые данные о валидации (см. сопроводительный bayesianOptimizationForLSTM.mlx для деталей). Несмотря на то, что несколько архитектур могут работать, в вычислительном отношении самый эффективный разыскивается. Кроме того, было замечено, что, когда сложность передаточной функции увеличивается, применяя LSTM к другим линейным передаточным функциям, архитектура сети значительно не изменяется. Скорее номер эпох должен был обучить сетевые увеличения. Количество скрытых модулей, требуемых для моделирования системы, связано с тем, сколько времени движущие силы берут к ослаблению. В этом случае существует две отличных части к ответу: высокочастотный ответ и низкочастотный ответ. Более высокое количество скрытых модулей требуется, чтобы получать низкочастотный ответ. Если более низкое количество модулей выбрано, высокочастотный ответ все еще моделируется. Однако оценка низкочастотного ответа ухудшается.

Подготовка сетевой архитектуры

% Network architecture
numResponses = 1;
featureDimension = 1;
numHiddenUnits = 100;
maxEpochs = 1000;
miniBatchSize = 200;

Networklayers = [sequenceInputLayer(featureDimension) ...
    lstmLayer(numHiddenUnits) ...
    lstmLayer(numHiddenUnits) ...
    fullyConnectedLayer(numResponses) ...
    regressionLayer];

Установка сетевой начальной скорости обучения важна; использование начальной скорости обучения, которая является слишком высокими результатами в высоких градиентах, который приводит к более длительным учебным временам. Более длительные учебные времена могли привести к насыщению полносвязного слоя сети. Когда сеть насыщает, выходные параметры отличаются и сетевые выходные параметры NaN значение. Следовательно небольшая начальная скорость обучения, значение по умолчанию 0,01, использовалась. Это приводит к монотонно уменьшающейся невязке и кривым потерь. Кусочное расписание уровня использовалось, чтобы избежать алгоритма оптимизации, захватываемого в локальных минимумах в начале стандартной программы оптимизации.

options = trainingOptions('adam', ...
    'MaxEpochs',maxEpochs, ...
    'MiniBatchSize',miniBatchSize, ...
    'GradientThreshold',10, ...
    'Shuffle','once', ...
    'Plots','training-progress',...
    'ExecutionEnvironment','gpu',...
    'LearnRateSchedule','piecewise',...
    'LearnRateDropPeriod',100,...
    'Verbose',0,...
    'ValidationData',[{xval'} {yval'}]);

loadNetwork = true; %Set to true to train the network using a parallel pool.
if loadNetwork
    load('fourthOrderMdlnet','fourthOrderNet')
else
    poolobj = parpool;
    fourthOrderNet = trainNetwork(urgs,yrgs,Networklayers,options);
    delete(poolobj)
    save('fourthOrderMdlnet','fourthOrderNet','urgs','yrgs');
end

Оценка производительности модели

Уровень сети измерен тем, насколько успешный это было в получении динамического поведения системы. Способность получить динамическое поведение системы в свою очередь измеряется способностью сети точно предсказать ответ системы на вход шага.

Во-первых, создайте вход шага.

stepTime = 2; % In seconds
stepAmplitue = 0.1;
stepDuration = 4; % In seconds

% Constuct step signal and system output
time = (0:1/fs:stepDuration)';
stepSignal = [zeros(sum(time<=stepTime),1);stepAmplitue*ones(sum(time>stepTime),1)];
systemResponse = lsim(fourthOrderMdl,stepSignal,time);

% Tranpose input and output signal for network inputs.
stepSignal = stepSignal';
systemResponse = systemResponse';

Обучивший сеть используется, чтобы оценить ответ системы. После этого система и оцененные ответы построены для сравнения.

fourthOrderMixedNetStep = predict(fourthOrderNet,stepSignal);
Warning: Support for GPU devices with Compute Capability 3.0 will be removed in a future MATLAB release. For more information on GPU support, see <a href="matlab:web('http://www.mathworks.com/help/parallel-computing/gpu-support-by-release.html','-browser')">GPU Support by Release</a>.
figure
title('Step response estimation')
plot(time,systemResponse,'k', time, fourthOrderMixedNetStep)
grid on
legend('System','Estimated')
title('Fourth Order Step')

Корректировка подгонки и инициализация сети

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

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

Установка начального состояния сети

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

Поскольку шаг только происходит в 2 секунды, мы интересуемся только ячейкой и скрытыми значениями состояния в течение первых двух секунд. Маркер времени в течение 2 секунд задан, и скрытые состояния и состояния ячейки затем извлечены до этого маркера времени.

stepMarker = time <= 2;
yhat = zeros(sum(stepMarker),1);
hiddenState = zeros(sum(stepMarker),200); %200 LTSM units
cellState = zeros(sum(stepMarker),200);
for ntime = 1:sum(stepMarker)
    [fourthOrderNet,yhat(ntime)] = predictAndUpdateState(fourthOrderNet,stepSignal(ntime)');
    hiddenState(ntime,:) = fourthOrderNet.Layers(2,1).HiddenState;
    cellState(ntime,:) = fourthOrderNet.Layers(2,1).CellState;
end

Затем постройте скрытые состояния и состояния ячейки за период перед шагом.

figure
subplot(2,1,1)
plot(time(1:200),hiddenState(1:200,:))
grid on
axis tight
title('Hidden State')
subplot(2,1,2)
plot(time(1:200),cellState(1:200,:))
grid on
axis tight
title('Cell State')

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

initializationSignalDuration = 10; %second
initializationValue = 0;
initilizationSignal = initializationValue*ones(1,initializationSignalDuration*fs);

fourthOrderNet = predictAndUpdateState(fourthOrderNet,initilizationSignal);

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

figure
zeroMapping = predict(fourthOrderNet,initilizationSignal);
plot(zeroMapping)
axis tight

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

fourthOrderMixedNetStep = predict(fourthOrderNet,stepSignal);

figure
title('Step response estimation')
plot(time,systemResponse,'k', ...
    time,fourthOrderMixedNetStep,'b')
grid on
legend('System','Estimated')
title('Fourth Order Step - Adjusted State')

Корректировка сетевого смещения

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

bias = mean(predict(fourthOrderNet,initilizationSignal));
fourthOrderMixedNetStep = fourthOrderMixedNetStep-bias;

figure
title('Step response estimation')
plot(time,systemResponse,'k',time,fourthOrderMixedNetStep,'b-')
legend('System','Estimated')
title('Fourth Order Step - Adjusted Offset')

Перемещение за пределами учебной области значений

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

Временной сдвиг

Временной сдвиг введен путем корректировки времени шага. В наборе обучающих данных это было установлено в 2 секунды. Здесь это установлено в 3 секунды.

stepTime = 3; % In seconds
stepAmplitue = 0.1;
stepDuration = 5; % In seconds
[stepSignal,systemResponse,time] = generateStepResponse(fourthOrderMdl,stepTime,stepAmplitue,stepDuration);

fourthOrderMixedNetStep = predict(fourthOrderNet,stepSignal);
bias = fourthOrderMixedNetStep(1) - initializationValue;
fourthOrderMixedNetStep = fourthOrderMixedNetStep-bias;

figure
plot(time,systemResponse,'k', time,fourthOrderMixedNetStep,'b')
grid on
axis tight

Амплитудный сдвиг

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

figure
histogram(urgs,'Normalization','pdf')
grid on

Затем амплитуда ступенчатой функции установлена согласно процентили распределения. График коэффициента ошибок как функция процентили также представлен.

pValues = [60:2:98, 90:1:98, 99:0.1:99.9 99.99];
stepAmps = prctile(urgs,pValues); % Amplitudes
stepTime = 3; % In seconds
stepDuration = 5; % In seconds

stepMSE = zeros(length(stepAmps),1);
fourthOrderMixedNetStep = cell(length(stepAmps),1);
steps = cell(length(stepAmps),1);

for nAmps = 1:length(stepAmps)
    % Fourth order mixed
    [stepSignal,systemResponse,time] = generateStepResponse(fourthOrderMdl,stepTime,stepAmps(nAmps),stepDuration);
    
    fourthOrderMixedNetStep{nAmps} = predict(fourthOrderNet,stepSignal);
    bias = fourthOrderMixedNetStep{nAmps}(1) - initializationValue;
    fourthOrderMixedNetStep{nAmps} = fourthOrderMixedNetStep{nAmps}-bias;
    
    stepMSE(nAmps) = sqrt(sum((systemResponse-fourthOrderMixedNetStep{nAmps}).^2));
    steps{nAmps,1} = systemResponse;
end

figure
plot(pValues,stepMSE,'bo')
title('Prediction Error as a function of deviation from training range')
grid on
axis tight

subplot(2,1,1)
plot(time,steps{1},'k', time,fourthOrderMixedNetStep{1},'b')
grid on
axis tight
title('Best Performance')
xlabel('time')
ylabel('System Response')
subplot(2,1,2)
plot(time,steps{end},'k', time,fourthOrderMixedNetStep{end},'b')
grid on
axis tight
title('Worst Performance')
xlabel('time')
ylabel('System Response')

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

Изменение Системной пропускной способности

В этом разделе мы смотрим кратко эффекта системная пропускная способность на количестве скрытого модульного выбора для LSTM. Это сделано путем моделирования передаточной функции смешанной динамики четвертого порядка с четырьмя различными сетями:

  • маленькая сеть, которая имеет 5 скрытых модулей и один слой LSTM,

  • средняя сеть с 10 скрытыми модулями и одним слоем LSTM,

  • полная сеть с 100 скрытыми модулями и одним слоем LSTM,

  • глубокая сеть с двумя слоями LSTM (каждый с 100 скрытыми модулями).

Эти сети были обучены и обновили свои состояния, таким же образом аналогичные описанному выше. Загрузите обучивший нейронные сети

load('variousHiddenUnitNets.mat')

Сгенерируйте сигнал шага

stepTime = 2; % In seconds
stepAmplitue = 0.1;
stepDuration = 4; % In seconds

% Constuct step signal
time = (0:1/fs:stepDuration)';

stepSignal = [zeros(sum(time<=stepTime),1);stepAmplitue*ones(sum(time>stepTime),1)];
systemResponse = lsim(fourthOrderMdl,stepSignal,time);

% Transpose input and output signal for network inputs.
stepSignal = stepSignal';
systemResponse = systemResponse';

Оцените, что отклик системы с помощью различного обучил нейронные сети

smallNetStep = predict(smallNet,stepSignal)-smallNetZeroMapping(end);
medNetStep = predict(medNet,stepSignal)-medNetZeroMapping(end);
fullnetStep = predict(fullNet,stepSignal) - fullNetZeroMapping(end);
doubleNetStep = predict(doubleNet,stepSignal) - doubleNetZeroMapping(end);
 

Постройте оценочный ответ

figure
title('Step response estimation')
plot(time,systemResponse,'k', ...
    time,doubleNetStep,'g', ...
    time,fullnetStep,'r', ...
    time,medNetStep,'c', ...
    time,smallNetStep,'b')
grid on
legend({'System','Double Net','Full Net','Med Net','Small Net'},'Location','northwest')
title('Fourth Order Step')

Скользящее среднее значение ответов построено для того, чтобы сравнить медленную различную динамику системы. Способность LSTM получить долгосрочную динамику линейной системы непосредственно связана с динамикой системы и номера iof скрытые модули в LSTM. Количество слоев в LSTM непосредственно не связано с долгосрочным поведением, а скорее добавляет гибкость, чтобы настроить оценку от первого слоя.

figure
title('Slow dynamics component')
plot(time,movmean(systemResponse,50),'k')
hold on
plot(time,movmean(doubleNetStep,50),'g')
plot(time,movmean(fullnetStep,50),'r')
plot(time,movmean(medNetStep,50),'c')
plot(time,movmean(smallNetStep,50),'b')
grid on
legend('System','Double Net','Full net','Med Net','Small Net','Location','northwest')
title('Fourth Order Step')

Добавление шума

В этом разделе случайный шум добавляется к выходу отклика системы. Цель состоит в том, чтобы исследовать эффект шума на производительности LSTM. С этой целью белый шум с уровнями 1%, 5% и 10% был добавлен к измеренным откликам системы. Оценить робастность LSTMs к шуму tfest функция используется в качестве базовой линии, чтобы сравнить оценки с. LSTM и tfest применяется к тем же наборам данных.

Ступенчатая функция до используется:

stepTime = 2; % In seconds
stepAmplitue = 0.1;
stepDuration = 4; % In seconds

[stepSignal,systemResponse,time] = generateStepResponse(fourthOrderMdl,stepTime,stepAmplitue,stepDuration);

Загрузка обучила нейронные сети, и оцените ответ системы

load('noisyDataNetworks.mat')
netNoise1Step = predictAndAdjust(netNoise1,stepSignal,initilizationSignal,initializationValue);
netNoise5Step = predictAndAdjust(netNoise5,stepSignal,initilizationSignal,initializationValue);
netNoise10Step = predictAndAdjust(netNoise10,stepSignal,initilizationSignal,initializationValue);

Средство оценки передаточной функции (tfest) используется, чтобы оценить, что функция на вышеупомянутом уровне шума сравнивает упругость сетей к шуму (см. сопроводительный noiseLevelModels.m для получения дополнительной информации).

load('noisyDataTFs.mat')
tfStepNoise1 = lsim(tfNoise1,stepSignal,time);
tfStepNoise5 = lsim(tfNoise5,stepSignal,time);
tfStepNoise10 = lsim(tfNoise10,stepSignal,time);

Постройте сгенерированные ответы.

figure
plot(time,systemResponse,'k', ...
    time,netNoise1Step, ...
    time,netNoise5Step, ...
    time,netNoise10Step)
grid on
legend('System Response','1% Noise','5% Noise','10% Noise')
title('Deep LSTM with noisy data')

Подобный график сделан для подходящих передаточных функций.

figure
plot(time,systemResponse,'k', ...
    time,tfStepNoise1, ...
    time,tfStepNoise5, ...
    time,tfStepNoise10)
grid on
legend('System Response','1% Noise','5% Noise','10% Noise')
title('Transfer functions fitted to noisy data')

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

msefun = @(y,yhat) mean(sqrt((y-yhat).^2)/length(y));

% LSTM errors
lstmMSE(1,:) = msefun(systemResponse,netNoise1Step);
lstmMSE(2,:) = msefun(systemResponse,netNoise5Step);
lstmMSE(3,:) = msefun(systemResponse,netNoise10Step);

% Transfer function errors
tfMSE(1,:) = msefun(systemResponse,tfStepNoise1');
tfMSE(2,:) = msefun(systemResponse,tfStepNoise5');
tfMSE(3,:) = msefun(systemResponse,tfStepNoise10');

mseTbl = array2table([lstmMSE tfMSE],'VariableNames',{'LSTMMSE','TFMSE'})
mseTbl=3×2 table
     LSTMMSE        TFMSE   
    __________    __________

    1.0115e-05    8.8621e-07
    2.5577e-05    9.9064e-06
    5.1791e-05    3.6831e-05

Ссылки

[1] 1] Gers, Феликс А., Юрген Шмидхубер и Фред Камминс. “Обучение Забыть: Непрерывное Предсказание с LSTM”. Нейронный Расчет 12, № 10 (октябрь 2000): 2451–71.

[2] Могилы, Алекс, Сантьяго Фернандес и Юрген Шмидхубер. “Двунаправленные Сети LSTM для Улучшенной Классификации Фонем и Распознавания”. Международная конференция по вопросам Искусственных Нейронных сетей (сентябрь 2005): 799–804. Варшава, Польша: Спрингер.

[3] Могилы, Алекс, Абдель-рахман Мохамед и Джеффри Хинтон. “Распознавание речи с глубокими рекуррентными нейронными сетями”. Международная конференция IEEE по вопросам акустики, речи и обработки сигналов (март 2013):6645–6649. IEEE.

[4] Hochreiter, Sepp. “Исчезающая проблема Градиента Во время Изучения Текущих Нейронных Сетей и проблемных Решений”. Международный журнал Неопределенности, Нечеткости и Систем, Основанных на знаниях, 06, № 02 (апрель 1998): 107–16.

Функции помощника

function [stepSignal,systemResponse,time] = generateStepResponse(model,stepTime,stepAmp,signalDuration)
%Generates a step response for the given model.
%

%Check model type
modelType = class(model);
if nargin < 2
    stepTime = 1;
end

if nargin < 3
    stepAmp = 1;
end

if nargin < 4
    signalDuration = 10;
end

% Constuct step signal
if model.Ts == 0
    Ts = 1e-2;
    time = (0:Ts:signalDuration)';
else
    time = (0:model.Ts:signalDuration)';
end
stepSignal = [zeros(sum(time<=stepTime),1);stepAmp*ones(sum(time>stepTime),1)];
switch modelType
    case {'tf', 'zpk'}
        systemResponse = lsim(model,stepSignal,time);
    case 'idpoly'
        systemResponse = sim(model,stepSignal,time);
    otherwise
        error('Model passed is not supported')
end

stepSignal = stepSignal';
systemResponse = systemResponse';
end