From aa60f40a68413f318b0aafbc70fcfb53c918470a Mon Sep 17 00:00:00 2001 From: Martin Craig Date: Fri, 15 Mar 2019 11:30:04 +0000 Subject: [PATCH] Update model example and added ref to standalone release --- doc/building.rst | 105 +++--- doc/exp_test_biexp_good.png | Bin 0 -> 21027 bytes doc/exp_test_biexp_improved.png | Bin 0 -> 25384 bytes doc/exp_test_single.png | Bin 0 -> 31040 bytes doc/getting.rst | 33 +- doc/index.rst | 4 +- doc/models.rst | 595 ++++++++++++++++++++++---------- 7 files changed, 492 insertions(+), 245 deletions(-) create mode 100644 doc/exp_test_biexp_good.png create mode 100644 doc/exp_test_biexp_improved.png create mode 100644 doc/exp_test_single.png diff --git a/doc/building.rst b/doc/building.rst index 6e60a27..de97700 100644 --- a/doc/building.rst +++ b/doc/building.rst @@ -5,16 +5,17 @@ I have an official FSL distribution installed --------------------------------------------- You can build Fabber using the FSL build system. First you need to set -up your development environment: - -:: +up your development environment:: source $FSLDIR/etc/fslconf/fsl-devel.sh export FSLDEVDIR= -Then building Fabber should be a case of: +``FSLDEVDIR`` is an anternate prefix to ``FSLDIR`` which is used to +store updated code separately from the official FSL release. Most +FSL-based scripts should use code installed in ``FSLDEVDIR`` in preference +to the main FSL release code. -:: +Building Fabber should be a case of:: cd fabber_core make install @@ -23,75 +24,59 @@ This approach uses the same build tools as the rest of FSL which is important on some platforms, notably OSX. It will install the updated code into whatever prefix you selected as ``FSLDEVDIR``. -I don't have an FSL distribution which supports development ------------------------------------------------------------ - -Fabber has an alternative build system using ``cmake`` as its build -tool. CMake is cross-platform and is designed for out-of-source builds, -so you create a separate build directory and all the compiled files end -up there. This helps to keep your source tree free of build artifacts. - -Some convenience scripts are provided to build and install Fabber. You -need to ensure that ``FSLDIR`` is set to point to wherever the FSL -dependencies are installed, then for example to do a build which -includes debug symbols on Linux or OSX you run: - -:: +Building new or updated model libraries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - scripts/build.sh debug +Model libraries are distributed separately from the Fabber core. +If you need an updated version of a model library, for example +the ASL model library, you first need to get the source code +for the models library. A number of model libraries are +available in our `Github repositories `_ +all named ``fabber_models_``. -The executables and libraries will end up in ``build_debug``. Other -options are: +Then to build and install the updated model libraries you would then +run, for example:: -:: + cd fabber_models_asl + make install - scripts/build.sh release - - scripts/build.sh relwithdebinfo - -The last example is quite useful - it produces a release build with full -optimization (typically about 2x faster than a debug build) but keeps -the debug symbols in the executable so debugging is possible. +Adding your own models +---------------------- -You can also look at the scripts - they are very simple - to see the -actual CMake commands being run. +If you want to create your own model to use with the Fabber core +model fitting engine, see `Building a new model`_. Once you've +designed and coded your model there are two ways to incorporate +it into the Fabber system: -Pitfalls -~~~~~~~~ +Adding models directly to the core +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -gcc vs Clang on OSX -^^^^^^^^^^^^^^^^^^^ +If you wish, you can add your own models directly into the Fabber source +tree and build the executable as above. This is not generally +recommended because your model will be built into the core executable, however +it can be the quickest way to get an existing model built in. You will +need to follow these steps: -OSX has Apple’s ``Clang`` compiler and the LLVM C++ standard library -``libc++`` as its default for compiling C++. However FSL uses ``gcc`` -and the GNU ``libstdc++`` library for OSX builds. These are not -compatible. If you are building against a standard FSL installation you -need to force CMake to use ``gcc`` and ``libstdc++``. To do this: +1. Add your model source code into the fabber_core directory, e.g.:: -1. Uncomment the following line in CMakeLists.txt + fabber_core/fwdmodel_mine.cc + fabber_core/fwdmodel_mine.h - set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -std=c++11 - -stdlib=libstdc++”) +2. Edit ``Makefile`` to add your model to the list of core objects, e.g.:: -2. Add the following directives to the ``cmake`` command itself (edit - the scripts/build.sh script if you are using it): + COREOBJS = fwdmodel_mine.o noisemodel.o fwdmodel.o inference.o fwdmodel_linear.o fwdmodel_poly.o convergence.o motioncorr.o priors.o transforms.o - -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ +3. Run ``make install`` again to build and install a new executable -Adding your own models -~~~~~~~~~~~~~~~~~~~~~~ +Creating a new models library +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you wish, you can add your own models directly into the Fabber source -tree and build the executable as above. This is not generally -recommended because your model will be built into libfabbercore, however -it can be the quickest way to get an existing model built in. You will -need to follow these steps: +This is the preferred approach if you want to distribute your new models. A template +for a new model library including a simple sine-function implementation is +included with the Fabber source code in ``fabber_core/examples``. See +`Building a new model`_ for a full tutorial on this example which includes +how to set up the build scripts. -1. Add your model source code into the fabber_core directory, - e.g. \ ``fabber_core/fwdmodel_mine.cc`` and - ``fabber_core/fwdmodel_mine.h`` +.. _Building a new model: models -2. Edit CMakeLists.txt to add your model sources as follows:: - # Core objects - things that implement the framework for inference - set(CORE_SRC noisemodel.cc fwdmodel.cc inference.cc) diff --git a/doc/exp_test_biexp_good.png b/doc/exp_test_biexp_good.png new file mode 100644 index 0000000000000000000000000000000000000000..c074bcf635f95756115468bb6f9dea58305aa960 GIT binary patch literal 21027 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 N-314$Y75^7{{h^uGR*)0 literal 0 HcmV?d00001 diff --git a/doc/exp_test_biexp_improved.png b/doc/exp_test_biexp_improved.png new file mode 100644 index 0000000000000000000000000000000000000000..0b3e7cd679d0eb87e2b301632889070eee01a670 GIT binary patch literal 25384 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, and this is the easiest way to get Fabber if you want to use existing models for fMRI data . This documentation describes the -version of Fabber included with FSL v6.0.1 and above. +version of Fabber included with *FSL v6.0.1 and above*. + +Addition tools that can use Fabber will work with a correctly installed +FSL distribution although currently not all models we have developed are +available in FSL. + +Standalone Fabber distribution +------------------------------ + +Standalone versions of Fabber including a selection of model libraries are available +for a number of platforms. These may be useful if you don't want the rest of FSL +or if you need a more up to date version of Fabber than the one included with FSL. + +The current standalone release can be found at `https://github.com/ibme-qubic/fabber_core/releases`_. + +This distribution can be used with tools requiring a Fabber installation such as +the `Python API `_, or Fabber-based plugins +for `Quantiphyse `_. You should set the environment +variable ``FABBERDIR`` to the unpacked distribution directory to ensure these tools +can find Fabber. -If you want to build your own models, or if you need a more up to date -version of Fabber than the one included with FSL, you can build Fabber from -the source code available in the `Github repository `_. -Pre-built binaries may be available for some platforms. +Building from source code +------------------------- -To build the source code, see `Building Fabber`_. +You can build Fabber from the source code available in the `Github repository `_. +You will need an FSL installation for this. For instructions see `Building Fabber`_. .. _Building Fabber: building diff --git a/doc/index.rst b/doc/index.rst index 14ae4dd..d61f6b5 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -46,8 +46,8 @@ for ASL, CEST, DSC, DCE and dual echo fMRI data. Fabber is distributed as part of `FSL `_, however you should ensure that you are using **FSL v6.0.1 as a minimum version**. -A GUI tool for processing ASL, CEST and (soon) DSC and DCE data using Fabber -is `Quantiphyse `_. +The `Quantiphyse `_ visual analysis +tool contains plugins to analyse ASL, CEST, DSC and DCE fMRI data using Fabber. .. toctree:: :maxdepth: 1 diff --git a/doc/models.rst b/doc/models.rst index c0c89dc..99c29c0 100644 --- a/doc/models.rst +++ b/doc/models.rst @@ -1,35 +1,34 @@ -Building new models -=================== +Building a new model +==================== -For most applications, a model will need to be constructed. This will +For most new applications, a model will need to be constructed. This will include adjustable parameters which Fabber will then fit. A complete example model is provided in the ``examples`` subdirectory of the Fabber source code. This provides an easy template to implement a new model. In the next section we will go through this example. +We will assume only some basic knowledge of ``C++`` for this example. + A simple example ---------------- To create a new Fabber model it is necessary to create an instance of the class ``FwdModel``. As an example, we will create a model which fits the -data to a sine function with an amplitude, frequency and phase shift, -plus an optional constant offset: +data to sum of exponential functions, each with an amplitude and decay rate. .. math:: - A\sin(B(t+C)) [+D] + \sum{A_n\exp(-R_nt) .. note:: - The source code and ``CMakeLists.txt`` file for this example are in the - fabber source code, in the examples subdirectory. We will assume you + The source code and ``Makefile`` file for this example are in the + Fabber source code, in the ``examples`` subdirectory. We will assume you have this to hand as we go through the process! -First we will create the interface .h file which shows the methods we -will need to implement: - -.. code:: +First we will create the interface ``fwdmodel_exp.h`` file which shows the methods we +will need to implement:: - // fwdmodel_sine.h - A simple sine curve fitting model + // fwdmodel_exp.h - A simple exponential sum model #pragma once #include "fabber_core/fwdmodel.h" @@ -39,12 +38,12 @@ will need to implement: #include #include - class SineFwdModel : public FwdModel { + class ExpFwdModel : public FwdModel { public: static FwdModel* NewInstance(); - SineFwdModel() - : m_include_offset(false) + ExpFwdModel() + : m_num(1), m_dt(1.0) { } @@ -56,125 +55,171 @@ will need to implement: void EvaluateModel(const NEWMAT::ColumnVector ¶ms, NEWMAT::ColumnVector &result, const std::string &key="") const; - + + protected: + void GetParameterDefaults(std::vector ¶ms) const; + private: - bool m_include_offset; - static FactoryRegistration registration; + int m_num; + double m_dt; + static FactoryRegistration registration; }; We have not made our methods virtual, so nobody will be able to create a -subclass of our model. If we wanted this to be the case all the +subclass of our model. If we wanted this to be the case all themake non-static methods would need to be virtual, and we would need to add a -virtual destructor. +virtual destructor. This is sometimes useful when you want to create +variations on a basic model. -We have also declared a private variable to hold whether we are going to -use the optional offset or not. +Most of the code above is completely generic to any model. The only parts which +are specific to our exp-function model are: -We will now implement these methods one by one. Many of them are -straightforward. We start our implementation file as follows + - The name ``ExpFwdModel`` + - The private variables ``m_num`` (the number of exponentials in our sum) and + ``m_dt`` (the time between data points). -:: +We will now implement these methods one by one. Many of them are +straightforward. We start our implementation file ``fwdmodel_exp.cc`` as follows:: - // fwdmodel_sine.cc - Implements a simple sine curve fitting model - #include "fwdmodel_sine.h" + // fwdmodel_exp.cc - Implements a simple exp curve fitting model + #include "fwdmodel_exp.h" - #include "fabber_core/fwdmodel.h" + #include #include using namespace std; using namespace NEWMAT; -Here are the first few methods: +This just declares some standard headers we will use. If you prefer to fully qualify your +namespaces you can leave out the ``using namespace`` lines. -:: +We need to implement a couple of methods to ensure that our model is visible to the +Fabber system:: - FactoryRegistration SineFwdModel::registration("sine"); + FactoryRegistration ExpFwdModel::registration("exp"); - FwdModel* SineFwdModel::NewInstance() + FwdModel* ExpFwdModel::NewInstance() { - return new SineFwdModel(); + return new ExpFwdModel(); } The first line here registers our model so that it is known to Fabber by -the name *sine*. The second line is a *Factory method* used so that +the name ``exp`` The second line is a *Factory method* used so that Fabber can create a new instance of our model when its name appears on -the command line. +the command line:: -:: - - string SineFwdModel::ModelVersion() const - { - return "1.0"; - } + string ExpFwdModel::ModelVersion() const + { + return "1.0"; + } - string SineFwdModel::GetDescription() const + string ExpFwdModel::GetDescription() const { - return "Example model which uses a sine function"; + return "Example model of a sum of exponentials"; } We’ve given our model a version number, if we update it at some later stage we should change the number returned so anybody using the model will know it has changed and what version they have. There's also -a brief description which fabber will return when the user requests help on the model. - -:: +a brief description which fabber will return when the user requests help on the model:: - static int NUM_OPTIONS = 1; - static OptionSpec OPTIONS[] = - { - {"use-offset", OPT_BOOL, "If True, allow an additional constant offset parameter", OPT_NONREQ, "false"}, - {""} - }; + static OptionSpec OPTIONS[] = { + { "dt", OPT_FLOAT, "Time separation between samples", OPT_REQ, "" }, + { "num-exps", OPT_INT, "Number of independent exponentials in sum", OPT_NONREQ, "1" }, + { "" } + }; - void SineFwdModel::GetOptions(vector &opts) const - { - for (int i = 0; OPTIONS[i].name != ""; i++) - { - opts.push_back(OPTIONS[i]); - } - } + void ExpFwdModel::GetOptions(vector &opts) const + { + for (int i = 0; OPTIONS[i].name != ""; i++) + { + opts.push_back(OPTIONS[i]); + } + } This is the suggested way to declare the options that your model can -take. It is a little cumbersome when there is only one option, but if -you have many options it will make it clear to see what they are. - -The OptionSpec definition contains, in order, the option name, a type -indicator (OPT_STR, OPT_INT, OPT_BOOL, OPT_FILE) which indicates what -kind of data is expected, a human readable description, whether the -option must be specified (OPT_REQ) or not (OPT_NONREQ), and finally the -default value if any. - -We have a single non-mandatory Boolean option - whether to allow the -extra constant offset. If not specified, it defaults to false, so not -including an offset. +take - in this case the user can choose how many exponentials to include in the sum +and what the time resolution in the data is. Each option is listed in the ``OPTIONS`` array which +**ends with an empty option** (important!). + +An option is described by: + + - It's name which generally should *not* include underscores (hyphen is OK as in this + case). This translates into a command line option ``--use-offset``. + - An option type. Possibilities are: + - ``OPT_BOOL`` for a Yes/No boolean + - ``OPT_FLOAT`` for a decimal number + - ``OPT_INT`` for a whole number + - ``OPT_STR`` for text + - ``OPT_MATRIX`` for a small matrix (specified by giving the filename of + a text file which contains the matrix data in tab-separated form) + - ``OPT_IMAGE`` for a 3D image specified as a Nifti file + - ``OPT_TIMESERIES`` for a 4D image specified as a Nifti file + - ``OPT_FILE`` for a generic filename + - A brief description of the option. This will be displayed when ``--help`` is + requested for the model + - ``OPT_NONREQ`` if the option is not mandatory (does not need to be specified) + or ``OPT_REQ`` if the option must be provided by the user. + - An indication of the default value. This value is not actually used to initialize + anything but is shown in ``--help`` to explain to the user what the default is + if the option is not given. So it can contain any text (e.g. ``"0.7 for PASL, 1.3 for pCASL"``. + You should not specify a default for a mandatory option (``OPT_REQ``) + +In this case we have made the time resolution option mandatory, but the number +of exponentials defaults to 1 if not specified. + +This option system is a little cumbersome when there is only one option, but if +you have many it will make it clear to see what they are. Most +real models will have many configuration options, for example an ASL +model will need to know details of the sequence such as the TIs/PLDs, +the bolus duration, the labelling method, number of repeats, etc... + +Options specified by the user are captured in the ``FabberRunData`` +object which we use to set the variables in our model class +in the ``Initialize`` method. ``Initialize`` is called before the model +will be used. Its purpose is to allow the model to set up any internal +variables based on the user-supplied options. Here we capture the +time resolution option and the number of exponentials - note that +the latter has a default value. + + void ExpFwdModel::Initialize(FabberRunData& rundata) + { + m_dt = rundata.GetDouble("dt"); + m_num = rundata.GetIntDefault("num-exps", 1); + } -:: +We use the term *Options* to distinguish user-specified or default model +configuration from *Parameters* which are the parts of the model inferred by +the Fabber process. Next we need to specify what parameters our model +includes:: - void SineFwdModel::GetParameterDefaults(std::vector ¶ms) const + void ExpFwdModel::GetParameterDefaults(std::vector ¶ms) const { params.clear(); int p=0; - params.push_back(Parameter(p++, "a", DistParams(1, 1e6), DistParams(1, 1e6))); - params.push_back(Parameter(p++, "b", DistParams(1, 1e6), DistParams(1, 1e6))); - params.push_back(Parameter(p++, "c", DistParams(0, 1e6), DistParams(0, 1e6))); - if (m_include_offset) { - params.push_back(Parameter(p++, "d", DistParams(0, 1e6), DistParams(0, 1e6))); + for (int i=0; i`` and ``r`` for each exponential +in the sum, where ```` is 1, 2, ... As well as a name, each parameter has two ``DistParams`` instances defining the *prior* and *initial posterior* distribution for the parameter. -The ``DistParams`` instances take two parameters - a mean and a variance. +``DistParams`` take two parameters - a mean and a variance. At this +point we will diverge slightly to explain what these mean. Priors and Posteriors ~~~~~~~~~~~~~~~~~~~~~ + *Priors* are central to Bayesian inference, and describe the extent of our belief about a parameter's -value before we have seen any data. +value *before we have seen any data*. For example if a parameter represents the T_1 value of grey matter in the brain there is a well known range of plausible values. By declaring a @@ -192,137 +237,333 @@ The second ``DistParams`` instance represents the initial *posterior*. This is t point for the optimisation as it tries to find the best values for each parameter. Usually this does not matter too much and can often be set to be identical to the prior. -Sometimes it -is helpful to set this to a more restrictive value (lower variance) to avoid numerical -instability. it is also possible to adjust this on a per-voxel basis - i.e. when the data -being fitted is available. We will not do that here, but it can be useful when fitting, for +Sometimes, however, it may be helpful to give the initial posterior a more restrictive (lower) +variance to avoid numerical instability. + +It is also possible to adjust the initial posterior on a per-voxel basis using the actual +voxel data. We will not do that here, but it can be useful when fitting, for example, a constant offset, where we can tell the optimisation to start with a value that is the mean of the data. This may help avoid instability and local minima. In general it is against the spirit of the Bayesian approach to modify the priors on the -basis of the data, and no means are provided to do this. It is possible to modify the priors -on a global basis but this is not encouraged and in general a model should try to provide +basis of the data, and no means are provided to do this. It is possible for the user to modify +the priors on a global basis but this is not encouraged and in general a model should try to provide good priors that will not need modification. -:: +We now go back to our model code where we finally reach the point where we write +the code to calculate our model:: - void SineFwdModel::Initialize(FabberRunData& rundata) - { - m_include_offset = rundata.GetBool("use-offset"); - } + void ExpFwdModel::EvaluateModel(const NEWMAT::ColumnVector ¶ms, + NEWMAT::ColumnVector &result, + const std::string &key) const + { + result.ReSize(data.Nrows()); + result = 0; + + for (int i=0; i/libfabber_models_sine.so + #!/bin/env python + import sys + import traceback -Both of these should contain the new model, as you can show by using the -``--listmodels`` option + from fabber import self_test, FabberException + + save = "--save" in sys.argv + try: + rundata= { + "model" : "exp", # Exponential model + "num-exps" : 1, # Single exponential function + "dt" : 0.02, # With 100 time points time values will range from 0 to 2 + } + params = { + "amp1" : [1, 0.5], # Amplitude + "r1" : [1.0, 0.8], # Decay rate + } + test_config = { + "nt" : 100, # Number of time points + "noise" : 0.1, # Amplitude of Gaussian noise to add to simulated data + "patchsize" : 20, # Each patch is 20 voxels along each dimension + } + result, log = self_test("exp", rundata, params, save_input=save, save_output=save, invert=True, **test_config) + except FabberException, e: + print e.log + traceback.print_exc() + except: + traceback.print_exc() + +The test script generates a test Nifti image containing 'patches' of +data chequerboard style, each of which corresponds to a combination +of true parameter values. As Fabber is designed to work on 3D timeseries +data you can only vary three model parameters in each test - others +must have fixed values. + +The test data is generated both 'clean' and with added Gaussian +noise of specified amplitude. The model is then run on the noisy +data to determine how closely the true parameter values can +be recovered. In this case we get the following output:: + + python test_single.py --save + + Running self test for model exp + Saving test data to Nifti file: test_data_exp + Saving clean data to Nifti file: test_data_exp_clean + Inverting test data - running Fabber: 100% + + Parameter: amp1 + Input 1.000000 -> 0.999701 Output + Input 0.500000 -> 0.500674 Output + Parameter: r1 + Input 1.000000 -> 1.000728 Output + Input 0.800000 -> 0.801230 Output + Noise: Input 0.100000 -> 0.099521 Output + +For each parameter, the input (`ground truth`) value is given and +also the mean inferred value across the patch. In this case +it has recovered the parameters pretty well on average. An +example plot of a single voxel might look like this: + +.. image:: exp_test_single.png + +The orange line is the noisy data it's trying to fit while the +two smooth lines represent the 'true' data and the model fit. +In fact for this example typically the model fit is much closer +to the true data - we have chosen this voxel as an example +so it is possible to see them separately! + +Testing the model - bi-exponential +---------------------------------- -Running our simple example --------------------------- +Fitting to a single exponential is not too challenging - here +we will test fitting to a bi-exponential where there are two +different decay rates. We will find that we need to improve +the model to get a better fit. -We will show how to run the model using the GUI tool as it makes the -results more visually obvious. However you can run from the command line -if you prefer. +First we can modify the test script to test a bi-exponential +(``test_biexp.py`` in examples):: -On creating a new Fabber configuration file we need to ensure that we -are using the correct Fabber executable with our model built in to it. -Once this is set, you will see that the ‘sine’ model appears in the list -of forward models. Clicking on ‘Options’ then shows our single option. -We will leave it unset initially. + #!/bin/env python -.. figure:: /uploads/e50fd8068e61fa5d778e582df8be0ec3/sine_options.PNG - :alt: sine_options + import sys + import traceback - sine_options + from fabber import self_test, FabberException -We now click on ‘Run’ using the nlls inference method and a sample fMRI -data set. After some time, Fabber completes and we can see a list of our -output files. If we make the model fit visible and click on points in -the image data we can see how well our model fits the data. + save = "--save" in sys.argv + try: + rundata= { + "model" : "exp", + "num-exps" : 2, + "dt" : 0.02, + "max-iterations" : 50, + } + params = { + "amp1" : [1, 0.5], # Amplitude first exponential + "amp2" : 0.5, # Amplitude second exponential + "r1" : [1.0, 0.8], # Decay rate of first exponential + "r2" : 6.0, # Decay rate of second exponential + } + test_config = { + "nt" : 100, # Number of time points + "noise" : 0.1, # Amplitude of Gaussian noise to add to simulated data + "patchsize" : 20, # Each patch is 20 voxels along each dimension + } + result, log = self_test("exp", rundata, params, save_input=save, save_output=save, invert=True, **test_config) + except FabberException, e: + print e.log + traceback.print_exc() + except: + traceback.print_exc() + +This is similar to the last test but we have set ``num-exps`` to 2 and added +parameters for a fixed second exponential curve with a faster decay rate. +If we run this we get output something like this:: + + python test_biexp.py --save + Running self test for model exp + Saving test data to Nifti file: test_data_exp + Saving clean data to Nifti file: test_data_exp_clean + Inverting test data - running Fabber: 100% + + Parameter: amp1 + Input 1.000000 -> 0.633822 Output + Input 0.500000 -> 0.309912 Output + Parameter: r1 + Input 1.000000 -> 19693700210770313216.000000 Output + Input 0.800000 -> -324689116576874496.000000 Output + Noise: Input 0.100000 -> 0.150277 Output + +This isn't looking too encouraging. If we examine the model fit +against the data we find that actually most voxels have fitted +quite well: + +.. image:: exp_test_biexp_good.png + +However a few voxels have ended up with very unrealistic +parameter values. This kind of behaviour is a risk with model fitting - +in trying to find the best solution the inference can end up +finding a local minimum which is a long way from the true +minimum. + +We will show two additions we can make to our model to improve this +behaviour. + +Initialising the posterior +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The initial posterior is a 'first guess' at the parameter values +and can be based on the data. Fabber models can use their knowledge +of the model to make a better guess by overriding the ``InitVoxelPosterior`` +method. We firstly add this method to ``fwdmodel_exp.h`` + + void InitVoxelPosterior(MVNDist &posterior) const; + +Now we implement it in ``fwdmodel_exp.cc`` + + void ExpFwdModel::InitVoxelPosterior(MVNDist &posterior) const + { + double data_max = data.Maximum(); + + for (int i=0; i ¶ms) const + { + params.clear(); + + int p=0; + for (int i=0; i`` at the top of ``fwdmodel_exp.cc``. - modelfit_sine_nlls_no_offset +With these changes we still retain some bad fitting voxels but +fewer than previously. The output of the test script is now:: -Not very well - because we allowed no offset, the best fit is nearly -linear. Let’s switch that option on, and try again. + python test_biexp.py --save + Running self test for model exp + Saving test data to Nifti file: test_data_exp + Saving clean data to Nifti file: test_data_exp_clean + Inverting test data - running Fabber: 100% -.. figure:: /uploads/03e57bbd242d9d8de1ef3c47627d4dac/modelfit_sine_nlls_offset.PNG - :alt: modelfit_sine_nlls_offset + Parameter: amp1 + Input 1.000000 -> 9.651313 Output + Input 0.500000 -> 0.499837 Output + Parameter: r1 + Input 1.000000 -> 6.453277 Output + Input 0.800000 -> 124170.593750 Output + Noise: Input 0.100000 -> 0.099531 Output - modelfit_sine_nlls_offset +So we have a reduction in the number of extreme values. In this case we can't actually trust the +self-test output because sometimes the inference 'swaps' the exponentials around making +``amp1`` = ``amp2`` and ``r1`` = ``r2``. But viewing the model fit +visually shows sensible fitting in the overwhelming majority of voxels:: -That’s a bit better - it’s able to pick up the general variation in the -signal. Although it’s clear that this example is not giving us any -physical information we can see how the model has tried to reproduce the -general shape of the data by varying the allowed parameters. +.. image:: exp_test_biexp_improved.png Changing the example to your own model -------------------------------------- -To implement a single new model, it should be as simple as: +To summaries, these are the main steps you'll need to take to +change this example into your own new model: -- Edit the source files, ``Makefile`` and ``CMakeLists.txt`` to change - references to ``sine`` to the name of your model -- Rename source files, e.g. \ ``fwdmodel_sine.cc`` -> - ``fwdmodel_.cc`` +- Edit the ``Makefile`` to change references to ``exp`` to the name of your model +- Rename source files, e.g. ``fwdmodel_exp.cc`` -> ``fwdmodel_.cc`` - Add your model options to the options list in the ``.cc`` file -- Implement the ``Evaluate`` and ``HardcodedInitialDists`` methods for - your model +- Add any model-specific private variables in the ``.h`` file +- Implement the ``Initialize``, ``GetParameterDefaults``, ``Evaluate`` methods for + your model. +- If required, implement ``InitVoxelPosterior``