Files
ncnn/tools/ncnnoptimize.cpp
nihui 8bc95a1e7f pnnx: store bool MemoryData attributes as raw int8 (#7006)
* pnnx: store bool MemoryData attributes as raw int8

Keep bool constants as one byte per element and select MemoryData
load_type=3. Implement the reserved raw int8 ModelBin loading mode and
pad exported bool data to four bytes so subsequent weights stay aligned.
This preserves the byte representation used by the proposed logical ops.

Add regression coverage for scalar and padded bool exports in both fp16
modes, multidimensional MemoryData, raw int8 loading through reference
and copy readers, trailing fp32 weights, and missing padding. Document
the format.

Based on the bool attribute serialization fixes proposed in:
https://github.com/Tencent/ncnn/pull/6956
https://github.com/Tencent/ncnn/pull/6866

Fixes #6855

* tools: preserve byte MemoryData when optimizing models

Save MemoryData load_type so raw int8 constants retain their byte
representation after ModelWriter saves the model. Skip fusion paths that
interpret MemoryData as float32 when its elements are not four bytes.

This change does not add bool arithmetic support or automatic type
promotion. Byte bool storage is intended for future logical, comparison
and selection operators.

Validated ncnnoptimize save and reload for scalar and one-to-five-element
bool constants in fp32 and fp16 storage modes, preserving byte values
and element size in all 12 cases.

---------

Co-authored-by: ktz03 <2484593937@qq.com>
Co-authored-by: Giulio Corradi <53930027+giuliocorradi@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 10:09:00 +08:00

2885 lines
84 KiB
C++

// Copyright 2019 Tencent
// SPDX-License-Identifier: BSD-3-Clause
#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <algorithm>
#include <map>
#include <set>
#include <vector>
// ncnn public header
#include "datareader.h"
#include "layer.h"
#include "layer_type.h"
#include "net.h"
// ncnn private header
#include "modelwriter.h"
class DataReaderFromEmpty : public ncnn::DataReader
{
public:
virtual int scan(const char* format, void* p) const
{
return 0;
}
virtual size_t read(void* buf, size_t size) const
{
memset(buf, 0, size);
return size;
}
};
class NetOptimize : public ModelWriter
{
public:
NetOptimize();
public:
int fuse_batchnorm_scale();
int fuse_convolution_batchnorm();
int fuse_convolution_mul();
int fuse_convolution_add();
int fuse_convolutiondepthwise_batchnorm();
int fuse_convolutiondepthwise_mul();
int fuse_convolutiondepthwise_add();
int fuse_deconvolution_batchnorm();
int fuse_deconvolution_mul();
int fuse_deconvolution_add();
int fuse_deconvolutiondepthwise_batchnorm();
int fuse_innerproduct_batchnorm();
int fuse_innerproduct_add();
int fuse_innerproduct_dropout();
int fuse_convolution_activation();
int fuse_convolutiondepthwise_activation();
int fuse_deconvolution_activation();
int fuse_deconvolutiondepthwise_activation();
int fuse_innerproduct_activation();
int fuse_memorydata_binaryop();
int fuse_binaryop_eltwise();
int eliminate_dropout();
int eliminate_pooling1x1();
int eliminate_noop();
int eliminate_split();
int eliminate_orphaned_memorydata();
int eliminate_flatten_after_global_pooling();
int eliminate_reshape_after_global_pooling();
int eliminate_flatten_after_innerproduct();
int eliminate_reshape_before_binaryop();
int replace_reduction_with_global_pooling();
int replace_prelu_with_leaky_relu();
int replace_convolution_with_innerproduct_after_global_pooling();
int replace_convolution_with_innerproduct_after_innerproduct();
};
NetOptimize::NetOptimize()
: ModelWriter()
{
}
int NetOptimize::fuse_batchnorm_scale()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "BatchNorm")
continue;
// BatchNorm - Scale
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Scale")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse BatchNorm - Scale to BatchNorm
ncnn::BatchNorm* batchnorm = (ncnn::BatchNorm*)layers[i];
ncnn::Scale* scale = (ncnn::Scale*)layers[j];
fprintf(stderr, "fuse_batchnorm_scale %s %s\n", batchnorm->name.c_str(), scale->name.c_str());
{
// v = ((v - mean) / sqrt(var + eps) * slope + bias) * s + b
// = (v - mean) / sqrt(var + eps) * (slope * s) + (bias * s + b)
int channels = batchnorm->channels;
float* slope = batchnorm->slope_data;
float* bias = batchnorm->bias_data;
for (int q = 0; q < channels; q++)
{
slope[q] = slope[q] * scale->scale_data[q];
if (scale->bias_term)
bias[q] = bias[q] * scale->scale_data[q] + scale->bias_data[q];
else
bias[q] = bias[q] * scale->scale_data[q];
}
}
int top_blob_index_final = scale->tops[0];
batchnorm->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
scale->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolution_batchnorm()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Convolution")
continue;
// Convolution - BatchNorm
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BatchNorm")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Convolution - BatchNorm to Convolution
ncnn::Convolution* convolution = (ncnn::Convolution*)layers[i];
ncnn::BatchNorm* batchnorm = (ncnn::BatchNorm*)layers[j];
fprintf(stderr, "fuse_convolution_batchnorm %s %s\n", convolution->name.c_str(), batchnorm->name.c_str());
{
int channels = batchnorm->channels;
float eps = batchnorm->eps;
// a = bias - slope * mean / sqrt(var + eps)
// b = slope / sqrt(var + eps)
// value = value * b + a
std::vector<float> a(channels);
std::vector<float> b(channels);
for (int i = 0; i < channels; i++)
{
float sqrt_var = static_cast<float>(sqrt(batchnorm->var_data[i] + eps));
a[i] = batchnorm->bias_data[i] - batchnorm->slope_data[i] * batchnorm->mean_data[i] / sqrt_var;
b[i] = batchnorm->slope_data[i] / sqrt_var;
}
if (convolution->bias_term == 0)
{
// init bias as zero
convolution->bias_term = 1;
convolution->bias_data = ncnn::Mat(channels);
convolution->bias_data.fill(0.f);
}
const int weight_per_outch = convolution->weight_data_size / channels;
float* weight = convolution->weight_data;
float* bias = convolution->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= b[i];
}
bias[i] = bias[i] * b[i] + a[i];
}
}
int top_blob_index_final = batchnorm->tops[0];
convolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
batchnorm->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolution_mul()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Convolution")
continue;
// Convolution - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Convolution - BinaryOp to Convolution
ncnn::Convolution* convolution = (ncnn::Convolution*)layers[i];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (binaryop->op_type != 2 || binaryop->with_scalar)
continue;
// MemoryData - ..... - BinaryOp
size_t k = 0;
for (; k < j; k++)
{
if (layers[k]->type != "MemoryData")
continue;
if (layers[k]->tops[0] == binaryop->bottoms[1])
break;
}
if (k == j)
continue;
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[k];
if (memorydata->data.elemsize != 4u)
continue;
int channels = convolution->num_output;
if (memorydata->w != channels || memorydata->h != 0 || memorydata->c != 0)
{
// not bias-like broadcasting type
continue;
}
fprintf(stderr, "fuse_convolution_mul %s %s\n", convolution->name.c_str(), binaryop->name.c_str());
{
const int weight_per_outch = convolution->weight_data_size / channels;
float* weight = convolution->weight_data;
float* bias = convolution->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= memorydata->data[i];
}
if (bias)
{
bias[i] = bias[i] * memorydata->data[i];
}
}
}
int top_blob_index_final = binaryop->tops[0];
convolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
binaryop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolution_add()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Convolution")
continue;
// Convolution - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Convolution - BinaryOp to Convolution
ncnn::Convolution* convolution = (ncnn::Convolution*)layers[i];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (binaryop->op_type != 0 || binaryop->with_scalar)
continue;
// MemoryData - ..... - BinaryOp
size_t k = 0;
for (; k < j; k++)
{
if (layers[k]->type != "MemoryData")
continue;
if (layers[k]->tops[0] == binaryop->bottoms[1])
break;
}
if (k == j)
continue;
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[k];
if (memorydata->data.elemsize != 4u)
continue;
int channels = convolution->num_output;
bool broadcasting_type_ok = false;
if (memorydata->w == channels && memorydata->h == 0 && memorydata->c == 0)
broadcasting_type_ok = true;
if (memorydata->w == 1 && memorydata->h == 1 && memorydata->c == channels)
broadcasting_type_ok = true;
if (!broadcasting_type_ok)
{
// not bias-like broadcasting type
continue;
}
fprintf(stderr, "fuse_convolution_add %s %s\n", convolution->name.c_str(), binaryop->name.c_str());
ncnn::Mat bias_data = memorydata->data.reshape(channels);
{
if (convolution->bias_term == 0)
{
// init bias
convolution->bias_term = 1;
convolution->bias_data = bias_data;
}
else
{
float* bias = convolution->bias_data;
for (int i = 0; i < channels; i++)
{
bias[i] = bias[i] + bias_data[i];
}
}
}
int top_blob_index_final = binaryop->tops[0];
convolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
binaryop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolutiondepthwise_batchnorm()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "ConvolutionDepthWise")
continue;
// ConvolutionDepthWise - BatchNorm
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BatchNorm")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse ConvolutionDepthWise - BatchNorm to ConvolutionDepthWise
ncnn::ConvolutionDepthWise* convolutiondepthwise = (ncnn::ConvolutionDepthWise*)layers[i];
ncnn::BatchNorm* batchnorm = (ncnn::BatchNorm*)layers[j];
fprintf(stderr, "fuse_convolutiondepthwise_batchnorm %s %s\n", convolutiondepthwise->name.c_str(), batchnorm->name.c_str());
{
int channels = batchnorm->channels;
float eps = batchnorm->eps;
// a = bias - slope * mean / sqrt(var + eps)
// b = slope / sqrt(var + eps)
// value = value * b + a
std::vector<float> a(channels);
std::vector<float> b(channels);
for (int i = 0; i < channels; i++)
{
float sqrt_var = static_cast<float>(sqrt(batchnorm->var_data[i] + eps));
a[i] = batchnorm->bias_data[i] - batchnorm->slope_data[i] * batchnorm->mean_data[i] / sqrt_var;
b[i] = batchnorm->slope_data[i] / sqrt_var;
}
if (convolutiondepthwise->bias_term == 0)
{
// init bias as zero
convolutiondepthwise->bias_term = 1;
convolutiondepthwise->bias_data = ncnn::Mat(channels);
convolutiondepthwise->bias_data.fill(0.f);
}
const int weight_per_outch = convolutiondepthwise->weight_data_size / channels;
float* weight = convolutiondepthwise->weight_data;
float* bias = convolutiondepthwise->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= b[i];
}
bias[i] = bias[i] * b[i] + a[i];
}
}
int top_blob_index_final = batchnorm->tops[0];
convolutiondepthwise->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
batchnorm->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolutiondepthwise_mul()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "ConvolutionDepthWise")
continue;
// ConvolutionDepthWise - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse ConvolutionDepthWise - BinaryOp to ConvolutionDepthWise
ncnn::ConvolutionDepthWise* convolutiondepthwise = (ncnn::ConvolutionDepthWise*)layers[i];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (binaryop->op_type != 2 || binaryop->with_scalar)
continue;
// MemoryData - ..... - BinaryOp
size_t k = 0;
for (; k < j; k++)
{
if (layers[k]->type != "MemoryData")
continue;
if (layers[k]->tops[0] == binaryop->bottoms[1])
break;
}
if (k == j)
continue;
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[k];
if (memorydata->data.elemsize != 4u)
continue;
int channels = convolutiondepthwise->num_output;
if (memorydata->w != channels || memorydata->h != 0 || memorydata->c != 0)
{
// not bias-like broadcasting type
continue;
}
fprintf(stderr, "fuse_convolutiondepthwise_mul %s %s\n", convolutiondepthwise->name.c_str(), binaryop->name.c_str());
{
const int weight_per_outch = convolutiondepthwise->weight_data_size / channels;
float* weight = convolutiondepthwise->weight_data;
float* bias = convolutiondepthwise->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= memorydata->data[i];
}
if (bias)
{
bias[i] = bias[i] * memorydata->data[i];
}
}
}
int top_blob_index_final = binaryop->tops[0];
convolutiondepthwise->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
binaryop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolutiondepthwise_add()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "ConvolutionDepthWise")
continue;
// ConvolutionDepthWise - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse ConvolutionDepthWise - BinaryOp to ConvolutionDepthWise
ncnn::ConvolutionDepthWise* convolutiondepthwise = (ncnn::ConvolutionDepthWise*)layers[i];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (binaryop->op_type != 0 || binaryop->with_scalar)
continue;
// MemoryData - ..... - BinaryOp
size_t k = 0;
for (; k < j; k++)
{
if (layers[k]->type != "MemoryData")
continue;
if (layers[k]->tops[0] == binaryop->bottoms[1])
break;
}
if (k == j)
continue;
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[k];
if (memorydata->data.elemsize != 4u)
continue;
int channels = convolutiondepthwise->num_output;
bool broadcasting_type_ok = false;
if (memorydata->w == channels && memorydata->h == 0 && memorydata->c == 0)
broadcasting_type_ok = true;
if (memorydata->w == 1 && memorydata->h == 1 && memorydata->c == channels)
broadcasting_type_ok = true;
if (!broadcasting_type_ok)
{
// not bias-like broadcasting type
continue;
}
fprintf(stderr, "fuse_convolutiondepthwise_add %s %s\n", convolutiondepthwise->name.c_str(), binaryop->name.c_str());
ncnn::Mat bias_data = memorydata->data.reshape(channels);
{
if (convolutiondepthwise->bias_term == 0)
{
// init bias
convolutiondepthwise->bias_term = 1;
convolutiondepthwise->bias_data = bias_data;
}
else
{
float* bias = convolutiondepthwise->bias_data;
for (int i = 0; i < channels; i++)
{
bias[i] = bias[i] + bias_data[i];
}
}
}
int top_blob_index_final = binaryop->tops[0];
convolutiondepthwise->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
binaryop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_deconvolution_batchnorm()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Deconvolution")
continue;
// Deconvolution - BatchNorm
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BatchNorm")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Deconvolution - BatchNorm to Deconvolution
ncnn::Deconvolution* deconvolution = (ncnn::Deconvolution*)layers[i];
ncnn::BatchNorm* batchnorm = (ncnn::BatchNorm*)layers[j];
fprintf(stderr, "fuse_deconvolution_batchnorm %s %s\n", deconvolution->name.c_str(), batchnorm->name.c_str());
{
int channels = batchnorm->channels;
float eps = batchnorm->eps;
// a = bias - slope * mean / sqrt(var + eps)
// b = slope / sqrt(var + eps)
// value = value * b + a
std::vector<float> a(channels);
std::vector<float> b(channels);
for (int i = 0; i < channels; i++)
{
float sqrt_var = static_cast<float>(sqrt(batchnorm->var_data[i] + eps));
a[i] = batchnorm->bias_data[i] - batchnorm->slope_data[i] * batchnorm->mean_data[i] / sqrt_var;
b[i] = batchnorm->slope_data[i] / sqrt_var;
}
if (deconvolution->bias_term == 0)
{
// init bias as zero
deconvolution->bias_term = 1;
deconvolution->bias_data = ncnn::Mat(channels);
deconvolution->bias_data.fill(0.f);
}
const int weight_per_outch = deconvolution->weight_data_size / channels;
float* weight = deconvolution->weight_data;
float* bias = deconvolution->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= b[i];
}
bias[i] = bias[i] * b[i] + a[i];
}
}
int top_blob_index_final = batchnorm->tops[0];
deconvolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
batchnorm->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_deconvolution_mul()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Deconvolution")
continue;
// Deconvolution - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Deconvolution - BinaryOp to Deconvolution
ncnn::Deconvolution* deconvolution = (ncnn::Deconvolution*)layers[i];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (binaryop->op_type != 2 || binaryop->with_scalar)
continue;
// MemoryData - ..... - BinaryOp
size_t k = 0;
for (; k < j; k++)
{
if (layers[k]->type != "MemoryData")
continue;
if (layers[k]->tops[0] == binaryop->bottoms[1])
break;
}
if (k == j)
continue;
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[k];
if (memorydata->data.elemsize != 4u)
continue;
int channels = deconvolution->num_output;
if (memorydata->w != channels || memorydata->h != 0 || memorydata->c != 0)
{
// not bias-like broadcasting type
continue;
}
fprintf(stderr, "fuse_deconvolution_mul %s %s\n", deconvolution->name.c_str(), binaryop->name.c_str());
{
const int weight_per_outch = deconvolution->weight_data_size / channels;
float* weight = deconvolution->weight_data;
float* bias = deconvolution->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= memorydata->data[i];
}
if (bias)
{
bias[i] = bias[i] * memorydata->data[i];
}
}
}
int top_blob_index_final = binaryop->tops[0];
deconvolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
binaryop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_deconvolution_add()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Deconvolution")
continue;
// Deconvolution - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Deconvolution - BinaryOp to Deconvolution
ncnn::Deconvolution* deconvolution = (ncnn::Deconvolution*)layers[i];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (binaryop->op_type != 0 || binaryop->with_scalar)
continue;
// MemoryData - ..... - BinaryOp
size_t k = 0;
for (; k < j; k++)
{
if (layers[k]->type != "MemoryData")
continue;
if (layers[k]->tops[0] == binaryop->bottoms[1])
break;
}
if (k == j)
continue;
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[k];
if (memorydata->data.elemsize != 4u)
continue;
int channels = deconvolution->num_output;
bool broadcasting_type_ok = false;
if (memorydata->w == channels && memorydata->h == 0 && memorydata->c == 0)
broadcasting_type_ok = true;
if (memorydata->w == 1 && memorydata->h == 1 && memorydata->c == channels)
broadcasting_type_ok = true;
if (!broadcasting_type_ok)
{
// not bias-like broadcasting type
continue;
}
fprintf(stderr, "fuse_deconvolution_add %s %s\n", deconvolution->name.c_str(), binaryop->name.c_str());
ncnn::Mat bias_data = memorydata->data.reshape(channels);
{
if (deconvolution->bias_term == 0)
{
// init bias
deconvolution->bias_term = 1;
deconvolution->bias_data = bias_data;
}
else
{
float* bias = deconvolution->bias_data;
for (int i = 0; i < channels; i++)
{
bias[i] = bias[i] + bias_data[i];
}
}
}
int top_blob_index_final = binaryop->tops[0];
deconvolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
binaryop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_deconvolutiondepthwise_batchnorm()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "DeconvolutionDepthWise")
continue;
// DeconvolutionDepthWise - BatchNorm
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BatchNorm")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse DeconvolutionDepthWise - BatchNorm to DeconvolutionDepthWise
ncnn::DeconvolutionDepthWise* deconvolutiondepthwise = (ncnn::DeconvolutionDepthWise*)layers[i];
ncnn::BatchNorm* batchnorm = (ncnn::BatchNorm*)layers[j];
fprintf(stderr, "fuse_deconvolutiondepthwise_batchnorm %s %s\n", deconvolutiondepthwise->name.c_str(), batchnorm->name.c_str());
{
int channels = batchnorm->channels;
float eps = batchnorm->eps;
// a = bias - slope * mean / sqrt(var + eps)
// b = slope / sqrt(var + eps)
// value = value * b + a
std::vector<float> a(channels);
std::vector<float> b(channels);
for (int i = 0; i < channels; i++)
{
float sqrt_var = static_cast<float>(sqrt(batchnorm->var_data[i] + eps));
a[i] = batchnorm->bias_data[i] - batchnorm->slope_data[i] * batchnorm->mean_data[i] / sqrt_var;
b[i] = batchnorm->slope_data[i] / sqrt_var;
}
if (deconvolutiondepthwise->bias_term == 0)
{
// init bias as zero
deconvolutiondepthwise->bias_term = 1;
deconvolutiondepthwise->bias_data = ncnn::Mat(channels);
deconvolutiondepthwise->bias_data.fill(0.f);
}
const int weight_per_outch = deconvolutiondepthwise->weight_data_size / channels;
float* weight = deconvolutiondepthwise->weight_data;
float* bias = deconvolutiondepthwise->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= b[i];
}
bias[i] = bias[i] * b[i] + a[i];
}
}
int top_blob_index_final = batchnorm->tops[0];
deconvolutiondepthwise->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
batchnorm->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_innerproduct_batchnorm()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "InnerProduct")
continue;
// InnerProduct - BatchNorm
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BatchNorm")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse InnerProduct - BatchNorm to InnerProduct
ncnn::InnerProduct* innerproduct = (ncnn::InnerProduct*)layers[i];
ncnn::BatchNorm* batchnorm = (ncnn::BatchNorm*)layers[j];
fprintf(stderr, "fuse_innerproduct_batchnorm %s %s\n", innerproduct->name.c_str(), batchnorm->name.c_str());
{
int channels = batchnorm->channels;
float eps = batchnorm->eps;
// a = bias - slope * mean / sqrt(var + eps)
// b = slope / sqrt(var + eps)
// value = value * b + a
std::vector<float> a(channels);
std::vector<float> b(channels);
for (int i = 0; i < channels; i++)
{
float sqrt_var = static_cast<float>(sqrt(batchnorm->var_data[i] + eps));
a[i] = batchnorm->bias_data[i] - batchnorm->slope_data[i] * batchnorm->mean_data[i] / sqrt_var;
b[i] = batchnorm->slope_data[i] / sqrt_var;
}
if (innerproduct->bias_term == 0)
{
// init bias as zero
innerproduct->bias_term = 1;
innerproduct->bias_data = ncnn::Mat(channels);
innerproduct->bias_data.fill(0.f);
}
const int weight_per_outch = innerproduct->weight_data_size / channels;
float* weight = innerproduct->weight_data;
float* bias = innerproduct->bias_data;
for (int i = 0; i < channels; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= b[i];
}
bias[i] = bias[i] * b[i] + a[i];
}
}
int top_blob_index_final = batchnorm->tops[0];
innerproduct->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
batchnorm->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_innerproduct_add()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "InnerProduct")
continue;
// InnerProduct - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse InnerProduct - BinaryOp to InnerProduct
ncnn::InnerProduct* innerproduct = (ncnn::InnerProduct*)layers[i];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (binaryop->op_type != 0 || binaryop->with_scalar)
continue;
// MemoryData - ..... - BinaryOp
size_t k = 0;
for (; k < j; k++)
{
if (layers[k]->type != "MemoryData")
continue;
if (layers[k]->tops[0] == binaryop->bottoms[1])
break;
}
if (k == j)
continue;
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[k];
if (memorydata->data.elemsize != 4u)
continue;
int channels = innerproduct->num_output;
bool broadcasting_type_ok = false;
if (memorydata->w == channels && memorydata->h == 0 && memorydata->c == 0)
broadcasting_type_ok = true;
if (memorydata->w == 1 && memorydata->h == 1 && memorydata->c == channels)
broadcasting_type_ok = true;
if (!broadcasting_type_ok)
{
// not bias-like broadcasting type
continue;
}
fprintf(stderr, "fuse_innerproduct_add %s %s\n", innerproduct->name.c_str(), binaryop->name.c_str());
ncnn::Mat bias_data = memorydata->data.reshape(channels);
{
if (innerproduct->bias_term == 0)
{
// init bias
innerproduct->bias_term = 1;
innerproduct->bias_data = bias_data;
}
else
{
float* bias = innerproduct->bias_data;
for (int i = 0; i < channels; i++)
{
bias[i] = bias[i] + bias_data[i];
}
}
}
int top_blob_index_final = binaryop->tops[0];
innerproduct->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
binaryop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_innerproduct_dropout()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "InnerProduct")
continue;
// InnerProduct - Dropout
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Dropout")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse InnerProduct - Dropout to InnerProduct
ncnn::InnerProduct* innerproduct = (ncnn::InnerProduct*)layers[i];
ncnn::Dropout* dropout = (ncnn::Dropout*)layers[j];
fprintf(stderr, "fuse_innerproduct_dropout %s %s\n", innerproduct->name.c_str(), dropout->name.c_str());
float scale = dropout->scale;
if (scale != 1.f)
{
const int num_output = innerproduct->num_output;
const int weight_per_outch = innerproduct->weight_data_size / num_output;
float* weight = innerproduct->weight_data;
for (int i = 0; i < num_output; i++)
{
float* conv_weight_outch = weight + weight_per_outch * i;
for (int j = 0; j < weight_per_outch; j++)
{
conv_weight_outch[j] *= scale;
}
}
if (innerproduct->bias_term)
{
float* bias = innerproduct->bias_data;
for (int i = 0; i < num_output; i++)
{
bias[i] *= scale;
}
}
}
int top_blob_index_final = dropout->tops[0];
innerproduct->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
dropout->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolution_activation()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Convolution")
continue;
// Convolution - Activation
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "ReLU" && layers[j]->type != "Clip" && layers[j]->type != "Sigmoid" && layers[j]->type != "Mish" && layers[j]->type != "HardSwish")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Convolution - Activation to Convolution
ncnn::Convolution* convolution = (ncnn::Convolution*)layers[i];
ncnn::Layer* activation = layers[j];
fprintf(stderr, "fuse_convolution_activation %s %s\n", convolution->name.c_str(), activation->name.c_str());
if (activation->type == "ReLU")
{
ncnn::ReLU* relu = (ncnn::ReLU*)activation;
if (relu->slope == 0.f)
{
convolution->activation_type = 1;
}
else
{
convolution->activation_type = 2;
convolution->activation_params = ncnn::Mat(1);
convolution->activation_params[0] = relu->slope;
}
}
else if (activation->type == "Clip")
{
ncnn::Clip* clip = (ncnn::Clip*)activation;
convolution->activation_type = 3;
convolution->activation_params = ncnn::Mat(2);
convolution->activation_params[0] = clip->min;
convolution->activation_params[1] = clip->max;
}
else if (activation->type == "Sigmoid")
{
convolution->activation_type = 4;
}
else if (activation->type == "Mish")
{
convolution->activation_type = 5;
}
else if (activation->type == "HardSwish")
{
ncnn::HardSwish* hardswish = (ncnn::HardSwish*)activation;
convolution->activation_type = 6;
convolution->activation_params = ncnn::Mat(2);
convolution->activation_params[0] = hardswish->alpha;
convolution->activation_params[1] = hardswish->beta;
}
int top_blob_index_final = activation->tops[0];
convolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
activation->type = "ncnnfused";
}
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Convolution1D")
continue;
// Convolution1D - Activation
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "ReLU" && layers[j]->type != "Clip" && layers[j]->type != "Sigmoid" && layers[j]->type != "Mish")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Convolution1D - Activation to Convolution1D
ncnn::Convolution1D* convolution = (ncnn::Convolution1D*)layers[i];
ncnn::Layer* activation = layers[j];
fprintf(stderr, "fuse_convolution1d_activation %s %s\n", convolution->name.c_str(), activation->name.c_str());
if (activation->type == "ReLU")
{
ncnn::ReLU* relu = (ncnn::ReLU*)activation;
if (relu->slope == 0.f)
{
convolution->activation_type = 1;
}
else
{
convolution->activation_type = 2;
convolution->activation_params = ncnn::Mat(1);
convolution->activation_params[0] = relu->slope;
}
}
else if (activation->type == "Clip")
{
ncnn::Clip* clip = (ncnn::Clip*)activation;
convolution->activation_type = 3;
convolution->activation_params = ncnn::Mat(2);
convolution->activation_params[0] = clip->min;
convolution->activation_params[1] = clip->max;
}
else if (activation->type == "Sigmoid")
{
convolution->activation_type = 4;
}
else if (activation->type == "Mish")
{
convolution->activation_type = 5;
}
int top_blob_index_final = activation->tops[0];
convolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
activation->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_convolutiondepthwise_activation()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "ConvolutionDepthWise")
continue;
// ConvolutionDepthWise - Activation
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "ReLU" && layers[j]->type != "Clip" && layers[j]->type != "Sigmoid" && layers[j]->type != "Mish" && layers[j]->type != "HardSwish")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse ConvolutionDepthWise - Activation to ConvolutionDepthWise
ncnn::ConvolutionDepthWise* convolutiondepthwise = (ncnn::ConvolutionDepthWise*)layers[i];
ncnn::Layer* activation = layers[j];
fprintf(stderr, "fuse_convolutiondepthwise_activation %s %s\n", convolutiondepthwise->name.c_str(), activation->name.c_str());
if (activation->type == "ReLU")
{
ncnn::ReLU* relu = (ncnn::ReLU*)activation;
if (relu->slope == 0.f)
{
convolutiondepthwise->activation_type = 1;
}
else
{
convolutiondepthwise->activation_type = 2;
convolutiondepthwise->activation_params = ncnn::Mat(1);
convolutiondepthwise->activation_params[0] = relu->slope;
}
}
else if (activation->type == "Clip")
{
ncnn::Clip* clip = (ncnn::Clip*)activation;
convolutiondepthwise->activation_type = 3;
convolutiondepthwise->activation_params = ncnn::Mat(2);
convolutiondepthwise->activation_params[0] = clip->min;
convolutiondepthwise->activation_params[1] = clip->max;
}
else if (activation->type == "Sigmoid")
{
convolutiondepthwise->activation_type = 4;
}
else if (activation->type == "Mish")
{
convolutiondepthwise->activation_type = 5;
}
else if (activation->type == "HardSwish")
{
ncnn::HardSwish* hardswish = (ncnn::HardSwish*)activation;
convolutiondepthwise->activation_type = 6;
convolutiondepthwise->activation_params = ncnn::Mat(2);
convolutiondepthwise->activation_params[0] = hardswish->alpha;
convolutiondepthwise->activation_params[1] = hardswish->beta;
}
int top_blob_index_final = activation->tops[0];
convolutiondepthwise->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
activation->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_deconvolution_activation()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Deconvolution")
continue;
// Deconvolution - Activation
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "ReLU" && layers[j]->type != "Clip" && layers[j]->type != "Sigmoid")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse Deconvolution - Activation to Deconvolution
ncnn::Deconvolution* deconvolution = (ncnn::Deconvolution*)layers[i];
ncnn::Layer* activation = layers[j];
fprintf(stderr, "fuse_deconvolution_activation %s %s\n", deconvolution->name.c_str(), activation->name.c_str());
if (activation->type == "ReLU")
{
ncnn::ReLU* relu = (ncnn::ReLU*)activation;
if (relu->slope == 0.f)
{
deconvolution->activation_type = 1;
}
else
{
deconvolution->activation_type = 2;
deconvolution->activation_params = ncnn::Mat(1);
deconvolution->activation_params[0] = relu->slope;
}
}
else if (activation->type == "Clip")
{
ncnn::Clip* clip = (ncnn::Clip*)activation;
deconvolution->activation_type = 3;
deconvolution->activation_params = ncnn::Mat(2);
deconvolution->activation_params[0] = clip->min;
deconvolution->activation_params[1] = clip->max;
}
else if (activation->type == "Sigmoid")
{
deconvolution->activation_type = 4;
}
int top_blob_index_final = activation->tops[0];
deconvolution->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
activation->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_deconvolutiondepthwise_activation()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "DeconvolutionDepthWise")
continue;
// DeconvolutionDepthWise - Activation
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "ReLU" && layers[j]->type != "Clip" && layers[j]->type != "Sigmoid")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse DeconvolutionDepthWise - Activation to DeconvolutionDepthWise
ncnn::DeconvolutionDepthWise* deconvolutiondepthwise = (ncnn::DeconvolutionDepthWise*)layers[i];
ncnn::Layer* activation = layers[j];
fprintf(stderr, "fuse_deconvolutiondepthwise_activation %s %s\n", deconvolutiondepthwise->name.c_str(), activation->name.c_str());
if (activation->type == "ReLU")
{
ncnn::ReLU* relu = (ncnn::ReLU*)activation;
if (relu->slope == 0.f)
{
deconvolutiondepthwise->activation_type = 1;
}
else
{
deconvolutiondepthwise->activation_type = 2;
deconvolutiondepthwise->activation_params = ncnn::Mat(1);
deconvolutiondepthwise->activation_params[0] = relu->slope;
}
}
else if (activation->type == "Clip")
{
ncnn::Clip* clip = (ncnn::Clip*)activation;
deconvolutiondepthwise->activation_type = 3;
deconvolutiondepthwise->activation_params = ncnn::Mat(2);
deconvolutiondepthwise->activation_params[0] = clip->min;
deconvolutiondepthwise->activation_params[1] = clip->max;
}
else if (activation->type == "Sigmoid")
{
deconvolutiondepthwise->activation_type = 4;
}
int top_blob_index_final = activation->tops[0];
deconvolutiondepthwise->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
activation->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_innerproduct_activation()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "InnerProduct")
continue;
// InnerProduct - Activation
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "ReLU" && layers[j]->type != "Clip" && layers[j]->type != "Sigmoid" && layers[j]->type != "Mish" && layers[j]->type != "HardSwish")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse InnerProduct - Activation to InnerProduct
ncnn::InnerProduct* innerproduct = (ncnn::InnerProduct*)layers[i];
ncnn::Layer* activation = layers[j];
fprintf(stderr, "fuse_innerproduct_activation %s %s\n", innerproduct->name.c_str(), activation->name.c_str());
if (activation->type == "ReLU")
{
ncnn::ReLU* relu = (ncnn::ReLU*)activation;
if (relu->slope == 0.f)
{
innerproduct->activation_type = 1;
}
else
{
innerproduct->activation_type = 2;
innerproduct->activation_params = ncnn::Mat(1);
innerproduct->activation_params[0] = relu->slope;
}
}
else if (activation->type == "Clip")
{
ncnn::Clip* clip = (ncnn::Clip*)activation;
innerproduct->activation_type = 3;
innerproduct->activation_params = ncnn::Mat(2);
innerproduct->activation_params[0] = clip->min;
innerproduct->activation_params[1] = clip->max;
}
else if (activation->type == "Sigmoid")
{
innerproduct->activation_type = 4;
}
else if (activation->type == "Mish")
{
innerproduct->activation_type = 5;
}
else if (activation->type == "HardSwish")
{
ncnn::HardSwish* hardswish = (ncnn::HardSwish*)activation;
innerproduct->activation_type = 6;
innerproduct->activation_params = ncnn::Mat(2);
innerproduct->activation_params[0] = hardswish->alpha;
innerproduct->activation_params[1] = hardswish->beta;
}
int top_blob_index_final = activation->tops[0];
innerproduct->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
activation->type = "ncnnfused";
}
return 0;
}
int NetOptimize::fuse_memorydata_binaryop()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "MemoryData")
continue;
// MemoryData - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index || layers[j]->bottoms[1] == top_blob_index)
break;
}
if (j == layer_count)
continue;
// fuse MemoryData - BinaryOp to BinaryOp
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[i];
if (memorydata->data.elemsize != 4u)
continue;
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
if (memorydata->w != 1 || memorydata->h != 0 || memorydata->c != 0)
{
// not a scalar
continue;
}
int memorydata_index = 1;
if (binaryop->bottoms[0] == top_blob_index)
{
int op_type = binaryop->op_type;
if (op_type == ncnn::BinaryOp::Operation_ADD
|| op_type == ncnn::BinaryOp::Operation_MUL
|| op_type == ncnn::BinaryOp::Operation_MAX
|| op_type == ncnn::BinaryOp::Operation_MIN)
{
memorydata_index = 0;
}
else if (op_type == ncnn::BinaryOp::Operation_SUB)
{
binaryop->op_type = ncnn::BinaryOp::Operation_RSUB;
memorydata_index = 0;
}
else if (op_type == ncnn::BinaryOp::Operation_DIV)
{
binaryop->op_type = ncnn::BinaryOp::Operation_RDIV;
memorydata_index = 0;
}
else
{
// non interchangeable binaryop
continue;
}
}
float scalar = memorydata->data[0];
binaryop->with_scalar = 1;
binaryop->b = scalar;
fprintf(stderr, "fuse_memorydata_binaryop %s %s\n", memorydata->name.c_str(), binaryop->name.c_str());
binaryop->bottoms.erase(binaryop->bottoms.begin() + memorydata_index);
memorydata->type = "ncnnfused";
}
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "MemoryData")
continue;
// MemoryData - Split - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j0 = i + 1;
for (; j0 < layer_count; j0++)
{
if (layers[j0]->type != "Split")
continue;
if (layers[j0]->bottoms.size() != 1)
continue;
if (layers[j0]->bottoms[0] == top_blob_index)
break;
}
if (j0 == layer_count)
continue;
int split_top_blob_index = -1;
size_t j1 = j0 + 1;
for (; j1 < layer_count; j1++)
{
if (layers[j1]->type != "BinaryOp")
continue;
if (layers[j1]->bottoms.size() != 2)
continue;
for (int k = 0; k < (int)layers[j0]->tops.size(); k++)
{
if (layers[j1]->bottoms[0] == layers[j0]->tops[k] || layers[j1]->bottoms[1] == layers[j0]->tops[k])
{
split_top_blob_index = k;
break;
}
}
if (split_top_blob_index != -1)
break;
}
if (j1 == layer_count)
continue;
// fuse MemoryData - Split - BinaryOp to BinaryOp
ncnn::MemoryData* memorydata = (ncnn::MemoryData*)layers[i];
if (memorydata->data.elemsize != 4u)
continue;
ncnn::Split* split = (ncnn::Split*)layers[j0];
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j1];
if (memorydata->w != 1 || memorydata->h != 0 || memorydata->c != 0)
{
// not a scalar
continue;
}
int memorydata_index = 1;
if (binaryop->bottoms[0] == split->tops[split_top_blob_index])
{
int op_type = binaryop->op_type;
if (op_type == ncnn::BinaryOp::Operation_ADD
|| op_type == ncnn::BinaryOp::Operation_MUL
|| op_type == ncnn::BinaryOp::Operation_MAX
|| op_type == ncnn::BinaryOp::Operation_MIN)
{
memorydata_index = 0;
}
else if (op_type == ncnn::BinaryOp::Operation_SUB)
{
binaryop->op_type = ncnn::BinaryOp::Operation_RSUB;
memorydata_index = 0;
}
else if (op_type == ncnn::BinaryOp::Operation_DIV)
{
binaryop->op_type = ncnn::BinaryOp::Operation_RDIV;
memorydata_index = 0;
}
else
{
// non interchangeable binaryop
continue;
}
}
float scalar = memorydata->data[0];
binaryop->with_scalar = 1;
binaryop->b = scalar;
fprintf(stderr, "fuse_memorydata_binaryop %s %s\n", memorydata->name.c_str(), binaryop->name.c_str());
binaryop->bottoms.erase(binaryop->bottoms.begin() + memorydata_index);
split->tops.erase(split->tops.begin() + split_top_blob_index);
if (split->tops.empty())
{
split->type = "ncnnfused";
memorydata->type = "ncnnfused";
}
i--;
}
return 0;
}
int NetOptimize::fuse_binaryop_eltwise()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "BinaryOp")
continue;
if (layers[i]->bottoms.size() != 2)
continue;
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[i];
if (binaryop->op_type != ncnn::BinaryOp::Operation_ADD)
continue;
if (binaryop->with_scalar)
continue;
// BinaryOp - BinaryOp - BinaryOp
int bottom_blob_index_0 = binaryop->bottoms[0];
int bottom_blob_index_1 = binaryop->bottoms[1];
size_t j0 = 0;
for (; j0 < i; j0++)
{
if (layers[j0]->type != "BinaryOp")
continue;
if (layers[j0]->bottoms.size() != 1)
continue;
if (((ncnn::BinaryOp*)layers[j0])->op_type != ncnn::BinaryOp::Operation_MUL)
continue;
if (layers[j0]->tops[0] == bottom_blob_index_0)
break;
}
size_t j1 = 0;
for (; j1 < i; j1++)
{
if (layers[j1]->type != "BinaryOp")
continue;
if (layers[j1]->bottoms.size() != 1)
continue;
if (((ncnn::BinaryOp*)layers[j1])->op_type != ncnn::BinaryOp::Operation_MUL)
continue;
if (layers[j1]->tops[0] == bottom_blob_index_1)
break;
}
if (j0 == i && j1 == i)
continue;
ncnn::BinaryOp* binaryop0 = (ncnn::BinaryOp*)layers[j0];
ncnn::BinaryOp* binaryop1 = (ncnn::BinaryOp*)layers[j1];
fprintf(stderr, "fuse_binaryop_eltwise %s %s %s\n", binaryop0->name.c_str(), binaryop1->name.c_str(), binaryop->name.c_str());
ncnn::Eltwise* eltwise = (ncnn::Eltwise*)ncnn::create_layer_cpu("Eltwise");
eltwise->type = "Eltwise";
eltwise->name = binaryop->name;
eltwise->bottoms = binaryop->bottoms;
eltwise->tops = binaryop->tops;
ncnn::ParamDict pd;
eltwise->load_param(pd);
eltwise->op_type = ncnn::Eltwise::Operation_SUM;
eltwise->coeffs = ncnn::Mat(2);
if (j0 != i && j1 != i)
{
// fuse BinaryOp - BinaryOp - BinaryOp to Eltwise
eltwise->coeffs[0] = binaryop0->b;
eltwise->coeffs[1] = binaryop1->b;
eltwise->bottoms[0] = binaryop0->bottoms[0];
eltwise->bottoms[1] = binaryop1->bottoms[0];
binaryop0->type = "ncnnfused";
binaryop1->type = "ncnnfused";
}
if (j0 != i && j1 == i)
{
// fuse BinaryOp - X - BinaryOp to Eltwise
eltwise->coeffs[0] = binaryop0->b;
eltwise->coeffs[1] = 1.f;
eltwise->bottoms[0] = binaryop0->bottoms[0];
binaryop0->type = "ncnnfused";
}
if (j0 == i && j1 != i)
{
// fuse X - BinaryOp - BinaryOp to Eltwise
eltwise->coeffs[0] = 1.f;
eltwise->coeffs[1] = binaryop1->b;
eltwise->bottoms[1] = binaryop1->bottoms[0];
binaryop1->type = "ncnnfused";
}
layers[i] = eltwise;
delete binaryop;
}
return 0;
}
int NetOptimize::eliminate_dropout()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Dropout")
continue;
ncnn::Dropout* dropout = (ncnn::Dropout*)layers[i];
if (dropout->scale != 1.f)
continue;
// Any - Dropout
int bottom_blob_index = layers[i]->bottoms[0];
int j = i - 1;
for (; j >= 0; j--)
{
if (layers[j]->type == "ncnnfused")
continue;
if (layers[j]->tops.size() != 1)
continue;
if (layers[j]->tops[0] == bottom_blob_index)
break;
}
if (j == -1)
continue;
ncnn::Layer* any = layers[j];
fprintf(stderr, "eliminate_dropout %s %s\n", any->name.c_str(), dropout->name.c_str());
int top_blob_index_final = dropout->tops[0];
any->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = j;
dropout->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_pooling1x1()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Pooling")
continue;
ncnn::Pooling* pooling = (ncnn::Pooling*)layers[i];
if (pooling->pad_left != 0 || pooling->pad_right != 0 || pooling->pad_top != 0 || pooling->pad_bottom != 0)
continue;
if (pooling->kernel_w != 1 || pooling->kernel_h != 1 || pooling->stride_w != 1 || pooling->stride_h != 1)
continue;
if (pooling->global_pooling != 0)
continue;
// Any - Pooling
int bottom_blob_index = layers[i]->bottoms[0];
int top_i = -1;
int j = i - 1;
for (; j >= 0; j--)
{
if (layers[j]->type == "ncnnfused")
continue;
for (size_t k = 0; k < layers[j]->tops.size(); k++)
{
if (layers[j]->tops[k] == bottom_blob_index)
{
top_i = k;
break;
}
}
if (top_i != -1)
break;
}
if (j == -1)
continue;
ncnn::Layer* any = layers[j];
fprintf(stderr, "eliminate_pooling1x1 %s %s\n", any->name.c_str(), pooling->name.c_str());
int top_blob_index_final = pooling->tops[0];
any->tops[top_i] = top_blob_index_final;
blobs[top_blob_index_final].producer = j;
pooling->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_noop()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Noop")
continue;
ncnn::Layer* noop = layers[i];
if (noop->bottoms.empty())
{
// Noop
fprintf(stderr, "eliminate_noop %s\n", noop->name.c_str());
size_t top_blob_count = noop->tops.size();
for (size_t j = 0; j < top_blob_count; j++)
{
int top_blob_index_final = noop->tops[j];
blobs[top_blob_index_final].producer = -1;
}
noop->type = "ncnnfused";
continue;
}
// Any - Noop
int bottom_blob_index = noop->bottoms[0];
int j = i - 1;
int any_k = -1;
for (; j >= 0; j--)
{
if (layers[j]->type == "ncnnfused")
continue;
bool link_noop = false;
size_t top_blob_count = layers[j]->tops.size();
for (size_t k = 0; k < top_blob_count; k++)
{
if (layers[j]->tops[k] == bottom_blob_index)
{
link_noop = true;
any_k = k;
break;
}
}
if (link_noop)
break;
}
if (j == -1 || any_k == -1)
continue;
ncnn::Layer* any = layers[j];
fprintf(stderr, "eliminate_noop %s %s\n", any->name.c_str(), noop->name.c_str());
int top_blob_index_final = noop->tops[0];
any->tops[any_k] = top_blob_index_final;
blobs[top_blob_index_final].producer = j;
noop->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_split()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Split")
continue;
ncnn::Layer* split = layers[i];
int real_split_output_count = 0;
int real_split_top_blob_index = -1;
size_t top_blob_count = split->tops.size();
for (size_t j = 0; j < top_blob_count; j++)
{
int top_blob_index_final = split->tops[j];
if (blobs[top_blob_index_final].consumer != -1)
{
real_split_output_count += 1;
real_split_top_blob_index = j;
}
}
if (real_split_output_count > 1)
continue;
// Any - Pooling
int bottom_blob_index = split->bottoms[0];
int top_i = -1;
int j = i - 1;
for (; j >= 0; j--)
{
if (layers[j]->type == "ncnnfused")
continue;
for (size_t k = 0; k < layers[j]->tops.size(); k++)
{
if (layers[j]->tops[k] == bottom_blob_index)
{
top_i = k;
break;
}
}
if (top_i != -1)
break;
}
if (j == -1)
continue;
ncnn::Layer* any = layers[j];
fprintf(stderr, "eliminate_split %s %s\n", any->name.c_str(), split->name.c_str());
int top_blob_index_final = split->tops[real_split_top_blob_index];
any->tops[top_i] = top_blob_index_final;
blobs[top_blob_index_final].producer = j;
split->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_orphaned_memorydata()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "MemoryData")
continue;
// MemoryData - X
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type == "ncnnfused")
continue;
bool orphaned = true;
for (size_t k = 0; k < layers[j]->bottoms.size(); k++)
{
if (layers[j]->bottoms[k] == top_blob_index)
{
orphaned = false;
break;
}
}
if (!orphaned)
break;
}
if (j < layer_count)
continue;
// assert orphaned == true
fprintf(stderr, "eliminate_orphaned_memorydata %s\n", layers[i]->name.c_str());
layers[i]->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_reshape_after_global_pooling()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Pooling")
continue;
ncnn::Pooling* pooling = (ncnn::Pooling*)layers[i];
if (pooling->global_pooling == 0)
continue;
// Pooling - Reshape
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Reshape")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
ncnn::Reshape* reshape = (ncnn::Reshape*)layers[j];
if (reshape->h != -233 || reshape->c != -233)
continue;
if (!reshape->shape_expr.empty())
continue;
fprintf(stderr, "eliminate_reshape_after_global_pooling %s %s\n", pooling->name.c_str(), reshape->name.c_str());
int top_blob_index_final = reshape->tops[0];
pooling->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
reshape->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_flatten_after_global_pooling()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Pooling")
continue;
ncnn::Pooling* pooling = (ncnn::Pooling*)layers[i];
if (pooling->global_pooling == 0)
continue;
// Pooling - Flatten
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Flatten")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
ncnn::Flatten* flatten = (ncnn::Flatten*)layers[j];
fprintf(stderr, "eliminate_flatten_after_global_pooling %s %s\n", pooling->name.c_str(), flatten->name.c_str());
int top_blob_index_final = flatten->tops[0];
pooling->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
flatten->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_flatten_after_innerproduct()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "InnerProduct")
continue;
// InnerProduct - Flatten
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Flatten")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
ncnn::InnerProduct* innerproduct = (ncnn::InnerProduct*)layers[i];
ncnn::Flatten* flatten = (ncnn::Flatten*)layers[j];
fprintf(stderr, "eliminate_flatten_after_innerproduct %s %s\n", innerproduct->name.c_str(), flatten->name.c_str());
int top_blob_index_final = flatten->tops[0];
innerproduct->tops[0] = top_blob_index_final;
blobs[top_blob_index_final].producer = i;
flatten->type = "ncnnfused";
}
return 0;
}
int NetOptimize::eliminate_reshape_before_binaryop()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Reshape")
continue;
ncnn::Reshape* reshape = (ncnn::Reshape*)layers[i];
if (reshape->w != 1 || reshape->h != 1)
continue;
if (!reshape->shape_expr.empty())
continue;
// Reshape - BinaryOp
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "BinaryOp")
continue;
if (layers[j]->bottoms.size() != 2)
continue;
if (layers[j]->bottoms[0] == top_blob_index || layers[j]->bottoms[1] == top_blob_index)
break;
}
if (j == layer_count)
continue;
ncnn::BinaryOp* binaryop = (ncnn::BinaryOp*)layers[j];
fprintf(stderr, "eliminate_reshape_before_binaryop %s %s\n", reshape->name.c_str(), binaryop->name.c_str());
int bottom_blob_index_final = reshape->bottoms[0];
if (layers[j]->bottoms[0] == top_blob_index)
binaryop->bottoms[0] = bottom_blob_index_final;
if (layers[j]->bottoms[1] == top_blob_index)
binaryop->bottoms[1] = bottom_blob_index_final;
blobs[bottom_blob_index_final].consumer = j;
reshape->type = "ncnnfused";
}
return 0;
}
int NetOptimize::replace_reduction_with_global_pooling()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Reduction")
continue;
ncnn::Reduction* reduction1 = (ncnn::Reduction*)layers[i];
if (reduction1->operation != 3 || reduction1->reduce_all != 0 || reduction1->coeff != 1.f)
continue;
if (reduction1->axes.w != 1)
continue;
const int* axes_ptr = reduction1->axes;
if (axes_ptr[0] != 2 && axes_ptr[0] != 3)
continue;
// Reduction(2/3) - Reduction(2)
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Reduction")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
ncnn::Reduction* reduction2 = (ncnn::Reduction*)layers[j];
if (reduction2->operation != 3 || reduction2->reduce_all != 0 || reduction2->coeff != 1.f)
continue;
if (reduction2->axes.w != 1)
continue;
const int* axes2_ptr = reduction2->axes;
if (axes2_ptr[0] != 2)
continue;
fprintf(stderr, "replace_reduction_with_global_pooling %s %s\n", reduction1->name.c_str(), reduction2->name.c_str());
ncnn::Pooling* pooling = (ncnn::Pooling*)ncnn::create_layer_cpu("Pooling");
pooling->type = "Pooling";
pooling->name = reduction2->name;
pooling->bottoms = reduction2->bottoms;
pooling->tops = reduction2->tops;
ncnn::ParamDict pd;
pooling->load_param(pd);
pooling->pooling_type = 1;
pooling->global_pooling = 1;
layers[j] = pooling;
delete reduction2;
int bottom_blob_index_final = reduction1->bottoms[0];
pooling->bottoms[0] = bottom_blob_index_final;
blobs[bottom_blob_index_final].consumer = j;
reduction1->type = "ncnnfused";
}
return 0;
}
int NetOptimize::replace_prelu_with_leaky_relu()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "PReLU")
continue;
ncnn::PReLU* prelu = (ncnn::PReLU*)layers[i];
if (prelu->num_slope != 1)
continue;
fprintf(stderr, "replace_prelu_with_leaky_relu %s\n", prelu->name.c_str());
ncnn::ReLU* relu = (ncnn::ReLU*)ncnn::create_layer_cpu("ReLU");
relu->type = "ReLU";
relu->name = prelu->name;
relu->bottoms = prelu->bottoms;
relu->tops = prelu->tops;
ncnn::ParamDict pd;
relu->load_param(pd);
relu->slope = prelu->slope_data[0];
layers[i] = relu;
delete prelu;
}
return 0;
}
int NetOptimize::replace_convolution_with_innerproduct_after_global_pooling()
{
const size_t layer_count = layers.size();
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "Pooling")
continue;
ncnn::Pooling* pooling = (ncnn::Pooling*)layers[i];
if (pooling->global_pooling == 0)
continue;
// Pooling - Convolution
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Convolution")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
ncnn::Convolution* convolution = (ncnn::Convolution*)layers[j];
fprintf(stderr, "replace_convolution_with_innerproduct_after_global_pooling %s %s\n", pooling->name.c_str(), convolution->name.c_str());
ncnn::InnerProduct* innerproduct = (ncnn::InnerProduct*)ncnn::create_layer_cpu("InnerProduct");
innerproduct->type = "InnerProduct";
innerproduct->name = convolution->name;
innerproduct->bottoms = convolution->bottoms;
innerproduct->tops = convolution->tops;
ncnn::ParamDict pd;
innerproduct->load_param(pd);
innerproduct->num_output = convolution->num_output;
innerproduct->bias_term = convolution->bias_term;
innerproduct->weight_data_size = convolution->weight_data_size;
innerproduct->int8_scale_term = convolution->int8_scale_term;
innerproduct->weight_data = convolution->weight_data;
innerproduct->bias_data = convolution->bias_data;
#if NCNN_INT8
innerproduct->weight_data_int8_scales = convolution->weight_data_int8_scales;
innerproduct->bottom_blob_int8_scales = convolution->bottom_blob_int8_scales;
#endif
innerproduct->activation_type = convolution->activation_type;
innerproduct->activation_params = convolution->activation_params;
layers[j] = innerproduct;
delete convolution;
}
return 0;
}
int NetOptimize::replace_convolution_with_innerproduct_after_innerproduct()
{
const size_t layer_count = layers.size();
for (;;)
{
bool replaced = false;
for (size_t i = 0; i < layer_count; i++)
{
if (layers[i]->type != "InnerProduct")
continue;
// InnerProduct - Convolution
int top_blob_index = layers[i]->tops[0];
size_t j = i + 1;
for (; j < layer_count; j++)
{
if (layers[j]->type != "Convolution")
continue;
if (layers[j]->bottoms.size() != 1)
continue;
if (layers[j]->bottoms[0] == top_blob_index)
break;
}
if (j == layer_count)
continue;
ncnn::InnerProduct* innerproduct = (ncnn::InnerProduct*)layers[i];
ncnn::Convolution* convolution = (ncnn::Convolution*)layers[j];
fprintf(stderr, "replace_convolution_with_innerproduct_after_innerproduct %s %s\n", innerproduct->name.c_str(), convolution->name.c_str());
ncnn::InnerProduct* innerproduct2 = (ncnn::InnerProduct*)ncnn::create_layer_cpu("InnerProduct");
innerproduct2->type = "InnerProduct";
innerproduct2->name = convolution->name;
innerproduct2->bottoms = convolution->bottoms;
innerproduct2->tops = convolution->tops;
ncnn::ParamDict pd;
innerproduct2->load_param(pd);
innerproduct2->num_output = convolution->num_output;
innerproduct2->bias_term = convolution->bias_term;
innerproduct2->weight_data_size = convolution->weight_data_size;
innerproduct->int8_scale_term = convolution->int8_scale_term;
innerproduct2->weight_data = convolution->weight_data;
innerproduct2->bias_data = convolution->bias_data;
#if NCNN_INT8
innerproduct->weight_data_int8_scales = convolution->weight_data_int8_scales;
innerproduct->bottom_blob_int8_scales = convolution->bottom_blob_int8_scales;
#endif
innerproduct2->activation_type = convolution->activation_type;
innerproduct2->activation_params = convolution->activation_params;
layers[j] = innerproduct2;
delete convolution;
replaced = true;
}
if (!replaced)
break;
}
return 0;
}
int main(int argc, char** argv)
{
if (argc < 6)
{
fprintf(stderr, "usage: %s [inparam] [inbin] [outparam] [outbin] [flag] [cutstart] [cutend]\n", argv[0]);
return -1;
}
const char* inparam = argv[1];
const char* inbin = argv[2];
const char* outparam = argv[3];
const char* outbin = argv[4];
int flag = atoi(argv[5]);
const char* cutstartname = nullptr;
const char* cutendname = nullptr;
if (argc > 6)
{
cutstartname = argv[6];
}
if (argc > 7)
{
cutendname = argv[7];
}
NetOptimize optimizer;
if (flag == 65536 || flag == 1)
{
optimizer.storage_type = 1;
}
else if (flag == 2)
{
optimizer.storage_type = 2;
}
else
{
optimizer.storage_type = 0;
}
optimizer.load_param(inparam);
if (strcmp(inbin, "null") == 0)
{
DataReaderFromEmpty dr;
optimizer.load_model(dr);
optimizer.gen_random_weight = true;
}
else
optimizer.load_model(inbin);
if (optimizer.set_cutparam(cutstartname, cutendname) < 0)
{
return -1;
}
optimizer.fuse_batchnorm_scale();
optimizer.fuse_convolution_batchnorm();
optimizer.fuse_convolution_mul();
optimizer.fuse_convolution_add();
optimizer.fuse_convolutiondepthwise_batchnorm();
optimizer.fuse_convolutiondepthwise_mul();
optimizer.fuse_convolutiondepthwise_add();
optimizer.fuse_deconvolution_batchnorm();
optimizer.fuse_deconvolution_mul();
optimizer.fuse_deconvolution_add();
optimizer.fuse_deconvolutiondepthwise_batchnorm();
optimizer.fuse_innerproduct_batchnorm();
optimizer.fuse_innerproduct_add();
optimizer.fuse_innerproduct_dropout();
optimizer.replace_reduction_with_global_pooling();
optimizer.replace_prelu_with_leaky_relu();
optimizer.fuse_convolution_activation();
optimizer.fuse_convolutiondepthwise_activation();
optimizer.fuse_deconvolution_activation();
optimizer.fuse_deconvolutiondepthwise_activation();
optimizer.fuse_innerproduct_activation();
optimizer.fuse_memorydata_binaryop();
optimizer.fuse_binaryop_eltwise();
optimizer.eliminate_dropout();
optimizer.eliminate_pooling1x1();
optimizer.eliminate_noop();
optimizer.eliminate_split();
optimizer.eliminate_flatten_after_global_pooling();
optimizer.eliminate_reshape_after_global_pooling();
optimizer.eliminate_reshape_before_binaryop();
optimizer.replace_convolution_with_innerproduct_after_global_pooling();
optimizer.replace_convolution_with_innerproduct_after_innerproduct();
optimizer.eliminate_flatten_after_innerproduct();
optimizer.eliminate_orphaned_memorydata();
optimizer.shape_inference();
optimizer.estimate_memory_footprint();
optimizer.save(outparam, outbin);
return 0;
}