/******************************************************************************* * 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 #include #include #include namespace sd { namespace ops { namespace helpers { template constexpr T negative_infinity() { return -DataTypeUtils::infOrMax(); } // choose ptr[index*element_stride] template typename std::enable_if::type element(Type *ptr, int index, int element_stride) { return ptr[index * element_stride]; } // choose ptr[index] assuming element_stride is 1 template typename std::enable_if::type element(Type *ptr, int index, int element_stride) { return ptr[index]; } template T local_log(T x) { if (x > 0) { return (math::p_log(x)); } return (negative_infinity()); } template 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(x2 - x1))); } // x2 is max return (x2 + local_log(1 + math::p_exp(x1 - x2))); } template 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() == 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 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(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(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