Using Pretrained DLCHOMP Optimizer to Predict Higher Number of Waypoints
This example shows you how to a pretrained dlCHOMP
optimizer to work with a different number of waypoints than what it was trained on.
In this example, you'll start with a dlCHOMP
object that has a network that has been pretrained for a robot with an appropriate obstacle set. However, the pretrained network also uses a specific number of waypoints. To change these number of waypoints, you'll need to modify the network using transfer learning.
In this example it is assumed that the pretrained dlCHOMP optimizer is trained for the target robot kukaIiwa7
as specified by its RigidBodyTree
property and has the desired basis point set encoder as specified by its BPSEncoder
property. Since the RigidBodyTree
and the BPSEncoder
properties meet the desired requirements, but the NumWaypoints
property does not, transfer learning can be performed by tweaking the network architecture stored in the underlying Network
property of the pretrained dlCHOMP
object. If you would like to change the optimization options such as SmoothnessOptions
, CollisionOptions
or SolverOptions
properties instead, refer to the Using Pretrained DLCHOMP Optimizer in Unseen Obstacle Environment example.
To try out pretrained networks for other robots, see Pretrained Optimizers.
This example uses a pretrained network to initialize a new dlCHOMP
optimizer and then trains it on a training dataset that meets our target trajectory requirement of a higher number of waypoints.
Background
Transfer learning is a deep learning approach in which a model that has been trained for one task is used as a starting point for a model that performs a similar task. Updating and retraining a network with transfer learning is usually much faster and easier than training a network from scratch.
Transfer learning is used because:
It enables you to train models with less labeled data by reusing popular models that have already been trained on large data sets.
It can reduce training time and computing resources. With transfer learning, the weights are not learned from scratch because the pretrained model has already learned the weights based on previous learnings.
For more information about transfer learning, see What Is Transfer Learning?
Download Pretrained DLCHOMP Optimizer
The pretrained DLCHOMP optimizer to be downloaded was trained for the target robot kukaIiwa7
as specified by its RigidBodyTree
property and has the desired basis point set encoder as specified by its BPSEncoder
property. Since the RigidBodyTree
and the BPSEncoder
properties meet the desired requirements, but the NumWaypoints
property does not, transfer learning can be performed by tweaking the network architecture stored in the underlying Network
property of the pretrained dlCHOMP
object. This will be covered in the next section.
For now, download the pretrained DLCHOMP optimizer. Also obtain the dlCHOMPDataOptions
object that was used to generate the training dataset used to train this pretrained optimizer.
dlCHOMPComponent = "rst/data/dlCHOMP/R2024a"; pretrainedDLCHOMPFilename = "kukaIiwa7DLCHOMPTrained.zip"; disp("Downloading previously trained kukaIiwa7 dlCHOMP optimizer (46 MB)...");
Downloading previously trained kukaIiwa7 dlCHOMP optimizer (46 MB)...
pretrainedDLCHOMPFolder = exampleHelperDownloadData(dlCHOMPComponent,pretrainedDLCHOMPFilename); pretrainedDLCHOMPMATPath = fullfile(pretrainedDLCHOMPFolder,"trainedDLCHOMP.mat"); loadedData = load(pretrainedDLCHOMPMATPath,"robotName","trainedDLCHOMP","trainInfo","trainOptions","trainLossType","dataOptions"); pretrainedRobotName = loadedData.robotName; pretrainedDLCHOMP = loadedData.trainedDLCHOMP; pretrainedTrainInfo = loadedData.trainInfo; pretrainedTrainOptions = loadedData.trainOptions; pretrainedTrainLossType = loadedData.trainLossType; pretrainedDataOptions = loadedData.dataOptions;
As can be seen in the training plot above, the pretrainedDLCHOMP
optimizer took 1500 iterations to get trained, and hence converge, from scratch.
Modify Last Layer of Pretrained Network to Predict Higher Number of Waypoints
First, specify the desired target number of waypoints.
desiredNumWaypoints = 50;
Compare the desiredNumWaypoints
with the NumWaypoints
property of the pretrainedDLCHOMP
object.
isequal(desiredNumWaypoints,pretrainedDLCHOMP.NumWaypoints)
ans = logical
0
As can be seen, the number of waypoints do not match as the pretrainedDLCHOMP
was trained to predict 40 waypoints which does not match the desired 50 waypoints.
Since the NumWaypoints
property only affects the last FullyConnectedLayer
of the neural network property Network
of a given dlCHOMP
object, the rest of the network can still be reused from the pretrainedDLCHOMP
object.
To do this, first, convert the Network
property of the pretrainedDLCHOMP
object into a LayerGraph.
pretrainedLayerGraph = layerGraph(pretrainedDLCHOMP.Network);
Then determine the configuration size of the robot of interest.
configurationSize = numel(pretrainedDLCHOMP.RigidBodyTree.homeConfiguration());
Then create a new FullyConnectedLayer
object which meets the desired target trajectory requirement of outputting a higher number of waypoints than before.
desiredFinalLayerName = pretrainedDLCHOMP.Network.OutputNames{1}; desiredOutputSize = configurationSize * (desiredNumWaypoints-2); % The network only predicts the intermediate waypoints newFinalLayer = fullyConnectedLayer( ... desiredOutputSize,"Name",desiredFinalLayerName)
newFinalLayer = FullyConnectedLayer with properties: Name: 'FinalFCLayer' Hyperparameters InputSize: 'auto' OutputSize: 336 Learnable Parameters Weights: [] Bias: [] Show all properties
As can be seen, this new layer has empty Weights
and Bias
properties, which indicates that it has not learned any parameters since it has not been trained yet.
Change the learning rates so that learning is faster in this new layer than in the transferred layers by setting its WeightLearnRateFactor
and BiasLearnRateFactor
to 10
.
newFinalLayer.WeightLearnRateFactor = 10; newFinalLayer.BiasLearnRateFactor = 10;
Now, replace the existing final FullyConnectedLayer
with this new layer that was created.
modifiedLayerGraph = pretrainedLayerGraph.replaceLayer( ...
desiredFinalLayerName, newFinalLayer);
modifiedNetwork = dlnetwork(modifiedLayerGraph);
Finally, create a new dlCHOMP
object by setting its NumWaypoints
property to desiredNumWaypoints
and setting its Network
property to modifiedNetwork
at object construction time. All other properties will stay the same as that of the pretrainedDLCHOMP
object.
There is a need to create a new object since the NumWaypoints
and Network
properties of a dlCHOMP
object are read-only and cannot be modified post object construction.
modifiedDLCHOMP = dlCHOMP( ... pretrainedDLCHOMP.RigidBodyTree, ... pretrainedDLCHOMP.BPSEncoder, ... desiredNumWaypoints, ... CollisionOptions=pretrainedDLCHOMP.CollisionOptions, ... SmoothnessOptions=pretrainedDLCHOMP.SmoothnessOptions, ... SolverOptions=pretrainedDLCHOMP.SolverOptions, ... RigidBodyTreeSpheres=pretrainedDLCHOMP.RigidBodyTreeSpheres, ... Network=modifiedNetwork)
modifiedDLCHOMP = dlCHOMP with properties: RigidBodyTree: [1×1 rigidBodyTree] RigidBodyTreeSpheres: [11×1 table] SmoothnessOptions: [1×1 chompSmoothnessOptions] SolverOptions: [1×1 chompSolverOptions] CollisionOptions: [1×1 chompCollisionOptions] SphericalObstacles: [4×0 double] BPSEncoder: [1×1 bpsEncoder] NumWaypoints: 50 Network: [1×1 dlnetwork] NumInputs: [14 10000] NumOutputs: 336
Now, for using the modifiedDLCHOMP
optimizer for inference, it needs to have all of the learnable parameters in its Network
property in a learned state.
Since the final FullyConnectedLayer
of the pretrained network was replaced with a new layer with non-learned parameters, there is a need to generate a small dataset for retraining the optimizer to meet this one last unsatisfied objective.
Generate Training Dataset Matching Desired Number of Waypoints
First, create a new desiredDataOptions
object which is a copy of the pretrainedDataOptions
object that was used to generate the training dataset for the pretrainedDLCHOMP
object.
desiredDataOptions = pretrainedDataOptions
desiredDataOptions = dlCHOMPDataOptions with properties: RadiusRangeOfObstacles: [0.0500 0.2000] CountRangeOfObstacles: [20 25] MinDistanceFromBase: 0.2100 NumSamples: 5000 ValidationSplit: 0.2000 RandomStream: 'threefry' Seed: 0
Modify the dlCHOMPDataOptions
properties of the desiredDataOptions
object that affect environment diversity and the training versus validation data set splits.
As explained in the Background section, there is a need to generate a much smaller training dataset as compared to what the pretrainedDLCHOMP
object was trained on.
For this, decrease the training dataset size in the desiredDataOptions
object to be much smaller than that of the pretrainedDataOptions
object. Set the NumSamples
property of desiredDataOptions
to be 1/5
th of that of pretrainedDataOptions
which amounts to 1000 as compared to 5000 data samples. For more information on these properties, see dlCHOMPDataOptions
.
desiredDataOptions.NumSamples = pretrainedDataOptions.NumSamples/5
desiredDataOptions = dlCHOMPDataOptions with properties: RadiusRangeOfObstacles: [0.0500 0.2000] CountRangeOfObstacles: [20 25] MinDistanceFromBase: 0.2100 NumSamples: 1000 ValidationSplit: 0.2000 RandomStream: 'threefry' Seed: 0
Keep 20% of the generated data set for validation and the rest for training. To do this set the ValidationSplit
property to 0.2
.
desiredDataOptions.ValidationSplit = 0.2;
Now, execute the following code snippet to generate the 1000 samples. Note that this takes 4 hrs on a Windows 10 system with 48 GB of RAM. Hence, this dataset is downloaded by default as generateData
is set to false
below.
If you want to wait for data generation, set generateData
to true
. Otherwise, set generateData
to false.
generateData = false;
If generateData
is false
, download the required data. Otherwise, generate the data.
dataSamplesFolder = "allData"; if generateData == false dataSamplesFilename = "kukaIiwa7DataForRetrainingDLCHOMPToHigherWaypoints.zip"; disp("Downloading data for retraining kukaIiwa7 dlCHOMP optimizer (244 MB)..."); dataSamplesFolder = exampleHelperDownloadData(dlCHOMPComponent, ... dataSamplesFilename, 'DataFolder', dataSamplesFolder); validIndices = 1:floor(desiredDataOptions.ValidationSplit * desiredDataOptions.NumSamples); trainIndices = (numel(validIndices)+1):desiredDataOptions.NumSamples; validDS = dlCHOMPDatastore(dataSamplesFolder,validIndices); trainDS = dlCHOMPDatastore(dataSamplesFolder,trainIndices); else seed = 100; rng(seed,"twister"); [trainDS,validDS] = generateSamples(modifiedDLCHOMP,desiredDataOptions,dataSamplesFolder); end
Downloading data for retraining kukaIiwa7 dlCHOMP optimizer (244 MB)...
Retrain Modified dlCHOMP Optimizer
Let's retrain the modifiedDLCHOMP
optimizer object using the new data that was obtained.
Use the same training options object pretrainedTrainOptions
by setting its ValidationData
property to the new validation datastore validDS
that was obtained earlier. Keep the other properties the same.
trainOptions = pretrainedTrainOptions; trainOptions.ValidationData = validDS;
Use the loss type pretrainedTrainLossType
that was used to train the pretrainedDLCHOMP
object.
Now, retrain the modifiedDLCHOMP
optimizer object using the training datastore trainDS
, loss type pretrainedTrainLossType
and the training options trainOptions
.
Retraining takes around 10 minutes on a Windows 10 system with 48 GB RAM using 8 parallel pool workers. So download this previously retrained dlCHOMP
object by default as the doRetraining
is set to false
below.
If you want to retrain the object yourself, set doRetraining
to true
. Otherwise, set doRetraining
to false
.
doRetraining = false;
If doRetraining
is false
, down
load a new pretrained dlCHOMP
optimizer object. Otherwise, retrain the existing object.
if doRetraining == false retrainedDLCHOMPFilename = "kukaIiwa7DLCHOMPRetrainedForHigherWaypoints.zip"; disp("Downloading previously retrained kukaIiwa7 dlCHOMP optimizer (44 MB)..."); retrainedDLCHOMPFolder = exampleHelperDownloadData(dlCHOMPComponent,retrainedDLCHOMPFilename); retrainedDLCHOMPMATPath = fullfile(retrainedDLCHOMPFolder,"retrainedDLCHOMP.mat"); load(retrainedDLCHOMPMATPath,"retrainedDLCHOMP","retrainInfo"); else retrainInfo = trainDLCHOMP(modifiedDLCHOMP,trainDS,pretrainedTrainLossType,trainOptions); retrainedDLCHOMP = modifiedDLCHOMP; end
Downloading previously retrained kukaIiwa7 dlCHOMP optimizer (44 MB)...
As can be seen above, the dlCHOMP
optimizer converged much faster while training, within about 150 iterations, which is much smaller than the nearly 1500 iterations that were needed to train the pretrained dlCHOMP
optimizer from scratch as was shown in the training plot under the Download Pretrained DLCHOMP Optimizer section above. This indicates that the network had already learned weights based on previous learnings and simply had to be tuned to predict a higher number of waypoints than before.
Infer Using Retrained dlCHOMP Optimizer
Now that the retrained optimizer is available, it can be used for inference on an unseen target environment of interest.
Generate One Unseen Target Environment
Let's generate an unseen target environment using the same dlCHOMPDataOptions
object were chosen for generating the training and validation data sets to ensure the same environment complexity. However, change the properties that affect the environment diversity to ensure that an unseen data sample is generated.
First, to generate only 1 data sample, set the NumSamples
property to 1
and the ValidationSplit
property to 0
respectively.
desiredDataOptions.NumSamples = 1; desiredDataOptions.ValidationSplit = 0;
Next, to ensure an unseen data sample is generated, change the Seed
value to differ it from the one used when generating the training and validation data sets used to train the optimizer. Since the default Seed
value of 0 was used then, let's use a value of 300 now.
desiredDataOptions.Seed = 300
desiredDataOptions = dlCHOMPDataOptions with properties: RadiusRangeOfObstacles: [0.0500 0.2000] CountRangeOfObstacles: [20 25] MinDistanceFromBase: 0.2100 NumSamples: 1 ValidationSplit: 0 RandomStream: 'threefry' Seed: 300
Choose a new location for saving the generated unseen sample.
folderForUnseenData = "unseenData";
Generate an unseen target environment.
rng(desiredDataOptions.Seed,"twister");
testDS = retrainedDLCHOMP.generateSamples(desiredDataOptions,folderForUnseenData);
------------------------ Starting data generation [1/1] Data Samples Written | ┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃ 100.00% complete | Total Run Time: 00:00:02 secs Estimated Time Remaining: 00:00:00 secs ------------------------
filePathToUnseenSample = resolve(testDS.FileSet).FileName(1); [unseenStart,unseenGoal,unseenObstacles,chompTrajectory] = exampleHelperExtractDataFromDLCHOMPSample(filePathToUnseenSample);
Infer On Unseen Target Environment
Make the retrainedDLCHOMP
optimizer aware of the unseen spherical obstacles.
retrainedDLCHOMP.SphericalObstacles = unseenObstacles;
Finally, use the retrainedDLCHOMP
optimizer for inference on this unseen target environment.
[optimWptsDLCHOMPNew,optimTptsDLCHOMPNew,solninfoDLCHOMPNew] = retrainedDLCHOMP.optimize(unseenStart,unseenGoal);
Let's visualize this trajectory in an animated fashion.
% Create new figure figure; % Show animated robot trajectory ax = show(retrainedDLCHOMP,unseenStart); title(sprintf("dlCHOMP Optimized End-Effector Trajectory with %d Waypoints\nUnseen Target Environment",desiredNumWaypoints)); axis(ax,"equal"); exampleHelperShowAnimatedRobotTrajectory(ax,retrainedDLCHOMP.RigidBodyTree,optimWptsDLCHOMPNew);
Conclusion
In conclusion, you learned how to tweak a pretrained dlCHOMP
optimizer's network architecture meant to predict a low number of waypoints, to meet the target requirement of predicting a higher number of waypoints. You also learned how to retrain this modified dlCHOMP
optimizer to tune it to predict a higher number of waypoints. Finally, you used this retrained dlCHOMP
optimizer for motion planning in an unseen target obstacle environment.