[Datumaro] Add generic accuracy checker model launcher (#1661)
* Refactor inference wrapper * Add accuracy checker launcher wrapper * t * rename method * Add importer for openvino launcher * Move openvino plugin to iecore * add generic AC launcher * Implement cli for AC launcher * move ac plugin dir * prevent tf reimport * Fix outputs conversion * t * add pytorch model example * Require config path in launcher * Clear extra whitespacemain
parent
fefcb51331
commit
ae3b06b465
@ -0,0 +1,116 @@
|
||||
|
||||
# Copyright (C) 2020 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from datumaro.util.tf_util import import_tf
|
||||
import_tf() # prevent TF loading and potential interpeter crash
|
||||
|
||||
from itertools import groupby
|
||||
|
||||
from accuracy_checker.adapters import create_adapter
|
||||
from accuracy_checker.data_readers import DataRepresentation
|
||||
from accuracy_checker.launcher import InputFeeder, create_launcher
|
||||
from accuracy_checker.postprocessor import PostprocessingExecutor
|
||||
from accuracy_checker.preprocessor import PreprocessingExecutor
|
||||
from accuracy_checker.utils import extract_image_representations
|
||||
|
||||
from datumaro.components.extractor import AnnotationType, LabelCategories
|
||||
|
||||
from .representation import import_predictions
|
||||
|
||||
|
||||
class _FakeDataset:
|
||||
def __init__(self, metadata=None):
|
||||
self.metadata = metadata or {}
|
||||
|
||||
class GenericAcLauncher:
|
||||
@staticmethod
|
||||
def from_config(config):
|
||||
launcher_config = config['launcher']
|
||||
launcher = create_launcher(launcher_config)
|
||||
|
||||
dataset = _FakeDataset()
|
||||
adapter_config = config.get('adapter') or launcher_config.get('adapter')
|
||||
label_config = adapter_config.get('labels') \
|
||||
if isinstance(adapter_config, dict) else None
|
||||
if label_config:
|
||||
assert isinstance(label_config, (list, dict))
|
||||
if isinstance(label_config, list):
|
||||
label_config = dict(enumerate(label_config))
|
||||
|
||||
dataset.metadata = {'label_map': {
|
||||
int(key): label for key, label in label_config.items()
|
||||
}}
|
||||
adapter = create_adapter(adapter_config, launcher, dataset)
|
||||
|
||||
preproc_config = config.get('preprocessing')
|
||||
preproc = None
|
||||
if preproc_config:
|
||||
preproc = PreprocessingExecutor(preproc_config,
|
||||
dataset_meta=dataset.metadata,
|
||||
input_shapes=launcher.inputs_info_for_meta()
|
||||
)
|
||||
|
||||
postproc_config = config.get('postprocessing')
|
||||
postproc = None
|
||||
if postproc_config:
|
||||
postproc = PostprocessingExecutor(postproc_config,
|
||||
dataset_meta=dataset.metadata,
|
||||
)
|
||||
|
||||
return __class__(launcher,
|
||||
adapter=adapter, preproc=preproc, postproc=postproc)
|
||||
|
||||
def __init__(self, launcher, adapter=None,
|
||||
preproc=None, postproc=None, input_feeder=None):
|
||||
self._launcher = launcher
|
||||
self._input_feeder = input_feeder or InputFeeder(
|
||||
launcher.config.get('inputs', []), launcher.inputs,
|
||||
launcher.fit_to_input, launcher.default_layout
|
||||
)
|
||||
self._adapter = adapter
|
||||
self._preproc = preproc
|
||||
self._postproc = postproc
|
||||
|
||||
self._categories = self._init_categories()
|
||||
|
||||
def launch_raw(self, inputs):
|
||||
ids = range(len(inputs))
|
||||
inputs = [DataRepresentation(inp, identifier=id)
|
||||
for id, inp in zip(ids, inputs)]
|
||||
_, batch_meta = extract_image_representations(inputs)
|
||||
|
||||
if self._preproc:
|
||||
inputs = self._preproc.process(inputs)
|
||||
|
||||
inputs = self._input_feeder.fill_inputs(inputs)
|
||||
outputs = self._launcher.predict(inputs, batch_meta)
|
||||
|
||||
if self._adapter:
|
||||
outputs = self._adapter.process(outputs, ids, batch_meta)
|
||||
|
||||
if self._postproc:
|
||||
outputs = self._postproc.process(outputs)
|
||||
|
||||
return outputs
|
||||
|
||||
def launch(self, inputs):
|
||||
outputs = self.launch_raw(inputs)
|
||||
return [import_predictions(g) for _, g in
|
||||
groupby(outputs, key=lambda o: o.identifier)]
|
||||
|
||||
def categories(self):
|
||||
return self._categories
|
||||
|
||||
def _init_categories(self):
|
||||
if self._adapter is None or self._adapter.label_map is None:
|
||||
return None
|
||||
|
||||
label_map = sorted(self._adapter.label_map.items(), key=lambda e: e[0])
|
||||
|
||||
label_cat = LabelCategories()
|
||||
for _, label in label_map:
|
||||
label_cat.add(label)
|
||||
|
||||
return { AnnotationType.label: label_cat }
|
||||
@ -0,0 +1,62 @@
|
||||
|
||||
# Copyright (C) 2020 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from datumaro.util.tf_util import import_tf
|
||||
import_tf() # prevent TF loading and potential interpeter crash
|
||||
|
||||
import accuracy_checker.representation as ac
|
||||
|
||||
import datumaro.components.extractor as dm
|
||||
from datumaro.util.annotation_tools import softmax
|
||||
|
||||
def import_predictions(predictions):
|
||||
# Convert Accuracy checker predictions to Datumaro annotations
|
||||
|
||||
anns = []
|
||||
|
||||
for pred in predictions:
|
||||
anns.extend(import_prediction(pred))
|
||||
|
||||
return anns
|
||||
|
||||
def import_prediction(pred):
|
||||
if isinstance(pred, ac.ClassificationPrediction):
|
||||
scores = softmax(pred.scores)
|
||||
return (dm.Label(label_id, attributes={'score': float(score)})
|
||||
for label_id, score in enumerate(scores))
|
||||
elif isinstance(pred, ac.ArgMaxClassificationPrediction):
|
||||
return (dm.Label(int(pred.label)), )
|
||||
elif isinstance(pred, ac.CharacterRecognitionPrediction):
|
||||
return (dm.Label(int(pred.label)), )
|
||||
elif isinstance(pred, (ac.DetectionPrediction, ac.ActionDetectionPrediction)):
|
||||
return (dm.Bbox(x0, y0, x1 - x0, y1 - y0, int(label_id),
|
||||
attributes={'score': float(score)})
|
||||
for label, score, x0, y0, x1, y1 in zip(pred.labels, pred.scores,
|
||||
pred.x_mins, pred.y_mins, pred.x_maxs, pred.y_maxs)
|
||||
)
|
||||
elif isinstance(pred, ac.DepthEstimationPrediction):
|
||||
return (dm.Mask(pred.depth_map), ) # 2d floating point mask
|
||||
# elif isinstance(pred, ac.HitRatioPrediction):
|
||||
# -
|
||||
elif isinstance(pred, ac.ImageInpaintingPrediction):
|
||||
return (dm.Mask(pred.value), ) # an image
|
||||
# elif isinstance(pred, ac.MultiLabelRecognitionPrediction):
|
||||
# -
|
||||
# elif isinstance(pred, ac.MachineTranslationPrediction):
|
||||
# -
|
||||
# elif isinstance(pred, ac.QuestionAnsweringPrediction):
|
||||
# -
|
||||
# elif isinstance(pred, ac.PoseEstimation3dPrediction):
|
||||
# -
|
||||
# elif isinstance(pred, ac.PoseEstimationPrediction):
|
||||
# -
|
||||
# elif isinstance(pred, ac.RegressionPrediction):
|
||||
# -
|
||||
else:
|
||||
raise NotImplementedError("Can't convert %s" % type(pred))
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
|
||||
# Copyright (C) 2020 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import os.path as osp
|
||||
import yaml
|
||||
|
||||
from datumaro.components.cli_plugin import CliPlugin
|
||||
from datumaro.components.launcher import Launcher
|
||||
|
||||
from .details.ac import GenericAcLauncher as _GenericAcLauncher
|
||||
|
||||
|
||||
class AcLauncher(Launcher, CliPlugin):
|
||||
"""
|
||||
Generic model launcher with Accuracy Checker backend.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def build_cmdline_parser(cls, **kwargs):
|
||||
parser = super().build_cmdline_parser(**kwargs)
|
||||
parser.add_argument('-c', '--config', type=osp.abspath, required=True,
|
||||
help="Path to the launcher configuration file (.yml)")
|
||||
return parser
|
||||
|
||||
def __init__(self, config, model_dir=None):
|
||||
model_dir = model_dir or ''
|
||||
with open(osp.join(model_dir, config), 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
self._launcher = _GenericAcLauncher.from_config(config)
|
||||
|
||||
def launch(self, inputs):
|
||||
return self._launcher.launch(inputs)
|
||||
|
||||
def categories(self):
|
||||
return self._launcher.categories()
|
||||
@ -0,0 +1,37 @@
|
||||
launcher:
|
||||
framework: pytorch
|
||||
module: samplenet.SampLeNet
|
||||
python_path: '.'
|
||||
checkpoint: 'samplenet.pth'
|
||||
|
||||
# launcher returns raw result, so it should be converted
|
||||
# to an appropriate representation with adapter
|
||||
adapter:
|
||||
type: classification
|
||||
labels:
|
||||
- label1
|
||||
- label2
|
||||
- label3
|
||||
- label4
|
||||
- label5
|
||||
- label6
|
||||
- label7
|
||||
- label8
|
||||
- label9
|
||||
- label10
|
||||
|
||||
# list of preprocessing, applied to each image during validation
|
||||
# order of entries matters
|
||||
preprocessing:
|
||||
# resize input image to topology input size
|
||||
# you may specify size to which image should be resized
|
||||
# via dst_width, dst_height fields
|
||||
- type: resize
|
||||
size: 32
|
||||
# topology is trained on RGB images, but Datumaro reads in BGR
|
||||
# so it must be converted to RGB
|
||||
- type: bgr_to_rgb
|
||||
# dataset mean and standard deviation
|
||||
- type: normalization
|
||||
mean: (125.307, 122.961, 113.8575)
|
||||
std: (51.5865, 50.847, 51.255)
|
||||
Binary file not shown.
@ -0,0 +1,38 @@
|
||||
"""
|
||||
Copyright (c) 2019 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
class SampLeNet(nn.Module):
|
||||
def __init__(self):
|
||||
super(SampLeNet, self).__init__()
|
||||
self.conv1 = nn.Conv2d(3, 6, 5)
|
||||
self.pool = nn.MaxPool2d(2, 2)
|
||||
self.conv2 = nn.Conv2d(6, 16, 5)
|
||||
self.fc1 = nn.Linear(16 * 5 * 5, 120)
|
||||
self.fc2 = nn.Linear(120, 84)
|
||||
self.fc3 = nn.Linear(84, 10)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.pool(F.relu(self.conv1(x)))
|
||||
x = self.pool(F.relu(self.conv2(x)))
|
||||
x = x.view(-1, 16 * 5 * 5)
|
||||
x = F.relu(self.fc1(x))
|
||||
x = F.relu(self.fc2(x))
|
||||
x = self.fc3(x)
|
||||
return x
|
||||
Loading…
Reference in New Issue