Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IVIM MIX Model (MicroLearn) #1726

Merged
merged 43 commits into from Mar 8, 2019
Merged

IVIM MIX Model (MicroLearn) #1726

merged 43 commits into from Mar 8, 2019

Conversation

ShreyasFadnavis
Copy link
Member

@ShreyasFadnavis ShreyasFadnavis commented Jan 24, 2019

First Model from the MicroLearn Framework in DIPY -- IVIM Fitting using Variable Projections

Fadnavis S, M. Reisert, H. Farooq, M. Afzali, C. Hu, B. Amirbekian, E. Garyfallidis, MicroLearn: Framework for machine learning, reconstruction, optimization and microstructure modeling, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019.

  • Improve the documentation for VarPro Framework

@pep8speaks
Copy link

pep8speaks commented Jan 24, 2019

Hello @ShreyasFadnavis, Thank you for updating !

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated at 2019-03-07 19:56:36 UTC

@codecov-io
Copy link

codecov-io commented Jan 24, 2019

Codecov Report

Merging #1726 into master will decrease coverage by 0.43%.
The diff coverage is 91.76%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1726      +/-   ##
==========================================
- Coverage   84.26%   83.82%   -0.44%     
==========================================
  Files         115      118       +3     
  Lines       13606    14227     +621     
  Branches     2144     2252     +108     
==========================================
+ Hits        11465    11926     +461     
- Misses       1643     1781     +138     
- Partials      498      520      +22
Impacted Files Coverage Δ
dipy/sims/voxel.py 90.58% <ø> (ø) ⬆️
dipy/reconst/dti.py 96.5% <ø> (ø) ⬆️
dipy/reconst/ivim.py 84% <91.76%> (+3.41%) ⬆️
dipy/workflows/align.py 91.59% <0%> (-3.86%) ⬇️
dipy/reconst/forecast.py 90.15% <0%> (-2.08%) ⬇️
dipy/reconst/mapmri.py 90.33% <0%> (-0.63%) ⬇️
dipy/workflows/reconst.py 76.89% <0%> (-0.6%) ⬇️
dipy/align/streamlinear.py 87.17% <0%> (-0.05%) ⬇️
dipy/io/image.py 100% <0%> (ø) ⬆️
dipy/data/__init__.py 82.05% <0%> (ø) ⬆️
... and 10 more

@ShreyasFadnavis ShreyasFadnavis changed the title [WIP] IVIM MIX Model IVIM MIX Model (MicroLearn) Mar 5, 2019
@ShreyasFadnavis
Copy link
Member Author

@arokem @skoudoro @Garyfallidis Can you please review this PR and let me know the changes?

Thanks!

Copy link
Contributor

@arokem arokem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very well done! I had a few small comments and questions here and there. My main comment is that I would like to see this incorporated into the example. I think that a comparison between the two models (e.g., in terms of model fit accuracy) would be really interesting and should be easy to incorporate into the existing example.


SCIPY_LESS_0_17 = (LooseVersion(scipy.version.short_version) <
LooseVersion('0.17'))

SCIPY_LESS_0_15 = (LooseVersion(scipy.version.short_version) <
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we increase the minimal scipy required? 0.15 was released in 2014, so maybe it's fine to go with that as a minimal supported version? @skoudoro, @Garyfallidis: what do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am ok with that


@multi_voxel_fit
def fit(self, data, mask=None):
""" Fit method of the Ivim model class.
def fit(self, data):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to the mask?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as for my last commit!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @multi_voxel decorator I believe adds the mask and does not need to be included in the fit I guess.. Does that make sense?

-- This is the reason I had taken it down..

self.exp_phi1 = np.zeros((self.bvals.shape[0], 2))

@multi_voxel_fit
def fit(self, data):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want an option to pass a mask here too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as for my last commit!

"""
Creates a structure for the combining the diffusion and pseudo-
diffusion by multipling with the bvals and then exponentiating each of
the twho components for fitting as per the IVIM- two compartment model.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "twho" => "two"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, will fix!

x_f[1:3] = x
return x_f

def Phi(self, x):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use lower case here? "phi" instead of "Phi"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, will do!

doc/examples/reconst_ivim.py Show resolved Hide resolved
def __init__(self, gtab, fit_method='VarPro', *args, **kwargs):
r""" Initialize an IvimModelVP class.

The IVIM model assumes that biological tissue includes a volume
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to avoid the duplication of the parts of the docstring that are identical between the two models?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skoudoro @Garyfallidis can you help me with this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, not aware of a clean way. Future work?


data_max = data.max()
data = data / data_max
data[data > 1] = 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this actually ever happen? I thought you just normalized by dividing by the maximum

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will take it down! I didn't realize that it was still there 👍
Really good catch!

data[data > 1] = 1
b = self.bvals

bounds = np.array([(0.005, 0.01), (10**-4, 0.001)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be configurable? What exactly do these values bound?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot to add documentation for this!

I will do so immediately. And, I do not want them to be configurable because the behavior of differential evolution would become intractable! I have set the parameters after a lot of trial and error!

In the coming days, I plan to Cythonize some parts of the code and then will play around with the parameters again, but for now, due to speed issues and stochastic nature of the optimization method, I want to keep them fixed!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add a parameter bounds_de = None and then set the default to these values. Should be no much work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now ...

@ShreyasFadnavis
Copy link
Member Author

ShreyasFadnavis commented Mar 7, 2019

Very well done! I had a few small comments and questions here and there. My main comment is that I would like to see this incorporated into the example. I think that a comparison between the two models (e.g., in terms of model fit accuracy) would be really interesting and should be easy to incorporate into the existing example.

@arokem Thank you so much for reviewing my PR!

Regarding the example, I have already created one, but need more time to write the documentation for it since the method is a little different. Here is the link to the example notebook for the MIX method of IVIM that I have created: Comparison Example for IVIM DIPY Workshop

It exactly replicates your example in http://nipy.org/dipy/examples_built/reconst_ivim.html#example-reconst-ivim for ready comparison.. I am working on creating a notebook where your and my example results will be placed side by side.

I want this PR to go into the release branch 0.16 and felt that merging the example would get complicated. I will open up a PR for it shortly!

Copy link
Member

@skoudoro skoudoro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ShreyasFadnavis,

Nice Documentation on your code! Below my comments

self.tol = tol
self.options = options
self.x_scale = x_scale
self.args = args
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the goal of self.args ? It is never used

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will take it down 👍 Thank you for the catch!

self.x_scale = x_scale
self.args = args
self.kwargs = kwargs
self.fit_method = fit_method
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.fit_methodis never used. Do you really need this variable on this class?

class IvimModelLM(ReconstModel):
"""Ivim model
"""
def __init__(self, gtab, fit_method='LM', *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the goal of fit_method in this class? What is the goal of *args in this class?

Furthermore, I think it will be better to be explicit and put all keywords arguments instead of **kwargs.

self.bounds = kwargs.get('bounds', None)
self.two_stage = kwargs.get('two_stage', True)
self.tol = kwargs.get('tol', 1e-15)
self.options = kwargs.get('options', {'gtol': 1e-15, 'ftol': 1e-15,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if I give this parameter: options={'eps': 1e15} ?

The user does not know what keyword should be in options. This is why I think it will be better to be explicit on the __init__and then, build your options variable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@@ -509,6 +536,362 @@ def _leastsq(self, data, x0):
return x0


class IvimModelVP(ReconstModel):

def __init__(self, gtab, fit_method='VarPro', *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before for fit_method and *args

else:
from scipy.optimize import differential_evolution

data_max = data.max()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be good to apply the mask here, even if 'multi_voxel_fit`will do it for you later

return IvimModelLM(gtab, fit_method='LM', *args, **kwargs)

elif fit_method == 'VarPro':
return IvimModelVP(gtab, fit_method='LM', *args, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be good to have an else in case there is an error on the fit_method.

You can call again IvimModelLM(gtab, fit_method='LM', *args, **kwargs)and add a warning for the wrong choice, selecting the default method

self.exp_phi1 = np.zeros((self.bvals.shape[0], 2))

@multi_voxel_fit
def fit(self, data, mask=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mask not needed the decorator can deal with that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!

return IvimModelVP(gtab, **kwargs)

else:
print('The fit_method option chosen was not correct. \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use warn instead of print like here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@@ -509,6 +552,346 @@ def _leastsq(self, data, x0):
return x0


class IvimModelVP(ReconstModel):

def __init__(self, gtab):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not forget tol and maxiter with their defaults values.

@ShreyasFadnavis
Copy link
Member Author

@arokem I have incorporated all the changes suggested by you, @skoudoro and @Garyfallidis .. Please can you take a look at it and merge if possible?

Let me know if you need any further information on my end!

@skoudoro skoudoro mentioned this pull request Mar 8, 2019
15 tasks
@skoudoro
Copy link
Member

skoudoro commented Mar 8, 2019

Thanks @ShreyasFadnavis for addressing all the issues. merging

@skoudoro skoudoro merged commit 7977f2b into dipy:master Mar 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants