http://blog.youkuaiyun.com/tangwei2014/article/details/46812153
博客中写了triplet_loss_layer的详细实现过程。但是,很多人(包括我)留言,不知道怎么用?不知道三元组怎么组装?
后来索性仿照网上一个python的实现思路,自己在前面加了一层triplet_sample_layer作为接口的过渡。现分享给大家。
功能就是,实现一个blob分解成对应的triplet_loss_layer需要的三个blob。
输出四个blob中,最后一个blob是为了适应上面博文的接口,作为冗余。
输入两个blob,也有冗余,可以不用输入标签的。
data_layer层读入的应该是三元组顺序正确(而不是随机乱序的)的路径、标签对文件。
其实,本质上仅仅实现了类似slice的功能。并没有做什么计算。
第一次向caffe中添加layer,特开通博客,留作纪念。
希望能对像我一样的菜鸟入门时,有所帮助。
1.triplet_sample_layer.hpp文件:
#ifndef CAFFE_TRIPLET_SAMPLE_LAYER_HPP_
#define CAFFE_TRIPLET_SAMPLE_LAYER_HPP_#include <vector>
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
namespace caffe {
/**
* @brief Computes the triplet sample
*/
template <typename Dtype>
class TripletSampleLayer : public Layer < Dtype > {
public:
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
explicit TripletSampleLayer(const LayerParameter& param)
:Layer<Dtype>(param){}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual inline int ExactNumBottomBlobs() const { return 2; }
virtual inline int ExactNumTopBlobs() const { return 4; }
virtual inline const char* type() const { return "TripletSample"; }
virtual inline bool AllowForceBackward(const int bottom_index) const {
return bottom_index != 4;
}
protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
};
} // namespace caffe
#endif // CAFFE_TRIPLET_SAMPLE_LAYER_HPP_
2.triplet_sample_layer.cpp文件:
/*
* triplet_sample_layer.cpp
*
* Created on: 2016.09.01
* Author: hecunxin
*/
#include <algorithm>
#include <vector>
#include "caffe/layer.hpp"
#include "caffe/layers/triplet_sample_layer.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template <typename Dtype>
void TripletSampleLayer<Dtype>::LayerSetUp(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
Layer<Dtype>::LayerSetUp(bottom, top);
CHECK_EQ(bottom[0]->num(), bottom[1]->num());
CHECK_EQ(bottom[0]->height(), 1);
CHECK_EQ(bottom[0]->width(), 1);
CHECK_EQ(bottom[1]->channels(), 1);
CHECK_EQ(bottom[1]->height(), 1);
CHECK_EQ(bottom[1]->width(), 1);
return;
}
template <typename Dtype>
void TripletSampleLayer<Dtype>::Forward_cpu(
const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top)
{
int j = 0;
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* top_data_anchor = top[0]->mutable_cpu_data();
//anchor,
Dtype* top_data_positive = top[1]->mutable_cpu_data();
//positive,
Dtype* top_data_negative = top[2]->mutable_cpu_data();
//negative,
Dtype* top_data_w = top[3]->mutable_cpu_data();
//w
const int count = top[0]->num();
for (int i = 0; i < count; ++ i)
{
caffe_copy(top[0]->channels() * top[0]->height() * top[0]->width(), bottom_data + i * 3 + 0, top_data_anchor + i);
//anchor,
caffe_copy(top[0]->channels() * top[0]->height() * top[0]->width(), bottom_data + i * 3 + 1, top_data_positive + i);
//positive,
caffe_copy(top[0]->channels() * top[0]->height() * top[0]->width(), bottom_data + i * 3 + 2, top_data_negative + i);
//negative,
top_data_w[i] = Dtype(1.);
}
}
template <typename Dtype>
void TripletSampleLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom)
{
if (propagate_down[0])
{
const int count = top[0]->num();
const Dtype* top_diff_anchor = top[0]->cpu_diff();
//anchor,
const Dtype* top_diff_positive = top[1]->cpu_diff();
//positive,
const Dtype* top_diff_negative = top[2]->cpu_diff();
//negative,
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
for (int i = 0; i < count; ++ i)
{
caffe_copy(top[0]->channels() * top[0]->height() * top[0]->width(), top_diff_anchor + i, bottom_diff + i * 3 + 0);
//anchor,
caffe_copy(top[0]->channels() * top[0]->height() * top[0]->width(), top_diff_positive + i, bottom_diff + i * 3 + 1);
//positive,
caffe_copy(top[0]->channels() * top[0]->height() * top[0]->width(), top_diff_negative + i, bottom_diff + i * 3 + 2);
//negative,
}
}
}
template <typename Dtype>
void TripletSampleLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
int count_ = bottom[0]->count() / 3;
vector<int> shape;
shape.push_back(bottom[0]->num() / 3);
shape.push_back(bottom[0]->channels());
shape.push_back(bottom[0]->height());
shape.push_back(bottom[0]->width());
for (int i = 0; i < top.size() - 1; ++i)
{
CHECK_NE(top[i], bottom[0]) << this->type() << " Layer does not "
"allow in-place computation.";
top[i]->Reshape(shape);
CHECK_EQ(count_, top[i]->count());
}
vector<int> shape3;
shape3.push_back(bottom[0]->num() / 3);
shape3.push_back(1);
shape3.push_back(1);
shape3.push_back(1);
top[3]->Reshape(shape3);
LOG(INFO) << "bottom0 shape:" << bottom[0]->shape_string();
LOG(INFO) << "bottom1 shape:" << bottom[1]->shape_string();
LOG(INFO) << "Top0 shape:" << top[0]->shape_string();
LOG(INFO) << "Top1 shape:" << top[1]->shape_string();
LOG(INFO) << "Top2 shape:" << top[2]->shape_string();
LOG(INFO) << "Top3 shape:" << top[3]->shape_string();
return;
}
#ifdef CPU_ONLY
STUB_GPU(TripletSampleLayer);
#endif
INSTANTIATE_CLASS(TripletSampleLayer);
REGISTER_LAYER_CLASS(TripletSample);
} // namespace caffe
3.triplet_sample_layer.cu文件:
/*
* triplet_sample_layer.cpp
*
* Created on: 2016.09.08
* Author: hecunxin
*/
#include <algorithm>
#include <vector>
#include "caffe/layer.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/layers/triplet_sample_layer.hpp"
namespace caffe {
template <typename Dtype>
void TripletSampleLayer<Dtype>::Forward_gpu(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
/*Forward_cpu(bottom, top);
return;*/
int j = 0;
const Dtype* bottom_data = bottom[0]->gpu_data();
Dtype* top_data_anchor = top[0]->mutable_gpu_data();
//anchor,
Dtype* top_data_positive = top[1]->mutable_gpu_data();
//positive,
Dtype* top_data_negative = top[2]->mutable_gpu_data();
//negative,
Dtype* top_data_w = top[3]->mutable_gpu_data();
//w
const int count = top[0]->num();
for (int i = 0; i < count; ++i)
{
caffe_gpu_memcpy(top[0]->channels() * top[0]->height() * top[0]->width(), bottom_data + i * 3 + 0, top_data_anchor + i);
//anchor,
caffe_gpu_memcpy(top[0]->channels() * top[0]->height() * top[0]->width(), bottom_data + i * 3 + 1, top_data_positive + i);
//positive,
caffe_gpu_memcpy(top[0]->channels() * top[0]->height() * top[0]->width(), bottom_data + i * 3 + 2, top_data_negative + i);
//negative,
caffe_gpu_set(top[3]->channels() * top[3]->height() * top[3]->width(), Dtype(1.), top_data_w + i);
//top_data_w[i] = Dtype(1.);
}
return;
}
template <typename Dtype>
void TripletSampleLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
/*Backward_cpu(top, propagate_down, bottom);
return;*/
if (propagate_down[0])
{
const int count = top[0]->num();
const Dtype* top_diff_anchor = top[0]->gpu_diff();
//anchor,
const Dtype* top_diff_positive = top[1]->gpu_diff();
//positive,
const Dtype* top_diff_negative = top[2]->gpu_diff();
//negative,
Dtype* bottom_diff = bottom[0]->mutable_gpu_diff();
for (int i = 0; i < count; ++i)
{
caffe_gpu_memcpy(top[0]->channels() * top[0]->height() * top[0]->width(), top_diff_anchor + i, bottom_diff + i * 3 + 0);
//anchor,
caffe_gpu_memcpy(top[0]->channels() * top[0]->height() * top[0]->width(), top_diff_positive + i, bottom_diff + i * 3 + 1);
//positive,
caffe_gpu_memcpy(top[0]->channels() * top[0]->height() * top[0]->width(), top_diff_negative + i, bottom_diff + i * 3 + 2);
//negative,
}
}
return;
}
INSTANTIATE_LAYER_GPU_FUNCS(TripletSampleLayer);
} // namespace caffe