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

Build R-CNN with ResNet #27

Merged
merged 17 commits into from
Jul 10, 2017
Merged

Build R-CNN with ResNet #27

merged 17 commits into from
Jul 10, 2017

Conversation

JihongJu
Copy link
Contributor

@JihongJu JihongJu commented Jun 9, 2017

  • Build an RCNN model with keras_resnet.ResNet50.
  • Implement heads described in Mask R-CNN.

My current implementation is:

        y = keras_resnet.ResNet50(inputs)
        features = y.layers[-2].output

        rpn_classification = keras.layers.Conv2D(
            9 * 1, (1, 1), activation="sigmoid")(features)
        rpn_regression = keras.layers.Conv2D(9 * 4, (1, 1))(features)

        rpn_prediction = keras.layers.concatenate(
            [rpn_classification, rpn_regression])

        # proposals of shape (1, None, 4)
        proposals = keras_rcnn.layers.object_detection.ObjectProposal(
            rois)([rpn_regression, rpn_classification])

        # slices of shape (1, None, 7, 7, 3)
        slices = keras_rcnn.layers.ROI((7, 7), rois)([inputs, proposals])
        
        # Implement the R-CNN heads shown in the figure below
        [score, boxes] = keras_rcnn.heads.ResHead(classes)(slices)

And the R-CNN heads from Mask R-CNN looks like
resnet_heads

That becomes:

        y = keras.layers.TimeDistributed(
            keras.layers.Conv2D(1024, (1, 1)))(x)

        # conv5 block as in Deep Residual Networks with first conv operates
        # on a 7x7 RoI with stride 1 (instead of 14x14 / stride 2)
        for i in range(3):
            y = _bottleneck(512, (1, 1))(y)

        y = keras.layers.TimeDistributed(
            keras.layers.BatchNormalization(axis=channel_axis))(y)
        y = keras.layers.TimeDistributed(
            keras.layers.Activation("relu"))(y)

        # class and box branches
        y = keras.layers.TimeDistributed(
            keras.layers.AveragePooling2D((7, 7)))(y)

        score = keras.layers.TimeDistributed(
            keras.layers.Dense(classes, activation="softmax"))(y)

        boxes = keras.layers.TimeDistributed(
            keras.layers.Dense(4 * classes))(y)

in keras_rcnn.heads.ResHead.

What do you think? @0x00b1

P.S. The API design should be discussed in #28

@codecov-io
Copy link

codecov-io commented Jun 9, 2017

Codecov Report

Merging #27 into master will increase coverage by 4.81%.
The diff coverage is 97.14%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #27      +/-   ##
==========================================
+ Coverage   48.44%   53.25%   +4.81%     
==========================================
  Files          15       17       +2     
  Lines         545      569      +24     
==========================================
+ Hits          264      303      +39     
+ Misses        281      266      -15
Impacted Files Coverage Δ
keras_rcnn/heads/__init__.py 100% <100%> (ø)
keras_rcnn/models.py 81.48% <100%> (+81.48%) ⬆️
keras_rcnn/heads/resnet.py 94.11% <94.11%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f3e6f4c...9aadff0. Read the comment docs.

@JihongJu JihongJu changed the title [Do not merge] Structure R-CNN with ResNet Build R-CNN with ResNet Jun 10, 2017
@0x00b1
Copy link
Contributor

0x00b1 commented Jun 10, 2017

@JihongJu Whoa! You’ve been busy! I’m excited to comment. Would you mind giving me a day or two? It’ll take some time to consider and respond.

@0x00b1
Copy link
Contributor

0x00b1 commented Jun 10, 2017

cc: @mcquin

else:
channel_axis = 1

y = keras.layers.TimeDistributed(
Copy link
Contributor

Choose a reason for hiding this comment

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

In my opinion, this is the trickiest part of the implementation.

I foresee two problems with using TimeDistributed.

  • I suspect that in nearly every situation TimeDistributed is used to step across a temporal dimension. Unfortunately, we’re the rare exception. It’ll be a challenge to explain this in our documentation (especially if we expect users to re-implement the CNN that use for feature extraction to use TimeDistributed). I’d really like Keras to modify the name to better emphasize its utility.

  • TimeDistributed requires users to implement their feature extractor layer by layer instead of calling on a model instance directly. I’d really prefer something like:

x = keras.layers.Input((224, 224, 3))

y = keras_resnet.ResNet50(x)

y = keras.layers.TimeDistributed(y)

If it worked like this, we could hide the y = keras.layers.TimeDistributed(y) statement inside the RCNN constructor. We could probably implement our own wrapper that iterates across a model’s layers and appends a TimeDistributed layer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@0x00b1 I agree with you here. But I can hardly see how to implement such a wrapper.
For example, we have ResHead that takes feature x as input and returns some outputs y. We need to apply TimeDistributed to each of the layers in between.
Maybe you have good ideas how to do that?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good question. 😆

A quick method might be iterating over model.layers then pop each layer and append TimeDistributed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi, @JihongJu, I added a temporal module to keras-resnet. You should be able to use keras_resnet.block.temporal.basic, keras_resnet.block.temporal.bottleneck, and keras_resnet.block.temporal._shortcut.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@0x00b1 That would be great! I am a bit busy these days. I will get back to this asap.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@0x00b1 I sent a PR to keras-resnet to fix a bug for the temporal shortcut. And could you make a release (0.0.3?) afterwards?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Released (0.0.4 because I made a dumb release mistake!)

https://pypi.python.org/pypi/keras-resnet/0.0.4

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@0x00b1 Great! Thanks!

y2 = regions[:, 1] + regions[:, 3]
boxes = keras.backend.cast(boxes, keras.backend.floatx())
boxes = boxes / self.stride
x1 = boxes[..., 0]
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

import keras_rcnn.layers
import keras_rcnn.heads


class RCNN(keras.models.Model):
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks great!

We should also consider adding a features argument that users would use to pass their feature extractor (e.g. ResNet50). What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@0x00b1 Actually, I think we should go for two arguments: features and heads (maybe rename these two to keep consistency).

Copy link
Contributor

Choose a reason for hiding this comment

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

@JihongJu I like it! 👍

@0x00b1
Copy link
Contributor

0x00b1 commented Jun 15, 2017

@JihongJu I merged the backend and pooling changes.

@JihongJu
Copy link
Contributor Author

Great. I will continue the work on weekends.

@JihongJu
Copy link
Contributor Author

JihongJu commented Jul 4, 2017

@0x00b1 If you are okay with this design, I will continue with the Mask branch as in TODO with a new PR.

@0x00b1
Copy link
Contributor

0x00b1 commented Jul 10, 2017

@JihongJu Awesome! Merged! (I might do a little cleanup today or tomorrow.)

@0x00b1 0x00b1 merged commit eafbfcc into broadinstitute:master Jul 10, 2017
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.

3 participants