Save the state of the optimiser for input to a new run (granted)

8 replies [Last post]
tbates's picture
Offline
Joined: 07/31/2009

For lots of jobs, the ability to input the optimiser state from a previous run would be a massive productivity gain.

The cleanest way to implement it for the user would be as a parameter to mxRun, where you pass in the last fit, and this has in it the state of the optimiser which mxRun pushes into the current optimiser run as a set of start values.

fit = mxRun(model)
mxRun(model, fit)
#--> returns almost instantly having been started at the optimal location

more practically
mxRun(mxModel(model, someChange), fit)
#--> returns quickly having started close

mspiegel's picture
Offline
Joined: 07/31/2009
This is how I would set a

This is how I would set a free or fixed parameter to new values, using a plain ol' R function and without any feature creep. The string inside double-brackets refers to a named entity, not a free or fixed parameter. Probably something unanticipated would break if we changed this rule (as is usually the case).

setParameter <- function(model, name, value) {
   if (length(model@matrices) > 0) {
      for(i in 1:length(model@matrices)) {
         iMatrix <- model@matrices[[i]]
         iMatrix@values[!is.na(iMatrix@labels) & iMatrix@labels == name] <- value
         model@matrices[[i]] <- iMatrix
      }
   }
   model@submodels <- lapply(model@submodels, setParameter, name, value)
   return(model)
}

tbates's picture
Offline
Joined: 07/31/2009
Do I read you right that this

Do I read you right that this would not set starts for free parameters to the converged solution?

Setting the free parameter starts is critical to save running time, so if that is not possible then it is a significant limitation.

When you drop paths that are close to zero, or perhaps already estimated at zero, the solution before the drop is very close, so saves much time in convergence.

FYI: The feature I am promoting is one that Mx provided this via its SAVE command (page 108 of manual), which lets you do stuff like this:

Save aceind.mxs
!Test significance of common factor genetic path coefficients
Drop X 1 1 1 - X 1 nvar 1
End

Get aceind.mxs
!Test significance of common factor shared environment coefficients
Drop Y 1 1 1 - Y 1 nvar 1
End

neale's picture
Offline
Joined: 07/31/2009
I think the functionality of

I think the functionality of save and get is built in, given that parameter values are stored in the estimate matrices after optimization. Addition or deletion of a parameter from there (addition is a much more stable approach generally) is straightforward.

We are not being optimally (ahem) efficient in the use of the optimizer, however. The covariance matrix of the parameters that were already in the model has already been estimated during the first optimization run. I think the way we are calling NPSOL would not take advantage of this information. It's awkward because the new parameter vector may not be in the same order as the old. But in principle, if ordered, you might imagine restarting with

rbind(cbind(Oldcovmatrixofparams,zero),cbind(t(zero),iden))

where zero is a matrix of zeroes and iden is an identity matrix of order (numnewpars). There would be a bit of housekeeping (parameter vector order) in order to make this happen.

Ryne's picture
Offline
Joined: 07/31/2009
Some of this functionality is

Some of this functionality is currently supported. A model that has been run is still an MxModel object, just one with values matrices selected by the optimizer.

Say you have two models you're interested in, and they differ by a single parameter such that the second model has an additional free parameter over and above the first model. I'll say that that parameter is unnamed and in position [1,1] of a matrix called A. I could run the first model, then create a new model from the output of the first model, make some changes, then run that one. The second model would use the estimates from the first model as starting value for the second.

firstRun <- mxRun(first)
second <- firstRun
second[["A"]]@free <- TRUE
secondRun <- mxRun(second)

You could get clever and remove individual matrices and such from the first model and put them in other models.

However, I don't think one can access the current optimzer state unless the model has finished running. This is going to be something of an issue if/when Bayesian optimizers are supported, because the full optimization history is an important part of the model.

Steve's picture
Offline
Joined: 07/30/2009
Maybe I'm missing something,

Maybe I'm missing something, but doesn't Ryne's solution cover what Tim Bates is asking for? The output of an mxRun is just a model. You make the change you want to a copy of the output model (such as constraining a path to 0) and submit that to mxRun again.

I agree with Tim Brick that this is different than a full optimizer state push. But I can't see that being terribly useful unless it is being performed within the optimization kernel itself. Maybe the kernel wants to spawn off multiple copies of itself using OpenCL. :) Then maybe a state push might be useful. But there is just too much trouble that can result from giving a state push of the optimizer back to R.

tbates's picture
Offline
Joined: 07/31/2009
If the output of mxRun is a

If the output of mxRun is a model, then that covers exactly what people wanting to make changes from a set of values that are known to fit. So that's a fantastic facility! (in the simplest case, you could say mxRun(mxRun(hardModel), and while the first run statement might take a week, the second would return almost instantly.

But if I try and run a fit as a model, I get an error:

> fit= mxRun(model); mxRun(fit)
Running twinACE
Running twinACE
Error: The expected means matrix associated with the FIML objective function in model 'twinACE' does not contain dimnames.

Sounds like the equivalent of oldMx's SAVE feature comes for free, which is great, and there is just a (hopefully tiny) buglet in preserving dimname information in the fit.

tbrick's picture
Offline
Joined: 07/31/2009
There are two different

There are two different issues that are being broached here.

1) One issue is the ability to quickly and easily specify a new starting point for an optimization. In the case tbates mentions, it sounds like all that needs to be specified is starting values (indeed, the rest of the optimizer state probably changes if the model has been altered). Modifying starting values is also useful if you want to jitter starting values to avoid local minima or test for stability of a solution. On the other hand, if you want to restart a model that stopped because of an iteration limit, the rest of the state is useful.

I'd propose we handle these two cases separately. For the first case, I'd suggest a means of setting starting values in a batch. I can see two possibilities here.

The first is to provide a helper function or a flag to MxRun or MxModel that allows you to specify starting values directly.

The second raises the complexity level a notch, but might be handy in the long run. I also imagine there's a similar way to do it that doesn't raise the complexity as much--suggestions are welcome.

If we allow users to index free parameters the same way they can index matrices, they can set starting values by assignment. For example, a free parameter named "foo" might be changed to .9 by something like the following:

model[["foo"]]@values <- .9

which would update all the locations where foo existed. I realize this confounds the difference between free parameters and matrices even further than it was, but it might be useful.

2) The other issue centers on how accessible the details of the optimization are. Ultimately, this is going to have to depend on the optimizer, since different optimizers will want to provide different information. For example, listing bayesian prior probabilities for a non-bayesian optimizer may not make any sense at all. We may also want to detail out some MxOptions to determine how much data is reported on output.

At present, a good deal of the state of the optimizer is output on completion of the run. For example, the final gradient, hessian, and parameter estimates are all captured in the model output. Nothing about the state is currently accessible DURING the model fitting, and I don't see that changing easily. At best, we could set OpenMx to output a log file as it was running. That said, I think there's a middle ground that might be useful.

We've discussed the idea of a "warm start" (that is, picking up an optimization where you left off) once or twice during the developer meetings.

If, for example, the GUI wanted to update the user about the state of the optimization every ten steps, it could set an MxOption telling the optimizer quit after 10 iterations. With "warm-start" enabled and the option set, it could call MxRun (which would go 10 steps), read and process the results, then call MxRun again on the resulting model (to go another 10 steps), and repeat until the model completed.

To do this, we'd probably want to encapsulate the optimizer output a little better, (so there was a single list or structure that held it). But we could simply check to see if the model was in a partially completed state and had warm-start data ready (or maybe by specifying a "Warm Start" MxOption).

tbates's picture
Offline
Joined: 07/31/2009
closed: granted in full

this should come off the wishlist, as it has been granted: fits are models, and we can't can save sign-posts along the fit search.