第一步把tensorflow保存的.ckpt模型转为pb模型, 并记下模型的输入输出名字.
第二步去ncnn的github上把仓库clone下来, 按照上面的要求装好依赖并make.
第三步是修改ncnn的CMakeList, 具体修改的位置有:
ncnn/CMakeList.txt
文件, 在文件开头处加入add_definitions(-std=c++11)
, 末尾处加上add_subdirectory(examples)
, 如果ncnn没有examples
文件夹,就新建一个, 并加上CMakeList.txt
文件.ncnn/tools/CMakeList.txt
文件, 加入add_subdirectory(tensorflow)
原版的tools/tensorflow/tensorflow2ncnn.cpp
里, 不支持tensorflow的elu
, FusedBathNormalization
, Conv2dBackpropback
操作, 其实elu
是支持的,只需要仿照relu
的格式, 在.cpp文件里加上就行. FusedBatchNormalization
就是ncnn/layer/
里实现的batchnorm.cpp
, 只是`tensorflow2ncnn里没有写上, 可以增加下面的内容:
else if (node.op() == "FusedBatchNorm")
{
fprintf(pp, "%-16s", "BatchNorm");
}
...
else if (node.op() == "FusedBatchNorm")
{
std::cout << "node name is FusedBatchNorm" << std::endl;
tensorflow::TensorProto tensor;
find_tensor_proto(weights, node, tensor);
const tensorflow::TensorShapeProto& shape = tensor.tensor_shape();
const tensorflow::TensorProto& gamma = weights[node.input(1)];
const tensorflow::TensorProto& Beta = weights[node.input(2)];
const tensorflow::TensorProto& mean = weights[node.input(3)];
const tensorflow::TensorProto& var = weights[node.input(4)];
int channels = gamma.tensor_shape().dim(0).size(); // data size
int dtype = gamma.dtype();
switch (dtype){
case 1:
{
const float * gamma_tensor = reinterpret_cast(gamma.tensor_content().c_str());
const float * mean_data = reinterpret_cast(mean.tensor_content().c_str());
const float * var_data = reinterpret_cast(var.tensor_content().c_str());
const float * b_data = reinterpret_cast(Beta.tensor_content().c_str());
for (int i=0; i< channels; ++i)
{
fwrite(gamma_tensor+i, sizeof(float), 1, bp);
}
for (int i=0; i< channels; ++i)
{
fwrite(mean_data+i, sizeof(float), 1, bp);
}
for (int i=0; i< channels; ++i)
{
fwrite(var_data+i, sizeof(float), 1, bp);
}
for (int i=0; i< channels; ++i)
{
fwrite(b_data+i, sizeof(float), 1, bp);
}
}
default:
std::cerr << "Type is not supported." << std::endl;
}
fprintf(pp, " 0=%d", channels);
tensorflow::AttrValue value_epsilon;
if (find_attr_value(node, "epsilon", value_epsilon)){
float epsilon = value_epsilon.f();
fprintf(pp, " 1=%f", epsilon);
}
}
同理, Conv2dBackpropback
其实就是ncnn
里的反卷积操作, 只不过ncnn实现反卷积的操作和tensorflow内部实现反卷积的操作过程不一样, 但结果是一致的, 需要仿照普通卷积的写法加上去.
ncnn同样支持空洞卷积, 但无法识别tensorflow的空洞卷积, 具体原理可以看tensorflow空洞卷积的原理, tensorflow是改变featuremap做空洞卷积, 而ncnn是改变kernel做空洞卷积, 结果都一样. 需要对.proto文件修改即可完成空洞卷积.
总之ncnn对tensorflow的支持很不友好, 有的层还需要自己手动去实现, 还是很麻烦.