Main Content

Sequence-to-One Regression Using Deep Learning

This example shows how to predict the frequency of a waveform using a long short-term memory (LSTM) neural network.

You can use an LSTM neural network to predict a numeric response of a sequence using a training set of sequences and target values. An LSTM network is a recurrent neural network (RNN) that processes input data by looping over time steps and updating the network state. The network state contains information remembered over previous time steps. Examples of numeric responses of a sequence include:

  • Properties of the sequence, such as its frequency, maximum value, and mean.

  • Values of past or future time steps of the sequence.

This example trains a sequence-to-one regression LSTM network using the Waveform data set, which contains 1000 synthetically generated waveforms of varying lengths with three channels. To determine the frequency of a waveform using conventional methods, see fft.

Load Sequence Data

Load the example data from WaveformData.mat. The data is a numObservations-by-1 cell array of sequences, where numObservations is the number of sequences. Each sequence is a numChannels-by-numTimeSteps numeric array, where numChannels is the number of channels of the sequence and numTimeSteps is the number of time steps in the sequence. The corresponding targets are in a numObservations-by-numResponses numeric array of the frequencies of the waveforms, where numResponses is the number of channels of the targets.

load WaveformData

View the number of observations.

numObservations = numel(data)
numObservations = 1000

View the sizes of the first few sequences and the corresponding frequencies.

data(1:4)
ans=4×1 cell array
    {3×103 double}
    {3×136 double}
    {3×140 double}
    {3×124 double}

freq(1:4,:)
ans = 4×1

    5.8922
    2.2557
    4.5250
    4.4418

View the number of channels of the sequences. For network training, each sequence must have the same number of channels.

numChannels = size(data{1},1)
numChannels = 3

View the number of responses (the number of channels of the targets).

numResponses = size(freq,2)
numResponses = 1

Visualize the first few sequences in plots.

figure
tiledlayout(2,2)
for i = 1:4
    nexttile
    stackedplot(data{i}', DisplayLabels="Channel " + (1:numChannels))

    xlabel("Time Step")
    title("Frequency: " + freq(i))
end

Prepare Data for Training

Set aside data for validation and testing. Partition the data into a training set containing 80% of the data, a validation set containing 10% of the data, and a test set containing the remaining 10% of the data.

[idxTrain,idxValidation,idxTest] = trainingPartitions(numObservations, [0.8 0.1 0.1]);

XTrain = data(idxTrain);
XValidation = data(idxValidation);
XTest = data(idxTest);

TTrain = freq(idxTrain);
TValidation = freq(idxValidation);
TTest = freq(idxTest);

Define LSTM Network Architecture

Create an LSTM regression network.

  • Use a sequence input layer with an input size that matches the number of channels of the input data.

  • For a better fit and to prevent the training from diverging, set the Normalization option of the sequence input layer to "zscore". This normalizes the sequence data to have zero mean and unit variance.

  • Use an LSTM layer with 100 hidden units. The number of hidden units determines how much information is learned by the layer. Larger values can yield more accurate results but can be more susceptible to overfitting to the training data.

  • To output a single time step for each sequence, set the OutputMode option of the LSTM layer to "last".

  • To specify the number of values to predict, include a fully connected layer with a size matching the number of predictors, followed by a regression layer.

numHiddenUnits = 100;

layers = [ ...
    sequenceInputLayer(numChannels, Normalization="zscore")
    lstmLayer(numHiddenUnits, OutputMode="last")
    fullyConnectedLayer(numResponses)
    regressionLayer]
layers = 
  4×1 Layer array with layers:

     1   ''   Sequence Input      Sequence input with 3 dimensions
     2   ''   LSTM                LSTM with 100 hidden units
     3   ''   Fully Connected     1 fully connected layer
     4   ''   Regression Output   mean-squared-error

Specify Training Options

Specify the training options.

  • Train using the Adam optimizer.

  • Train for 250 epochs. For larger data sets, you might not need to train for as many epochs for a good fit.

  • Specify the sequences and responses used for validation.

  • Output the network that gives the best, i.e. lowest, validation loss.

  • Set the learning rate to 0.005.

  • Truncate the sequences in each mini-batch to have the same length as the shortest sequence. Truncating the sequences ensures that no padding is added, at the cost of discarding data. For sequences where all of the time steps in the sequence are likely to contain important information, truncation can prevent the network from achieving a good fit.

  • Display the training process in a plot.

  • Disable the verbose output.

options = trainingOptions("adam", ...
    MaxEpochs=250, ...
    ValidationData={XValidation TValidation}, ...
    OutputNetwork="best-validation-loss", ...
    InitialLearnRate=0.005, ...
    SequenceLength="shortest", ...
    Plots="training-progress", ...
    Verbose= false);

Train LSTM Network

Train the LSTM network with the specified training options using the trainNetwork function.

net = trainNetwork(XTrain, TTrain, layers, options);

Test LSTM Network

Make predictions using the test data.

YTest = predict(net,XTest, SequenceLength="shortest");

Visualize the first few predictions in a plot.

figure
tiledlayout(2,2)
for i = 1:4
    nexttile
    stackedplot(XTest{i}',DisplayLabels="Channel " + (1:numChannels))

    xlabel("Time Step")
    title("Predicted Frequency: " + string(YTest(i)))
end

Visualize the mean squared errors in a histogram.

figure
histogram(mean((TTest - YTest).^2,2))
xlabel("Error")
ylabel("Frequency")

Calculate the overall root mean squared error.

rmse = sqrt(mean((YTest-TTest).^2))
rmse = single
    0.6865

Plot the predicted frequencies against the actual frequencies.

figure
scatter(YTest,TTest, "b+");
xlabel("Predicted Frequency")
ylabel("Actual Frequency")
hold on

m = min(freq);
M=max(freq);
xlim([m M])
ylim([m M])
plot([m M], [m M], "r--")

See Also

| | | |

Related Topics