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

Conversation

@ShreyasFadnavis
Copy link
Member

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Member Author

commented Mar 7, 2019

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

Thanks!

@arokem
Copy link
Member

left a comment

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) <

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

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?

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

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):

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

What happened to the mask?

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

Done as for my last commit!

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

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):

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

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

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

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.

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

Typo: "twho" => "two"

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

Thanks, will fix!

x_f[1:3] = x
return x_f

def Phi(self, x):

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

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

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

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

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

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

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

@skoudoro @Garyfallidis can you help me with this?

This comment has been minimized.

Copy link
@Garyfallidis

Garyfallidis Mar 7, 2019

Member

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


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

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

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

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

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)])

This comment has been minimized.

Copy link
@arokem

arokem Mar 7, 2019

Member

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

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

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!

This comment has been minimized.

Copy link
@Garyfallidis

Garyfallidis Mar 7, 2019

Member

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

This comment has been minimized.

Copy link
@Garyfallidis

Garyfallidis Mar 7, 2019

Member

For now ...

@ShreyasFadnavis

This comment has been minimized.

Copy link
Member Author

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!

@skoudoro
Copy link
Member

left a comment

Hi @ShreyasFadnavis,

Nice Documentation on your code! Below my comments

self.tol = tol
self.options = options
self.x_scale = x_scale
self.args = args

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

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

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

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

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

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):

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

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,

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

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.

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

Done!

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


class IvimModelVP(ReconstModel):

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

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

same as before for fit_method and *args

else:
from scipy.optimize import differential_evolution

data_max = data.max()

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

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)

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

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):

This comment has been minimized.

Copy link
@Garyfallidis

Garyfallidis Mar 7, 2019

Member

mask not needed the decorator can deal with that.

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

Cool!

return IvimModelVP(gtab, **kwargs)

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

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

Can you use warn instead of print like here

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

Okay!

This comment has been minimized.

Copy link
@ShreyasFadnavis

ShreyasFadnavis Mar 7, 2019

Author Member

Done!

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


class IvimModelVP(ReconstModel):

def __init__(self, gtab):

This comment has been minimized.

Copy link
@skoudoro

skoudoro Mar 7, 2019

Member

Do not forget tol and maxiter with their defaults values.

@ShreyasFadnavis

This comment has been minimized.

Copy link
Member Author

commented Mar 8, 2019

@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 referenced this pull request Mar 8, 2019
14 of 15 tasks complete
@skoudoro

This comment has been minimized.

Copy link
Member

commented Mar 8, 2019

Thanks @ShreyasFadnavis for addressing all the issues. merging

@skoudoro skoudoro merged commit 7977f2b into nipy:master Mar 8, 2019

4 checks passed

codecov/patch 91.76% of diff hit (target 84.26%)
Details
codecov/project Absolute coverage decreased by -0.43% but relative coverage increased by +7.5% compared to 08bf785
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@arokem arokem referenced this pull request May 2, 2019
5 of 5 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.