Files
2026-07-13 12:47:05 +08:00

146 lines
5.8 KiB
C++

/*******************************************************************************
* Copyright (c) 2021 Deeplearning4j Contributors
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://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.
*
* SPDX-License-Identifier: Apache-2.0
*******************************************************************************/
//
// @author AbdelRauf
//
#ifndef LIBND4J_HELPERS_CTCLOSS_H
#define LIBND4J_HELPERS_CTCLOSS_H
#include <graph/Context.h>
#include <math/platformmath.h>
#include <ops/declarable/helpers/helpers.h>
#include <type_traits>
namespace sd {
namespace ops {
namespace helpers {
template <typename T>
constexpr T negative_infinity() {
return -DataTypeUtils::infOrMax<T>();
}
// choose ptr[index*element_stride]
template <bool HasStride, typename Type>
typename std::enable_if<HasStride == true, Type &>::type element(Type *ptr, int index, int element_stride) {
return ptr[index * element_stride];
}
// choose ptr[index] assuming element_stride is 1
template <bool HasStride, typename Type>
typename std::enable_if<HasStride == false, Type &>::type element(Type *ptr, int index, int element_stride) {
return ptr[index];
}
template <typename T>
T local_log(T x) {
if (x > 0) {
return (math::p_log<T>(x));
}
return (negative_infinity<T>());
}
template <typename T>
T log_sum_exp(T x1, T x2) {
// substituting this : std::log(std::exp(arg1 - cMax) + std::exp(arg2 - cMax)) + cMax
// if arg1==cMax : std::log(1 + std::exp(arg2 - cMax)) + cMax
if (x1 >= x2) {
// x1 is max
return (x1 + local_log(1 + math::p_exp<T>(x2 - x1)));
}
// x2 is max
return (x2 + local_log(1 + math::p_exp<T>(x1 - x2)));
}
template <typename T>
T log_sum_exp(T arg1, T arg2, T arg3) {
auto c_max = std::max(arg1, arg2);
c_max = std::max(c_max, arg3);
if (negative_infinity<T>() == c_max) {
c_max = 0;
}
return math::p_log(math::p_exp(arg1 - c_max) + math::p_exp(arg2 - c_max) + math::p_exp(arg3 - c_max)) +
c_max;
}
template <bool HasElementStride, typename Type, typename IndexType>
Type softmax_normalization_term(const Type *log_p, const uint64_t len_c, const uint64_t element_stride) {
Type max_p;
for (uint64_t c = 0; c < len_c; ++c) {
max_p = std::max(max_p, element<HasElementStride>(log_p, c, element_stride));
}
// Get normalization term of softmax: log(sum(exp(logit[j]-max_p))).
Type logsumexp = Type(0.0);
for (uint64_t c = 0; c < len_c; ++c) {
logsumexp += math::p_exp(element<HasElementStride>(log_p, c, element_stride) - max_p);
}
logsumexp = math::p_log(logsumexp);
return max_p + logsumexp;
}
/**
* @brief Implementation of CTC loss function
* References:
Connectionist Temporal Classification - Labeling Unsegmented Sequence Data
with Recurrent Neural Networks:
[Graves et al., 2006](https://dl.acm.org/citation.cfm?id=1143891)
([pdf](http://www.cs.toronto.edu/~graves/icml_2006.pdf))
*
* @param block Context
* @param logits NDArray {BATCH_LEN, MAX_FRAME_LEN, CLASS_LEN }. It should include a blank label as well.
* NOTE: log softmax of rnn output. so we expect softmax normalized
* @param targetLabels NDArray {BATCH_LEN, MAX_TARGET_LEN}
* @param logitsLengths NDArray {BATCH_LEN} Length of input sequence in logits
* @param targetLabelLengths NDArray {BATCH_LEN} Length of label sequence in labels
* @param logLosses NDArray {BATCH_LEN} or EMPTY. if empty it will be skipped. negative log probabilities of loss
* @param gradients NDArray {BATCH_LEN, MAX_FRAME_LEN, CLASS_LEN } or EMPTY. gradients
* @param blankIndex index of the blank label in logits
*/
SD_LIB_HIDDEN void ctcLoss(sd::graph::Context &block, NDArray&logitsInput, NDArray&targetLabels,
NDArray&logitsLengths, NDArray&targetLabelLengths, NDArray &logLosses,
NDArray &gradients, int blankIndex);
/**
* @brief Implementation of CTC beam search
*
* @param logit NDArray {BATCH_LEN, MAX_FRAME_LEN, CLASS_LEN }. log probabilities. It should include a blank label as
* well.
* @param sequence_length NDArray {BATCH_LEN} length of frames. type integer
* @param result_sequences NDArray {BATCH_LEN, NBEST, MAX_FRAME_LEN} result sequences.
* NOTE: result_sequences NdArray should be c order and have ews == 1. type integer.
* @param result_probs NDArray {BATCH_LEN, NBEST} negative log probabilities for each sequence.
* NOTE: result_probs NdArray should be c order and have ews == 1
* @param result_sequences_length NDArray {BATCH_LEN, NBEST} the length of each sequence in result_sequences.
* NOTE: result_sequences_length NdArray should be c order and have ews == 1
* @param blank_index the index of the blank label in logits
* @param beam_width the width of the beam search.
* @param nbest_len the number of top best results that should be returned. if it is greather than beam_width it will
* be defaulted to beam_width size.
* @param normalize_logits when its true it will normalize logits. by default it is assumed logit contains already
* normalized log-probabilities NOTE: maximum value of integer type should be >= CLASS_LEN to make sense. And also user
* should consider frame lengthes as well.
*/
SD_LIB_HIDDEN void beamSearch(NDArray&logit, NDArray&sequence_length, NDArray &result_sequences,
NDArray &result_probs, NDArray &result_sequences_length, int blank_index, int beam_width,
int nbest_len, bool normalize_logits);
} // namespace helpers
} // namespace ops
} // namespace sd
#endif