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
Xval #335
Xval #335
Conversation
Wow! This is cool. Have you tested what happens when a model does not provide a predict method? |
I think that if it doesn't have a |
OK - these commits should answer @Garyfallidis questions. The issue with other SHM-based models is that they don't have a response function defined, so we need to fall back on a default response function (here it's a tensor with evals |
@arokem, this stuff looks awesome. I don't think generalizing the idea of a response function to all odf models is conceptually sound, but think we should try and implement predict methods for as many models/fits as possible. I think for all our predict methods we should include a closed loop test, IE I think, both from a design point of view and a conceptual point of view, predict should defer to the model. Somehow it makes sense that the models know the best way to get from a fit or a set of parameters back to the signal. Also this will allow us to use the same fit type for multiple models like we're currently doing with the
|
Thanks for the feedback @MrBago! On Mon, Mar 17, 2014 at 7:29 PM, MrBago notifications@github.com wrote:
Does it make sense to have the response function in the CSA model? Right
But you think that this method should call out to a method of the
|
@@ -20,6 +22,9 @@ def fit(self, data): | |||
|
|||
class OdfFit(ReconstFit): | |||
|
|||
def __init__(self, model, data): | |||
ReconstFit.__init__(self, model, data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't OdfFit inherit init from ReconstFit automatically? Is there a reason you're doing this kind of explicit inheritance?
We're abusing the ODF concept a little, in principle the csd model produces
The CSD model should pass the closed loop test for well behaved signals,
Yep that exactly what I mean, Ideally I'd like to be able to do something model = SomeModel(gtab, ...) fit = model.fit(data) |
self.model.cache_set("prediction_matrix", | ||
sphere, | ||
prediction_matrix) | ||
return prediction_matrix.T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function has a few issues. First you want to predict signal from the coefficients of the csd fit. You can do this by using simply taking dot(Pmatrix, fit.shm_coeff)
. In this case Pmatrix == dot(SH_basis * model.R)
. You can take a look at the fit method to see how we get the SH-basis and model.R
is the spherical harmonic representation of the response function, meaning that coef-SIG = dot(R, coef-FOD)
. You can re-compute R if you want, but it's already computed for every CSD model.
Keep in mind with the response function that it needs to be axially symmetric, this is taken care of by the sh_to_rh
function when R is created. We've actually done something very weird with the response function, we represent it as a tensor and that's not very good. What actually happens is that an axially symmetric SH function is fit to the tensor and that's the true response function.
Alright. This is ready for a re-review. In particular, I have implemented @MrBago's recent suggestions. Please take a look when you get a chance. |
return pred_sig | ||
|
||
|
||
class ConstrainedSphericalDeconvModel(OdfModel, Cache): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... why you have moved the classes at the end of the file? Please put them up again!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This follows the general rule that things are used after they are defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well... @stefanv may have a different opinion on that. Also this rule makes sense for C code but not for Python.
I personally think that when classes are defined at the top it is easier to get to the most important parts of the code faster. The CSDModel is a good example.
But because people reviewing my code have asked me in the past to put classes'
definitions at the top and I started using this style... I saw the benefits myself and
I wanted to share this experience with you too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've found that it's helpful to know where to look for something. If you
see something (a function, variable, etc.) being used in code, you know you
will always find it defined, allocated, or imported somewhere earlier in
the file. So, you always know which way to search.
On Wed, Jun 18, 2014 at 5:05 PM, Eleftherios Garyfallidis <
notifications@github.com> wrote:
In dipy/reconst/csdeconv.py:
- if np.iterable(S0):
# If it's an array, we need to give it one more dimension:
S0 = S0[..., None]
This is the key operation: convolve and multiply by S0:
- pre_pred_sig = S0 * np.dot(predict_matrix, sh_coeff)
Now put everything in its right place:
- pred_sig = np.zeros(pre_pred_sig.shape[:-1] + (gtab.bvals.shape[0],))
- pred_sig[..., ~gtab.b0s_mask] = pre_pred_sig
- pred_sig[..., gtab.b0s_mask] = S0
- return pred_sig
+class ConstrainedSphericalDeconvModel(OdfModel, Cache):
Well... @stefanv https://github.com/stefanv may have a different
opinion on that. Also this rule makes sense for C code but not for Python.I personally think that when classes are defined at the top it is easier
to get to the most important parts of the code faster. The CSDModel is a
good example.
But because people reviewing my code have asked me in the past to put
classes'
definitions at the top and I started using this style... I saw the
benefits myself and
I wanted to share this experience with you too.—
Reply to this email directly or view it on GitHub
https://github.com/nipy/dipy/pull/335/files#r13947669.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way, we need to decide on a standard and stick to it everywhere. As
it is, parts of the code (e.g. reconst.dti) adhere to one convention, and
other parts adhere to another. That's certainly more confusing than any one
of these conventions by itself.
On Wed, Jun 18, 2014 at 6:48 PM, Ariel Rokem arokem@gmail.com wrote:
I've found that it's helpful to know where to look for something. If you
see something (a function, variable, etc.) being used in code, you know you
will always find it defined, allocated, or imported somewhere earlier in
the file. So, you always know which way to search.On Wed, Jun 18, 2014 at 5:05 PM, Eleftherios Garyfallidis <
notifications@github.com> wrote:In dipy/reconst/csdeconv.py:
- if np.iterable(S0):
# If it's an array, we need to give it one more dimension:
S0 = S0[..., None]
This is the key operation: convolve and multiply by S0:
- pre_pred_sig = S0 * np.dot(predict_matrix, sh_coeff)
Now put everything in its right place:
- pred_sig = np.zeros(pre_pred_sig.shape[:-1] + (gtab.bvals.shape[0],))
- pred_sig[..., ~gtab.b0s_mask] = pre_pred_sig
- pred_sig[..., gtab.b0s_mask] = S0
- return pred_sig
+class ConstrainedSphericalDeconvModel(OdfModel, Cache):
Well... @stefanv https://github.com/stefanv may have a different
opinion on that. Also this rule makes sense for C code but not for Python.I personally think that when classes are defined at the top it is easier
to get to the most important parts of the code faster. The CSDModel is a
good example.
But because people reviewing my code have asked me in the past to put
classes'
definitions at the top and I started using this style... I saw the
benefits myself and
I wanted to share this experience with you too.—
Reply to this email directly or view it on GitHub
https://github.com/nipy/dipy/pull/335/files#r13947669.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if I shared an opinion about this in the past, but I don't see any problem with the way @arokem implemented it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - you made this comment about the dti module, when I was working on some of the metrics (that are now implemented at the top of the file). And I've followed it since. It's worked very nicely for me so far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, so that makes sense to me: to define things so that any name seen must already have been defined (I somehow thought @Garyfallidis suggested that I said something different in the past).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you asked me to change the CSD class to put it on the top of the file. Don't you remember?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - I moved the classes back to the top of the file. We can have the
discussion about the general policy some other time.
On Wed, Jul 2, 2014 at 12:22 PM, Eleftherios Garyfallidis <
notifications@github.com> wrote:
In dipy/reconst/csdeconv.py:
- if np.iterable(S0):
# If it's an array, we need to give it one more dimension:
S0 = S0[..., None]
This is the key operation: convolve and multiply by S0:
- pre_pred_sig = S0 * np.dot(predict_matrix, sh_coeff)
Now put everything in its right place:
- pred_sig = np.zeros(pre_pred_sig.shape[:-1] + (gtab.bvals.shape[0],))
- pred_sig[..., ~gtab.b0s_mask] = pre_pred_sig
- pred_sig[..., gtab.b0s_mask] = S0
- return pred_sig
+class ConstrainedSphericalDeconvModel(OdfModel, Cache):
Yes, you asked me to change the CSD class to put it on the top of the file.
—
Reply to this email directly or view it on GitHub
https://github.com/nipy/dipy/pull/335/files#r14477908.
Thanks for the comments @Garyfallidis. Still working on an example. I should have that up later today. |
assert_array_almost_equal(dmfit.predict(gtab, S0=100), S) | ||
|
||
assert_array_almost_equal(dm.predict(dmfit.model_params, S0=100), S) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why you selected to have the predict method at the Model class and not at the Fit class?
I would think that because you have already the params after the model.fit call then it would make sense
to call predict from the Fit. Can you elaborate a bit on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's now implemented in both the Model and the Fit, following @MrBago's
suggestion. I think that's pretty nice. You can use the API you prefer,
depending on your use-case. Indeed, in the kfold cross-validation, I use
the Fit object's predict
method.
On Wed, Jun 18, 2014 at 5:33 PM, Eleftherios Garyfallidis <
notifications@github.com> wrote:
In dipy/reconst/tests/test_dti.py:
assert_array_almost_equal(dmfit.predict(gtab, S0=100), S)
- assert_array_almost_equal(dm.predict(dmfit.model_params, S0=100), S)
Why you selected to have the predict method at the Model class and not at
the Fit class?
I would think that because you have already the params after the model.fit
call then it would make sense
to call predict from the Fit. Can you elaborate a bit on this?—
Reply to this email directly or view it on GitHub
https://github.com/nipy/dipy/pull/335/files#r13948572.
The example is now ready for review. Thanks! |
Does anyone have the chance to take a look at the example? Other than that - any more comments? @MrBago - were all your previous comments properly addressed? |
""" | ||
Predict a signal from sh coefficients for this model | ||
""" | ||
return csd_predict(sh_coeff, self.gtab, response=self.response, S0=S0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is csd_predict
meant to be exposed to the public? Otherwise rename _csd_predict
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any reason that this should not be publicly exposed. I have now also documented the return value.
Thanks @stefanv for that over-zealous review ;-) I hope that I have addressed most of your comments. In particular, I would like to hear more about what you were thinking about when you wrote that comment about scikit-learn. I'd be happy to know whether they have tools that we could just somehow apply to this problem and would do this for us. I doubt that's the case. Consider the handling of gradient-tables, S0, etc. |
OK - I think that I have addressed all of @stefanv's comments. At the very least, I have responded to all of them :-) Anything more? Are we ready to merge this thing? |
@arokem Any reason in particular you think the review was over-zealous? Along with the smiley, I'm not sure whether I should tone down any specific aspect to make my comments more acceptable. Usually when I do reviews the comments are meant as recommendations, so I leave it up to the author to decide whether to respond or not (and you did, to all of them). |
Sorry - didn't mean to be too facetious. I was just amused by how detailed On Fri, Jun 27, 2014 at 10:10 AM, Stefan van der Walt <
|
Oh sorry - still need to do round-tripping. Don't merge yet! |
Ah, no worries--I like people poking fun at me (as much as I dislike offending!). I am a total OCD programming nazi :) |
OK - now it's really ready (I think)! |
Thanks, the doc update clarified things for me! |
Cool. Thanks again for the careful read. On Fri, Jun 27, 2014 at 10:48 AM, Stefan van der Walt <
|
Is this ready to be merged? Any more comments? |
Give me this afternoon to have a last check and if all is good will merge it. |
Sweet. Thanks! On Wed, Jul 2, 2014 at 11:31 AM, Eleftherios Garyfallidis <
|
Nice work. Now @maurozucchelli and @mpaquette can write their predict functions for the SHORE etc and test them with your framework. |
Yip! GGT! On Wed, Jul 2, 2014 at 1:39 PM, Eleftherios Garyfallidis <
|
This revives #235. Now that all the other elements are in place, I think that this is ready for review and in principle ready to be merged upstream (once it's been reviewed and fixed up, of course!). I am excited about this PR - evaluation of DWI models is something that I have been thinking a lot about in the past couple of years, since I started working on DWI.
This PR also demonstrates the power and elegance of the uniform
Model
/Fit
API we have in place. GGT!For now, this PR only implements cross-validation for CSD and for the tensor model, but future work could extend this to other models, by implementing their
predict
methods.Also : a lot of PEP8 fixes in the CSD module and elsewhere :-)