|
|
|
@ -12,25 +12,27 @@ 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/fluid/operators/beam_search_op.h"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "paddle/fluid/framework/lod_tensor.h"
|
|
|
|
|
#include "paddle/fluid/framework/op_registry.h"
|
|
|
|
|
#include "paddle/fluid/operators/beam_search_op.h"
|
|
|
|
|
|
|
|
|
|
namespace paddle {
|
|
|
|
|
namespace operators {
|
|
|
|
|
|
|
|
|
|
void BeamSearch::operator()(const framework::LoDTensor &pre_ids,
|
|
|
|
|
const framework::LoDTensor &pre_scores,
|
|
|
|
|
framework::LoDTensor *selected_ids,
|
|
|
|
|
framework::LoDTensor *selected_scores) {
|
|
|
|
|
auto abs_lod = framework::ToAbsOffset(ids_->lod());
|
|
|
|
|
auto &high_level = abs_lod[lod_level_];
|
|
|
|
|
|
|
|
|
|
auto items = SelectTopBeamSizeItems();
|
|
|
|
|
auto items = SelectTopBeamSizeItems(pre_ids, pre_scores);
|
|
|
|
|
auto selected_items = ToMap(items, high_level.back());
|
|
|
|
|
VLOG(3) << "selected_items:";
|
|
|
|
|
for (size_t i = 0; i < selected_items.size(); ++i) {
|
|
|
|
@ -39,7 +41,8 @@ void BeamSearch::operator()(const framework::LoDTensor &pre_ids,
|
|
|
|
|
VLOG(3) << ItemToString(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PruneEndidCandidates(pre_ids, &selected_items);
|
|
|
|
|
|
|
|
|
|
PruneEndBeams(pre_ids, &selected_items);
|
|
|
|
|
// calculate the output tensor's height
|
|
|
|
|
size_t num_instances = std::accumulate(
|
|
|
|
|
std::begin(selected_items), std::end(selected_items), 0,
|
|
|
|
@ -61,12 +64,6 @@ void BeamSearch::operator()(const framework::LoDTensor &pre_ids,
|
|
|
|
|
size_t low_offset = 0;
|
|
|
|
|
for (auto &items : selected_items) {
|
|
|
|
|
low_level.push_back(low_offset);
|
|
|
|
|
sort(items.begin(), items.end(), [](const Item &a, const Item &b) {
|
|
|
|
|
if (a.offset < b.offset) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return a.id < b.id;
|
|
|
|
|
});
|
|
|
|
|
for (auto &item : items) {
|
|
|
|
|
ids_data[low_offset] = item.id;
|
|
|
|
|
scores_data[low_offset] = item.score;
|
|
|
|
@ -86,6 +83,33 @@ void BeamSearch::operator()(const framework::LoDTensor &pre_ids,
|
|
|
|
|
selected_scores->set_lod(lod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BeamSearch::PruneEndBeams(const framework::LoDTensor &pre_ids,
|
|
|
|
|
std::vector<std::vector<Item>> *items) {
|
|
|
|
|
auto *pre_ids_data = pre_ids.data<int64_t>();
|
|
|
|
|
auto abs_lod = framework::ToAbsOffset(ids_->lod());
|
|
|
|
|
auto &high_level = abs_lod[lod_level_];
|
|
|
|
|
for (size_t src_idx = 0; src_idx < high_level.size(); ++src_idx) {
|
|
|
|
|
size_t src_prefix_start = high_level[src_idx];
|
|
|
|
|
size_t src_prefix_end = high_level[src_idx + 1];
|
|
|
|
|
bool finish_flag = true;
|
|
|
|
|
for (size_t offset = src_prefix_start; offset < src_prefix_end; offset++) {
|
|
|
|
|
for (auto &item : items->at(offset)) {
|
|
|
|
|
if (item.id != static_cast<size_t>(end_id_) ||
|
|
|
|
|
pre_ids_data[offset] != end_id_) {
|
|
|
|
|
finish_flag = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!finish_flag) break;
|
|
|
|
|
}
|
|
|
|
|
if (finish_flag) { // all branchs of the beam (source sentence) end and
|
|
|
|
|
// prune this beam
|
|
|
|
|
for (size_t offset = src_prefix_start; offset < src_prefix_end; offset++)
|
|
|
|
|
items->at(offset).clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BeamSearch::PruneEndidCandidates(const framework::LoDTensor &pre_ids,
|
|
|
|
|
std::vector<std::vector<Item>> *items) {
|
|
|
|
|
auto *pre_ids_data = pre_ids.data<int64_t>();
|
|
|
|
@ -115,13 +139,14 @@ std::vector<std::vector<BeamSearch::Item>> BeamSearch::ToMap(
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::vector<BeamSearch::Item>>
|
|
|
|
|
BeamSearch::SelectTopBeamSizeItems() {
|
|
|
|
|
std::vector<std::vector<BeamSearch::Item>> BeamSearch::SelectTopBeamSizeItems(
|
|
|
|
|
const framework::LoDTensor &pre_ids,
|
|
|
|
|
const framework::LoDTensor &pre_scores) {
|
|
|
|
|
std::vector<std::vector<Item>> result;
|
|
|
|
|
std::vector<Item> items;
|
|
|
|
|
// for each source sentence, select the top beam_size items across all
|
|
|
|
|
// candidate sets.
|
|
|
|
|
while (NextItemSet(&items)) {
|
|
|
|
|
while (NextItemSet(pre_ids, pre_scores, &items)) {
|
|
|
|
|
std::nth_element(std::begin(items), std::begin(items) + beam_size_,
|
|
|
|
|
std::end(items), [](const Item &a, const Item &b) {
|
|
|
|
|
// TODO(superjom) make score's comparation customizable.
|
|
|
|
@ -146,7 +171,9 @@ BeamSearch::SelectTopBeamSizeItems() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// the candidates of a source
|
|
|
|
|
bool BeamSearch::NextItemSet(std::vector<BeamSearch::Item> *items) {
|
|
|
|
|
bool BeamSearch::NextItemSet(const framework::LoDTensor &pre_ids,
|
|
|
|
|
const framework::LoDTensor &pre_scores,
|
|
|
|
|
std::vector<BeamSearch::Item> *items) {
|
|
|
|
|
if (sent_offset_ >= ids_->NumElements(lod_level_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -164,14 +191,25 @@ bool BeamSearch::NextItemSet(std::vector<BeamSearch::Item> *items) {
|
|
|
|
|
instance_dim *= ids.dims()[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto *pre_ids_data = pre_ids.data<int64_t>();
|
|
|
|
|
auto *pre_scores_data = pre_scores.data<float>();
|
|
|
|
|
items->clear();
|
|
|
|
|
items->reserve(framework::product(ids.dims()));
|
|
|
|
|
for (size_t offset = abs_lod[lod_level_][sent_offset_];
|
|
|
|
|
offset < abs_lod[lod_level_][sent_offset_ + 1]; offset++) {
|
|
|
|
|
for (size_t d = 0; d < instance_dim; d++) {
|
|
|
|
|
const size_t dim_offset = offset * instance_dim + d;
|
|
|
|
|
items->emplace_back(offset, ids_data[dim_offset],
|
|
|
|
|
scores_data[dim_offset]);
|
|
|
|
|
auto pre_id = pre_ids_data[offset];
|
|
|
|
|
auto pre_score = pre_scores_data[offset];
|
|
|
|
|
if (pre_id == end_id_) {
|
|
|
|
|
// Allocate all probability mass to eos_id for finished branchs and the
|
|
|
|
|
// other
|
|
|
|
|
// candidate ids can be ignored.
|
|
|
|
|
items->emplace_back(offset, end_id_, pre_score);
|
|
|
|
|
} else {
|
|
|
|
|
for (size_t d = 0; d < instance_dim; d++) {
|
|
|
|
|
const size_t dim_offset = offset * instance_dim + d;
|
|
|
|
|
items->emplace_back(offset, ids_data[dim_offset],
|
|
|
|
|
scores_data[dim_offset]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -199,7 +237,8 @@ class BeamSearchOpMaker : public framework::OpProtoAndCheckerMaker {
|
|
|
|
|
public:
|
|
|
|
|
void Make() override {
|
|
|
|
|
// inputs and outputs stored in proto
|
|
|
|
|
AddInput("pre_ids", "ids in previous step");
|
|
|
|
|
AddInput("pre_ids", "ids in the previous step");
|
|
|
|
|
AddInput("pre_scores", "accumulated scores in the previous step");
|
|
|
|
|
AddInput("ids", "a LoDTensor of shape of [None,k]");
|
|
|
|
|
AddInput("scores",
|
|
|
|
|
"a LoDTensor that has the same shape and LoD with `ids`");
|
|
|
|
@ -253,10 +292,12 @@ class BeamSearchInferVarType : public framework::VarTypeInference {
|
|
|
|
|
void operator()(const framework::OpDesc &op_desc,
|
|
|
|
|
framework::BlockDesc *block) const override {
|
|
|
|
|
for (auto &o : op_desc.Output("selected_ids")) {
|
|
|
|
|
block->Var(o)->SetType(framework::proto::VarType::LOD_TENSOR);
|
|
|
|
|
auto &selected_ids = block->FindRecursiveOrCreateVar(o);
|
|
|
|
|
selected_ids.SetType(framework::proto::VarType::LOD_TENSOR);
|
|
|
|
|
}
|
|
|
|
|
for (auto &o : op_desc.Output("selected_scores")) {
|
|
|
|
|
block->Var(o)->SetType(framework::proto::VarType::LOD_TENSOR);
|
|
|
|
|
auto &selected_scores = block->FindRecursiveOrCreateVar(o);
|
|
|
|
|
selected_scores.SetType(framework::proto::VarType::LOD_TENSOR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|