├── README.md ├── layers ├── caffe.proto ├── ordinal_regression_loss_layer.cpp ├── ordinal_regression_loss_layer.cu ├── ordinal_regression_loss_layer.hpp └── test_ordinal_regression_loss_layer.cpp └── train_test.prototxt /README.md: -------------------------------------------------------------------------------- 1 | Ordinal Regression 2 | ================== 3 | 4 | Caffe Loss Layer for `Ordinal Regression with Multiple Output CNN for Age Estimation`. 5 | 6 | ## How to 7 | 8 | You need to install [Caffe][caffe] first. Copy relative files to Caffe's source code tree. 9 | 10 | ``` 11 | export CAFFE_HOME=/path/to/caffe 12 | cp layers/ordinal_regression_loss_layer.hpp $CAFFE_HOME/include/caffe/layers/ordinal_regression_loss_layer.hpp 13 | cp layers/ordinal_regression_loss_layer.cpp $CAFFE_HOME/src/caffe/layers/ordinal_regression_loss_layer.cpp 14 | cp layers/ordinal_regression_loss_layer.cu $CAFFE_HOME/src/caffe/layers/ordinal_regression_loss_layer.cu 15 | cp layers/test_ordinal_regression_loss_layer.cpp $CAFFE_HOME/src/caffe/test/test_ordinal_regression_loss_layer.cpp 16 | ``` 17 | 18 | Modify `$CAFFE_HOME/src/caffe/proto/caffe.proto` according to `layers/caffe.proto` 19 | 20 | After all, follow Caffe's documents and compile it. 21 | 22 | ## Test the layer 23 | 24 | `make runtest GTEST_FILTER='OrdinalRegressionLossLayerTest/*'` 25 | 26 | ## References 27 | 28 | - [Caffe][caffe] 29 | - [Ordinal Regression with Multiple Output CNN for Age Estimation](http://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Niu_Ordinal_Regression_With_CVPR_2016_paper.pdf) 30 | 31 | 32 | [caffe]: https://github.com/BVLC/caffe 33 | -------------------------------------------------------------------------------- /layers/caffe.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package caffe; 4 | 5 | message LayerParameter { 6 | // 147 may need to be changed due to caffe's proto 7 | optional OrdinalRegressionParameter ordinal_regression_loss_param = 147; 8 | } 9 | 10 | message OrdinalRegressionParameter { 11 | optional uint32 k = 1; 12 | optional string weight_file = 2; 13 | } 14 | -------------------------------------------------------------------------------- /layers/ordinal_regression_loss_layer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "caffe/layers/ordinal_regression_loss_layer.hpp" 6 | 7 | namespace caffe { 8 | 9 | template 10 | void OrdinalRegressionLossLayer::LayerSetUp( 11 | const vector*>& bottom, const vector*>& top) { 12 | LossLayer::LayerSetUp(bottom, top); 13 | // k 14 | bool has_k = this->layer_param_.ordinal_regression_loss_param().has_k(); 15 | if (has_k) { 16 | k_ = this->layer_param_.ordinal_regression_loss_param().k(); 17 | } 18 | else { 19 | k_ = bottom[0]->shape(1) / 2; 20 | } 21 | // weight for every label 22 | vector shape(1, k_); 23 | weight_.Reshape(shape); 24 | Dtype* weight_data = weight_.mutable_cpu_data(); 25 | bool has_weight_file = this->layer_param_.ordinal_regression_loss_param().has_weight_file(); 26 | if (has_weight_file) { 27 | string weight_file = this->layer_param_.ordinal_regression_loss_param().weight_file(); 28 | std::ifstream fin; 29 | fin.open(weight_file.c_str()); 30 | Dtype weight; 31 | for (int i = 0; i < k_; i++) { 32 | fin >> weight; 33 | weight_data[i] = weight; 34 | } 35 | fin.close(); 36 | } 37 | else { 38 | for (int i = 0; i < k_; i++) { 39 | weight_data[i] = Dtype(1) / k_; 40 | } 41 | } 42 | } 43 | 44 | template 45 | void OrdinalRegressionLossLayer::Reshape( 46 | const vector*>& bottom, const vector*>& top) { 47 | LossLayer::Reshape(bottom, top); 48 | CHECK_EQ(bottom[0]->count(1), 2 * k_) << "Input must be (?, 2K) dimension."; 49 | prob_.ReshapeLike(*bottom[0]); 50 | } 51 | 52 | template 53 | void OrdinalRegressionLossLayer::Forward_cpu( 54 | const vector*>& bottom, const vector*>& top) { 55 | const int n = bottom[0]->shape(0); 56 | const int m = 2 * k_; 57 | const Dtype* bottom_data = bottom[0]->cpu_data(); 58 | Dtype* prob_data = prob_.mutable_cpu_data(); 59 | // get prob 60 | for (int i = 0; i < n; i++) { 61 | const int offset = bottom[0]->offset(i); 62 | const Dtype* x = bottom_data + offset; 63 | Dtype* y = prob_data + offset; 64 | for (int j = 0; j < m; j += 2) { 65 | const Dtype max_input = std::max(x[j], x[j+1]); 66 | y[j] = std::exp(x[j] - max_input); 67 | y[j+1] = std::exp(x[j+1] - max_input); 68 | const Dtype sum = y[j] + y[j+1]; 69 | y[j] /= sum; 70 | y[j+1] /= sum; 71 | } 72 | } 73 | // calc loss 74 | Dtype loss = 0; 75 | const Dtype* weight_data = weight_.cpu_data(); 76 | const Dtype* label_data = bottom[1]->cpu_data(); 77 | for (int i = 0; i < n; i++) { 78 | const Dtype* y = prob_data + prob_.offset(i); 79 | const int label = static_cast(label_data[i]); 80 | for (int j = 0; j <= label; j++) { 81 | loss -= weight_data[j] * std::log(std::max(y[2*j+1], Dtype(FLT_MIN))); 82 | } 83 | for (int j = label+1; j < k_; j++) { 84 | loss -= weight_data[j] * std::log(std::max(y[2*j], Dtype(FLT_MIN))); 85 | } 86 | } 87 | top[0]->mutable_cpu_data()[0] = loss / n; 88 | } 89 | 90 | template 91 | void OrdinalRegressionLossLayer::Backward_cpu(const vector*>& top, 92 | const vector& propagate_down, const vector*>& bottom) { 93 | if (propagate_down[1]) { 94 | LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; 95 | } 96 | if (propagate_down[0]) { 97 | const int n = bottom[0]->shape(0); 98 | const Dtype* prob_data = prob_.cpu_data(); 99 | const Dtype* label_data = bottom[1]->cpu_data(); 100 | const Dtype* weight_data = weight_.cpu_data(); 101 | Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); 102 | caffe_copy(prob_.count(), prob_data, bottom_diff); 103 | for (int i = 0; i < n; i++) { 104 | const int offset = bottom[0]->offset(i); 105 | Dtype* dx = bottom_diff + offset; 106 | const int label = static_cast(label_data[i]); 107 | for (int j = 0; j <= label; j++) { 108 | dx[2*j+1] -= 1; 109 | } 110 | for (int j = label+1; j < k_; j++) { 111 | dx[2*j] -= 1; 112 | } 113 | for (int j = 0; j < k_; j++) { 114 | dx[2*j] *= weight_data[j]; 115 | dx[2*j+1] *= weight_data[j]; 116 | } 117 | } 118 | const Dtype scale = 1.0 / n; 119 | caffe_scal(prob_.count(), scale, bottom_diff); 120 | } 121 | } 122 | 123 | #ifdef CPU_ONLY 124 | STUB_GPU(OrdinalRegressionLossLayer) 125 | #endif // CPU_ONLY 126 | 127 | INSTANTIATE_CLASS(OrdinalRegressionLossLayer); 128 | REGISTER_LAYER_CLASS(OrdinalRegressionLoss); 129 | 130 | } // namespace caffe 131 | -------------------------------------------------------------------------------- /layers/ordinal_regression_loss_layer.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include "caffe/util/math_functions.hpp" 3 | #include "caffe/layers/ordinal_regression_loss_layer.hpp" 4 | 5 | namespace caffe { 6 | 7 | template 8 | __global__ void OrdinalRegressionLossForward(const int k, const int n, 9 | const Dtype* x, Dtype* y, const Dtype* label, const Dtype* weight, Dtype* loss) { 10 | CUDA_KERNEL_LOOP(idx, n) { 11 | const int sample_idx = idx / k; 12 | const int label_idx = idx % k; 13 | const int offset = 2*idx; 14 | const int this_label = static_cast(label[sample_idx]); 15 | const Dtype this_weight = weight[label_idx]; 16 | const Dtype* x_data = x + offset; 17 | Dtype* y_data = y + offset; 18 | Dtype* loss_data = loss + offset; 19 | const Dtype max_input = max(x_data[0], x_data[1]); 20 | y_data[0] = exp(x_data[0] - max_input); 21 | y_data[1] = exp(x_data[1] - max_input); 22 | const Dtype sum = y_data[0] + y_data[1]; 23 | y_data[0] /= sum; 24 | y_data[1] /= sum; 25 | if (label_idx <= this_label) { 26 | loss_data[0] = 0; 27 | loss_data[1] = -log(max(y_data[1], Dtype(FLT_MIN))); 28 | } 29 | else { 30 | loss_data[0] = -log(max(y_data[0], Dtype(FLT_MIN))); 31 | loss_data[1] = 0; 32 | } 33 | loss_data[0] *= this_weight; 34 | loss_data[1] *= this_weight; 35 | } 36 | } 37 | 38 | template 39 | void OrdinalRegressionLossLayer::Forward_gpu( 40 | const vector*>& bottom, const vector*>& top) { 41 | const int n = bottom[0]->shape(0); 42 | const int nthread = n * k_; 43 | const Dtype* x = bottom[0]->gpu_data(); 44 | const Dtype* label = bottom[1]->gpu_data(); 45 | const Dtype* weight = weight_.gpu_data(); 46 | Dtype* y = prob_.mutable_gpu_data(); 47 | Dtype* loss_data = bottom[0]->mutable_gpu_diff(); // reuse 48 | OrdinalRegressionLossForward<<>>(k_, nthread, x, y, label, weight, loss_data); 50 | Dtype loss; 51 | caffe_gpu_asum(bottom[0]->count(), loss_data, &loss); 52 | top[0]->mutable_cpu_data()[0] = loss / n; 53 | } 54 | 55 | template 56 | __global__ void OrdinalRegressionLossBackward(const int k, const int n, 57 | Dtype* dx, const Dtype* label, const Dtype* weight) { 58 | CUDA_KERNEL_LOOP(idx, n) { 59 | const int sample_idx = idx / k; 60 | const int label_idx = idx % k; 61 | const int offset = 2*idx; 62 | const int this_label = static_cast(label[sample_idx]); 63 | const Dtype this_weight = weight[label_idx]; 64 | Dtype* dx_data = dx + offset; 65 | if (label_idx <= this_label) { 66 | dx_data[1] -= 1; 67 | } 68 | else { 69 | dx_data[0] -= 1; 70 | } 71 | dx_data[0] *= this_weight; 72 | dx_data[1] *= this_weight; 73 | } 74 | } 75 | 76 | template 77 | void OrdinalRegressionLossLayer::Backward_gpu(const vector*>& top, 78 | const vector& propagate_down, const vector*>& bottom) { 79 | if (propagate_down[1]) { 80 | LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; 81 | } 82 | if (propagate_down[0]) { 83 | const int n = bottom[0]->shape(0); 84 | const int nthread = n * k_; 85 | const Dtype* prob_data = prob_.gpu_data(); 86 | const Dtype* label_data = bottom[1]->gpu_data(); 87 | const Dtype* weight_data = weight_.gpu_data(); 88 | Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); 89 | caffe_gpu_memcpy(prob_.count() * sizeof(Dtype), prob_data, bottom_diff); 90 | OrdinalRegressionLossBackward<<>>(k_, nthread, bottom_diff, label_data, weight_data); 92 | const Dtype scale = 1.0 / n; 93 | caffe_gpu_scal(prob_.count(), scale, bottom_diff); 94 | } 95 | } 96 | 97 | INSTANTIATE_LAYER_GPU_FUNCS(OrdinalRegressionLossLayer); 98 | 99 | } // namespace caffe 100 | -------------------------------------------------------------------------------- /layers/ordinal_regression_loss_layer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CAFFE_ORDINAL_REGRESSION_LOSS_LAYER_HPP_ 2 | #define CAFFE_ORDINAL_REGRESSION_LOSS_LAYER_HPP_ 3 | 4 | #include 5 | #include "caffe/blob.hpp" 6 | #include "caffe/layer.hpp" 7 | #include "caffe/proto/caffe.pb.h" 8 | #include "caffe/layers/loss_layer.hpp" 9 | 10 | namespace caffe { 11 | 12 | template 13 | class OrdinalRegressionLossLayer : public LossLayer { 14 | public: 15 | explicit OrdinalRegressionLossLayer(const LayerParameter& param) 16 | : LossLayer(param) {} 17 | 18 | virtual void LayerSetUp(const vector*>& bottom, 19 | const vector*>& top); 20 | virtual void Reshape(const vector*>& bottom, 21 | const vector*>& top); 22 | 23 | virtual inline const char* type() const { return "OrdinalRegressionLoss"; } 24 | 25 | inline const Blob* prob() const { return &prob_; } 26 | 27 | protected: 28 | virtual void Forward_cpu(const vector*>& bottom, 29 | const vector*>& top); 30 | virtual void Forward_gpu(const vector*>& bottom, 31 | const vector*>& top); 32 | 33 | virtual void Backward_cpu(const vector*>& top, 34 | const vector& propagate_down, const vector*>& bottom); 35 | virtual void Backward_gpu(const vector*>& top, 36 | const vector& propagate_down, const vector*>& bottom); 37 | 38 | int k_; 39 | Blob weight_; 40 | Blob prob_; 41 | }; 42 | 43 | } // namespace caffe 44 | 45 | #endif // CAFFE_ORDINAL_REGRESSION_LOSS_LAYER_HPP_ 46 | -------------------------------------------------------------------------------- /layers/test_ordinal_regression_loss_layer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "gtest/gtest.h" 6 | #include "caffe/blob.hpp" 7 | #include "caffe/common.hpp" 8 | #include "caffe/filler.hpp" 9 | #include "caffe/layers/ordinal_regression_loss_layer.hpp" 10 | #include "caffe/test/test_caffe_main.hpp" 11 | #include "caffe/test/test_gradient_check_util.hpp" 12 | 13 | using boost::scoped_ptr; 14 | 15 | namespace caffe { 16 | 17 | template 18 | class OrdinalRegressionLossLayerTest : public MultiDeviceTest { 19 | typedef typename TypeParam::Dtype Dtype; 20 | 21 | protected: 22 | OrdinalRegressionLossLayerTest() 23 | : blob_bottom_data_(new Blob(100, 200, 1, 1)), 24 | blob_bottom_label_(new Blob(100, 1, 1, 1)), 25 | blob_top_loss_(new Blob()) { 26 | Caffe::set_random_seed(0); 27 | FillerParameter filler_param; 28 | filler_param.set_std(10); 29 | GaussianFiller filler(filler_param); 30 | filler.Fill(this->blob_bottom_data_); 31 | for (int i = 0; i < blob_bottom_label_->count(); i++) { 32 | blob_bottom_label_->mutable_cpu_data()[i] = caffe_rng_rand() % 100; 33 | } 34 | blob_bottom_vec_.push_back(blob_bottom_data_); 35 | blob_bottom_vec_.push_back(blob_bottom_label_); 36 | blob_top_vec_.push_back(blob_top_loss_); 37 | } 38 | 39 | virtual ~OrdinalRegressionLossLayerTest() { 40 | delete blob_bottom_data_; 41 | delete blob_bottom_label_; 42 | delete blob_top_loss_; 43 | } 44 | 45 | Blob* const blob_bottom_data_; 46 | Blob* const blob_bottom_label_; 47 | Blob* const blob_top_loss_; 48 | vector*> blob_bottom_vec_; 49 | vector*> blob_top_vec_; 50 | }; 51 | 52 | TYPED_TEST_CASE(OrdinalRegressionLossLayerTest, TestDtypesAndDevices); 53 | 54 | // TYPED_TEST(OrdinalRegressionLossLayerTest, TestGradient) { 55 | // typedef typename TypeParam::Dtype Dtype; 56 | // LayerParameter layer_param; 57 | // OrdinalRegressionLossLayer layer(layer_param); 58 | // GradientChecker checker(1e-2, 1e-2, 1701); 59 | // checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, 60 | // this->blob_top_vec_, 0); 61 | // } 62 | 63 | TYPED_TEST(OrdinalRegressionLossLayerTest, TestForward) { 64 | typedef typename TypeParam::Dtype Dtype; 65 | LayerParameter layer_param; 66 | scoped_ptr > layer( 67 | new OrdinalRegressionLossLayer(layer_param)); 68 | layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); 69 | layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); 70 | Dtype loss = this->blob_top_loss_->cpu_data()[0]; 71 | 72 | std::cout << loss << endl; 73 | 74 | const Blob* prob = layer->prob(); 75 | const Dtype* prob_data = prob->cpu_data(); 76 | 77 | std::cout << prob_data[0] << " " << prob_data[1] << endl; 78 | std::cout << prob_data[2] << " " << prob_data[3] << endl; 79 | std::cout << prob_data[4] << " " << prob_data[5] << endl; 80 | std::cout << prob_data[6] << " " << prob_data[7] << endl; 81 | std::cout << prob_data[8] << " " << prob_data[9] << endl; 82 | } 83 | 84 | TYPED_TEST(OrdinalRegressionLossLayerTest, TestBackward) { 85 | typedef typename TypeParam::Dtype Dtype; 86 | LayerParameter layer_param; 87 | scoped_ptr > layer( 88 | new OrdinalRegressionLossLayer(layer_param)); 89 | layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); 90 | layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); 91 | vector propagate_down; 92 | propagate_down.push_back(true); 93 | propagate_down.push_back(false); 94 | layer->Backward(this->blob_top_vec_, propagate_down, this->blob_bottom_vec_); 95 | 96 | const Dtype* bottom_diff_data = this->blob_bottom_data_->cpu_diff(); 97 | std::cout << bottom_diff_data[0] << " " << bottom_diff_data[1] << endl; 98 | std::cout << bottom_diff_data[2] << " " << bottom_diff_data[3] << endl; 99 | std::cout << bottom_diff_data[4] << " " << bottom_diff_data[5] << endl; 100 | std::cout << bottom_diff_data[6] << " " << bottom_diff_data[7] << endl; 101 | std::cout << bottom_diff_data[8] << " " << bottom_diff_data[9] << endl; 102 | } 103 | 104 | } // namespace caffe 105 | -------------------------------------------------------------------------------- /train_test.prototxt: -------------------------------------------------------------------------------- 1 | name: "Ordianl Regression 4 layers" 2 | layer { 3 | top: "data" 4 | type: "Data" 5 | top: "label" 6 | name: "data" 7 | data_param { 8 | source: "../data/lmdb_union/train" 9 | batch_size: 256 10 | backend: LMDB 11 | } 12 | transform_param { 13 | crop_size: 60 14 | mirror: true 15 | mean_value: 128 16 | mean_value: 128 17 | mean_value: 128 18 | scale: 0.0078125 19 | } 20 | include { 21 | phase: TRAIN 22 | } 23 | } 24 | layer { 25 | top: "data" 26 | top: "label" 27 | name: "data" 28 | type: "Data" 29 | 30 | data_param { 31 | source: "../data/lmdb_union/test" 32 | batch_size: 256 33 | backend: LMDB 34 | } 35 | transform_param { 36 | crop_size: 60 37 | mirror: false 38 | mean_value: 128 39 | mean_value: 128 40 | mean_value: 128 41 | scale: 0.0078125 42 | } 43 | include { 44 | phase: TEST 45 | } 46 | } 47 | 48 | 49 | 50 | 51 | 52 | layer { 53 | bottom: "data" 54 | name: "conv_1" 55 | top: "conv_1" 56 | type: "Convolution" 57 | convolution_param { 58 | num_output: 20 59 | kernel_size: 5 60 | pad: 0 61 | weight_filler { 62 | type: "xavier" 63 | } 64 | bias_filler { 65 | type: "constant" 66 | } 67 | 68 | } 69 | param { 70 | lr_mult: 1 71 | decay_mult: 0 72 | } 73 | param { 74 | lr_mult: 2 75 | decay_mult: 0 76 | } 77 | } 78 | 79 | layer { 80 | bottom: "conv_1" 81 | name: "relu_1" 82 | top: "conv_1" 83 | type: "ReLU" 84 | } 85 | 86 | layer { 87 | bottom: "conv_1" 88 | name: "norm_1" 89 | top: "norm_1" 90 | type: "LRN" 91 | } 92 | layer { 93 | bottom: "norm_1" 94 | name: "pool_1" 95 | top: "pool_1" 96 | type: "Pooling" 97 | pooling_param { 98 | pool: MAX 99 | kernel_size: 2 100 | stride: 2 101 | } 102 | } 103 | 104 | 105 | 106 | layer { 107 | bottom: "pool_1" 108 | name: "conv_2" 109 | top: "conv_2" 110 | type: "Convolution" 111 | convolution_param { 112 | num_output: 40 113 | kernel_size: 7 114 | weight_filler { 115 | type: "xavier" 116 | } 117 | bias_filler { 118 | type: "constant" 119 | } 120 | } 121 | param { 122 | lr_mult: 1 123 | decay_mult: 0 124 | } 125 | param { 126 | lr_mult: 2 127 | decay_mult: 0 128 | } 129 | } 130 | 131 | layer { 132 | bottom: "conv_2" 133 | name: "relu_2" 134 | top: "conv_2" 135 | type: "ReLU" 136 | } 137 | layer { 138 | bottom: "conv_2" 139 | name: "norm_2" 140 | top: "norm_2" 141 | type: "LRN" 142 | } 143 | layer { 144 | bottom: "norm_2" 145 | name: "pool_2" 146 | top: "pool_2" 147 | type: "Pooling" 148 | pooling_param { 149 | pool: MAX 150 | kernel_size: 2 151 | stride: 2 152 | } 153 | } 154 | 155 | 156 | 157 | 158 | layer { 159 | bottom: "pool_2" 160 | name: "conv_3" 161 | top: "conv_3" 162 | type: "Convolution" 163 | convolution_param { 164 | num_output: 80 165 | kernel_size: 11 166 | weight_filler { 167 | type: "xavier" 168 | } 169 | bias_filler { 170 | type: "constant" 171 | } 172 | } 173 | param { 174 | lr_mult: 1 175 | decay_mult: 0 176 | } 177 | param { 178 | lr_mult: 2 179 | decay_mult: 0 180 | } 181 | } 182 | 183 | 184 | layer { 185 | bottom: "conv_3" 186 | name: "relu_3" 187 | top: "conv_3" 188 | type: "ReLU" 189 | } 190 | 191 | layer { 192 | bottom: "conv_3" 193 | name: "norm_3" 194 | top: "norm_3" 195 | type: "LRN" 196 | } 197 | 198 | layer { 199 | bottom: "norm_3" 200 | name: "fc" 201 | top: "fc" 202 | type: "InnerProduct" 203 | inner_product_param { 204 | num_output: 80 205 | weight_filler { 206 | type: "xavier" 207 | } 208 | bias_filler { 209 | type: "constant" 210 | } 211 | } 212 | param { 213 | lr_mult: 1 214 | decay_mult: 1 215 | } 216 | param { 217 | lr_mult: 1 218 | decay_mult: 0 219 | } 220 | } 221 | 222 | layer { 223 | bottom: "fc" 224 | name: "fc2" 225 | top: "fc2" 226 | type: "InnerProduct" 227 | inner_product_param { 228 | num_output: 160 229 | weight_filler { 230 | type: "xavier" 231 | } 232 | bias_filler { 233 | type: "constant" 234 | } 235 | } 236 | param { 237 | lr_mult: 1 238 | decay_mult: 1 239 | } 240 | param { 241 | lr_mult: 1 242 | decay_mult: 0 243 | } 244 | } 245 | 246 | layer { 247 | bottom: "fc2" 248 | name: "fc_output" 249 | top: "fc_output" 250 | type: "InnerProduct" 251 | inner_product_param { 252 | num_output: 200 253 | weight_filler { 254 | type: "xavier" 255 | } 256 | bias_filler { 257 | type: "constant" 258 | } 259 | } 260 | param { 261 | lr_mult: 1 262 | decay_mult: 1 263 | } 264 | param { 265 | lr_mult: 1 266 | decay_mult: 0 267 | } 268 | } 269 | 270 | layer { 271 | bottom: "fc_output" 272 | bottom: "label" 273 | top: "mae" 274 | name: "mae" 275 | type: "MAE" 276 | } 277 | layer { 278 | bottom: "fc_output" 279 | bottom: "label" 280 | top: "loss" 281 | name: "loss" 282 | type: "OrdinalRegressionLoss" 283 | include { 284 | phase: TRAIN 285 | } 286 | ordinal_regression_loss_param { 287 | # weight_file: "../data/lmdb_union/weight.txt" 288 | } 289 | } 290 | layer { 291 | bottom: "fc_output" 292 | bottom: "label" 293 | top: "loss" 294 | name: "loss" 295 | type: "OrdinalRegressionLoss" 296 | include { 297 | phase: TEST 298 | } 299 | ordinal_regression_loss_param { 300 | # weight_file: "../data/lmdb_union/weight.txt" 301 | } 302 | } 303 | --------------------------------------------------------------------------------