Files
paddlepaddle--paddle/paddle/phi/kernels/cpu/crf_decoding_kernel.cc
T
2026-07-13 12:40:42 +08:00

166 lines
6.4 KiB
C++

// Copyright (c) 2024 PaddlePaddle Authors. 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.
#include "paddle/phi/kernels/crf_decoding_kernel.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/eigen/common.h"
#include "paddle/phi/kernels/funcs/jit/kernels.h"
#include "paddle/phi/kernels/funcs/math_function.h"
namespace phi {
template <typename T, typename Context>
void Decode(const Context& dev_ctx,
const DenseTensor& emission_weights,
const DenseTensor& transition_weights,
DenseTensor* decoded_path) {
auto emission_dims = emission_weights.dims();
const size_t seq_len = emission_dims[0];
const size_t tag_num = emission_dims[1];
const T* x = emission_weights.data<T>();
const T* w = transition_weights.data<T>();
int64_t* path = decoded_path->data<int64_t>();
// alpha is a memo table. An element alpha(k, v) records the score of the
// best sequence of tags from position 1 to position k with v being the end
// tag.
DenseTensor alpha;
alpha.Resize(emission_dims);
T* alpha_value = dev_ctx.template Alloc<T>(&alpha);
DenseTensor track;
track.Resize(emission_dims);
int* track_value = dev_ctx.template Alloc<int>(&track);
auto ker =
jit::KernelFuncs<jit::CRFDecodingTuple<T>, CPUPlace>::Cache().At(tag_num);
ker(static_cast<int>(seq_len), x, w, alpha_value, track_value, tag_num);
T max_score = -std::numeric_limits<T>::max();
int max_i = 0;
for (size_t i = 0; i < tag_num; ++i) {
T score = alpha_value[(seq_len - 1) * tag_num + i] + w[tag_num + i];
if (score > max_score) {
max_score = score;
max_i = i;
}
}
path[seq_len - 1] = max_i;
for (int k = seq_len - 1; k >= 1; --k) {
path[k - 1] = max_i = track_value[k * tag_num + max_i];
}
}
// Slice() needs to be used in *.cc files, otherwise there is a error in
// test/custom_runtime/extension_header_test.cc
template <typename T, typename Context>
void CRFDecodingOpKernel(const Context& dev_ctx,
const DenseTensor& emission,
const DenseTensor& transition,
const optional<DenseTensor>& label,
const optional<DenseTensor>& length,
DenseTensor* viterbi_path) {
auto* emission_weights = &emission;
auto* transition_weights = &transition;
auto* label_p = label.get_ptr();
auto* decoded_path = viterbi_path;
int64_t* path = dev_ctx.template Alloc<int64_t>(decoded_path);
funcs::SetConstant<Context, int64_t>()(dev_ctx, decoded_path, 0);
bool has_length = length.get_ptr() != nullptr;
if (has_length) {
auto* length_p = length.get_ptr();
const size_t seq_num = length_p->numel();
const int64_t* length_data = length_p->data<int64_t>();
auto in_dims = emission_weights->dims();
DenseTensor emission_weights_tmp = *emission_weights;
emission_weights_tmp.Resize(
make_ddim({in_dims[0] * in_dims[1], in_dims[2]}));
decoded_path->Resize({in_dims[0] * in_dims[1], 1});
for (size_t i = 0; i < seq_num; ++i) {
if (length_data[i] == 0) continue;
int64_t start_pos = i * in_dims[1];
int64_t end_pos = start_pos + static_cast<int64_t>(length_data[i]);
DenseTensor decoded_path_one_seq =
decoded_path->Slice(start_pos, end_pos);
Decode<T, Context>(dev_ctx,
emission_weights_tmp.Slice(start_pos, end_pos),
*transition_weights,
&decoded_path_one_seq);
}
decoded_path->Resize({in_dims[0], in_dims[1]});
if (label) {
const int64_t* label_value = label_p->data<int64_t>();
for (size_t i = 0; i < seq_num; ++i) {
for (int64_t j = 0; j < in_dims[1]; ++j) {
int64_t start_pos = i * in_dims[1];
if (j < length_data[i]) {
path[start_pos + j] =
label_value[start_pos + j] == path[start_pos + j] ? 1 : 0;
} else {
path[start_pos + j] = 0;
}
}
}
}
} else {
PADDLE_ENFORCE_EQ(emission_weights->NumLevels(),
1UL,
common::errors::InvalidArgument(
"The Input(Emission) should be a sequence with lod "
"level 1. But received: lod level %u.",
emission_weights->NumLevels()));
auto lod = emission_weights->lod();
PADDLE_ENFORCE_GT(
lod.size(),
0,
common::errors::InvalidArgument(
"Input(Emission) must be a sequence. But received: lod level %u.",
lod.size()));
const size_t level = 0;
const size_t seq_num = lod[level].size() - 1;
for (size_t i = 0; i < seq_num; ++i) {
if (lod[level][i] == lod[level][i + 1]) continue;
int64_t start_pos = static_cast<int64_t>(lod[level][i]);
int64_t end_pos = static_cast<int64_t>(lod[level][i + 1]);
DenseTensor decoded_path_one_seq =
decoded_path->Slice(start_pos, end_pos);
Decode<T, Context>(dev_ctx,
emission_weights->Slice(start_pos, end_pos),
*transition_weights,
&decoded_path_one_seq);
}
if (label) {
PADDLE_ENFORCE_EQ(label_p->NumLevels(),
1UL,
common::errors::InvalidArgument(
"The Input(label) should be a sequence with lod "
"level 1. But received: lod level %u.",
label_p->NumLevels()));
const int64_t* label_value = label_p->data<int64_t>();
size_t numel = label->numel();
for (size_t i = 0; i < numel; ++i) {
path[i] = label_value[i] == path[i] ? 1 : 0;
}
}
}
}
} // namespace phi
PD_REGISTER_KERNEL(
crf_decoding, CPU, ALL_LAYOUT, phi::CRFDecodingOpKernel, float, double) {
kernel->OutputAt(0).SetDataType(phi::DataType::INT64);
}