之前写了一系列caffe官网中关于caffe python使用例子的翻译。在最后一个例子:http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/siamese/mnist_siamese.ipynb中,将mnist降维到2维并将其可视化。我当时按例子跑了下代码并且大致翻译了下:http://blog.csdn.net/thystar/article/details/50715835 , 这几天看deepid忽然发现这个东西我没看到重点,在这个例子中,训练使用的prototxt和mnist是不一样的,其网络结构如下:(有些地方省略)
name: "mnist_siamese_train_test"
layer {
name: "pair_data" # 图像是成对输入的
type: "Data"
top: "pair_data"
top: "sim" # 是一个二进制的标签,说明两幅图像是否属于同一类
include {
phase: TRAIN
}
transform_param {
scale: 0.00390625
}
data_param {
source: "examples/siamese/mnist_siamese_train_leveldb"
batch_size: 64
}
}
layer {
name: "slice_pair"
type: "Slice"
bottom: "pair_data"
top: "data"
top: "data_p"
slice_param {
slice_dim: 1
slice_point: 1
}
}
layer {
...... conv和pooling层
}
layer {
......全连接层
}
layer {
name: "feat"
type: "InnerProduct"
bottom: "ip2"
top: "feat" #第一幅图提取的特征。
param {
name: "feat_w"
lr_mult: 1
}
param {
name: "feat_b"
lr_mult: 2
}
inner_product_param {
num_output: 2 #特征维度为2
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
...data_p的卷积层和全连接层
}
layer {
name: "feat_p"
type: "InnerProduct"
bottom: "ip2_p"
top: "feat_p"
param {
name: "feat_w"
lr_mult: 1
}
param {
name: "feat_b"
lr_mult: 2
}
inner_product_param {
num_output: 2
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "loss"
type: "ContrastiveLoss"
bottom: "feat"
bottom: "feat_p"
bottom: "sim"
top: "loss"
contrastive_loss_param {
margin: 1
}
}
这里,手画了个渣图大概说明下这个网络结构:
这里网络的第二层:
layer {
name: "slice_pair"
type: "Slice"
bottom: "pair_data"
top: "data"
top: "data_p"
slice_param {
slice_dim: 1
slice_point: 1
}
}
将两幅图分开,分别做卷积并提取特征。
在最后一层使用contrastiveloss损失函数:其输入是两个特征和标签。
layer {
name: "loss"
type: "ContrastiveLoss"
contrastive_loss_param {
margin: 1.0
}
bottom: "feat"
bottom: "feat_p"
bottom: "sim"
top: "loss"
}
大致解释下这个损失函数cpp代码
#include <algorithm>
#include <vector>
#include "caffe/layers/contrastive_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template <typename Dtype>
void ContrastiveLossLayer<Dtype>::LayerSetUp(//装载该层数据
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
LossLayer<Dtype>::LayerSetUp(bottom, top);
CHECK_EQ(bottom[0]->channels(), bottom[1]->channels());
CHECK_EQ(bottom[0]->height(), 1);
CHECK_EQ(bottom[0]->width(), 1); //bottom[0]为第一个特征
CHECK_EQ(bottom[1]->height(), 1);
CHECK_EQ(bottom[1]->width(), 1);//bottom[1]为第二个特征
CHECK_EQ(bottom[2]->channels(), 1);
CHECK_EQ(bottom[2]->height(), 1);
CHECK_EQ(bottom[2]->width(), 1);//bottom[2]为标签
diff_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
diff_sq_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
dist_sq_.Reshape(bottom[0]->num(), 1, 1, 1);
// vector of ones used to sum along channels
summer_vec_.Reshape(bottom[0]->channels(), 1, 1, 1);
for (int i = 0; i < bottom[0]->channels(); ++i)
summer_vec_.mutable_cpu_data()[i] = Dtype(1);
}
template <typename Dtype>
void ContrastiveLossLayer<Dtype>::Forward_cpu(//前向传播过程
const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
int count = bottom[0]->count();
caffe_sub(
count,
bottom[0]->cpu_data(), // a 第一个特征
bottom[1]->cpu_data(), // b 第二个特征
diff_.mutable_cpu_data()); // a_i-b_i 两个特征的对应元素相减
const int channels = bottom[0]->channels();
Dtype margin = this->layer_param_.contrastive_loss_param().margin(); //读取prototxt中设置好的margin, 一般为1
bool legacy_version =
this->layer_param_.contrastive_loss_param().legacy_version();
Dtype loss(0.0);
for (int i = 0; i < bottom[0]->num(); ++i) {
dist_sq_.mutable_cpu_data()[i] = caffe_cpu_dot(channels,
diff_.cpu_data() + (i*channels), diff_.cpu_data() + (i*channels));
if (static_cast<int>(bottom[2]->cpu_data()[i])) { // 如果sim标签为1,说明为同类
loss += dist_sq_.cpu_data()[i];//损失为特征的距离,这里表示为dist
} else { // sim标签为0
if (legacy_version) {//legacy_version不知道是什么估计对于不同类的情况由不同的函数形式。
loss += std::max(margin - dist_sq_.cpu_data()[i], Dtype(0.0));//这是,loss为0和m-dist的最大值,这个表示,如果dist大于1,则说明这个两个特征距离足够源,loss=0, 如果dist小于1,则说明两个特征距离太近,需要算入loss中
} else {
Dtype dist = std::max<Dtype>(margin - sqrt(dist_sq_.cpu_data()[i]),
Dtype(0.0));
loss += dist*dist;
}
}
}
loss = loss / static_cast<Dtype>(bottom[0]->num()) / Dtype(2);
top[0]->mutable_cpu_data()[0] = loss;
}
//反向传播过程这里不解释了。
#ifdef CPU_ONLY
STUB_GPU(ContrastiveLossLayer);
#endif
INSTANTIATE_CLASS(ContrastiveLossLayer);
REGISTER_LAYER_CLASS(ContrastiveLoss);
} // namespace caffe
如果文章中有问题希望阅读者帮忙纠正!