Files
wehub-resource-sync ba4be087d5
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
CICD NeMo / cicd-test-container-build (push) Has been cancelled
CICD NeMo / cicd-import-tests (push) Has been cancelled
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Has been cancelled
CICD NeMo / cicd-main-unit-tests (push) Has been cancelled
CICD NeMo / cicd-main-speech (push) Has been cancelled
CICD NeMo / Nemo_CICD_Test (push) Has been cancelled
CICD NeMo / Coverage (e2e) (push) Has been cancelled
CICD NeMo / Coverage (unit-test) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CICD NeMo / cicd-wait-in-queue (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:28:58 +08:00

141 lines
5.6 KiB
Python

# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
#
# 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.
from dataclasses import dataclass
import torch
from nemo.collections.asr.parts.utils.asr_confidence_utils import (
ConfidenceConfig,
ConfidenceMethodConfig,
get_confidence_aggregation_bank,
get_confidence_measure_bank,
)
from nemo.collections.asr.parts.utils.rnnt_utils import Hypothesis
# frozen is required to allow hashing of this class and use it
# as a dictionary key when running confidence tuning
@dataclass(frozen=True)
class ConfidenceSpec:
exclude_blank: bool
aggregation: str
confidence_type: str
alpha: float
def to_confidence_config(self) -> ConfidenceConfig:
"""Converts confidence spec to the confidence config.
Internally, the tuning procedure uses this "spec" objects as they
are more aligned with how things are implemented. But when it's time
to save the models or call transcribe, we need to use the proper
object of type ``ConfidenceConfig``.
"""
if self.confidence_type == 'max_prob':
name = 'max_prob'
entropy_type = 'tsallis' # can be any
entropy_norm = 'lin' # can be any
else:
name, entropy_type, entropy_norm = self.confidence_type.split("_")
return ConfidenceConfig(
exclude_blank=self.exclude_blank,
aggregation=self.aggregation,
method_cfg=ConfidenceMethodConfig(
name=name,
entropy_type=entropy_type,
alpha=self.alpha,
entropy_norm=entropy_norm,
),
)
def get_filtered_logprobs(hypothesis: Hypothesis, exclude_blank: bool) -> torch.Tensor:
"""Returns logprobs from the hypothesis object with optional blanks filter.
This function supports both CTC and Transducer hypotheses. Will place the
logprobs on GPU if it's available.
Args:
hypothesis: generated hypothesis as returned from the transcribe
method of the ASR model.
exclude_blank: whether to filter out all ``<blank>`` tokens.
Returns:
torch.Tensor: of shape [S, V], where S is (filtered) sequence length and
V is the vocabulary size.
"""
if isinstance(hypothesis.alignments, list): # Transducer
filtered_logprobs = []
for alignment in hypothesis.alignments:
for align_elem in alignment:
if not exclude_blank:
filtered_logprobs.append(align_elem[0])
elif align_elem[1].item() != align_elem[0].shape[-1] - 1:
filtered_logprobs.append(align_elem[0])
if not filtered_logprobs: # for the edge-case of all blanks
filtered_logprobs.append(align_elem[0])
filtered_logprobs = torch.stack(filtered_logprobs)
if torch.cuda.is_available(): # by default logprobs are placed on cpu in nemo
filtered_logprobs = filtered_logprobs.cuda()
else: # CTC
logprobs = hypothesis.y_sequence
if torch.cuda.is_available(): # by default logprobs are placed on cpu in nemo
logprobs = logprobs.cuda()
if exclude_blank: # filtering blanks
labels = logprobs.argmax(dim=-1)
filtered_logprobs = logprobs[labels != logprobs.shape[1] - 1]
if filtered_logprobs.shape[0] == 0: # for the edge-case of all blanks
filtered_logprobs = logprobs[:1]
else:
filtered_logprobs = logprobs
# need to make sure logprobs are always normalized, so checking if they sum up to 1
if not torch.allclose(filtered_logprobs[0].exp().sum(), torch.tensor(1.0)):
filtered_logprobs = torch.log_softmax(filtered_logprobs, dim=1)
return filtered_logprobs
def compute_confidence(hypothesis: Hypothesis, confidence_cfg: ConfidenceConfig) -> float:
"""Computes confidence score of the full utterance from a given hypothesis.
This is essentially a re-implementation of the built-in confidence
computation in NeMo. The difference is that we aggregate full-utterance
scores, while core functionality only supports word and token level
aggregations.
Args:
hypothesis: generated hypothesis as returned from the transcribe
method of the ASR model.
confidence_cfg: confidence config specifying what kind of
method/aggregation should be used.
Returns:
float: confidence score.
"""
filtered_logprobs = get_filtered_logprobs(hypothesis, confidence_cfg.exclude_blank)
vocab_size = filtered_logprobs.shape[1]
aggr_func = get_confidence_aggregation_bank()[confidence_cfg.aggregation]
if confidence_cfg.method_cfg.name == "max_prob":
conf_type = "max_prob"
alpha = 1.0
else:
conf_type = f"entropy_{confidence_cfg.method_cfg.entropy_type}_{confidence_cfg.method_cfg.entropy_norm}"
alpha = confidence_cfg.method_cfg.alpha
conf_func = get_confidence_measure_bank()[conf_type]
conf_value = aggr_func(conf_func(filtered_logprobs, v=vocab_size, t=alpha)).cpu().item()
return conf_value