[Opset13][pyAPI] Python API Multinomial-13 (#20400)

* Init Multinomial op python API

* Add python tests for Multinomial op

* Update num_samples input description
This commit is contained in:
Katarzyna Mitrus 2023-10-13 12:50:02 +02:00 committed by Alexander Nesterov
parent e8d80f9b0d
commit 146b0c0be8
3 changed files with 120 additions and 0 deletions

View File

@ -106,6 +106,7 @@ from openvino.runtime.opset1.ops import minimum
from openvino.runtime.opset4.ops import mish from openvino.runtime.opset4.ops import mish
from openvino.runtime.opset1.ops import mod from openvino.runtime.opset1.ops import mod
from openvino.runtime.opset9.ops import multiclass_nms from openvino.runtime.opset9.ops import multiclass_nms
from openvino.runtime.opset13.ops import multinomial
from openvino.runtime.opset1.ops import multiply from openvino.runtime.opset1.ops import multiply
from openvino.runtime.opset6.ops import mvn from openvino.runtime.opset6.ops import mvn
from openvino.runtime.opset1.ops import negative from openvino.runtime.opset1.ops import negative

View File

@ -110,6 +110,47 @@ def bitwise_xor(
) )
@nameable_op
def multinomial(
probs: NodeInput,
num_samples: NodeInput,
convert_type: str,
with_replacement: bool,
log_probs: bool,
global_seed: int = 0,
op_seed: int = 0,
) -> Node:
"""Return a node which generates a sequence of class indices sampled from the multinomial distribution.
:param probs: Tensor with probabilities of floating-point type, and shape [class_size] or [batch_size, class_size].
:param num_samples: Tensor (scalar or 1D) a single element of type i32 or i64,
specifying the number of samples to draw from the multinomial distribution.
:param convert_type: Specifies the output tensor type, possible values: 'i64', 'i32'.
:param with_replacement: Flag that specifies whether to sample with replacement.
:param log_probs: Flag that specifies whether *probs* should be treated as unnormalized log probabilities.
:param global_seed: Specifies global seed value. Required to be a positive integer or 0.
:param op_seed: Specifies operational seed value. Required to be a positive integer or 0.
:return: The new node performing Multinomial operation.
"""
inputs = as_nodes(probs, num_samples)
if global_seed < 0:
raise RuntimeError(f"global_seed should be positive or 0. Got: {global_seed}")
if op_seed < 0:
raise RuntimeError(f"op_seed should be positive or 0. Got: {op_seed}")
attributes = {
"convert_type": convert_type,
"with_replacement": with_replacement,
"log_probs": log_probs,
"global_seed": global_seed,
"op_seed": op_seed,
}
return _get_node_factory_opset13().create("Multinomial", inputs, attributes)
@nameable_op @nameable_op
def nms_rotated( def nms_rotated(
boxes: NodeInput, boxes: NodeInput,

View File

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import numpy as np
import pytest
import openvino.runtime.opset13 as ops
from openvino.runtime import PartialShape, Dimension, Type
@pytest.mark.parametrize(
("probs_shape", "num_samples_shape", "convert_type", "with_replacement", "log_probs", "global_seed", "op_seed", "expected_out_shape"),
[
([4, 16], [], "i32", False, True, 7461, 1546, PartialShape([4, -1])),
([8], [1], "i64", True, False, 0, 0, PartialShape([-1])),
],
)
def test_multinomial_param_inputs(probs_shape, num_samples_shape, convert_type, with_replacement, log_probs, global_seed, op_seed, expected_out_shape):
probs = ops.parameter(probs_shape, dtype=np.float32)
num_samples = ops.parameter(num_samples_shape, dtype=np.int32)
op = ops.multinomial(probs, num_samples,
convert_type=convert_type,
with_replacement=with_replacement,
log_probs=log_probs,
global_seed=global_seed,
op_seed=op_seed)
assert op.get_output_size() == 1
assert op.get_type_name() == "Multinomial"
assert op.get_output_element_type(0) == Type.i32 if convert_type == "i32" else Type.i64
assert op.get_output_partial_shape(0) == expected_out_shape
@pytest.mark.parametrize(
("probs_array", "num_samples_val", "convert_type", "with_replacement", "log_probs", "global_seed", "op_seed", "expected_out_shape"),
[
(np.array([0.7, 0.3, 0.6, 0.5]), 3, "i32", False, True, 111, 222, PartialShape([3])),
(np.array([[0.7, 0.3], [0.6, 0.5]]), 2, "i64", True, False, 111, 222, PartialShape([2, 2])),
],
)
def test_multinomial_const_inputs(probs_array, num_samples_val, convert_type, with_replacement, log_probs, global_seed, op_seed, expected_out_shape):
probs = ops.constant(probs_array, dtype=np.float32)
num_samples = ops.constant(num_samples_val, dtype=np.int32)
op = ops.multinomial(probs, num_samples,
convert_type=convert_type,
with_replacement=with_replacement,
log_probs=log_probs,
global_seed=global_seed,
op_seed=op_seed)
assert op.get_output_size() == 1
assert op.get_type_name() == "Multinomial"
assert op.get_output_element_type(0) == Type.i32 if convert_type == "i32" else Type.i64
assert op.get_output_partial_shape(0) == expected_out_shape
@pytest.mark.parametrize(
("probs_shape", "num_samples_shape", "convert_type", "with_replacement", "log_probs", "expected_out_shape"),
[
([10], [1], "i32", True, True, PartialShape([-1])),
([2, 16], [], "i64", False, False, PartialShape([2, -1])),
],
)
def test_multinomial_default_attrs(probs_shape, num_samples_shape, convert_type, with_replacement, log_probs, expected_out_shape):
probs = ops.parameter(probs_shape, dtype=np.float32)
num_samples = ops.parameter(num_samples_shape, dtype=np.int32)
op = ops.multinomial(probs, num_samples,
convert_type=convert_type,
with_replacement=with_replacement,
log_probs=log_probs)
assert op.get_output_size() == 1
assert op.get_type_name() == "Multinomial"
assert op.get_output_element_type(0) == Type.i32 if convert_type == "i32" else Type.i64
assert op.get_output_partial_shape(0) == expected_out_shape