-
Notifications
You must be signed in to change notification settings - Fork 147
Converted Team 'grt123' identification and classification algorithms #122
Conversation
|
||
|
||
|
||
def test_classify_predict_model_load(dicom_path): |
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.
As @lamby already stated here it'd be nice to still have some tests that validate the output of the models. I'm currently working on some tags to only execute these long running tests if one explicitly desires to do so. So feel free to write this test a bit more detailed :) Edit: I added this decorator in c3c626f
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.
Sure, I'll have a look at that tomorrow.
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 updated the test cases to be a little more comprehensive.
@dchansen I'm not sure this is a feasible compromise. Is there a way to reimplement this in Tensorflow? |
@isms I agree that it's not a feasible compromise at all. I did it mainly to highlight the issue. I think we should have a single deep learning framework for the project as a whole, as they don't really play well together. I am not sure if Keras is the best choice, but either way, the project leads should make the decisions. Perhaps we should open an issue? Converting the model itself into Keras would relatively easy, but converting the weights is somewhat more involved. Converting to Caffe2 or CNTK would be easy, as they all support the ONNX format. |
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.
Nice job with this! Here some other thoughts...
use git-lfs for the dsb2017_detector.ckpt
model
adopt google style docs
use new style classes
fix spacing issues (have you run flake8
?)
integrate skip_slow_test
|
||
reader = sitk.ImageSeriesReader() | ||
filenames = reader.GetGDCMSeriesFileNames(dicom_path) | ||
if len(filenames) == 0: |
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.
if not filenames:
if np.average([min_distance[i] for i in range(label.shape[0]) if slice_area[i] > area_th]) < dist_th: | ||
valid_label.add(vol.label) | ||
|
||
if len(valid_label) == 0: |
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.
if not valid_label:
bw3 = bw1 & bw2 | ||
label = measure.label(bw, connectivity=1) | ||
label3 = measure.label(bw3, connectivity=1) | ||
l_list = list(set(np.unique(label)) - {0}) |
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.
You should be able to iterate fine without needing to cast this to a list
sum = 0 | ||
while sum < np.sum(area) * cover: | ||
sum = sum + area[count] | ||
count = count + 1 |
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.
sum += area[count]
and count += 1
I am a little unsure why, but the conflict between Tensorflow and Pytorch seems gone. It was related to this issue in Tensorflow tensorflow/models#523 |
This should block on #118 due to the slow test stuff. |
Looking pretty good! Just a few questions for you. I noticed that in $ git lfs pull
Git LFS: (540 of 540 files, 2 skipped) 497.08 MB / 497.08 MB, 41.20 MB skipped
[eb2c037dd55e2f49da95657f7a7851cbcfe2f2b516848ed03f8c5c820f3e16b4] Object does not exist on the server: [404] Object does not exist on the server
[3a46182c1c4883e170d5b576d3a2645309f34817c53c16d4f7f29fbdcf799308] Object does not exist on the server: [404] Object does not exist on the server I'll try to pinpoint which files exactly are missing. Meanwhile, can you verify that the model you added to |
The git lfs issue should be resolved now. I have fixed all the flake8 issues, except the complexity ones. Hopefully, I will have them fixed by the end of the day. |
Awesome!
Ok, I understand. I'm taking a look to see how to integrate both versions.
Great! |
Ok... here's my attempt. What do you think? import SimpleITK as sitk
from src.algorithms.classify.src import gtr123_model
from src.preprocess.load_ct import load_ct, MetaData
def predict(dicom_path, centroids, model_path=None,
preprocess_ct=None, preprocess_model_input=None):
reader = sitk.ImageSeriesReader()
filenames = reader.GetGDCMSeriesFileNames(dicom_path)
if not filenames:
raise FileNotFoundError()
reader.SetFileNames(reader.GetGDCMSeriesFileNames(dicom_path))
image = reader.Execute()
if preprocess_ct:
meta = load_ct(dicom_path)[1]
voxel_data = preprocess_ct(image, MetaData(meta))
else:
voxel_data = image
if preprocess_model_input:
preprocessed = preprocess_model_input(voxel_data, centroids)
else:
preprocessed = voxel_data
return gtr123_model.predict(preprocessed, centroids) Since the keras model is now gone, do you see any reason to continue to keeping |
This would work for now. |
Agreed. However, I think your PR can work without that at this stage (MVP). I looked around some more and saw that |
Looking good! What are your thoughts about incorporating something like I mentioned here? Main reason is just so that we don't disable current functionality with this PR. I think with that, we can get this merged. |
I'm trying to do a reasonable merge right now, and algorith.identify.trained_model is giving me a headache. I would suggest that each model independently implements a predict method taking only a dicom path (for identification) or a dicom path and a list of candidates. trained_model.predict would then take the same argument, plus a string identifying the algorithm to be used . How does this sound? |
@dchansen I'm right now working on that, also I've tried to convert the grt123 classification model into Keras (TF), but I've faced troubles with the |
Do you mind elaborating? What specifically is causing issues?
So if I understand you correctly, def predict(dicom_path):
model_path = 'src/algorithms/identify/assets/dsb2017_detector.ckpt'
... and from src.algorithms.identify import src
def predict(dicom_path, algorithm='gtr123_model'):
model = func(src, algorithm)
return model.predict(dicom_path) Is this correct? |
@vessemer I am a little unsure about the benefit of moving the model to Keras now that Tensorflow and Torch seem to work without disturbing one another. Different approaches will require different enough post- and pre-processing that they can't be represented as a Keras model anyway. One difference between the Torch and Keras batchnorm is that they use a different epsilon value (1e-5 vs 1e-3), so maybe that's the issue? |
@reubano Yes, that was my thought exactly. The problem right now is that all the models take ownership of trained_model.predict, and there is no good way of switching between particular models. Ideally, we might also like to do meta-model, combining many different ones, and a uniform interface would help greatly. |
@dchansen, thank you for your input, but I've taken into account the difference of the default values, I've used the nn-transfer for the model standardization. |
@vessemer Well, if you can share the code, I can have a look at it. |
For now, I think this is the preferred course of action. We want to focus on one main model for each of (classify, identify, and segment). Later on, we may consider making it easier to switch out the models. The current area of flexibility is in the serialized trained data, e.g., Also, let me know what exactly is giving you a headache so I can try and help you out. |
My issue was that I had to disable a lot of code in identify.trained_model |
I was literally suggesting you just copy/paste my code from here. |
else: | ||
preprocessed = voxel_data | ||
|
||
return gtr123_model.predict(preprocessed, centroids) |
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.
...
model_path = model_path or 'src/algorithms/classify/assets/gtr123_model.ckpt'
...
return gtr123_model.predict(image, centroids, model_path)
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.
Not sure what you're asking here?
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.
Just reintroducing the model_path
part from here. Also just noticed a typo which I've fixed. Maybe that tripped you up.. sorry!
Just about there! Does this solve your headache with |
From the travis log $ flake8 prediction
prediction/src/algorithms/identify/trained_model.py:13:1: F401 'glob' imported but unused
prediction/src/algorithms/identify/trained_model.py:15:1: F401 'dicom' imported but unused
prediction/src/algorithms/identify/trained_model.py:16:1: F401 'src.preprocess.errors.EmptyDicomSeriesException' imported but unused
prediction/src/algorithms/identify/trained_model.py:17:1: F401 'src.preprocess.lung_segmentation.save_lung_segments' imported but unused
prediction/src/algorithms/classify/trained_model.py:68:56: E231 missing whitespace after ','
prediction/src/preprocess/extract_lungs.py:57:19: E127 continuation line over-indented for visual indent
prediction/src/preprocess/extract_lungs.py:66:18: E127 continuation line over-indented for visual indent
prediction/src/preprocess/extract_lungs.py:67:18: E127 continuation line over-indented for visual indent
prediction/src/preprocess/extract_lungs.py:68:18: E127 continuation line over-indented for visual indent
prediction/src/tests/test_endpoints.py:10:1: F811 redefinition of unused 'json' from line 7
The command "flake8 prediction" exited with 1.
$ pycodestyle prediction
prediction/src/algorithms/classify/trained_model.py:68:56: E231 missing whitespace after ','
prediction/src/preprocess/extract_lungs.py:57:19: E127 continuation line over-indented for visual indent
prediction/src/preprocess/extract_lungs.py:66:18: E127 continuation line over-indented for visual indent
prediction/src/preprocess/extract_lungs.py:67:18: E127 continuation line over-indented for visual indent
prediction/src/preprocess/extract_lungs.py:68:18: E127 continuation line over-indented for visual indent |
The test run in my local environment (outside docker). Inside docker they run when individually, but not when running them all at once. I suspect it is tensorflow/torch shenanigans, but it is quite tricky to debug. |
Using a different docker image solved the problem for me. |
Yea that is odd... which image works? And what is the one that doesn't? |
|
I changed the image to be Ubuntu based instead. All tests should pass now. |
Ok.... looks like the tests passed, but there are some new lint errors now $ flake8 prediction
prediction/src/tests/__init__.py:1:1: F401 'torch' imported but unused
prediction/src/tests/__init__.py:1:13: E261 at least two spaces before inline comment
prediction/src/tests/__init__.py:1:14: E262 inline comment should start with '# '
The command "flake8 prediction" exited with 1.
$ pycodestyle prediction
prediction/src/tests/__init__.py:1:13: E261 at least two spaces before inline comment
prediction/src/tests/__init__.py:1:14: E262 inline comment should start with '# '
The command "pycodestyle prediction" exited with 1. |
The torch import is required simply because torch needs to be imported before Keras, always. I am very open to other ways of doing that. |
Try adding this on the offending line: Edit: if that doesn't work, try this: |
Looks like everything is a go now! Do you mind squashing to 1 commit? Or perhaps two, 1 for each issue (if it's possible to separate them). |
d888503
to
11b8146
Compare
I seem to have messed that up rather thoroughly. This will take me a while to sort out. |
No worries :). Perhaps I can help out. What seems to be the issue(s)? |
Just not very experienced in using git rebase. I found myself in the situation of having to redo every merge made on the project for the last ten days. |
Instead of rebasing, I prefer to squash the commits like so. It fulfills the purpose as well, namely having only 1 commit in the end :) |
You can also take a look at the advice I posted here. |
Also |
@WGierke Thank you! Uploading the squashed commit now. |
1ac544e
to
524f843
Compare
$ flake8 interface
interface/backend/api/tests.py:44:1: W293 blank line contains whitespace
interface/backend/api/tests.py:48:57: E502 the backslash is redundant between brackets
interface/backend/api/tests.py:49:81: E502 the backslash is redundant between brackets
interface/backend/api/serializers.py:74:1: E302 expected 2 blank lines, found 1
interface/backend/api/serializers.py:120:17: E127 continuation line over-indented for visual indent
interface/backend/api/views.py:24:1: F811 redefinition of unused 'Response' from line 13
interface/backend/api/views.py:47:37: W291 trailing whitespace
The command "flake8 interface" exited with 1.
$ pycodestyle interface
interface/backend/api/serializers.py:74:1: E302 expected 2 blank lines, found 1
interface/backend/api/serializers.py:120:17: E127 continuation line over-indented for visual indent
interface/backend/api/tests.py:44:1: W293 blank line contains whitespace
interface/backend/api/tests.py:48:57: E502 the backslash is redundant between brackets
interface/backend/api/tests.py:49:81: E502 the backslash is redundant between brackets
interface/backend/api/views.py:47:37: W291 trailing whitespace
The command "pycodestyle interface" exited with 1. |
But the pull request doesn't include those files?? |
Ok... master looks good. You mind rebasing @dchansen to ensure everything is good to go? |
closes #4 |
Description
This pull request implements the grt123 identification algorithm.
It is based on PyTorch, which currently conflicts with Tensorflow, so the pull request also disables the Keras import.
While the algorithm is quite fast on GPU, it is painfully slow (but working) on the CPU due to a bug in PyTorch. The bug is fixed in the PyTorch development branch, but the next release is about 2 months off.
Reference to official issue
#4
#1
Motivation and Context
This Pull request implements part of the prediction service (https://concept-to-clinic.readthedocs.io/en/latest/design-doc.html#detect-prediction-service)
How Has This Been Tested?
The algorithm has largely been tested by manually confirming about 5 random LUNA2016 datasets - as the loaded model has already been tested extensively elsewhere, this was deemed sufficient.
It has been tested both outside of docker, on a Titan X GPU, and inside docker on an i7 Intel CPU.
Screenshots (if appropriate):
CLA