Skip to content

Commit 48ccaf9

Browse files
eaidovaAleksei Korobeinikovanzhella-pankratova
authored
BERT NER Demo: support dynamic shape (#3337) (#3344)
* BERT NER Demo: support dynamic shape * Update demos/bert_named_entity_recognition_demo/python/bert_named_entity_recognition_demo.py Co-authored-by: Aleksei Korobeinikov <[email protected]> * Update demos/common/python/openvino/model_zoo/model_api/adapters/openvino_adapter.py Co-authored-by: Aleksei Korobeinikov <[email protected]> * Apply suggestions from code review Co-authored-by: Anzhella Pankratova <[email protected]> * Apply suggestions from code review Co-authored-by: Anzhella Pankratova <[email protected]> * fix bounds for filtered_labels_id in BertNamedEntityRecognition wrapper (#3341) Co-authored-by: Aleksei Korobeinikov <[email protected]> Co-authored-by: Anzhella Pankratova <[email protected]> Co-authored-by: Aleksei Korobeinikov <[email protected]> Co-authored-by: Anzhella Pankratova <[email protected]>
1 parent 78e8443 commit 48ccaf9

File tree

6 files changed

+46
-14
lines changed

6 files changed

+46
-14
lines changed

demos/bert_named_entity_recognition_demo/python/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ Options:
7070
-nthreads NUM_THREADS, --num_threads NUM_THREADS
7171
Optional. Number of threads to use for inference on
7272
CPU (including HETERO cases).
73+
--dynamic_shape Optional. Run model with dynamic input sequence. If
74+
not provided, input sequence is padded to max_seq_len
7375
```
7476

7577
## Demo Inputs
@@ -118,7 +120,7 @@ Exemplary command:
118120
## Classifying Documents with Long Texts
119121

120122
Notice that when the original "context" (text from the url) does not fit the model input
121-
(128 for the Bert-Base), the demo reshapes model to maximum sentence length in the "context".
123+
(128 for the Bert-Base), the demo reshapes model to maximum sentence length in the "context" and pad all input sequences to maximum sentence length if model executed with static shape.
122124

123125
## See Also
124126

demos/bert_named_entity_recognition_demo/python/bert_named_entity_recognition_demo.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ def build_argparser():
7272
default='', type=str)
7373
args.add_argument('-nthreads', '--num_threads', default=None, type=int,
7474
help='Optional. Number of threads to use for inference on CPU (including HETERO cases).')
75+
args.add_argument('--dynamic_shape', action='store_true',
76+
help='Optional. Run model with dynamic input sequence. If not provided, input sequence is padded to max_seq_len')
7577
return parser
7678

7779

@@ -116,9 +118,10 @@ def main():
116118
elif args.adapter == 'ovms':
117119
model_adapter = OVMSAdapter(args.model)
118120

119-
model = BertNamedEntityRecognition(model_adapter, {'vocab': vocab, 'input_names': args.input_names})
121+
enable_padding = not args.dynamic_shape
122+
model = BertNamedEntityRecognition(model_adapter, {'vocab': vocab, 'input_names': args.input_names, 'enable_padding': enable_padding})
120123
if max_sentence_length > model.max_length:
121-
model.reshape(max_sentence_length)
124+
model.reshape(max_sentence_length if enable_padding else (1, max_sentence_length))
122125
model.log_layers_info()
123126

124127
pipeline = AsyncPipeline(model)

demos/common/python/openvino/model_zoo/model_api/adapters/openvino_adapter.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from pathlib import Path
1919

2020
try:
21-
from openvino.runtime import AsyncInferQueue, Core, PartialShape, layout_helpers, get_version
21+
from openvino.runtime import AsyncInferQueue, Core, PartialShape, layout_helpers, get_version, Dimension
2222
openvino_absent = False
2323
except ImportError:
2424
openvino_absent = True
@@ -89,20 +89,21 @@ def log_runtime_settings(self):
8989
def get_input_layers(self):
9090
inputs = {}
9191
for input in self.model.inputs:
92-
input_layout = self.get_layout_for_input(input)
93-
inputs[input.get_any_name()] = Metadata(input.get_names(), list(input.shape), input_layout, input.get_element_type().get_type_name())
92+
input_shape = get_input_shape(input)
93+
input_layout = self.get_layout_for_input(input, input_shape)
94+
inputs[input.get_any_name()] = Metadata(input.get_names(), input_shape, input_layout, input.get_element_type().get_type_name())
9495
inputs = self._get_meta_from_ngraph(inputs)
9596
return inputs
9697

97-
def get_layout_for_input(self, input) -> str:
98+
def get_layout_for_input(self, input, shape=None) -> str:
9899
input_layout = ''
99100
if self.model_parameters['input_layouts']:
100101
input_layout = Layout.from_user_layouts(input.get_names(), self.model_parameters['input_layouts'])
101102
if not input_layout:
102103
if not layout_helpers.get_layout(input).empty:
103104
input_layout = Layout.from_openvino(input)
104105
else:
105-
input_layout = Layout.from_shape(input.shape)
106+
input_layout = Layout.from_shape(shape if shape is not None else input.shape)
106107
return input_layout
107108

108109
def get_output_layers(self):
@@ -114,7 +115,9 @@ def get_output_layers(self):
114115
return outputs
115116

116117
def reshape_model(self, new_shape):
117-
new_shape = {k: PartialShape(v) for k, v in new_shape.items()}
118+
new_shape = {name: PartialShape(
119+
[Dimension(dim) if not isinstance(dim, tuple) else Dimension(dim[0], dim[1])
120+
for dim in shape]) for name, shape in new_shape.items()}
118121
self.model.reshape(new_shape)
119122

120123
def get_raw_result(self, request):
@@ -157,3 +160,24 @@ def operations_by_type(self, operation_type):
157160
layer_name = node.get_friendly_name()
158161
layers_info[layer_name] = Metadata(type=node.get_type_name(), meta=node.get_attributes())
159162
return layers_info
163+
164+
165+
def get_input_shape(input_tensor):
166+
def string_to_tuple(string, casting_type=int):
167+
processed = string.replace(' ', '').replace('(', '').replace(')', '').split(',')
168+
processed = filter(lambda x: x, processed)
169+
return tuple(map(casting_type, processed)) if casting_type else tuple(processed)
170+
if not input_tensor.partial_shape.is_dynamic:
171+
return list(input_tensor.shape)
172+
ps = str(input_tensor.partial_shape)
173+
preprocessed = ps.replace('{', '(').replace('}', ')').replace('?', '-1')
174+
preprocessed = preprocessed.replace('(', '').replace(')', '')
175+
if '..' in preprocessed:
176+
shape_list = []
177+
for dim in preprocessed.split(','):
178+
if '..' in dim:
179+
shape_list.append(string_to_tuple(dim.replace('..', ',')))
180+
else:
181+
shape_list.append(int(dim))
182+
return shape_list
183+
return string_to_tuple(preprocessed)

demos/common/python/openvino/model_zoo/model_api/models/bert.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import numpy as np
1515

1616
from .model import Model, WrapperError
17-
from .types import DictValue, NumericalValue, StringValue
17+
from .types import DictValue, NumericalValue, StringValue, BooleanValue
1818

1919

2020
class Bert(Model):
@@ -37,13 +37,15 @@ def parameters(cls):
3737
parameters.update({
3838
'vocab': DictValue(),
3939
'input_names': StringValue(description='Comma-separated names of input layers'),
40+
'enable_padding': BooleanValue(
41+
description='Should be input sequence padded to max sequence len or not', default_value=True
42+
)
4043
})
4144
return parameters
4245

4346
def preprocess(self, inputs):
4447
input_ids, attention_mask, token_type_ids = self.form_request(inputs)
45-
46-
pad_len = self.pad_input(input_ids, attention_mask, token_type_ids)
48+
pad_len = self.pad_input(input_ids, attention_mask, token_type_ids) if self.enable_padding else 0
4749
meta = {'pad_len': pad_len, 'inputs': inputs}
4850

4951
return self.create_input_dict(input_ids, attention_mask, token_type_ids), meta
@@ -79,7 +81,7 @@ def reshape(self, new_length):
7981
default_input_shape = input_info.shape
8082
super().reshape(new_shapes)
8183
self.logger.debug("\tReshape model from {} to {}".format(default_input_shape, new_shapes[input_name]))
82-
self.max_length = new_length
84+
self.max_length = new_length if not isinstance(new_length, tuple) else new_length[1]
8385

8486

8587
class BertNamedEntityRecognition(Bert):

demos/common/python/openvino/model_zoo/model_api/models/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def validate(self, value):
3434
def get_value(self, value):
3535
errors = self.validate(value)
3636
if len(errors) == 0:
37-
return value if value else self.default_value
37+
return value if value is not None else self.default_value
3838

3939
def build_error():
4040
pass

models/public/bert-base-ner/model.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ conversion_to_onnx_args:
8686
- --input-shapes=[1,128],[1,128],[1,128]
8787
- --output-file=$conv_dir/bert-base-ner.onnx
8888
- --inputs-dtype=long
89+
- '--conversion-param=dynamic_axes={"input_ids": {0: "batch_size", 1: "sequence_len"},"attention_mask": {0: "batch_size", 1: "sequence_len"}, "token_type_ids": {0: "batch_size", 1: "sequence_len"}, "output": {0: "batch_size", 1: "sequence_len"}}'
8990
input_info:
9091
- name: input_ids
9192
shape: [1, 128]

0 commit comments

Comments
 (0)