// 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. #pragma once #include "paddle/phi/core/dense_tensor.h" #include "paddle/phi/kernels/funcs/beam_search_decode.h" namespace phi { struct BeamSearchDecodeFunctor { BeamSearchDecodeFunctor(const TensorArray& step_ids, const TensorArray& step_scores, DenseTensor* id_tensor, DenseTensor* score_tensor, size_t beam_size, int end_id) : beam_size_(beam_size), end_id_(end_id), step_ids_origin_(step_ids), step_scores_origin_(step_scores), id_tensor_(id_tensor), score_tensor_(score_tensor) { tensor_on_gpu_ = false; // First make a copy of GPU data on CPU if (step_ids_origin_[0].place().GetType() == AllocationType::GPU || step_ids_origin_[0].place().GetType() == AllocationType::CUSTOM) { if (step_ids_origin_[0].place().GetType() == AllocationType::GPU || step_ids_origin_[0].place().GetType() == AllocationType::CUSTOM) { tensor_on_gpu_ = true; } DeviceContextPool& pool = DeviceContextPool::Instance(); auto* dev_ctx = pool.Get(step_ids_origin_[0].place()); // Copy all tensors in the input tensor array for (auto& step_id : step_ids_origin_) { DenseTensor out; if (step_id.numel() > 0) { if (tensor_on_gpu_) { dev_ctx->Wait(); } Copy(*dev_ctx, step_id, CPUPlace(), false, &out); dev_ctx->Wait(); } out.set_lod(step_id.lod()); step_ids_.push_back(out); } } if (step_scores_origin_[0].place().GetType() == AllocationType::GPU || step_scores_origin_[0].place().GetType() == AllocationType::CUSTOM) { if (step_scores_origin_[0].place().GetType() == AllocationType::GPU || step_scores_origin_[0].place().GetType() == AllocationType::CUSTOM) { tensor_on_gpu_ = true; } DeviceContextPool& pool = DeviceContextPool::Instance(); auto* dev_ctx = pool.Get(step_scores_origin_[0].place()); // Copy all tensors in the input tensor array for (auto& step_score : step_scores_origin_) { DenseTensor out; if (step_score.numel() > 0) { if (tensor_on_gpu_) { dev_ctx->Wait(); } Copy(*dev_ctx, step_score, CPUPlace(), false, &out); dev_ctx->Wait(); } out.set_lod(step_score.lod()); step_scores_.push_back(out); } } } template void apply_mix() const { if (std::is_same::value) { PADDLE_THROW(common::errors::InvalidArgument( "beam search decode op does not support bool!")); } else { funcs::BeamSearchDecoder beam_search_decoder(beam_size_, end_id_); // Check if the tensor is on GPU. If so, use the CPU copy instead if (tensor_on_gpu_) { beam_search_decoder.Backtrace( step_ids_, step_scores_, id_tensor_, score_tensor_); } else { beam_search_decoder.Backtrace( step_ids_origin_, step_scores_origin_, id_tensor_, score_tensor_); } } } bool tensor_on_gpu_; size_t beam_size_; int end_id_; // TODO(Superjomn) Here might result serious performance issue in the // concurrency // scenarios. const TensorArray& step_ids_origin_; const TensorArray& step_scores_origin_; TensorArray step_ids_ = TensorArray(); TensorArray step_scores_ = TensorArray(); DenseTensor* id_tensor_; DenseTensor* score_tensor_; }; template void BeamSearchDecodeOpKernel(const Context& dev_ctx, const TensorArray& ids_in, const TensorArray& scores_in, int beam_size, int end_id, DenseTensor* sentence_ids, DenseTensor* sentence_scores) { const TensorArray* ids = &ids_in; const TensorArray* scores = &scores_in; const size_t step_num = ids->size(); PADDLE_ENFORCE_GT( step_num, 0UL, common::errors::InvalidArgument( "beam search steps, which is the " "size of Input(Ids) TensorArray. beam search steps should " "be larger than 0, but received %d. ", step_num)); const size_t source_num = ids->at(0).lod().at(0).size() - 1; PADDLE_ENFORCE_GT( source_num, 0UL, common::errors::InvalidArgument( "source_num is the sequence number of the " "first decoding step, indicating by Input(Ids)[0].lod[0].size. " "The number of source_num should be larger than " "0, but received %d. ", source_num)); for (size_t i = 0; i < step_num; ++i) { size_t tmp = ids->at(i).lod().size(); PADDLE_ENFORCE_EQ( tmp, 2UL, common::errors::InvalidArgument( "For the i step in beam search steps," "the size of Input(Ids)[i].lod() should larger than 2," "but received %d. ", tmp)); } // prepare output DenseTensor* sentenceIds = sentence_ids; DenseTensor* sentenceScores = sentence_scores; BeamSearchDecodeFunctor bs( *ids, *scores, sentenceIds, sentenceScores, beam_size, end_id); bs.apply_mix(); } } // namespace phi