Blobs are N-D arrays (for N not necessarily equals 4) #1970

Merged
merged 35 commits into from Mar 4, 2015

Conversation

Projects
None yet
9 participants
Contributor

jeffdonahue commented Feb 25, 2015

Replaces #1486 -- this PR is to master instead of dev. This is rebased and is ready for review @longjon.

This PR gives Blobs a vector<int> shape_ of dimensions, rather than the old num, channels, height, width. The first commit is all the changes needed to get Caffe to compile and everything to run as before with the new vector of tensor dimensions. The remaining commits generalize some existing classes to use the new tensor dimensions (but they are not necessary to make it run, as it's still fine to just use all 4-D blobs with extra singleton dimensions where needed).

Contributor

jeffdonahue commented Mar 2, 2015

I had added a commit intended to add matcaffe support for N-D array blobs, but decided to defer to #1913 to add the support, as I don't have much experience with matcaffe and it might require some special cases (e.g., I remembered that MATLAB doesn't support arrays with <2 axes so would need to figure out what to do with those cases -- maybe the MATLAB default of adding extra axes with dimension 1 would work fine, but not sure). But if that commit might be helpful in the development of #1913, feel free to cherry-pick or just refer to it here (but note that I didn't test or even try to compile it...).

So for now, matcaffe will continue to work fine for blobs with <= 4 axes, but will die on attempts to access blobs with >4 dimensions (per their use of num/channels/height/width, which call LegacyShape).

@Yangqing Yangqing commented on the diff Mar 2, 2015

src/caffe/proto/caffe.proto
@@ -2,13 +2,21 @@ syntax = "proto2";
package caffe;
+// Specifies the shape (dimensions) of a Blob.
+message BlobShape {
+ repeated int64 dim = 1 [packed = true];
@Yangqing

Yangqing Mar 2, 2015

Contributor

My 2 cents - uint32 should be enough? A 2G blob (8G in floats) looks big enough.

(But yeah, 640k used to be enough for everything.)

@jeffdonahue

jeffdonahue Mar 2, 2015

Contributor

My feeling is that it's probably worth future-proofing given the relatively tiny amount of extra storage cost (unless you're really using blobs with INT_MAX axes, as are now allowed...). I could make it uint32 for now and then later change it to int64 if/when we really find practical uses for blobs that large, but that might cause backward compatibility problems with (de)serialization that I don't even want to think about... (Why not uint64? I think it's worth keeping -1 and other <0 values reserved for various purposes -- e.g. setting axis [default = -1] to signify "all axes", or that an axis must be manually specified and should fail otherwise, etc. And it does seem pretty unlikely that in the reasonably foreseeable future we'll need 2^64 byte blobs, so 2^63-1 bytes seems like an okay max...)

I'm open to discussion on any of this though.

@Yangqing

Yangqing Mar 2, 2015

Contributor

Future-proof sounds better :) Thanks for checking with me!

@shelhamer

shelhamer Mar 2, 2015

Owner

But Jeff, there is no reason for any individual to have a > 2GB blob in their home. Haha no, I'm all for int64 dimensions.

shelhamer referenced this pull request Mar 2, 2015

Closed

Matcaffe2 #1913

Contributor

longjon commented Mar 2, 2015

Oops, just realized that I've been commenting on commits again. Agree with int64 blob dimensions. I'm ready to merge this today, pending comments made, the new type for Python Blob.reshape (which I'll do), and a nod from @shelhamer.

Contributor

longjon commented Mar 2, 2015

Added my Python update... this should be ready pending your approval of that, Travis, and @shelhamer.

Contributor

jeffdonahue commented Mar 3, 2015

Thanks Jon! Your updated Blob_Reshape looks good to me.

Owner

shelhamer commented Mar 3, 2015

The axis field in the proto is inconsistently (u)int32 -- should it be int32 everywhere to allow positive and negative indexing in Slice, Concat, and InnerProduct dimension picking? Or it's fine to keep this the same for now as current behavior.

@shelhamer shelhamer commented on an outdated diff Mar 3, 2015

src/caffe/layers/inner_product_layer.cpp
@@ -15,7 +15,11 @@ void InnerProductLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const int num_output = this->layer_param_.inner_product_param().num_output();
bias_term_ = this->layer_param_.inner_product_param().bias_term();
N_ = num_output;
- K_ = bottom[0]->count() / bottom[0]->num();
+ const int dim = this->layer_param_.inner_product_param().axis();
+ // Dimensions starting from "axis" are "flattened" into a single
+ // length K_ vector. For example, if bottom[0]'s shape is (N, C, H, W),
@shelhamer

shelhamer Mar 3, 2015

Owner

For example, if bottom[0]'s shape is (N, C, H, W) and axis = 1

Owner

shelhamer commented Mar 3, 2015

@jeffdonahue this is sweet. My only comments were minor, so do what you want with them and merge!

@shelhamer shelhamer commented on the diff Mar 3, 2015

src/caffe/layers/softmax_loss_layer.cpp
@@ -35,6 +35,9 @@ void SoftmaxWithLossLayer<Dtype>::Reshape(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
LossLayer<Dtype>::Reshape(bottom, top);
softmax_layer_->Reshape(softmax_bottom_vec_, softmax_top_vec_);
+ softmax_axis_ = this->layer_param_.softmax_param().axis();
+ outer_num_ = bottom[0]->count(0, softmax_axis_);
+ inner_num_ = bottom[0]->count(softmax_axis_ + 1);
@shelhamer

shelhamer Mar 3, 2015

Owner

This could be a good time to add a check that the prediction and target dimensions (except channel) agree?

@jeffdonahue

jeffdonahue Mar 3, 2015

Contributor

done in a717963 -- I was a little less strict about it, just checking that the number of labels matches the number of predictions, as I couldn't figure out a way to do it that's backward compatible and nice to use in the future. (For example, I didn't want to break the current behavior of (NxCxHxW) predictions with (Nx1xHxW) labels, but it seems silly to require the singleton axis in the labels, but requiring (NxHxW) labels -- no singleton axis -- breaks backwards compatibility.)

Contributor

jeffdonahue commented Mar 3, 2015

Hey Evan, thanks for the review! I made the additional commits above in response to your comments -- currently marked with fixup! to be autosquashed later. I thought you might want to take a look since, re negative indexing, I applied it in SliceLayer, ConcatLayer, SoftmaxLayer, and InnerProductLayer, and ended up adding an additional Blob method CanonicalAxisIndex (as I started to realize I was writing the same checks several times). I added a single test that actually uses it to TestConcatLayer.

Edit: I went ahead and squashed the fixups, but I saved the state before the squash in another branch "tensor-blob-presquash" if you still want to look at only the additional changes I made after your review.

jeffdonahue and others added some commits Nov 26, 2014

@jeffdonahue jeffdonahue Blobs are ND arrays (for N not necessarily equals 4).
vector<int> shape_ instead of (num, channels, height, width).
1434e87
@jeffdonahue jeffdonahue InnerProductLayer weights are 2D; biases are 1D 559ff3a
@jeffdonahue jeffdonahue LossLayer output is 0D (scalar) a0fa2a9
@jeffdonahue jeffdonahue InnerProductLayer can multiply along any axis 29581e6
@jeffdonahue jeffdonahue TestBlob: test that legacy BlobProtos are correctly handled by ShapeE…
…quals
c4e9ec4
@jeffdonahue jeffdonahue ConvLayer biases are 1D 94179cc
@jeffdonahue jeffdonahue Add BlobShape message; use for Net input shapes 5407f82
@jeffdonahue jeffdonahue add offset, {data,diff}_at nd blob accessors 119a1c6
@jeffdonahue jeffdonahue Fix sparse GaussianFiller for new IPLayer weight axes cf9fdda
@jeffdonahue jeffdonahue EltwiseLayer need not assume old 4D dim names bf73cb1
@jeffdonahue jeffdonahue ConcatLayer: generalized Blob axes 8afdcd0
@jeffdonahue jeffdonahue FlattenLayer: generalized Blob axes 1b97c06
@jeffdonahue jeffdonahue TestConcatLayer: fix style errors 704e524
@jeffdonahue jeffdonahue TestConcatLayer: add forward/gradient tests for concatenation along num d52e9a8
@jeffdonahue jeffdonahue SliceLayer: generalized Blob axes b868916
@jeffdonahue jeffdonahue common_layers.hpp: remove unused "Blob col_bob_" fb9caee
@jeffdonahue jeffdonahue AccuracyLayer output is 0D (scalar) d8c6aeb
@jeffdonahue jeffdonahue AccuracyLayer generalized to N instance axes 6b8a765
@jeffdonahue jeffdonahue Test{Net,Solver} fixes for AccuracyLayer generalization 8e96445
@jeffdonahue jeffdonahue SoftmaxLayer: generalized Blob axes abec302
@jeffdonahue jeffdonahue SplitLayer: change Reshape(n,h,c,w) to ReshapeLike(...) e2bc9f9
@jeffdonahue jeffdonahue DataLayer outputs 1D labels e56377d
@jeffdonahue jeffdonahue HDF5DataLayer shapes output according to HDF5 shape e6468e9
@jeffdonahue jeffdonahue MemoryDataLayer outputs 1D labels 7c8725b
@jeffdonahue jeffdonahue ImageDataLayer outputs 1D labels c87a136
@jeffdonahue jeffdonahue WindowDataLayer outputs 1D labels 9505001
@jeffdonahue jeffdonahue EuclideanLossLayer: generalized Blob axes fcbb933
@jeffdonahue jeffdonahue DummyDataLayer outputs blobs of arbitrary shape 7462c84
@jeffdonahue jeffdonahue PyBlobs support generalized axes 269dafa
@jeffdonahue jeffdonahue Add CHECK_EQ(4, ...)s to "vision layers" to enforce that the
num/channnels/height/width indexing is valid.
69fc1f6
@jeffdonahue jeffdonahue Add option not to reshape to Blob::FromProto; use when loading Blobs
from saved NetParameter

Want to keep the param Blob shape the layer has set, and not necessarily
adopt the one from the saved net (e.g. want to keep new 1D bias shape,
rather than take the (1 x 1 x 1 x D) shape from a legacy net).
71df6f9
@jeffdonahue jeffdonahue SoftmaxLossLayer generalized like SoftmaxLayer 94d93da
@jeffdonahue jeffdonahue CuDNNSoftmaxLayer: generalized Blob axes 60c288b
@longjon @jeffdonahue longjon [pycaffe] expose Blob.reshape as *args function aa242aa
@longjon @jeffdonahue longjon [pytest] use non-4d blobs in test_python_layer 8c79d65
Contributor

jeffdonahue commented Mar 4, 2015

This should be ready to go -- I'll merge as soon as @shelhamer or @longjon quickly OKs the small fixups I made last night in response to @shelhamer's suggestions (can see just those changes at jeffdonahue/caffe@eca584d...jeffdonahue:tensor-blob-presquash).

Contributor

bhack commented Mar 4, 2015

This will probably cross impact every new layer in the PRs queue. How many new layers we have in the "waiting list"? The merging order is not neutral because will impact the adaptation charge to developer of one PR instead of another.

Owner

shelhamer commented Mar 4, 2015

Thanks for the fixups @jeffdonahue -- this all looks good to me. This PR can be a guide to converting in-progress and future layers to N != 4. I'm happy to see brewing transcend to higher dimensions.

Next up: N-D convolution and pooling!

@shelhamer shelhamer added a commit that referenced this pull request Mar 4, 2015

@shelhamer shelhamer Merge pull request #1970 from jeffdonahue/tensor-blob
Blobs are N-D arrays (for N not necessarily equals 4)
85bb397

@shelhamer shelhamer merged commit 85bb397 into BVLC:master Mar 4, 2015

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details

ddetone commented Mar 4, 2015

@shelhamer Is there an existing branch in which someone has begun N-D convolution and pooling? I am currently working on my own 3-D convolution and pooling implementation.

jeffdonahue deleted the jeffdonahue:tensor-blob branch Mar 4, 2015

@shelhamer shelhamer added a commit to shelhamer/caffe that referenced this pull request Mar 7, 2015

@shelhamer shelhamer [pycaffe] no need to squeeze output after #1970
fix #2041 reported by @dgmp88
38ec847

MY data is 64 ..36..1..1 and label is 64..1..1..1
I guess the label must be changed to 64 ..36..1..1
Or I'll get the following errors.
Check failed: bottom[0]->shape(i) <= bottom[1]->shape(i) (36 vs. 1) Dimension mismatch between predictions and label

Can you give some tips?

Is there some thing wrong?
CHECK_LE(bottom[0]->shape(i), bottom[1]->shape(i))

  • top[0]->Reshape(1, 1, 1, 1); + << "Dimension mismatch between predictions and label.";?
Owner

shelhamer commented Mar 7, 2015

@jeffdonahue can you align https://github.com/BVLC/caffe/blob/master/docs/tutorial/net_layer_blob.md with the transcendence to N-D blob sometime?

@shelhamer shelhamer added a commit that referenced this pull request Mar 8, 2015

@shelhamer shelhamer [pycaffe] no need to squeeze output after #1970
fix #2041 reported by @dgmp88
fc35930

@jeffdonahue jeffdonahue added a commit to jeffdonahue/caffe that referenced this pull request Mar 9, 2015

@jeffdonahue jeffdonahue Fixup AccuracyLayer like SoftmaxLossLayer in #1970 -- fixes #2063 32e6e96

@jeffdonahue jeffdonahue added a commit to jeffdonahue/caffe that referenced this pull request Mar 9, 2015

@jeffdonahue jeffdonahue Fixup AccuracyLayer like SoftmaxLossLayer in #1970 -- fixes #2063 a3e4604

@jeffdonahue jeffdonahue added a commit to jeffdonahue/caffe that referenced this pull request Mar 9, 2015

@jeffdonahue jeffdonahue Fixup AccuracyLayer like SoftmaxLossLayer in #1970 -- fixes #2063 3385d1c

@jeffdonahue jeffdonahue added a commit to jeffdonahue/caffe that referenced this pull request Mar 9, 2015

@jeffdonahue jeffdonahue Fixup AccuracyLayer like SoftmaxLossLayer in #1970 -- fixes #2063 6d5c8b2

@jeffdonahue jeffdonahue added a commit to jeffdonahue/caffe that referenced this pull request Mar 9, 2015

@jeffdonahue jeffdonahue Fixup AccuracyLayer like SoftmaxLossLayer in #1970 -- fixes #2063 7a40f74

@jeffdonahue jeffdonahue added a commit that referenced this pull request Mar 9, 2015

@jeffdonahue jeffdonahue Merge pull request #2076 from jeffdonahue/accuracy-layer-fixes
Fixup AccuracyLayer like SoftmaxLossLayer in #1970
77ab8f6

@qinhongwei qinhongwei pushed a commit to qinhongwei/caffe that referenced this pull request Mar 12, 2015

cv [pycaffe] no need to squeeze output after #1970, add change to detect…
…or.py
844bdb6

seanbell referenced this pull request Mar 16, 2015

Closed

Extract Features from fc7 #700

@shelhamer shelhamer added a commit that referenced this pull request May 15, 2015

@shelhamer shelhamer Merge pull request #2201 from jeffdonahue/tutorial-fixes
Update docs for ND blobs (#1970) and layer type is a string (#1694)
af224c1

@myfavouritekk myfavouritekk added a commit to myfavouritekk/caffe that referenced this pull request May 15, 2015

@myfavouritekk myfavouritekk Merge branch 'master' into DeepCrowd
* master: (21 commits)
  Update docs for ND blobs (#1970) and layer type is a string (#1694)
  Add ReshapeParameter axis and num_axes to reshape only a particular span of the input shape
  basic tests (Forward, Gradient) for ReshapeLayer
  ReshapeLayer fixups for ND blobs
  Added a Reshape layer for copying-free modification of blob dimensions.
  Spatial Pyramid Pooling Layer
  remove bogus implementation of SigmoidCrossEntropyLossLayer::Forward_gpu
  remove superfluous empty destructors
  [pycaffe] use bp::object instead of PyObject* for self in Python layer
  python: PEP8; changed docstring documentation style to NumPyDoc style
  This imports the wrong io module in Python 3.
  check that count_ does not overflow in Blob::Reshape
  Modify for better readability regarding temporary bufffer for backward computation
  Fix redundancy of parameter backward computation
  Added support for original implementation, using (margin - d^2), through the legacy_version parameter.
  added epsilon to prevent possible division by zero in gradient calculation
  Fixed contrastive loss layer to be the same as proposed in Hadsell et al 2006
  remove spurious net.hpp includes
  always call Layer::Reshape in Layer::Forward
  Increment iter_ before snapshotting, remove +1 logic -- fixes final snapshot being off by one
  ...
2ba4fe4

@cbfinn cbfinn added a commit to cbfinn/caffe that referenced this pull request Aug 12, 2015

@shelhamer @cbfinn shelhamer + cbfinn [pycaffe] no need to squeeze output after #1970
fix #2041 reported by @dgmp88
0bde346

@cbfinn cbfinn added a commit to cbfinn/caffe that referenced this pull request Aug 12, 2015

@jeffdonahue @cbfinn jeffdonahue + cbfinn Fixup AccuracyLayer like SoftmaxLossLayer in #1970 -- fixes #2063 d37d928

This code implies that 'index' can be negative, however, line 150 indexes a std::vector<> that does not support negative indices. Blob::ShapeEquals() calls LegacyShape(index) with a negative index. Should probably drop pseudo-support for negative indices [Line 143 change -4 to 0. Line 144 delete '|| index < -num_axes()'.]

Contributor

seanbell replied Jan 16, 2016

shape is a function. shape_ is the std::vector. So it is ok to have a negative index in line 150, since shape calls CanonicalAxisIndex.

You are correct Sean. My mistake. I confused array indexing with function call syntax. Thank you.

Contributor

seanbell commented on src/caffe/blob.cpp in 1434e87 Jan 16, 2016

I think this is ok since LegacyShape can handle negative arguments (see other reply to your other note).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment