NNXがサポートしている最適化 eliminate_nop_pad を調べてみました。
最適化をしている箇所のソースがこちらです。
https://github.com/onnx/onnx/blob/master/onnx/optimizer/passes/eliminate_nop_pad.h
static bool is_nop_pad(Node* node, Graph& graph) {
if (node->hasAttribute(kpads)) {
// opset 10 and below
const auto& pads = node->is(kpads);
for (size_t i = 0; i < pads.size(); i++) {
// if pad constant_value is non-zero, this is not a nop pad
if (pads[i] != 0) {
return false;
}
}
return true;
} else {
// opset 11 and above
const auto& pads_name = node->inputs()[1]->uniqueName();
const auto pads_initializer = graph.getInitializer(pads_name);
// 'pad' node has the 'pads' input which has not been initialized -
// can't proceed with elimination
if (pads_initializer == graph.initializers().end())
return false;
// validate values within 'pads'
if (pads_initializer->elem_type() == TensorProto::INT64) {
const auto& pads = ParseData<int64_t>(&*pads_initializer);
for (const auto& val : pads) {
// if pad constant_value is non-zero, this is not a nop pad
if (val != 0) {
return false;
}
}
return true;
}
}
return false;
}
padサイズが0の時、何もPaddingしないの(nop)ので削除する最適化ですね。いちいち名前つけないで、operationがnopと同等の時削除する最適化でくくった方が良いような気がします。
これも実際にやってみましょう。
二つあるPadsのうち、片方がpads = [0, 0, 0, 0]、もう片方がpads = [0, 1, 0, 1]です。
最適化すると、片方だけ削除されるはずです。
passes = ['eliminate_nop_pad']
optimized_model = optimizer.optimize(model_def, passes)
上手く行きました。
全ソースはこちら。
import onnx
from onnx import helper
from onnx import TensorProto
from onnx import optimizer
X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 2])
Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [1, 4])
pad0 = helper.make_node(
'Pad',
['X'],
['pad0_out'],
mode = 'constant',
value = 1.5,
pads = [0, 1, 0, 1]
)
pad1 = helper.make_node(
'Pad',
['X'],
['pad1_out'],
mode = 'constant',
value = 1.5,
pads = [0, 0, 0, 0]
)
concat = helper.make_node(
'Concat',
['pad0_out', 'pad1_out'],
['Y'],
)
graph_def = helper.make_graph(
[pad0, pad1, concat],
'test-model',
[X],
[Y]
)
model_def = helper.make_model(
graph_def,
producer_name='onnx_example'
)
onnx.save(model_def, 'onnx/eliminate_nop_pad.onnx')
# 最適化パスを指定
passes = ['eliminate_nop_pad']
optimized_model = optimizer.optimize(model_def, passes)
onnx.save(optimized_model, 'onnx/eliminate_nop_pad_optimized.onnx')