在keras框架下训练unet,结果很好。但是在caffe框架下训练U-Net,效果总是不理想。思来想去只有两个地方不一样:
1. loss function的不同
2. 上采样的实现
1. 对于loss function:
caffe使用的是sigmoidcrossentropy,keras是binarycrossentropy
其实这两个是一个东西:只不过caffe把最后一层sigmoid层写道cross entropy loss里面去了;而keras是让输出经过一个sigmoid层以后再输入到binarycrossentropy loss层中。本质上是一样的
2. 上采样层
caffe是deconvolution,keras是upsampling+convolution层
deconvolution层是使用反卷积的方式扩大feature map,有参数
而upsampling层没有参数,就是双线性插值扩大feature map,再通过2*2的convolution层对feature map做一个卷积操作。
二者区别很大,也是导致caffe下unet训不出来的根本原因。
1)对于deconvolution层,其参数较难初始化,因此有两种方式较好地优化参数,一种是finetune,很遗憾unet没有训好的caffe版本。因此用HED代替UNET做分割,HED有训练好的model。
第二种是写一个脚本,直接给deconvolution层赋值一个参数,并在后续的训练中不更新这个参数。具体做法参考FCN的solver.py以及surgery.py
以上两种方法都尝试了,但是结果还是没有keras训练的好,比直接训练结果会好一点。
2)在caffe中加一个upsampling层,再改写prototxt网络使其和keras结构完全一样。
因为caffe没有upsampling层,弄起来比较费劲,参考yolo的caffe版,里面有upsampling层,将这个层copy到自己的caffe中,重新编译一下。
分别有四个文件需要更改\include\caffe\layers\upsample_layer.hpp
#ifndef CAFFE_UPSAMPLE_LAYER_HPP_
#define CAFFE_UPSAMPLE_LAYER_HPP_
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
namespace caffe {
template <typename Dtype>
class UpsampleLayer : public Layer<Dtype> {
public:
explicit UpsampleLayer(const LayerParameter& param)
: Layer<Dtype>(param) {}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual inline const char* type() const { return "Upsample"; }
vi